Generalize isds_FindDataBox()
[libisds.git] / src / isds.c
blob17679c6f475cb29144f5beeedb83384d2dcd7386
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include <inttypes.h> /* For PRIdMAX formatting macro */
10 #include "utils.h"
11 #if HAVE_LIBCURL
12 #include "soap.h"
13 #endif
14 #include "validator.h"
15 #include "crypto.h"
16 #include "physxml.h"
17 #include "system.h"
19 /* Global variables.
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities;
22 isds_log_level log_level;
23 isds_log_callback log_callback;
24 void *log_callback_data;
25 const char *version_gpgme = N_("n/a");
26 const char *version_gcrypt = N_("n/a");
27 const char *version_openssl = N_("n/a");
28 const char *version_expat = N_("n/a");
30 /* Locators */
31 /* Base URL of production ISDS instance */
32 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
33 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
34 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
36 /* Base URL of production ISDS instance */
37 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
38 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
39 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
41 /* Extension to MIME type map */
42 static const xmlChar *extension_map_mime[] = {
43 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
45 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
46 BAD_CAST "doc", BAD_CAST "application/msword",
47 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
48 "wordprocessingml.document",
49 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
50 BAD_CAST "prj", BAD_CAST "application/octet-stream",
51 BAD_CAST "qix", BAD_CAST "application/octet-stream",
52 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
53 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
54 BAD_CAST "shp", BAD_CAST "application/octet-stream",
55 BAD_CAST "shx", BAD_CAST "application/octet-stream",
56 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
57 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
58 BAD_CAST "edi", BAD_CAST "application/edifact",
59 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
60 BAD_CAST "gfs", BAD_CAST "application/xml",
61 BAD_CAST "gml", BAD_CAST "application/xml",
62 BAD_CAST "gif", BAD_CAST "image/gif",
63 BAD_CAST "htm", BAD_CAST "text/html",
64 BAD_CAST "html", BAD_CAST "text/html",
65 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
66 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
67 BAD_CAST "jfif", BAD_CAST "image/jpeg",
68 BAD_CAST "jpg", BAD_CAST "image/jpeg",
69 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
70 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
71 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
72 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
73 BAD_CAST "mpg", BAD_CAST "video/mpeg",
74 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
75 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
76 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
77 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
78 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
79 BAD_CAST "pdf", BAD_CAST "application/pdf",
80 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
81 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
82 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
83 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
84 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
85 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
86 BAD_CAST "png", BAD_CAST "image/png",
87 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
88 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
89 "presentationml.presentation",
90 BAD_CAST "rtf", BAD_CAST "application/rtf",
91 BAD_CAST "tif", BAD_CAST "image/tiff",
92 BAD_CAST "tiff", BAD_CAST "image/tiff",
93 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
94 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
95 BAD_CAST "txt", BAD_CAST "text/plain",
96 BAD_CAST "wav", BAD_CAST "audio/wav",
97 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
98 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
99 "spreadsheetml.sheet",
100 BAD_CAST "xml", BAD_CAST "application/xml",
101 BAD_CAST "xsd", BAD_CAST "application/xml",
102 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
105 /* Structure type to hold conversion table from status code to isds_error and
106 * long message */
107 struct code_map_isds_error {
108 const xmlChar **codes; /* NULL terminated array of status codes */
109 const char **meanings; /* Mapping to non-localized long messages */
110 const isds_error *errors; /* Mapping to isds_error code */
113 /* Deallocate structure isds_pki_credentials and NULL it.
114 * Pass-phrase is discarded.
115 * @pki credentials to to free */
116 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
117 if(!pki || !*pki) return;
119 free((*pki)->engine);
120 free((*pki)->certificate);
121 free((*pki)->key);
123 if ((*pki)->passphrase) {
124 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
125 free((*pki)->passphrase);
128 zfree((*pki));
132 /* Free isds_list with all member data.
133 * @list list to free, on return will be NULL */
134 void isds_list_free(struct isds_list **list) {
135 struct isds_list *item, *next_item;
137 if (!list || !*list) return;
139 for(item = *list; item; item = next_item) {
140 if (item->destructor) (item->destructor)(&(item->data));
141 next_item = item->next;
142 free(item);
145 *list = NULL;
149 /* Deallocate structure isds_hash and NULL it.
150 * @hash hash to to free */
151 void isds_hash_free(struct isds_hash **hash) {
152 if(!hash || !*hash) return;
153 free((*hash)->value);
154 zfree((*hash));
158 /* Deallocate structure isds_PersonName recursively and NULL it */
159 void isds_PersonName_free(struct isds_PersonName **person_name) {
160 if (!person_name || !*person_name) return;
162 free((*person_name)->pnFirstName);
163 free((*person_name)->pnMiddleName);
164 free((*person_name)->pnLastName);
165 free((*person_name)->pnLastNameAtBirth);
167 free(*person_name);
168 *person_name = NULL;
172 /* Deallocate structure isds_BirthInfo recursively and NULL it */
173 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
174 if (!birth_info || !*birth_info) return;
176 free((*birth_info)->biDate);
177 free((*birth_info)->biCity);
178 free((*birth_info)->biCounty);
179 free((*birth_info)->biState);
181 free(*birth_info);
182 *birth_info = NULL;
186 /* Deallocate structure isds_Address recursively and NULL it */
187 void isds_Address_free(struct isds_Address **address) {
188 if (!address || !*address) return;
190 free((*address)->adCode);
191 free((*address)->adCity);
192 free((*address)->adDistrict);
193 free((*address)->adStreet);
194 free((*address)->adNumberInStreet);
195 free((*address)->adNumberInMunicipality);
196 free((*address)->adZipCode);
197 free((*address)->adState);
199 free(*address);
200 *address = NULL;
204 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
205 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
206 if (!db_owner_info || !*db_owner_info) return;
208 free((*db_owner_info)->dbID);
209 free((*db_owner_info)->dbType);
210 free((*db_owner_info)->ic);
211 isds_PersonName_free(&((*db_owner_info)->personName));
212 free((*db_owner_info)->firmName);
213 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
214 isds_Address_free(&((*db_owner_info)->address));
215 free((*db_owner_info)->nationality);
216 free((*db_owner_info)->email);
217 free((*db_owner_info)->telNumber);
218 free((*db_owner_info)->identifier);
219 free((*db_owner_info)->aifoIsds);
220 free((*db_owner_info)->registryCode);
221 free((*db_owner_info)->dbState);
222 free((*db_owner_info)->dbEffectiveOVM);
223 free((*db_owner_info)->dbOpenAddressing);
225 free(*db_owner_info);
226 *db_owner_info = NULL;
229 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
230 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
231 if (!db_user_info || !*db_user_info) return;
233 free((*db_user_info)->userID);
234 free((*db_user_info)->userType);
235 free((*db_user_info)->userPrivils);
236 isds_PersonName_free(&((*db_user_info)->personName));
237 isds_Address_free(&((*db_user_info)->address));
238 free((*db_user_info)->biDate);
239 free((*db_user_info)->ic);
240 free((*db_user_info)->firmName);
241 free((*db_user_info)->caStreet);
242 free((*db_user_info)->caCity);
243 free((*db_user_info)->caZipCode);
244 free((*db_user_info)->caState);
245 free((*db_user_info)->aifo_ticket);
247 zfree(*db_user_info);
251 /* Deallocate struct isds_event recursively and NULL it */
252 void isds_event_free(struct isds_event **event) {
253 if (!event || !*event) return;
255 free((*event)->time);
256 free((*event)->type);
257 free((*event)->description);
258 zfree(*event);
262 /* Deallocate struct isds_envelope recursively and NULL it */
263 void isds_envelope_free(struct isds_envelope **envelope) {
264 if (!envelope || !*envelope) return;
266 free((*envelope)->dmID);
267 free((*envelope)->dbIDSender);
268 free((*envelope)->dmSender);
269 free((*envelope)->dmSenderAddress);
270 free((*envelope)->dmSenderType);
271 free((*envelope)->dmRecipient);
272 free((*envelope)->dmRecipientAddress);
273 free((*envelope)->dmAmbiguousRecipient);
274 free((*envelope)->dmType);
276 free((*envelope)->dmOrdinal);
277 free((*envelope)->dmMessageStatus);
278 free((*envelope)->dmDeliveryTime);
279 free((*envelope)->dmAcceptanceTime);
280 isds_hash_free(&(*envelope)->hash);
281 free((*envelope)->timestamp);
282 isds_list_free(&(*envelope)->events);
284 free((*envelope)->dmSenderOrgUnit);
285 free((*envelope)->dmSenderOrgUnitNum);
286 free((*envelope)->dbIDRecipient);
287 free((*envelope)->dmRecipientOrgUnit);
288 free((*envelope)->dmRecipientOrgUnitNum);
289 free((*envelope)->dmToHands);
290 free((*envelope)->dmAnnotation);
291 free((*envelope)->dmRecipientRefNumber);
292 free((*envelope)->dmSenderRefNumber);
293 free((*envelope)->dmRecipientIdent);
294 free((*envelope)->dmSenderIdent);
296 free((*envelope)->dmLegalTitleLaw);
297 free((*envelope)->dmLegalTitleYear);
298 free((*envelope)->dmLegalTitleSect);
299 free((*envelope)->dmLegalTitlePar);
300 free((*envelope)->dmLegalTitlePoint);
302 free((*envelope)->dmPersonalDelivery);
303 free((*envelope)->dmAllowSubstDelivery);
305 free((*envelope)->dmOVM);
306 free((*envelope)->dmPublishOwnID);
308 free(*envelope);
309 *envelope = NULL;
313 /* Deallocate struct isds_message recursively and NULL it */
314 void isds_message_free(struct isds_message **message) {
315 if (!message || !*message) return;
317 free((*message)->raw);
318 isds_envelope_free(&((*message)->envelope));
319 isds_list_free(&((*message)->documents));
320 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
322 free(*message);
323 *message = NULL;
327 /* Deallocate struct isds_document recursively and NULL it */
328 void isds_document_free(struct isds_document **document) {
329 if (!document || !*document) return;
331 if (!(*document)->is_xml) {
332 free((*document)->data);
334 free((*document)->dmMimeType);
335 free((*document)->dmFileGuid);
336 free((*document)->dmUpFileGuid);
337 free((*document)->dmFileDescr);
338 free((*document)->dmFormat);
340 free(*document);
341 *document = NULL;
345 /* Deallocate struct isds_message_copy recursively and NULL it */
346 void isds_message_copy_free(struct isds_message_copy **copy) {
347 if (!copy || !*copy) return;
349 free((*copy)->dbIDRecipient);
350 free((*copy)->dmRecipientOrgUnit);
351 free((*copy)->dmRecipientOrgUnitNum);
352 free((*copy)->dmToHands);
354 free((*copy)->dmStatus);
355 free((*copy)->dmID);
357 zfree(*copy);
361 /* Deallocate struct isds_message_status_change recursively and NULL it */
362 void isds_message_status_change_free(
363 struct isds_message_status_change **message_status_change) {
364 if (!message_status_change || !*message_status_change) return;
366 free((*message_status_change)->dmID);
367 free((*message_status_change)->time);
368 free((*message_status_change)->dmMessageStatus);
370 zfree(*message_status_change);
374 /* Deallocate struct isds_approval recursively and NULL it */
375 void isds_approval_free(struct isds_approval **approval) {
376 if (!approval || !*approval) return;
378 free((*approval)->refference);
380 zfree(*approval);
384 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
385 * The email string is deallocated too. */
386 void isds_credentials_delivery_free(
387 struct isds_credentials_delivery **credentials_delivery) {
388 if (!credentials_delivery || !*credentials_delivery) return;
390 free((*credentials_delivery)->email);
391 free((*credentials_delivery)->token);
392 free((*credentials_delivery)->new_user_name);
394 zfree(*credentials_delivery);
398 /* Deallocate struct isds_commercial_permission recursively and NULL it */
399 void isds_commercial_permission_free(
400 struct isds_commercial_permission **permission) {
401 if (NULL == permission || NULL == *permission) return;
403 free((*permission)->recipient);
404 free((*permission)->payer);
405 free((*permission)->expiration);
406 free((*permission)->count);
407 free((*permission)->reply_identifier);
409 zfree(*permission);
413 /* Deallocate struct isds_credit_event recursively and NULL it */
414 void isds_credit_event_free(struct isds_credit_event **event) {
415 if (NULL == event || NULL == *event) return;
417 free((*event)->time);
418 switch ((*event)->type) {
419 case ISDS_CREDIT_CHARGED:
420 free((*event)->details.charged.transaction);
421 break;
422 case ISDS_CREDIT_DISCHARGED:
423 free((*event)->details.discharged.transaction);
424 break;
425 case ISDS_CREDIT_MESSAGE_SENT:
426 free((*event)->details.message_sent.recipient);
427 free((*event)->details.message_sent.message_id);
428 break;
429 case ISDS_CREDIT_STORAGE_SET:
430 free((*event)->details.storage_set.new_valid_from);
431 free((*event)->details.storage_set.new_valid_to);
432 free((*event)->details.storage_set.old_capacity);
433 free((*event)->details.storage_set.old_valid_from);
434 free((*event)->details.storage_set.old_valid_to);
435 free((*event)->details.storage_set.initiator);
436 break;
437 case ISDS_CREDIT_EXPIRED:
438 break;
441 zfree(*event);
445 /* Deallocate struct isds_fulltext_result recursively and NULL it */
446 void isds_fulltext_result_free(
447 struct isds_fulltext_result **result) {
448 if (NULL == result || NULL == *result) return;
450 free((*result)->dbID);
451 free((*result)->name);
452 isds_list_free(&((*result)->name_match_start));
453 isds_list_free(&((*result)->name_match_end));
454 free((*result)->address);
455 isds_list_free(&((*result)->address_match_start));
456 isds_list_free(&((*result)->address_match_end));
457 free((*result)->ic);
458 free((*result)->biDate);
460 zfree(*result);
464 /* *DUP_OR_ERROR macros needs error label */
465 #define STRDUP_OR_ERROR(new, template) { \
466 if (!template) { \
467 (new) = NULL; \
468 } else { \
469 (new) = strdup(template); \
470 if (!new) goto error; \
474 #define FLATDUP_OR_ERROR(new, template) { \
475 if (!template) { \
476 (new) = NULL; \
477 } else { \
478 (new) = malloc(sizeof(*(new))); \
479 if (!new) goto error; \
480 memcpy((new), (template), sizeof(*(template))); \
484 /* Copy structure isds_pki_credentials recursively. */
485 struct isds_pki_credentials *isds_pki_credentials_duplicate(
486 const struct isds_pki_credentials *template) {
487 struct isds_pki_credentials *new = NULL;
489 if(!template) return NULL;
491 new = calloc(1, sizeof(*new));
492 if (!new) return NULL;
494 STRDUP_OR_ERROR(new->engine, template->engine);
495 new->certificate_format = template->certificate_format;
496 STRDUP_OR_ERROR(new->certificate, template->certificate);
497 new->key_format = template->key_format;
498 STRDUP_OR_ERROR(new->key, template->key);
499 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
501 return new;
503 error:
504 isds_pki_credentials_free(&new);
505 return NULL;
509 /* Copy structure isds_PersonName recursively */
510 struct isds_PersonName *isds_PersonName_duplicate(
511 const struct isds_PersonName *src) {
512 struct isds_PersonName *new = NULL;
514 if (!src) return NULL;
516 new = calloc(1, sizeof(*new));
517 if (!new) return NULL;
519 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
520 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
521 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
522 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
524 return new;
526 error:
527 isds_PersonName_free(&new);
528 return NULL;
532 /* Copy structure isds_BirthInfo recursively */
533 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
534 const struct isds_BirthInfo *template) {
535 struct isds_BirthInfo *new = NULL;
537 if (!template) return NULL;
539 new = calloc(1, sizeof(*new));
540 if (!new) return NULL;
542 FLATDUP_OR_ERROR(new->biDate, template->biDate);
543 STRDUP_OR_ERROR(new->biCity, template->biCity);
544 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
545 STRDUP_OR_ERROR(new->biState, template->biState);
547 return new;
549 error:
550 isds_BirthInfo_free(&new);
551 return NULL;
555 /* Copy structure isds_Address recursively */
556 struct isds_Address *isds_Address_duplicate(
557 const struct isds_Address *src) {
558 struct isds_Address *new = NULL;
560 if (!src) return NULL;
562 new = calloc(1, sizeof(*new));
563 if (!new) return NULL;
565 FLATDUP_OR_ERROR(new->adCode, src->adCode);
566 STRDUP_OR_ERROR(new->adCity, src->adCity);
567 STRDUP_OR_ERROR(new->adDistrict, src->adDistrict);
568 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
569 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
570 STRDUP_OR_ERROR(new->adNumberInMunicipality,
571 src->adNumberInMunicipality);
572 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
573 STRDUP_OR_ERROR(new->adState, src->adState);
575 return new;
577 error:
578 isds_Address_free(&new);
579 return NULL;
583 /* Copy structure isds_DbOwnerInfo recursively */
584 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
585 const struct isds_DbOwnerInfo *src) {
586 struct isds_DbOwnerInfo *new = NULL;
587 if (!src) return NULL;
589 new = calloc(1, sizeof(*new));
590 if (!new) return NULL;
592 STRDUP_OR_ERROR(new->dbID, src->dbID);
593 FLATDUP_OR_ERROR(new->dbType, src->dbType);
594 STRDUP_OR_ERROR(new->ic, src->ic);
596 if (src->personName) {
597 if (!(new->personName =
598 isds_PersonName_duplicate(src->personName)))
599 goto error;
602 STRDUP_OR_ERROR(new->firmName, src->firmName);
604 if (src->birthInfo) {
605 if (!(new->birthInfo =
606 isds_BirthInfo_duplicate(src->birthInfo)))
607 goto error;
610 if (src->address) {
611 if (!(new->address = isds_Address_duplicate(src->address)))
612 goto error;
615 STRDUP_OR_ERROR(new->nationality, src->nationality);
616 STRDUP_OR_ERROR(new->email, src->email);
617 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
618 STRDUP_OR_ERROR(new->identifier, src->identifier);
619 FLATDUP_OR_ERROR(new->aifoIsds, src->aifoIsds);
620 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
621 FLATDUP_OR_ERROR(new->dbState, src->dbState);
622 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
623 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
625 return new;
627 error:
628 isds_DbOwnerInfo_free(&new);
629 return NULL;
633 /* Copy structure isds_DbUserInfo recursively */
634 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
635 const struct isds_DbUserInfo *src) {
636 struct isds_DbUserInfo *new = NULL;
637 if (!src) return NULL;
639 new = calloc(1, sizeof(*new));
640 if (!new) return NULL;
642 STRDUP_OR_ERROR(new->userID, src->userID);
643 FLATDUP_OR_ERROR(new->userType, src->userType);
644 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
646 if (src->personName) {
647 if (!(new->personName =
648 isds_PersonName_duplicate(src->personName)))
649 goto error;
652 if (src->address) {
653 if (!(new->address = isds_Address_duplicate(src->address)))
654 goto error;
657 FLATDUP_OR_ERROR(new->biDate, src->biDate);
658 STRDUP_OR_ERROR(new->ic, src->ic);
659 STRDUP_OR_ERROR(new->firmName, src->firmName);
660 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
661 STRDUP_OR_ERROR(new->caCity, src->caCity);
662 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
663 STRDUP_OR_ERROR(new->caState, src->caState);
664 STRDUP_OR_ERROR(new->aifo_ticket, src->aifo_ticket);
666 return new;
668 error:
669 isds_DbUserInfo_free(&new);
670 return NULL;
673 #undef FLATDUP_OR_ERROR
674 #undef STRDUP_OR_ERROR
677 /* Logs libxml2 errors. Should be registered to libxml2 library.
678 * @ctx is unused currently
679 * @msg is printf-like formated message from libxml2 (UTF-8?)
680 * @... are variadic arguments for @msg */
681 static void log_xml(void *ctx, const char *msg, ...) {
682 va_list ap;
683 char *text = NULL;
685 /* Silent warning for unused function argument.
686 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
687 (void)ctx;
689 if (!msg) return;
691 va_start(ap, msg);
692 isds_vasprintf(&text, msg, ap);
693 va_end(ap);
695 if (text)
696 isds_log(ILF_XML, ILL_ERR, "%s", text);
697 free(text);
701 /* Initialize ISDS library.
702 * Global function, must be called before other functions.
703 * If it fails you can not use ISDS library and must call isds_cleanup() to
704 * free partially initialized global variables. */
705 isds_error isds_init(void) {
706 /* NULL global variables */
707 log_facilities = ILF_ALL;
708 log_level = ILL_WARNING;
709 log_callback = NULL;
710 log_callback_data = NULL;
712 #if ENABLE_NLS
713 /* Initialize gettext */
714 bindtextdomain(PACKAGE, LOCALEDIR);
715 #endif
717 #if HAVE_LIBCURL
718 /* Initialize CURL */
719 if (curl_global_init(CURL_GLOBAL_ALL)) {
720 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
721 return IE_ERROR;
723 #endif /* HAVE_LIBCURL */
725 /* Initialise cryptographic back-ends. */
726 if (IE_SUCCESS != _isds_init_crypto()) {
727 isds_log(ILF_ISDS, ILL_CRIT,
728 _("Initialization of cryptographic back-end failed\n"));
729 return IE_ERROR;
732 /* This can _exit() current program. Find not so assertive check. */
733 LIBXML_TEST_VERSION;
734 xmlSetGenericErrorFunc(NULL, log_xml);
736 /* Check expat */
737 if (_isds_init_expat(&version_expat)) {
738 isds_log(ILF_ISDS, ILL_CRIT,
739 _("expat library initialization failed\n"));
740 return IE_ERROR;
743 /* Allocate global variables */
746 return IE_SUCCESS;
750 /* Deinitialize ISDS library.
751 * Global function, must be called as last library function. */
752 isds_error isds_cleanup(void) {
753 /* XML */
754 xmlCleanupParser();
756 #if HAVE_LIBCURL
757 /* Curl */
758 curl_global_cleanup();
759 #endif
761 return IE_SUCCESS;
765 /* Return version string of this library. Version of dependencies can be
766 * embedded. Do no try to parse it. You must free it. */
767 char *isds_version(void) {
768 char *buffer = NULL;
770 isds_asprintf(&buffer,
771 #if HAVE_LIBCURL
772 # ifndef USE_OPENSSL_BACKEND
773 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
774 # else
775 _("%s (%s, %s, %s, libxml2 %s)"),
776 # endif
777 #else
778 # ifndef USE_OPENSSL_BACKEND
779 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
780 # else
781 _("%s (%s, %s, libxml2 %s)"),
782 # endif
783 #endif
784 PACKAGE_VERSION,
785 #if HAVE_LIBCURL
786 curl_version(),
787 #endif
788 #ifndef USE_OPENSSL_BACKEND
789 version_gpgme, version_gcrypt,
790 #else
791 version_openssl,
792 #endif
793 version_expat, xmlParserVersion);
794 return buffer;
798 /* Return text description of ISDS error */
799 const char *isds_strerror(const isds_error error) {
800 switch (error) {
801 case IE_SUCCESS:
802 return(_("Success")); break;
803 case IE_ERROR:
804 return(_("Unspecified error")); break;
805 case IE_NOTSUP:
806 return(_("Not supported")); break;
807 case IE_INVAL:
808 return(_("Invalid value")); break;
809 case IE_INVALID_CONTEXT:
810 return(_("Invalid context")); break;
811 case IE_NOT_LOGGED_IN:
812 return(_("Not logged in")); break;
813 case IE_CONNECTION_CLOSED:
814 return(_("Connection closed")); break;
815 case IE_TIMED_OUT:
816 return(_("Timed out")); break;
817 case IE_NOEXIST:
818 return(_("Not exist")); break;
819 case IE_NOMEM:
820 return(_("Out of memory")); break;
821 case IE_NETWORK:
822 return(_("Network problem")); break;
823 case IE_HTTP:
824 return(_("HTTP problem")); break;
825 case IE_SOAP:
826 return(_("SOAP problem")); break;
827 case IE_XML:
828 return(_("XML problem")); break;
829 case IE_ISDS:
830 return(_("ISDS server problem")); break;
831 case IE_ENUM:
832 return(_("Invalid enum value")); break;
833 case IE_DATE:
834 return(_("Invalid date value")); break;
835 case IE_2BIG:
836 return(_("Too big")); break;
837 case IE_2SMALL:
838 return(_("Too small")); break;
839 case IE_NOTUNIQ:
840 return(_("Value not unique")); break;
841 case IE_NOTEQUAL:
842 return(_("Values not equal")); break;
843 case IE_PARTIAL_SUCCESS:
844 return(_("Some suboperations failed")); break;
845 case IE_ABORTED:
846 return(_("Operation aborted")); break;
847 case IE_SECURITY:
848 return(_("Security problem")); break;
849 default:
850 return(_("Unknown error"));
855 /* Create ISDS context.
856 * Each context can be used for different sessions to (possibly) different
857 * ISDS server with different credentials. */
858 struct isds_ctx *isds_ctx_create(void) {
859 struct isds_ctx *context;
860 context = malloc(sizeof(*context));
861 if (context) memset(context, 0, sizeof(*context));
862 return context;
865 #if HAVE_LIBCURL
866 /* Close possibly opened connection to Czech POINT document deposit without
867 * resetting long_message buffer.
868 * XXX: Do not use czp_close_connection() if you do not want to destroy log
869 * message.
870 * @context is Czech POINT session context. */
871 static isds_error czp_do_close_connection(struct isds_ctx *context) {
872 if (!context) return IE_INVALID_CONTEXT;
873 _isds_close_connection(context);
874 return IE_SUCCESS;
878 /* Discard credentials.
879 * @context is ISDS context
880 * @discard_saved_username is true for removing saved username, false for
881 * keeping it.
882 * Only that. It does not cause log out, connection close or similar. */
883 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
884 _Bool discard_saved_username) {
885 if(!context) return IE_INVALID_CONTEXT;
887 if (context->username) {
888 memset(context->username, 0, strlen(context->username));
889 zfree(context->username);
891 if (context->password) {
892 memset(context->password, 0, strlen(context->password));
893 zfree(context->password);
895 isds_pki_credentials_free(&context->pki_credentials);
896 if (discard_saved_username && context->saved_username) {
897 memset(context->saved_username, 0, strlen(context->saved_username));
898 zfree(context->saved_username);
901 return IE_SUCCESS;
903 #endif /* HAVE_LIBCURL */
906 /* Destroy ISDS context and free memory.
907 * @context will be NULLed on success. */
908 isds_error isds_ctx_free(struct isds_ctx **context) {
909 if (!context || !*context) {
910 return IE_INVALID_CONTEXT;
913 #if HAVE_LIBCURL
914 /* Discard credentials and close connection */
915 switch ((*context)->type) {
916 case CTX_TYPE_NONE: break;
917 case CTX_TYPE_ISDS: isds_logout(*context); break;
918 case CTX_TYPE_CZP:
919 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
920 czp_do_close_connection(*context); break;
923 /* For sure */
924 _isds_discard_credentials(*context, 1);
926 /* Free other structures */
927 free((*context)->url);
928 free((*context)->tls_verify_server);
929 free((*context)->tls_ca_file);
930 free((*context)->tls_ca_dir);
931 free((*context)->tls_crl_file);
932 #endif /* HAVE_LIBCURL */
933 free((*context)->long_message);
935 free(*context);
936 *context = NULL;
937 return IE_SUCCESS;
941 /* Return long message text produced by library function, e.g. detailed error
942 * message. Returned pointer is only valid until new library function is
943 * called for the same context. Could be NULL, especially if NULL context is
944 * supplied. Return string is locale encoded. */
945 char *isds_long_message(const struct isds_ctx *context) {
946 if (!context) return NULL;
947 return context->long_message;
951 /* Stores message into context' long_message buffer.
952 * Application can pick the message up using isds_long_message().
953 * NULL @message truncates the buffer but does not deallocate it.
954 * @message is coded in locale encoding */
955 _hidden isds_error isds_log_message(struct isds_ctx *context,
956 const char *message) {
957 char *buffer;
958 size_t length;
960 if (!context) return IE_INVALID_CONTEXT;
962 /* FIXME: Check for integer overflow */
963 length = 1 + ((message) ? strlen(message) : 0);
964 buffer = realloc(context->long_message, length);
965 if (!buffer) return IE_NOMEM;
967 if (message)
968 strcpy(buffer, message);
969 else
970 *buffer = '\0';
972 context->long_message = buffer;
973 return IE_SUCCESS;
977 /* Appends message into context' long_message buffer.
978 * Application can pick the message up using isds_long_message().
979 * NULL message has void effect. */
980 _hidden isds_error isds_append_message(struct isds_ctx *context,
981 const char *message) {
982 char *buffer;
983 size_t old_length, length;
985 if (!context) return IE_INVALID_CONTEXT;
986 if (!message) return IE_SUCCESS;
987 if (!context->long_message)
988 return isds_log_message(context, message);
990 old_length = strlen(context->long_message);
991 /* FIXME: Check for integer overflow */
992 length = 1 + old_length + strlen(message);
993 buffer = realloc(context->long_message, length);
994 if (!buffer) return IE_NOMEM;
996 strcpy(buffer + old_length, message);
998 context->long_message = buffer;
999 return IE_SUCCESS;
1003 /* Stores formatted message into context' long_message buffer.
1004 * Application can pick the message up using isds_long_message(). */
1005 _hidden isds_error isds_printf_message(struct isds_ctx *context,
1006 const char *format, ...) {
1007 va_list ap;
1008 int length;
1010 if (!context) return IE_INVALID_CONTEXT;
1011 va_start(ap, format);
1012 length = isds_vasprintf(&(context->long_message), format, ap);
1013 va_end(ap);
1015 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1019 /* Set logging up.
1020 * @facilities is bit mask of isds_log_facility values,
1021 * @level is verbosity level. */
1022 void isds_set_logging(const unsigned int facilities,
1023 const isds_log_level level) {
1024 log_facilities = facilities;
1025 log_level = level;
1029 /* Register callback function libisds calls when new global log message is
1030 * produced by library. Library logs to stderr by default.
1031 * @callback is function provided by application libisds will call. See type
1032 * definition for @callback argument explanation. Pass NULL to revert logging to
1033 * default behaviour.
1034 * @data is application specific data @callback gets as last argument */
1035 void isds_set_log_callback(isds_log_callback callback, void *data) {
1036 log_callback = callback;
1037 log_callback_data = data;
1041 /* Log @message in class @facility with log @level into global log. @message
1042 * is printf(3) formatting string, variadic arguments may be necessary.
1043 * For debugging purposes. */
1044 _hidden isds_error isds_log(const isds_log_facility facility,
1045 const isds_log_level level, const char *message, ...) {
1046 va_list ap;
1047 char *buffer = NULL;
1048 int length;
1050 if (level > log_level) return IE_SUCCESS;
1051 if (!(log_facilities & facility)) return IE_SUCCESS;
1052 if (!message) return IE_INVAL;
1054 if (log_callback) {
1055 /* Pass message to application supplied callback function */
1056 va_start(ap, message);
1057 length = isds_vasprintf(&buffer, message, ap);
1058 va_end(ap);
1060 if (length == -1) {
1061 return IE_ERROR;
1063 if (length > 0) {
1064 log_callback(facility, level, buffer, length, log_callback_data);
1066 free(buffer);
1067 } else {
1068 /* Default: Log it to stderr */
1069 va_start(ap, message);
1070 vfprintf(stderr, message, ap);
1071 va_end(ap);
1072 /* Line buffered printf is default.
1073 * fflush(stderr);*/
1076 return IE_SUCCESS;
1080 /* Set timeout in milliseconds for each network job like connecting to server
1081 * or sending message. Use 0 to disable timeout limits. */
1082 isds_error isds_set_timeout(struct isds_ctx *context,
1083 const unsigned int timeout) {
1084 if (!context) return IE_INVALID_CONTEXT;
1085 zfree(context->long_message);
1087 #if HAVE_LIBCURL
1088 context->timeout = timeout;
1090 if (context->curl) {
1091 CURLcode curl_err;
1093 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1094 if (!curl_err)
1095 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1096 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1097 context->timeout);
1098 #else
1099 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1100 context->timeout / 1000);
1101 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1102 if (curl_err) return IE_ERROR;
1105 return IE_SUCCESS;
1106 #else /* not HAVE_LIBCURL */
1107 return IE_NOTSUP;
1108 #endif
1112 /* Register callback function libisds calls periodically during HTTP data
1113 * transfer.
1114 * @context is session context
1115 * @callback is function provided by application libisds will call. See type
1116 * definition for @callback argument explanation.
1117 * @data is application specific data @callback gets as last argument */
1118 isds_error isds_set_progress_callback(struct isds_ctx *context,
1119 isds_progress_callback callback, void *data) {
1120 if (!context) return IE_INVALID_CONTEXT;
1121 zfree(context->long_message);
1123 #if HAVE_LIBCURL
1124 context->progress_callback = callback;
1125 context->progress_callback_data = data;
1127 return IE_SUCCESS;
1128 #else /* not HAVE_LIBCURL */
1129 return IE_NOTSUP;
1130 #endif
1134 /* Change context settings.
1135 * @context is context which setting will be applied to
1136 * @option is name of option. It determines the type of last argument. See
1137 * isds_option definition for more info.
1138 * @... is value of new setting. Type is determined by @option
1139 * */
1140 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1141 ...) {
1142 isds_error err = IE_SUCCESS;
1143 va_list ap;
1144 #if HAVE_LIBCURL
1145 char *pointer, *string;
1146 #endif
1148 if (!context) return IE_INVALID_CONTEXT;
1149 zfree(context->long_message);
1151 va_start(ap, option);
1153 #define REPLACE_VA_BOOLEAN(destination) { \
1154 if (!(destination)) { \
1155 (destination) = malloc(sizeof(*(destination))); \
1156 if (!(destination)) { \
1157 err = IE_NOMEM; goto leave; \
1160 *(destination) = (_Bool) !!va_arg(ap, int); \
1163 #define REPLACE_VA_STRING(destination) { \
1164 string = va_arg(ap, char *); \
1165 if (string) { \
1166 pointer = realloc((destination), 1 + strlen(string)); \
1167 if (!pointer) { err = IE_NOMEM; goto leave; } \
1168 strcpy(pointer, string); \
1169 (destination) = pointer; \
1170 } else { \
1171 free(destination); \
1172 (destination) = NULL; \
1176 switch (option) {
1177 case IOPT_TLS_VERIFY_SERVER:
1178 #if HAVE_LIBCURL
1179 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1180 #else
1181 err = IE_NOTSUP; goto leave;
1182 #endif
1183 break;
1184 case IOPT_TLS_CA_FILE:
1185 #if HAVE_LIBCURL
1186 REPLACE_VA_STRING(context->tls_ca_file);
1187 #else
1188 err = IE_NOTSUP; goto leave;
1189 #endif
1190 break;
1191 case IOPT_TLS_CA_DIRECTORY:
1192 #if HAVE_LIBCURL
1193 REPLACE_VA_STRING(context->tls_ca_dir);
1194 #else
1195 err = IE_NOTSUP; goto leave;
1196 #endif
1197 break;
1198 case IOPT_TLS_CRL_FILE:
1199 #if HAVE_LIBCURL
1200 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1201 REPLACE_VA_STRING(context->tls_crl_file);
1202 #else
1203 isds_log_message(context,
1204 _("Curl library does not support CRL definition"));
1205 err = IE_NOTSUP;
1206 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1207 #else
1208 err = IE_NOTSUP; goto leave;
1209 #endif /* not HAVE_LIBCURL */
1210 break;
1211 case IOPT_NORMALIZE_MIME_TYPE:
1212 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1213 break;
1215 default:
1216 err = IE_ENUM; goto leave;
1219 #undef REPLACE_VA_STRING
1220 #undef REPLACE_VA_BOOLEAN
1222 leave:
1223 va_end(ap);
1224 return err;
1228 #if HAVE_LIBCURL
1229 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1230 * Destination for NULL argument will not be touched.
1231 * Destination pointers must be freed before calling this function.
1232 * If @username is @context->saved_username, the saved_username will not be
1233 * replaced. The saved_username is clobbered only if context has set otp
1234 * member.
1235 * Return IE_SUCCESS on success. */
1236 static isds_error _isds_store_credentials(struct isds_ctx *context,
1237 const char *username, const char *password,
1238 const struct isds_pki_credentials *pki_credentials) {
1239 if (NULL == context) return IE_INVALID_CONTEXT;
1241 /* FIXME: mlock password
1242 * (I have a library) */
1244 if (username) {
1245 context->username = strdup(username);
1246 if (context->otp && context->saved_username != username)
1247 context->saved_username = strdup(username);
1249 if (password) {
1250 if (NULL == context->otp_credentials)
1251 context->password = strdup(password);
1252 else
1253 context->password = _isds_astrcat(password,
1254 context->otp_credentials->otp_code);
1256 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1258 if ((NULL != username && NULL == context->username) ||
1259 (NULL != password && NULL == context->password) ||
1260 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1261 (context->otp && NULL != context->username &&
1262 NULL == context->saved_username)) {
1263 return IE_NOMEM;
1266 return IE_SUCCESS;
1268 #endif
1271 /* Connect and log into ISDS server.
1272 * All required arguments will be copied, you do not have to keep them after
1273 * that.
1274 * ISDS supports six different authentication methods. Exact method is
1275 * selected on @username, @password, @pki_credentials, and @otp arguments:
1276 * - If @pki_credentials == NULL, @username and @password must be supplied
1277 * and then
1278 * - If @otp == NULL, simple authentication by username and password will
1279 * be proceeded.
1280 * - If @otp != NULL, authentication by username and password and OTP
1281 * will be used.
1282 * - If @pki_credentials != NULL, then
1283 * - If @username == NULL, only certificate will be used
1284 * - If @username != NULL, then
1285 * - If @password == NULL, then certificate will be used and
1286 * @username shifts meaning to box ID. This is used for hosted
1287 * services.
1288 * - Otherwise all three arguments will be used.
1289 * Please note, that different cases require different certificate type
1290 * (system qualified one or commercial non qualified one). This library
1291 * does not check such political issues. Please see ISDS Specification
1292 * for more details.
1293 * @url is base address of ISDS web service. Pass extern isds_locator
1294 * variable to use production ISDS instance without client certificate
1295 * authentication (or extern isds_cert_locator with client certificate
1296 * authentication or extern isds_otp_locators with OTP authentication).
1297 * Passing NULL has the same effect, autoselection between isds_locator,
1298 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1299 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1300 * isds_otp_testing_locator) variable to select testing instance.
1301 * @username is user name of ISDS user or box ID
1302 * @password is user's secret password
1303 * @pki_credentials defines public key cryptographic material to use in client
1304 * authentication.
1305 * @otp selects one-time password authentication method to use, defines OTP
1306 * code (if known) and returns fine grade resolution of OTP procedure.
1307 * @return:
1308 * IE_SUCCESS if authentication succeeds
1309 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1310 * requested, fine grade reason will be set into @otp->resolution. Error
1311 * message from server can be obtained by isds_long_message() call.
1312 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1313 * server has sent OTP code through side channel. Application is expected to
1314 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1315 * this call to complete second phase of TOTP authentication;
1316 * or other appropriate error. */
1317 isds_error isds_login(struct isds_ctx *context, const char *url,
1318 const char *username, const char *password,
1319 const struct isds_pki_credentials *pki_credentials,
1320 struct isds_otp *otp) {
1321 #if HAVE_LIBCURL
1322 isds_error err = IE_NOT_LOGGED_IN;
1323 isds_error soap_err;
1324 xmlNsPtr isds_ns = NULL;
1325 xmlNodePtr request = NULL;
1326 #endif /* HAVE_LIBCURL */
1328 if (!context) return IE_INVALID_CONTEXT;
1329 zfree(context->long_message);
1331 #if HAVE_LIBCURL
1332 /* Close connection if already logged in */
1333 if (context->curl) {
1334 _isds_close_connection(context);
1337 /* Store configuration */
1338 context->type = CTX_TYPE_ISDS;
1339 zfree(context->url);
1341 /* Mangle base URI according to requested authentication method */
1342 if (NULL == pki_credentials) {
1343 isds_log(ILF_SEC, ILL_INFO,
1344 _("Selected authentication method: no certificate, "
1345 "username and password\n"));
1346 if (!username || !password) {
1347 isds_log_message(context,
1348 _("Both username and password must be supplied"));
1349 return IE_INVAL;
1351 context->otp_credentials = otp;
1352 context->otp = (NULL != context->otp_credentials);
1354 if (!context->otp) {
1355 /* Default locator is official system (without certificate or
1356 * OTP) */
1357 context->url = strdup((NULL != url) ? url : isds_locator);
1358 } else {
1359 const char *authenticator_uri = NULL;
1360 if (!url) url = isds_otp_locator;
1361 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1362 switch (context->otp_credentials->method) {
1363 case OTP_HMAC:
1364 isds_log(ILF_SEC, ILL_INFO,
1365 _("Selected authentication method: "
1366 "HMAC-based one-time password\n"));
1367 authenticator_uri =
1368 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1369 break;
1370 case OTP_TIME:
1371 isds_log(ILF_SEC, ILL_INFO,
1372 _("Selected authentication method: "
1373 "Time-based one-time password\n"));
1374 if (context->otp_credentials->otp_code == NULL) {
1375 isds_log(ILF_SEC, ILL_INFO,
1376 _("OTP code has not been provided by "
1377 "application, requesting server for "
1378 "new one.\n"));
1379 authenticator_uri =
1380 "%1$sas/processLogin?type=totp&sendSms=true&"
1381 "uri=%1$sapps/";
1382 } else {
1383 isds_log(ILF_SEC, ILL_INFO,
1384 _("OTP code has been provided by "
1385 "application, not requesting server "
1386 "for new one.\n"));
1387 authenticator_uri =
1388 "%1$sas/processLogin?type=totp&"
1389 "uri=%1$sapps/";
1391 break;
1392 default:
1393 isds_log_message(context,
1394 _("Unknown one-time password authentication "
1395 "method requested by application"));
1396 return IE_ENUM;
1398 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1399 return IE_NOMEM;
1401 } else {
1402 /* Default locator is official system (with client certificate) */
1403 context->otp = 0;
1404 context->otp_credentials = NULL;
1405 if (!url) url = isds_cert_locator;
1407 if (!username) {
1408 isds_log(ILF_SEC, ILL_INFO,
1409 _("Selected authentication method: system certificate, "
1410 "no username and no password\n"));
1411 password = NULL;
1412 context->url = _isds_astrcat(url, "cert/");
1413 } else {
1414 if (!password) {
1415 isds_log(ILF_SEC, ILL_INFO,
1416 _("Selected authentication method: system certificate, "
1417 "box ID and no password\n"));
1418 context->url = _isds_astrcat(url, "hspis/");
1419 } else {
1420 isds_log(ILF_SEC, ILL_INFO,
1421 _("Selected authentication method: commercial "
1422 "certificate, username and password\n"));
1423 context->url = _isds_astrcat(url, "certds/");
1427 if (!(context->url))
1428 return IE_NOMEM;
1430 /* Prepare CURL handle */
1431 context->curl = curl_easy_init();
1432 if (!(context->curl))
1433 return IE_ERROR;
1435 /* Build log-in request */
1436 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1437 if (!request) {
1438 isds_log_message(context, _("Could not build ISDS log-in request"));
1439 return IE_ERROR;
1441 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1442 if(!isds_ns) {
1443 isds_log_message(context, _("Could not create ISDS name space"));
1444 xmlFreeNode(request);
1445 return IE_ERROR;
1447 xmlSetNs(request, isds_ns);
1449 /* Store credentials */
1450 _isds_discard_credentials(context, 1);
1451 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1452 _isds_discard_credentials(context, 1);
1453 xmlFreeNode(request);
1454 return IE_NOMEM;
1457 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1458 username, url);
1460 /* XXX: ISDS documentation does not specify response body for
1461 * DummyOperation request. However real server sends back
1462 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1463 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1464 * SOAP body content, e.g. the dmStatus element. */
1466 /* Send log-in request */
1467 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1469 if (context->otp) {
1470 /* Revert context URL from OTP authentication service URL to OTP web
1471 * service base URL for subsequent calls. Potenial isds_login() retry
1472 * will re-set context URL again. */
1473 zfree(context->url);
1474 context->url = _isds_astrcat(url, "apps/");
1475 if (context->url == NULL) {
1476 soap_err = IE_NOMEM;
1478 /* Detach pointer to OTP credentials from context */
1479 context->otp_credentials = NULL;
1482 /* Remove credentials */
1483 _isds_discard_credentials(context, 0);
1485 /* Destroy log-in request */
1486 xmlFreeNode(request);
1488 if (soap_err) {
1489 _isds_close_connection(context);
1490 return soap_err;
1493 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1494 * authentication succeeded if soap_err == IE_SUCCESS */
1495 err = IE_SUCCESS;
1497 if (!err)
1498 isds_log(ILF_ISDS, ILL_DEBUG,
1499 _("User %s has been logged into server %s successfully\n"),
1500 username, url);
1501 return err;
1502 #else /* not HAVE_LIBCURL */
1503 return IE_NOTSUP;
1504 #endif
1508 /* Log out from ISDS server discards credentials and connection configuration. */
1509 isds_error isds_logout(struct isds_ctx *context) {
1510 if (!context) return IE_INVALID_CONTEXT;
1511 zfree(context->long_message);
1513 #if HAVE_LIBCURL
1514 if (context->curl) {
1515 if (context->otp) {
1516 isds_error err = _isds_invalidate_otp_cookie(context);
1517 if (err) return err;
1520 /* Close connection */
1521 _isds_close_connection(context);
1523 /* Discard credentials for sure. They should not survive isds_login(),
1524 * even successful .*/
1525 _isds_discard_credentials(context, 1);
1527 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1528 } else {
1529 _isds_discard_credentials(context, 1);
1531 zfree(context->url);
1532 return IE_SUCCESS;
1533 #else /* not HAVE_LIBCURL */
1534 return IE_NOTSUP;
1535 #endif
1539 /* Verify connection to ISDS is alive and server is responding.
1540 * Send dummy request to ISDS and expect dummy response. */
1541 isds_error isds_ping(struct isds_ctx *context) {
1542 #if HAVE_LIBCURL
1543 isds_error soap_err;
1544 xmlNsPtr isds_ns = NULL;
1545 xmlNodePtr request = NULL;
1546 #endif /* HAVE_LIBCURL */
1548 if (!context) return IE_INVALID_CONTEXT;
1549 zfree(context->long_message);
1551 #if HAVE_LIBCURL
1552 /* Check if connection is established */
1553 if (!context->curl) return IE_CONNECTION_CLOSED;
1556 /* Build dummy request */
1557 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1558 if (!request) {
1559 isds_log_message(context, _("Could build ISDS dummy request"));
1560 return IE_ERROR;
1562 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1563 if(!isds_ns) {
1564 isds_log_message(context, _("Could not create ISDS name space"));
1565 xmlFreeNode(request);
1566 return IE_ERROR;
1568 xmlSetNs(request, isds_ns);
1570 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1572 /* XXX: ISDS documentation does not specify response body for
1573 * DummyOperation request. However real server sends back
1574 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1575 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1576 * SOAP body content, e.g. the dmStatus element. */
1578 /* Send dummy request */
1579 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1581 /* Destroy log-in request */
1582 xmlFreeNode(request);
1584 if (soap_err) {
1585 isds_log(ILF_ISDS, ILL_DEBUG,
1586 _("ISDS server could not be contacted\n"));
1587 return soap_err;
1590 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1591 * authentication succeeded if soap_err == IE_SUCCESS */
1594 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1596 return IE_SUCCESS;
1597 #else /* not HAVE_LIBCURL */
1598 return IE_NOTSUP;
1599 #endif
1603 /* Send bogus request to ISDS.
1604 * Just for test purposes */
1605 isds_error isds_bogus_request(struct isds_ctx *context) {
1606 #if HAVE_LIBCURL
1607 isds_error err;
1608 xmlNsPtr isds_ns = NULL;
1609 xmlNodePtr request = NULL;
1610 xmlDocPtr response = NULL;
1611 xmlChar *code = NULL, *message = NULL;
1612 #endif
1614 if (!context) return IE_INVALID_CONTEXT;
1615 zfree(context->long_message);
1617 #if HAVE_LIBCURL
1618 /* Check if connection is established */
1619 if (!context->curl) {
1620 /* Testing printf message */
1621 isds_printf_message(context, "%s", _("I said connection closed"));
1622 return IE_CONNECTION_CLOSED;
1626 /* Build dummy request */
1627 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1628 if (!request) {
1629 isds_log_message(context, _("Could build ISDS bogus request"));
1630 return IE_ERROR;
1632 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1633 if(!isds_ns) {
1634 isds_log_message(context, _("Could not create ISDS name space"));
1635 xmlFreeNode(request);
1636 return IE_ERROR;
1638 xmlSetNs(request, isds_ns);
1640 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1642 /* Sent bogus request */
1643 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1645 /* Destroy request */
1646 xmlFreeNode(request);
1648 if (err) {
1649 isds_log(ILF_ISDS, ILL_DEBUG,
1650 _("Processing ISDS response on bogus request failed\n"));
1651 xmlFreeDoc(response);
1652 return err;
1655 /* Check for response status */
1656 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1657 &code, &message, NULL);
1658 if (err) {
1659 isds_log(ILF_ISDS, ILL_DEBUG,
1660 _("ISDS response on bogus request is missing status\n"));
1661 free(code);
1662 free(message);
1663 xmlFreeDoc(response);
1664 return err;
1666 if (xmlStrcmp(code, BAD_CAST "0000")) {
1667 char *code_locale = _isds_utf82locale((char*)code);
1668 char *message_locale = _isds_utf82locale((char*)message);
1669 isds_log(ILF_ISDS, ILL_DEBUG,
1670 _("Server refused bogus request (code=%s, message=%s)\n"),
1671 code_locale, message_locale);
1672 /* XXX: Literal error messages from ISDS are Czech messages
1673 * (English sometimes) in UTF-8. It's hard to catch them for
1674 * translation. Successfully gettextized would return in locale
1675 * encoding, unsuccessfully translated would pass in UTF-8. */
1676 isds_log_message(context, message_locale);
1677 free(code_locale);
1678 free(message_locale);
1679 free(code);
1680 free(message);
1681 xmlFreeDoc(response);
1682 return IE_ISDS;
1686 free(code);
1687 free(message);
1688 xmlFreeDoc(response);
1690 isds_log(ILF_ISDS, ILL_DEBUG,
1691 _("Bogus message accepted by server. This should not happen.\n"));
1693 return IE_SUCCESS;
1694 #else /* not HAVE_LIBCURL */
1695 return IE_NOTSUP;
1696 #endif
1700 #if HAVE_LIBCURL
1701 /* Serialize XML subtree to buffer preserving XML indentation.
1702 * @context is session context
1703 * @subtree is XML element to be serialized (with children)
1704 * @buffer is automatically reallocated buffer where serialize to
1705 * @length is size of serialized stream in bytes
1706 * @return standard error code, free @buffer in case of error */
1707 static isds_error serialize_subtree(struct isds_ctx *context,
1708 xmlNodePtr subtree, void **buffer, size_t *length) {
1709 isds_error err = IE_SUCCESS;
1710 xmlBufferPtr xml_buffer = NULL;
1711 xmlSaveCtxtPtr save_ctx = NULL;
1712 xmlDocPtr subtree_doc = NULL;
1713 xmlNodePtr subtree_copy;
1714 xmlNsPtr isds_ns;
1715 void *new_buffer;
1717 if (!context) return IE_INVALID_CONTEXT;
1718 if (!buffer) return IE_INVAL;
1719 zfree(*buffer);
1720 if (!subtree || !length) return IE_INVAL;
1722 /* Make temporary XML document with @subtree root element */
1723 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1724 * It can result in not well-formed on invalid XML tree (e.g. name space
1725 * prefix definition can miss. */
1726 /*FIXME */
1728 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1729 if (!subtree_doc) {
1730 isds_log_message(context, _("Could not build temporary document"));
1731 err = IE_ERROR;
1732 goto leave;
1735 /* XXX: Copy subtree and attach the copy to document.
1736 * One node can not bee attached into more document at the same time.
1737 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1738 * automatically.
1739 * XXX: Check xmlSaveTree() too. */
1740 subtree_copy = xmlCopyNodeList(subtree);
1741 if (!subtree_copy) {
1742 isds_log_message(context, _("Could not copy subtree"));
1743 err = IE_ERROR;
1744 goto leave;
1746 xmlDocSetRootElement(subtree_doc, subtree_copy);
1748 /* Only this way we get namespace definition as @xmlns:isds,
1749 * otherwise we get namespace prefix without definition */
1750 /* FIXME: Don't overwrite original default namespace */
1751 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1752 if(!isds_ns) {
1753 isds_log_message(context, _("Could not create ISDS name space"));
1754 err = IE_ERROR;
1755 goto leave;
1757 xmlSetNs(subtree_copy, isds_ns);
1760 /* Serialize the document into buffer */
1761 xml_buffer = xmlBufferCreate();
1762 if (!xml_buffer) {
1763 isds_log_message(context, _("Could not create xmlBuffer"));
1764 err = IE_ERROR;
1765 goto leave;
1767 /* Last argument 0 means to not format the XML tree */
1768 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1769 if (!save_ctx) {
1770 isds_log_message(context, _("Could not create XML serializer"));
1771 err = IE_ERROR;
1772 goto leave;
1774 /* XXX: According LibXML documentation, this function does not return
1775 * meaningful value yet */
1776 xmlSaveDoc(save_ctx, subtree_doc);
1777 if (-1 == xmlSaveFlush(save_ctx)) {
1778 isds_log_message(context,
1779 _("Could not serialize XML subtree"));
1780 err = IE_ERROR;
1781 goto leave;
1783 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1784 * even after xmlSaveFlush(). Thus close it here */
1785 xmlSaveClose(save_ctx); save_ctx = NULL;
1788 /* Store and detach buffer from xml_buffer */
1789 *buffer = xml_buffer->content;
1790 *length = xml_buffer->use;
1791 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1793 /* Shrink buffer */
1794 new_buffer = realloc(*buffer, *length);
1795 if (new_buffer) *buffer = new_buffer;
1797 leave:
1798 if (err) {
1799 zfree(*buffer);
1800 *length = 0;
1803 xmlSaveClose(save_ctx);
1804 xmlBufferFree(xml_buffer);
1805 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1806 return err;
1808 #endif /* HAVE_LIBCURL */
1811 #if 0
1812 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1813 * @context is session context
1814 * @document is original document where @nodeset points to
1815 * @nodeset is XPath node set to dump (recursively)
1816 * @buffer is automatically reallocated buffer where serialize to
1817 * @length is size of serialized stream in bytes
1818 * @return standard error code, free @buffer in case of error */
1819 static isds_error dump_nodeset(struct isds_ctx *context,
1820 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1821 void **buffer, size_t *length) {
1822 isds_error err = IE_SUCCESS;
1823 xmlBufferPtr xml_buffer = NULL;
1824 void *new_buffer;
1826 if (!context) return IE_INVALID_CONTEXT;
1827 if (!buffer) return IE_INVAL;
1828 zfree(*buffer);
1829 if (!document || !nodeset || !length) return IE_INVAL;
1830 *length = 0;
1832 /* Empty node set results into NULL buffer */
1833 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1834 goto leave;
1837 /* Resulting the document into buffer */
1838 xml_buffer = xmlBufferCreate();
1839 if (!xml_buffer) {
1840 isds_log_message(context, _("Could not create xmlBuffer"));
1841 err = IE_ERROR;
1842 goto leave;
1845 /* Iterate over all nodes */
1846 for (int i = 0; i < nodeset->nodeNr; i++) {
1847 /* Serialize node.
1848 * XXX: xmlNodeDump() appends to xml_buffer. */
1849 if (-1 ==
1850 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1851 isds_log_message(context, _("Could not dump XML node"));
1852 err = IE_ERROR;
1853 goto leave;
1857 /* Store and detach buffer from xml_buffer */
1858 *buffer = xml_buffer->content;
1859 *length = xml_buffer->use;
1860 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1862 /* Shrink buffer */
1863 new_buffer = realloc(*buffer, *length);
1864 if (new_buffer) *buffer = new_buffer;
1867 leave:
1868 if (err) {
1869 zfree(*buffer);
1870 *length = 0;
1873 xmlBufferFree(xml_buffer);
1874 return err;
1876 #endif
1878 #if 0
1879 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1880 * @context is session context
1881 * @document is original document where @nodeset points to
1882 * @nodeset is XPath node set to dump (recursively)
1883 * @buffer is automatically reallocated buffer where serialize to
1884 * @length is size of serialized stream in bytes
1885 * @return standard error code, free @buffer in case of error */
1886 static isds_error dump_nodeset(struct isds_ctx *context,
1887 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1888 void **buffer, size_t *length) {
1889 isds_error err = IE_SUCCESS;
1890 xmlBufferPtr xml_buffer = NULL;
1891 xmlSaveCtxtPtr save_ctx = NULL;
1892 void *new_buffer;
1894 if (!context) return IE_INVALID_CONTEXT;
1895 if (!buffer) return IE_INVAL;
1896 zfree(*buffer);
1897 if (!document || !nodeset || !length) return IE_INVAL;
1898 *length = 0;
1900 /* Empty node set results into NULL buffer */
1901 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1902 goto leave;
1905 /* Resulting the document into buffer */
1906 xml_buffer = xmlBufferCreate();
1907 if (!xml_buffer) {
1908 isds_log_message(context, _("Could not create xmlBuffer"));
1909 err = IE_ERROR;
1910 goto leave;
1912 if (xmlSubstituteEntitiesDefault(1)) {
1913 isds_log_message(context, _("Could not disable attribute escaping"));
1914 err = IE_ERROR;
1915 goto leave;
1917 /* Last argument means:
1918 * 0 to not format the XML tree
1919 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1920 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1921 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1922 if (!save_ctx) {
1923 isds_log_message(context, _("Could not create XML serializer"));
1924 err = IE_ERROR;
1925 goto leave;
1927 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1928 isds_log_message(context, _("Could not disable attribute escaping"));
1929 err = IE_ERROR;
1930 goto leave;
1934 /* Iterate over all nodes */
1935 for (int i = 0; i < nodeset->nodeNr; i++) {
1936 /* Serialize node.
1937 * XXX: xmlNodeDump() appends to xml_buffer. */
1938 /*if (-1 ==
1939 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1941 /* XXX: According LibXML documentation, this function does not return
1942 * meaningful value yet */
1943 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1944 if (-1 == xmlSaveFlush(save_ctx)) {
1945 isds_log_message(context,
1946 _("Could not serialize XML subtree"));
1947 err = IE_ERROR;
1948 goto leave;
1952 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1953 * even after xmlSaveFlush(). Thus close it here */
1954 xmlSaveClose(save_ctx); save_ctx = NULL;
1956 /* Store and detach buffer from xml_buffer */
1957 *buffer = xml_buffer->content;
1958 *length = xml_buffer->use;
1959 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1961 /* Shrink buffer */
1962 new_buffer = realloc(*buffer, *length);
1963 if (new_buffer) *buffer = new_buffer;
1965 leave:
1966 if (err) {
1967 zfree(*buffer);
1968 *length = 0;
1971 xmlSaveClose(save_ctx);
1972 xmlBufferFree(xml_buffer);
1973 return err;
1975 #endif
1978 #if HAVE_LIBCURL
1979 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1980 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1981 if (!string || !type) return IE_INVAL;
1983 if (!xmlStrcmp(string, BAD_CAST "FO"))
1984 *type = DBTYPE_FO;
1985 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1986 *type = DBTYPE_PFO;
1987 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1988 *type = DBTYPE_PFO_ADVOK;
1989 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1990 *type = DBTYPE_PFO_DANPOR;
1991 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1992 *type = DBTYPE_PFO_INSSPR;
1993 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1994 *type = DBTYPE_PO;
1995 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1996 *type = DBTYPE_PO_ZAK;
1997 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1998 *type = DBTYPE_PO_REQ;
1999 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
2000 *type = DBTYPE_OVM;
2001 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
2002 *type = DBTYPE_OVM_NOTAR;
2003 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
2004 *type = DBTYPE_OVM_EXEKUT;
2005 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2006 *type = DBTYPE_OVM_REQ;
2007 else
2008 return IE_ENUM;
2009 return IE_SUCCESS;
2013 /* Convert ISDS dbType enum @type to UTF-8 string.
2014 * @Return pointer to static string, or NULL if unknown enum value */
2015 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2016 switch(type) {
2017 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2018 * of view of generic public SOAP interface. */
2019 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2020 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2021 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2022 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2023 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2024 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2025 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2026 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2027 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2028 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2029 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2030 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2031 default: return NULL; break;
2036 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2037 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2038 if (!string || !type) return IE_INVAL;
2040 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2041 *type = USERTYPE_PRIMARY;
2042 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2043 *type = USERTYPE_ENTRUSTED;
2044 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2045 *type = USERTYPE_ADMINISTRATOR;
2046 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2047 *type = USERTYPE_OFFICIAL;
2048 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2049 *type = USERTYPE_OFFICIAL_CERT;
2050 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2051 *type = USERTYPE_LIQUIDATOR;
2052 else
2053 return IE_ENUM;
2054 return IE_SUCCESS;
2058 /* Convert ISDS userType enum @type to UTF-8 string.
2059 * @Return pointer to static string, or NULL if unknown enum value */
2060 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2061 switch(type) {
2062 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2063 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2064 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2065 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2066 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2067 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2068 default: return NULL; break;
2073 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2074 static isds_error string2isds_sender_type(const xmlChar *string,
2075 isds_sender_type *type) {
2076 if (!string || !type) return IE_INVAL;
2078 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2079 *type = SENDERTYPE_PRIMARY;
2080 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2081 *type = SENDERTYPE_ENTRUSTED;
2082 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2083 *type = SENDERTYPE_ADMINISTRATOR;
2084 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2085 *type = SENDERTYPE_OFFICIAL;
2086 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2087 *type = SENDERTYPE_VIRTUAL;
2088 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2089 *type = SENDERTYPE_OFFICIAL_CERT;
2090 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2091 *type = SENDERTYPE_LIQUIDATOR;
2092 else
2093 return IE_ENUM;
2094 return IE_SUCCESS;
2098 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2099 static isds_error string2isds_payment_type(const xmlChar *string,
2100 isds_payment_type *type) {
2101 if (!string || !type) return IE_INVAL;
2103 if (!xmlStrcmp(string, BAD_CAST "K"))
2104 *type = PAYMENT_SENDER;
2105 else if (!xmlStrcmp(string, BAD_CAST "O"))
2106 *type = PAYMENT_RESPONSE;
2107 else if (!xmlStrcmp(string, BAD_CAST "G"))
2108 *type = PAYMENT_SPONSOR;
2109 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2110 *type = PAYMENT_SPONSOR_LIMITED;
2111 else if (!xmlStrcmp(string, BAD_CAST "D"))
2112 *type = PAYMENT_SPONSOR_EXTERNAL;
2113 else if (!xmlStrcmp(string, BAD_CAST "E"))
2114 *type = PAYMENT_STAMP;
2115 else
2116 return IE_ENUM;
2117 return IE_SUCCESS;
2121 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2122 * ciEventType is integer but we convert it from string representation
2123 * directly. */
2124 static isds_error string2isds_credit_event_type(const xmlChar *string,
2125 isds_credit_event_type *type) {
2126 if (!string || !type) return IE_INVAL;
2128 if (!xmlStrcmp(string, BAD_CAST "1"))
2129 *type = ISDS_CREDIT_CHARGED;
2130 else if (!xmlStrcmp(string, BAD_CAST "2"))
2131 *type = ISDS_CREDIT_DISCHARGED;
2132 else if (!xmlStrcmp(string, BAD_CAST "3"))
2133 *type = ISDS_CREDIT_MESSAGE_SENT;
2134 else if (!xmlStrcmp(string, BAD_CAST "4"))
2135 *type = ISDS_CREDIT_STORAGE_SET;
2136 else if (!xmlStrcmp(string, BAD_CAST "5"))
2137 *type = ISDS_CREDIT_EXPIRED;
2138 else
2139 return IE_ENUM;
2140 return IE_SUCCESS;
2144 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2145 * @Return pointer to static string, or NULL if unknown enum value */
2146 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2147 switch(type) {
2148 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2149 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2150 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2151 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2152 default: return NULL; break;
2157 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2158 * ISDSSearch2/searchType value.
2159 * @Return pointer to static string, or NULL if unknown enum value */
2160 static const xmlChar *isds_fulltext_target2string(
2161 const isds_fulltext_target type) {
2162 switch(type) {
2163 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2164 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2165 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2166 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2167 default: return NULL; break;
2170 #endif /* HAVE_LIBCURL */
2173 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2174 * @Return IE_ENUM if @string is not valid enum member */
2175 static isds_error string2isds_FileMetaType(const xmlChar *string,
2176 isds_FileMetaType *type) {
2177 if (!string || !type) return IE_INVAL;
2179 if (!xmlStrcmp(string, BAD_CAST "main"))
2180 *type = FILEMETATYPE_MAIN;
2181 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2182 *type = FILEMETATYPE_ENCLOSURE;
2183 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2184 *type = FILEMETATYPE_SIGNATURE;
2185 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2186 *type = FILEMETATYPE_META;
2187 else
2188 return IE_ENUM;
2189 return IE_SUCCESS;
2193 /* Convert UTF-8 @string to ISDS hash @algorithm.
2194 * @Return IE_ENUM if @string is not valid enum member */
2195 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2196 isds_hash_algorithm *algorithm) {
2197 if (!string || !algorithm) return IE_INVAL;
2199 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2200 *algorithm = HASH_ALGORITHM_MD5;
2201 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2202 *algorithm = HASH_ALGORITHM_SHA_1;
2203 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2204 *algorithm = HASH_ALGORITHM_SHA_224;
2205 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2206 *algorithm = HASH_ALGORITHM_SHA_256;
2207 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2208 *algorithm = HASH_ALGORITHM_SHA_384;
2209 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2210 *algorithm = HASH_ALGORITHM_SHA_512;
2211 else
2212 return IE_ENUM;
2213 return IE_SUCCESS;
2217 #if HAVE_LIBCURL
2218 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2219 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2220 if (!time || !string) return IE_INVAL;
2222 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2223 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2224 return IE_ERROR;
2226 return IE_SUCCESS;
2230 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2231 * respects the @time microseconds too. */
2232 static isds_error timeval2timestring(const struct timeval *time,
2233 xmlChar **string) {
2234 struct tm broken;
2235 time_t seconds_as_time_t;
2237 if (!time || !string) return IE_INVAL;
2239 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2240 * 32-bit long in Microsoft API. Convert value to the type expected by
2241 * gmtime_r(). */
2242 seconds_as_time_t = time->tv_sec;
2243 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2244 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2246 /* TODO: small negative year should be formatted as "-0012". This is not
2247 * true for glibc "%04d". We should implement it.
2248 * time->tv_usec type is su_seconds_t which is required to be signed
2249 * integer to accomodate values from range [-1, 1000000].
2250 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2251 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2252 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2253 * of the range check above. */
2254 if (-1 == isds_asprintf((char **) string,
2255 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2256 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2257 broken.tm_hour, broken.tm_min, broken.tm_sec,
2258 (int32_t)time->tv_usec))
2259 return IE_ERROR;
2261 return IE_SUCCESS;
2263 #endif /* HAVE_LIBCURL */
2266 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2267 * It respects microseconds too. Microseconds are rounded half up.
2268 * In case of error, @time will be freed. */
2269 static isds_error timestring2timeval(const xmlChar *string,
2270 struct timeval **time) {
2271 struct tm broken;
2272 char *offset, *delim, *endptr;
2273 const int subsecond_resolution = 6;
2274 char subseconds[subsecond_resolution + 1];
2275 _Bool round_up = 0;
2276 int offset_hours, offset_minutes;
2277 int i;
2278 long int long_number;
2279 #ifdef _WIN32
2280 int tmp;
2281 #endif
2283 if (!time) return IE_INVAL;
2284 if (!string) {
2285 zfree(*time);
2286 return IE_INVAL;
2289 memset(&broken, 0, sizeof(broken));
2291 if (!*time) {
2292 *time = calloc(1, sizeof(**time));
2293 if (!*time) return IE_NOMEM;
2294 } else {
2295 memset(*time, 0, sizeof(**time));
2299 /* xsd:date is ISO 8601 string, thus ASCII */
2300 /*TODO: negative year */
2302 #ifdef _WIN32
2303 i = 0;
2304 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2305 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2306 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2307 &i)) < 6) {
2308 zfree(*time);
2309 return IE_DATE;
2312 broken.tm_year -= 1900;
2313 broken.tm_mon--;
2314 broken.tm_isdst = -1;
2315 offset = (char*)string + i;
2316 #else
2317 /* Parse date and time without subseconds and offset */
2318 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2319 if (!offset) {
2320 zfree(*time);
2321 return IE_DATE;
2323 #endif
2325 /* Get subseconds */
2326 if (*offset == '.' ) {
2327 offset++;
2329 /* Copy first 6 digits, pad it with zeros.
2330 * Current server implementation uses only millisecond resolution. */
2331 /* TODO: isdigit() is locale sensitive */
2332 for (i = 0;
2333 i < subsecond_resolution && isdigit(*offset);
2334 i++, offset++) {
2335 subseconds[i] = *offset;
2337 if (subsecond_resolution == i && isdigit(*offset)) {
2338 /* Check 7th digit for rounding */
2339 if (*offset >= '5') round_up = 1;
2340 offset++;
2342 for (; i < subsecond_resolution; i++) {
2343 subseconds[i] = '0';
2345 subseconds[subsecond_resolution] = '\0';
2347 /* Convert it into integer */
2348 long_number = strtol(subseconds, &endptr, 10);
2349 if (*endptr != '\0' || long_number == LONG_MIN ||
2350 long_number == LONG_MAX) {
2351 zfree(*time);
2352 return IE_DATE;
2354 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2355 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2356 * microseconds" and "the type shall be a signed integer capable of
2357 * storing values at least in the range [-1, 1000000]. */
2358 if (long_number < -1 || long_number >= 1000000) {
2359 zfree(*time);
2360 return IE_DATE;
2362 (*time)->tv_usec = long_number;
2364 /* Round the subseconds */
2365 if (round_up) {
2366 if (999999 == (*time)->tv_usec) {
2367 (*time)->tv_usec = 0;
2368 broken.tm_sec++;
2369 } else {
2370 (*time)->tv_usec++;
2374 /* move to the zone offset delimiter or signal NULL*/
2375 delim = strchr(offset, '-');
2376 if (!delim)
2377 delim = strchr(offset, '+');
2378 if (!delim)
2379 delim = strchr(offset, 'Z');
2380 offset = delim;
2383 /* Get zone offset */
2384 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2385 * "" equals to "Z" and it means UTC zone. */
2386 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2387 * colon separator */
2388 if (offset && (*offset == '-' || *offset == '+')) {
2389 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2390 zfree(*time);
2391 return IE_DATE;
2393 if (*offset == '+') {
2394 broken.tm_hour -= offset_hours;
2395 broken.tm_min -= offset_minutes;
2396 } else {
2397 broken.tm_hour += offset_hours;
2398 broken.tm_min += offset_minutes;
2402 /* Convert to time_t */
2403 (*time)->tv_sec = _isds_timegm(&broken);
2404 if ((*time)->tv_sec == (time_t) -1) {
2405 zfree(*time);
2406 return IE_DATE;
2409 return IE_SUCCESS;
2413 /* Convert unsigned int into isds_message_status.
2414 * @context is session context
2415 * @number is pointer to number value. NULL will be treated as invalid value.
2416 * @status is automatically reallocated status
2417 * @return IE_SUCCESS, or error code and free status */
2418 static isds_error uint2isds_message_status(struct isds_ctx *context,
2419 const unsigned long int *number, isds_message_status **status) {
2420 if (!context) return IE_INVALID_CONTEXT;
2421 if (!status) return IE_INVAL;
2423 free(*status); *status = NULL;
2424 if (!number) return IE_INVAL;
2426 if (*number < 1 || *number > 10) {
2427 isds_printf_message(context, _("Invalid message status value: %lu"),
2428 *number);
2429 return IE_ENUM;
2432 *status = malloc(sizeof(**status));
2433 if (!*status) return IE_NOMEM;
2435 **status = 1 << *number;
2436 return IE_SUCCESS;
2440 /* Convert event description string into isds_event members type and
2441 * description
2442 * @string is raw event description starting with event prefix
2443 * @event is structure where to store type and stripped description to
2444 * @return standard error code, unknown prefix is not classified as an error.
2445 * */
2446 static isds_error eventstring2event(const xmlChar *string,
2447 struct isds_event* event) {
2448 const xmlChar *known_prefixes[] = {
2449 BAD_CAST "EV0:",
2450 BAD_CAST "EV1:",
2451 BAD_CAST "EV2:",
2452 BAD_CAST "EV3:",
2453 BAD_CAST "EV4:",
2454 BAD_CAST "EV5:",
2455 BAD_CAST "EV11:",
2456 BAD_CAST "EV12:",
2457 BAD_CAST "EV13:"
2459 const isds_event_type types[] = {
2460 EVENT_ENTERED_SYSTEM,
2461 EVENT_ACCEPTED_BY_RECIPIENT,
2462 EVENT_ACCEPTED_BY_FICTION,
2463 EVENT_UNDELIVERABLE,
2464 EVENT_COMMERCIAL_ACCEPTED,
2465 EVENT_DELIVERED,
2466 EVENT_PRIMARY_LOGIN,
2467 EVENT_ENTRUSTED_LOGIN,
2468 EVENT_SYSCERT_LOGIN
2470 unsigned int index;
2471 size_t length;
2473 if (!string || !event) return IE_INVAL;
2475 if (!event->type) {
2476 event->type = malloc(sizeof(*event->type));
2477 if (!(event->type)) return IE_NOMEM;
2479 zfree(event->description);
2481 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2482 index++) {
2483 length = xmlUTF8Strlen(known_prefixes[index]);
2485 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2486 /* Prefix is known */
2487 *event->type = types[index];
2489 /* Strip prefix from description and spaces */
2490 /* TODO: Recognize all white spaces from UCS blank class and
2491 * operate on UTF-8 chars. */
2492 for (; string[length] != '\0' && string[length] == ' '; length++);
2493 event->description = strdup((char *) (string + length));
2494 if (!(event->description)) return IE_NOMEM;
2496 return IE_SUCCESS;
2500 /* Unknown event prefix.
2501 * XSD allows any string */
2502 char *string_locale = _isds_utf82locale((char *) string);
2503 isds_log(ILF_ISDS, ILL_WARNING,
2504 _("Unknown delivery info event prefix: %s\n"), string_locale);
2505 free(string_locale);
2507 *event->type = EVENT_UKNOWN;
2508 event->description = strdup((char *) string);
2509 if (!(event->description)) return IE_NOMEM;
2511 return IE_SUCCESS;
2515 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2516 * and leave label */
2517 #define EXTRACT_STRING(element, string) { \
2518 xmlXPathFreeObject(result); \
2519 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2520 if (NULL == (result)) { \
2521 err = IE_ERROR; \
2522 goto leave; \
2524 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2525 if (result->nodesetval->nodeNr > 1) { \
2526 isds_printf_message(context, _("Multiple %s element"), element); \
2527 err = IE_ERROR; \
2528 goto leave; \
2530 (string) = (char *) \
2531 xmlXPathCastNodeSetToString(result->nodesetval); \
2532 if (NULL == (string)) { \
2533 err = IE_ERROR; \
2534 goto leave; \
2539 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2541 char *string = NULL; \
2542 EXTRACT_STRING(element, string); \
2544 if (string) { \
2545 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2546 if (!(booleanPtr)) { \
2547 free(string); \
2548 err = IE_NOMEM; \
2549 goto leave; \
2552 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2553 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2554 *(booleanPtr) = 1; \
2555 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2556 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2557 *(booleanPtr) = 0; \
2558 else { \
2559 char *string_locale = _isds_utf82locale((char*)string); \
2560 isds_printf_message(context, \
2561 _("%s value is not valid boolean: %s"), \
2562 element, string_locale); \
2563 free(string_locale); \
2564 free(string); \
2565 err = IE_ERROR; \
2566 goto leave; \
2569 free(string); \
2573 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2575 char *string = NULL; \
2576 EXTRACT_STRING(element, string); \
2578 if (NULL == string) { \
2579 isds_printf_message(context, _("%s element is empty"), element); \
2580 err = IE_ERROR; \
2581 goto leave; \
2583 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2584 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2585 (boolean) = 1; \
2586 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2587 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2588 (boolean) = 0; \
2589 else { \
2590 char *string_locale = _isds_utf82locale((char*)string); \
2591 isds_printf_message(context, \
2592 _("%s value is not valid boolean: %s"), \
2593 element, string_locale); \
2594 free(string_locale); \
2595 free(string); \
2596 err = IE_ERROR; \
2597 goto leave; \
2600 free(string); \
2603 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2605 char *string = NULL; \
2606 EXTRACT_STRING(element, string); \
2607 if (string) { \
2608 long int number; \
2609 char *endptr; \
2611 number = strtol((char*)string, &endptr, 10); \
2613 if (*endptr != '\0') { \
2614 char *string_locale = _isds_utf82locale((char *)string); \
2615 isds_printf_message(context, \
2616 _("%s is not valid integer: %s"), \
2617 element, string_locale); \
2618 free(string_locale); \
2619 free(string); \
2620 err = IE_ISDS; \
2621 goto leave; \
2624 if (number == LONG_MIN || number == LONG_MAX) { \
2625 char *string_locale = _isds_utf82locale((char *)string); \
2626 isds_printf_message(context, \
2627 _("%s value out of range of long int: %s"), \
2628 element, string_locale); \
2629 free(string_locale); \
2630 free(string); \
2631 err = IE_ERROR; \
2632 goto leave; \
2635 free(string); string = NULL; \
2637 if (!(preallocated)) { \
2638 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2639 if (!(longintPtr)) { \
2640 err = IE_NOMEM; \
2641 goto leave; \
2644 *(longintPtr) = number; \
2648 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2650 char *string = NULL; \
2651 EXTRACT_STRING(element, string); \
2652 if (string) { \
2653 long int number; \
2654 char *endptr; \
2656 number = strtol((char*)string, &endptr, 10); \
2658 if (*endptr != '\0') { \
2659 char *string_locale = _isds_utf82locale((char *)string); \
2660 isds_printf_message(context, \
2661 _("%s is not valid integer: %s"), \
2662 element, string_locale); \
2663 free(string_locale); \
2664 free(string); \
2665 err = IE_ISDS; \
2666 goto leave; \
2669 if (number == LONG_MIN || number == LONG_MAX) { \
2670 char *string_locale = _isds_utf82locale((char *)string); \
2671 isds_printf_message(context, \
2672 _("%s value out of range of long int: %s"), \
2673 element, string_locale); \
2674 free(string_locale); \
2675 free(string); \
2676 err = IE_ERROR; \
2677 goto leave; \
2680 free(string); string = NULL; \
2681 if (number < 0) { \
2682 isds_printf_message(context, \
2683 _("%s value is negative: %ld"), element, number); \
2684 err = IE_ERROR; \
2685 goto leave; \
2688 if (!(preallocated)) { \
2689 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2690 if (!(ulongintPtr)) { \
2691 err = IE_NOMEM; \
2692 goto leave; \
2695 *(ulongintPtr) = number; \
2699 #define EXTRACT_DATE(element, tmPtr) { \
2700 char *string = NULL; \
2701 EXTRACT_STRING(element, string); \
2702 if (NULL != string) { \
2703 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2704 if (NULL == (tmPtr)) { \
2705 free(string); \
2706 err = IE_NOMEM; \
2707 goto leave; \
2709 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2710 if (err) { \
2711 if (err == IE_NOTSUP) { \
2712 err = IE_ISDS; \
2713 char *string_locale = _isds_utf82locale(string); \
2714 char *element_locale = _isds_utf82locale(element); \
2715 isds_printf_message(context, _("Invalid %s value: %s"), \
2716 element_locale, string_locale); \
2717 free(string_locale); \
2718 free(element_locale); \
2720 free(string); \
2721 goto leave; \
2723 free(string); \
2727 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2728 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2729 NULL); \
2730 if ((required) && (!string)) { \
2731 char *attribute_locale = _isds_utf82locale(attribute); \
2732 char *element_locale = \
2733 _isds_utf82locale((char *)xpath_ctx->node->name); \
2734 isds_printf_message(context, \
2735 _("Could not extract required %s attribute value from " \
2736 "%s element"), attribute_locale, element_locale); \
2737 free(element_locale); \
2738 free(attribute_locale); \
2739 err = IE_ERROR; \
2740 goto leave; \
2745 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2747 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2748 (xmlChar *) (string)); \
2749 if (!node) { \
2750 isds_printf_message(context, \
2751 _("Could not add %s child to %s element"), \
2752 element, (parent)->name); \
2753 err = IE_ERROR; \
2754 goto leave; \
2758 #define INSERT_STRING(parent, element, string) \
2759 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2761 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2763 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2764 else { INSERT_STRING(parent, element, "false"); } \
2767 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2769 if (booleanPtr) { \
2770 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2771 } else { \
2772 INSERT_STRING(parent, element, NULL); \
2776 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2777 if ((longintPtr)) { \
2778 /* FIXME: locale sensitive */ \
2779 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2780 err = IE_NOMEM; \
2781 goto leave; \
2783 INSERT_STRING(parent, element, buffer) \
2784 free(buffer); (buffer) = NULL; \
2785 } else { INSERT_STRING(parent, element, NULL) } \
2788 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2789 if ((ulongintPtr)) { \
2790 /* FIXME: locale sensitive */ \
2791 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2792 err = IE_NOMEM; \
2793 goto leave; \
2795 INSERT_STRING(parent, element, buffer) \
2796 free(buffer); (buffer) = NULL; \
2797 } else { INSERT_STRING(parent, element, NULL) } \
2800 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2802 /* FIXME: locale sensitive */ \
2803 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2804 err = IE_NOMEM; \
2805 goto leave; \
2807 INSERT_STRING(parent, element, buffer) \
2808 free(buffer); (buffer) = NULL; \
2811 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2812 * new attribute. */
2813 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2815 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2816 (xmlChar *) (string)); \
2817 if (!attribute_node) { \
2818 isds_printf_message(context, _("Could not add %s " \
2819 "attribute to %s element"), \
2820 (attribute), (parent)->name); \
2821 err = IE_ERROR; \
2822 goto leave; \
2826 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2827 if (string) { \
2828 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2829 if (length > (maximum)) { \
2830 isds_printf_message(context, \
2831 ngettext("%s has more than %d characters", \
2832 "%s has more than %d characters", (maximum)), \
2833 (name), (maximum)); \
2834 err = IE_2BIG; \
2835 goto leave; \
2837 if (length < (minimum)) { \
2838 isds_printf_message(context, \
2839 ngettext("%s has less than %d characters", \
2840 "%s has less than %d characters", (minimum)), \
2841 (name), (minimum)); \
2842 err = IE_2SMALL; \
2843 goto leave; \
2848 #define INSERT_ELEMENT(child, parent, element) \
2850 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2851 if (!(child)) { \
2852 isds_printf_message(context, \
2853 _("Could not add %s child to %s element"), \
2854 (element), (parent)->name); \
2855 err = IE_ERROR; \
2856 goto leave; \
2861 /* Find child element by name in given XPath context and switch context onto
2862 * it. The child must be uniq and must exist. Otherwise fails.
2863 * @context is ISDS context
2864 * @child is child element name
2865 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2866 * into it child. In error case, the @xpath_ctx keeps original value. */
2867 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2868 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2869 isds_error err = IE_SUCCESS;
2870 xmlXPathObjectPtr result = NULL;
2872 if (!context) return IE_INVALID_CONTEXT;
2873 if (!child || !xpath_ctx) return IE_INVAL;
2875 /* Find child */
2876 result = xmlXPathEvalExpression(child, xpath_ctx);
2877 if (!result) {
2878 err = IE_XML;
2879 goto leave;
2882 /* No match */
2883 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2884 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2885 char *child_locale = _isds_utf82locale((char*) child);
2886 isds_printf_message(context,
2887 _("%s element does not contain %s child"),
2888 parent_locale, child_locale);
2889 free(child_locale);
2890 free(parent_locale);
2891 err = IE_NOEXIST;
2892 goto leave;
2895 /* More matches */
2896 if (result->nodesetval->nodeNr > 1) {
2897 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2898 char *child_locale = _isds_utf82locale((char*) child);
2899 isds_printf_message(context,
2900 _("%s element contains multiple %s children"),
2901 parent_locale, child_locale);
2902 free(child_locale);
2903 free(parent_locale);
2904 err = IE_NOTUNIQ;
2905 goto leave;
2908 /* Switch context */
2909 xpath_ctx->node = result->nodesetval->nodeTab[0];
2911 leave:
2912 xmlXPathFreeObject(result);
2913 return err;
2918 #if HAVE_LIBCURL
2919 /* Find and convert XSD:gPersonName group in current node into structure
2920 * @context is ISDS context
2921 * @personName is automatically reallocated person name structure. If no member
2922 * value is found, will be freed.
2923 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2924 * elements
2925 * In case of error @personName will be freed. */
2926 static isds_error extract_gPersonName(struct isds_ctx *context,
2927 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2928 isds_error err = IE_SUCCESS;
2929 xmlXPathObjectPtr result = NULL;
2931 if (!context) return IE_INVALID_CONTEXT;
2932 if (!personName) return IE_INVAL;
2933 isds_PersonName_free(personName);
2934 if (!xpath_ctx) return IE_INVAL;
2937 *personName = calloc(1, sizeof(**personName));
2938 if (!*personName) {
2939 err = IE_NOMEM;
2940 goto leave;
2943 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2944 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2945 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2946 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2948 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2949 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2950 isds_PersonName_free(personName);
2952 leave:
2953 if (err) isds_PersonName_free(personName);
2954 xmlXPathFreeObject(result);
2955 return err;
2959 /* Find and convert XSD:gAddress group in current node into structure
2960 * @context is ISDS context
2961 * @address is automatically reallocated address structure. If no member
2962 * value is found, will be freed.
2963 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2964 * elements
2965 * In case of error @address will be freed. */
2966 static isds_error extract_gAddress(struct isds_ctx *context,
2967 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2968 isds_error err = IE_SUCCESS;
2969 xmlXPathObjectPtr result = NULL;
2971 if (!context) return IE_INVALID_CONTEXT;
2972 if (!address) return IE_INVAL;
2973 isds_Address_free(address);
2974 if (!xpath_ctx) return IE_INVAL;
2977 *address = calloc(1, sizeof(**address));
2978 if (!*address) {
2979 err = IE_NOMEM;
2980 goto leave;
2983 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2984 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2985 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2986 EXTRACT_STRING("isds:adNumberInMunicipality",
2987 (*address)->adNumberInMunicipality);
2988 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2989 EXTRACT_STRING("isds:adState", (*address)->adState);
2991 if (!(*address)->adCity && !(*address)->adStreet &&
2992 !(*address)->adNumberInStreet &&
2993 !(*address)->adNumberInMunicipality &&
2994 !(*address)->adZipCode && !(*address)->adState)
2995 isds_Address_free(address);
2997 leave:
2998 if (err) isds_Address_free(address);
2999 xmlXPathFreeObject(result);
3000 return err;
3004 /* Find and convert isds:biDate element in current node into structure
3005 * @context is ISDS context
3006 * @biDate is automatically reallocated birth date structure. If no member
3007 * value is found, will be freed.
3008 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3009 * element
3010 * In case of error @biDate will be freed. */
3011 static isds_error extract_BiDate(struct isds_ctx *context,
3012 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3013 isds_error err = IE_SUCCESS;
3014 xmlXPathObjectPtr result = NULL;
3015 char *string = NULL;
3017 if (!context) return IE_INVALID_CONTEXT;
3018 if (!biDate) return IE_INVAL;
3019 zfree(*biDate);
3020 if (!xpath_ctx) return IE_INVAL;
3022 EXTRACT_STRING("isds:biDate", string);
3023 if (string) {
3024 *biDate = calloc(1, sizeof(**biDate));
3025 if (!*biDate) {
3026 err = IE_NOMEM;
3027 goto leave;
3029 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3030 if (err) {
3031 if (err == IE_NOTSUP) {
3032 err = IE_ISDS;
3033 char *string_locale = _isds_utf82locale(string);
3034 isds_printf_message(context,
3035 _("Invalid isds:biDate value: %s"), string_locale);
3036 free(string_locale);
3038 goto leave;
3042 leave:
3043 if (err) zfree(*biDate);
3044 free(string);
3045 xmlXPathFreeObject(result);
3046 return err;
3050 /* Convert isds:dBOwnerInfo XML tree into structure
3051 * @context is ISDS context
3052 * @db_owner_info is automatically reallocated box owner info structure
3053 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3054 * In case of error @db_owner_info will be freed. */
3055 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3056 struct isds_DbOwnerInfo **db_owner_info,
3057 xmlXPathContextPtr xpath_ctx) {
3058 isds_error err = IE_SUCCESS;
3059 xmlXPathObjectPtr result = NULL;
3060 char *string = NULL;
3062 if (!context) return IE_INVALID_CONTEXT;
3063 if (!db_owner_info) return IE_INVAL;
3064 isds_DbOwnerInfo_free(db_owner_info);
3065 if (!xpath_ctx) return IE_INVAL;
3068 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3069 if (!*db_owner_info) {
3070 err = IE_NOMEM;
3071 goto leave;
3074 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3076 EXTRACT_STRING("isds:dbType", string);
3077 if (string) {
3078 (*db_owner_info)->dbType =
3079 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3080 if (!(*db_owner_info)->dbType) {
3081 err = IE_NOMEM;
3082 goto leave;
3084 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3085 if (err) {
3086 zfree((*db_owner_info)->dbType);
3087 if (err == IE_ENUM) {
3088 err = IE_ISDS;
3089 char *string_locale = _isds_utf82locale(string);
3090 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3091 string_locale);
3092 free(string_locale);
3094 goto leave;
3096 zfree(string);
3099 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3101 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3102 xpath_ctx);
3103 if (err) goto leave;
3105 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3107 (*db_owner_info)->birthInfo =
3108 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3109 if (!(*db_owner_info)->birthInfo) {
3110 err = IE_NOMEM;
3111 goto leave;
3113 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3114 xpath_ctx);
3115 if (err) goto leave;
3116 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3117 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3118 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3119 if (!(*db_owner_info)->birthInfo->biDate &&
3120 !(*db_owner_info)->birthInfo->biCity &&
3121 !(*db_owner_info)->birthInfo->biCounty &&
3122 !(*db_owner_info)->birthInfo->biState)
3123 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3125 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3126 if (err) goto leave;
3128 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3129 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3130 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3131 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3132 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3134 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3136 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3137 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3138 (*db_owner_info)->dbOpenAddressing);
3140 leave:
3141 if (err) isds_DbOwnerInfo_free(db_owner_info);
3142 free(string);
3143 xmlXPathFreeObject(result);
3144 return err;
3148 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3149 * @context is session context
3150 * @owner is libisds structure with box description.
3151 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3152 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3153 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3154 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3155 * ignored.
3156 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3157 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3158 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3159 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3160 * type. */
3161 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3162 const struct isds_DbOwnerInfo *owner, _Bool pfo_subtype,
3163 xmlNodePtr db_owner_info) {
3165 isds_error err = IE_SUCCESS;
3166 xmlNodePtr node;
3167 xmlChar *string = NULL;
3168 const xmlChar *type_string = NULL;
3170 if (!context) return IE_INVALID_CONTEXT;
3171 if (!owner || !db_owner_info) return IE_INVAL;
3174 /* XXX: All the elements except email and telNumber are mandatory. */
3175 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3176 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3178 if (pfo_subtype) {
3179 INSERT_BOOLEAN(db_owner_info, "aifoIsds", owner->aifoIsds);
3182 if (!pfo_subtype) {
3183 /* dbType */
3184 if (owner->dbType) {
3185 type_string = isds_DbType2string(*(owner->dbType));
3186 if (!type_string) {
3187 isds_printf_message(context, _("Invalid dbType value: %d"),
3188 *(owner->dbType));
3189 err = IE_ENUM;
3190 goto leave;
3193 INSERT_STRING(db_owner_info, "dbType", type_string);
3195 INSERT_STRING(db_owner_info, "ic", owner->ic);
3198 INSERT_STRING(db_owner_info, "pnFirstName",
3199 (NULL == owner->personName) ? NULL: owner->personName->pnFirstName);
3200 INSERT_STRING(db_owner_info, "pnMiddleName",
3201 (NULL == owner->personName) ? NULL: owner->personName->pnMiddleName);
3202 INSERT_STRING(db_owner_info, "pnLastName",
3203 (NULL == owner->personName) ? NULL: owner->personName->pnLastName);
3204 if (!pfo_subtype) {
3205 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3206 (NULL == owner->personName) ? NULL:
3207 owner->personName->pnLastNameAtBirth);
3209 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3212 if (NULL != owner->birthInfo && NULL != owner->birthInfo->biDate) {
3213 err = tm2datestring(owner->birthInfo->biDate, &string);
3214 if (err) goto leave;
3216 INSERT_STRING(db_owner_info, "biDate", string);
3217 zfree(string);
3219 INSERT_STRING(db_owner_info, "biCity",
3220 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCity);
3221 INSERT_STRING(db_owner_info, "biCounty",
3222 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCounty);
3223 INSERT_STRING(db_owner_info, "biState",
3224 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biState);
3226 if (pfo_subtype) {
3227 INSERT_LONGINT(db_owner_info, "adCode",
3228 (NULL == owner->address) ? NULL : owner->address->adCode,
3229 string);
3231 INSERT_STRING(db_owner_info, "adCity",
3232 (NULL == owner->address) ? NULL: owner->address->adCity);
3233 if (pfo_subtype) {
3234 INSERT_STRING(db_owner_info, "adDistrict",
3235 (NULL == owner->address) ? NULL: owner->address->adDistrict);
3237 INSERT_STRING(db_owner_info, "adStreet",
3238 (NULL == owner->address) ? NULL: owner->address->adStreet);
3239 INSERT_STRING(db_owner_info, "adNumberInStreet",
3240 (NULL == owner->address) ? NULL: owner->address->adNumberInStreet);
3241 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3242 (NULL == owner->address) ? NULL: owner->address->adNumberInMunicipality);
3243 INSERT_STRING(db_owner_info, "adZipCode",
3244 (NULL == owner->address) ? NULL: owner->address->adZipCode);
3245 INSERT_STRING(db_owner_info, "adState",
3246 (NULL == owner->address) ? NULL: owner->address->adState);
3248 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3250 if (!pfo_subtype) {
3251 INSERT_STRING(db_owner_info, "email", owner->email);
3252 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3254 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3255 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3257 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3258 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3260 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3262 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3263 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3264 owner->dbOpenAddressing);
3267 leave:
3268 free(string);
3269 return err;
3273 /* Convert XSD:tDbUserInfo XML tree into structure
3274 * @context is ISDS context
3275 * @db_user_info is automatically reallocated user info structure
3276 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3277 * In case of error @db_user_info will be freed. */
3278 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3279 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3280 isds_error err = IE_SUCCESS;
3281 xmlXPathObjectPtr result = NULL;
3282 char *string = NULL;
3284 if (!context) return IE_INVALID_CONTEXT;
3285 if (!db_user_info) return IE_INVAL;
3286 isds_DbUserInfo_free(db_user_info);
3287 if (!xpath_ctx) return IE_INVAL;
3290 *db_user_info = calloc(1, sizeof(**db_user_info));
3291 if (!*db_user_info) {
3292 err = IE_NOMEM;
3293 goto leave;
3296 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3298 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3300 EXTRACT_STRING("isds:userType", string);
3301 if (string) {
3302 (*db_user_info)->userType =
3303 calloc(1, sizeof(*((*db_user_info)->userType)));
3304 if (!(*db_user_info)->userType) {
3305 err = IE_NOMEM;
3306 goto leave;
3308 err = string2isds_UserType((xmlChar *)string,
3309 (*db_user_info)->userType);
3310 if (err) {
3311 zfree((*db_user_info)->userType);
3312 if (err == IE_ENUM) {
3313 err = IE_ISDS;
3314 char *string_locale = _isds_utf82locale(string);
3315 isds_printf_message(context,
3316 _("Unknown isds:userType value: %s"), string_locale);
3317 free(string_locale);
3319 goto leave;
3321 zfree(string);
3324 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3326 (*db_user_info)->personName =
3327 calloc(1, sizeof(*((*db_user_info)->personName)));
3328 if (!(*db_user_info)->personName) {
3329 err = IE_NOMEM;
3330 goto leave;
3333 err = extract_gPersonName(context, &(*db_user_info)->personName,
3334 xpath_ctx);
3335 if (err) goto leave;
3337 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3338 if (err) goto leave;
3340 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3341 if (err) goto leave;
3343 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3344 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3346 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3347 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3348 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3350 /* ???: Default value is "CZ" according specification. Should we provide
3351 * it? */
3352 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3354 leave:
3355 if (err) isds_DbUserInfo_free(db_user_info);
3356 free(string);
3357 xmlXPathFreeObject(result);
3358 return err;
3362 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3363 * @context is session context
3364 * @user is libisds structure with user description
3365 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3366 * @db_user_info is XML element of XSD:tDbUserInfo */
3367 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3368 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3369 xmlNodePtr db_user_info) {
3371 isds_error err = IE_SUCCESS;
3372 xmlNodePtr node;
3373 xmlAttrPtr attribute_node;
3374 xmlChar *string = NULL;
3376 if (!context) return IE_INVALID_CONTEXT;
3377 if (!user || !db_user_info) return IE_INVAL;
3379 /* Build XSD:tDbUserInfo */
3381 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3382 * allow it everywhere. */
3383 if (honor_aifo_ticket && user->aifo_ticket) {
3384 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3387 if (user->personName) {
3388 INSERT_STRING(db_user_info, "pnFirstName",
3389 user->personName->pnFirstName);
3390 INSERT_STRING(db_user_info, "pnMiddleName",
3391 user->personName->pnMiddleName);
3392 INSERT_STRING(db_user_info, "pnLastName",
3393 user->personName->pnLastName);
3394 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3395 user->personName->pnLastNameAtBirth);
3397 if (user->address) {
3398 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3399 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3400 INSERT_STRING(db_user_info, "adNumberInStreet",
3401 user->address->adNumberInStreet);
3402 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3403 user->address->adNumberInMunicipality);
3404 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3405 INSERT_STRING(db_user_info, "adState", user->address->adState);
3407 if (user->biDate) {
3408 if (!tm2datestring(user->biDate, &string))
3409 INSERT_STRING(db_user_info, "biDate", string);
3410 zfree(string);
3412 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3413 INSERT_STRING(db_user_info, "userID", user->userID);
3415 /* userType */
3416 if (user->userType) {
3417 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3418 if (!type_string) {
3419 isds_printf_message(context, _("Invalid userType value: %d"),
3420 *(user->userType));
3421 err = IE_ENUM;
3422 goto leave;
3424 INSERT_STRING(db_user_info, "userType", type_string);
3427 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3428 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3429 INSERT_STRING(db_user_info, "ic", user->ic);
3430 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3431 INSERT_STRING(db_user_info, "firmName", user->firmName);
3432 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3433 INSERT_STRING(db_user_info, "caCity", user->caCity);
3434 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3435 INSERT_STRING(db_user_info, "caState", user->caState);
3437 leave:
3438 free(string);
3439 return err;
3443 /* Convert XSD:tPDZRec XML tree into structure
3444 * @context is ISDS context
3445 * @permission is automatically reallocated commercial permission structure
3446 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3447 * In case of error @permission will be freed. */
3448 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3449 struct isds_commercial_permission **permission,
3450 xmlXPathContextPtr xpath_ctx) {
3451 isds_error err = IE_SUCCESS;
3452 xmlXPathObjectPtr result = NULL;
3453 char *string = NULL;
3455 if (!context) return IE_INVALID_CONTEXT;
3456 if (!permission) return IE_INVAL;
3457 isds_commercial_permission_free(permission);
3458 if (!xpath_ctx) return IE_INVAL;
3461 *permission = calloc(1, sizeof(**permission));
3462 if (!*permission) {
3463 err = IE_NOMEM;
3464 goto leave;
3467 EXTRACT_STRING("isds:PDZType", string);
3468 if (string) {
3469 err = string2isds_payment_type((xmlChar *)string,
3470 &(*permission)->type);
3471 if (err) {
3472 if (err == IE_ENUM) {
3473 err = IE_ISDS;
3474 char *string_locale = _isds_utf82locale(string);
3475 isds_printf_message(context,
3476 _("Unknown isds:PDZType value: %s"), string_locale);
3477 free(string_locale);
3479 goto leave;
3481 zfree(string);
3484 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3485 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3487 EXTRACT_STRING("isds:PDZExpire", string);
3488 if (string) {
3489 err = timestring2timeval((xmlChar *) string,
3490 &((*permission)->expiration));
3491 if (err) {
3492 char *string_locale = _isds_utf82locale(string);
3493 if (err == IE_DATE) err = IE_ISDS;
3494 isds_printf_message(context,
3495 _("Could not convert PDZExpire as ISO time: %s"),
3496 string_locale);
3497 free(string_locale);
3498 goto leave;
3500 zfree(string);
3503 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3504 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3506 leave:
3507 if (err) isds_commercial_permission_free(permission);
3508 free(string);
3509 xmlXPathFreeObject(result);
3510 return err;
3514 /* Convert XSD:tCiRecord XML tree into structure
3515 * @context is ISDS context
3516 * @event is automatically reallocated commercial credit event structure
3517 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3518 * In case of error @event will be freed. */
3519 static isds_error extract_CiRecord(struct isds_ctx *context,
3520 struct isds_credit_event **event,
3521 xmlXPathContextPtr xpath_ctx) {
3522 isds_error err = IE_SUCCESS;
3523 xmlXPathObjectPtr result = NULL;
3524 char *string = NULL;
3525 long int *number_ptr;
3527 if (!context) return IE_INVALID_CONTEXT;
3528 if (!event) return IE_INVAL;
3529 isds_credit_event_free(event);
3530 if (!xpath_ctx) return IE_INVAL;
3533 *event = calloc(1, sizeof(**event));
3534 if (!*event) {
3535 err = IE_NOMEM;
3536 goto leave;
3539 EXTRACT_STRING("isds:ciEventTime", string);
3540 if (string) {
3541 err = timestring2timeval((xmlChar *) string,
3542 &(*event)->time);
3543 if (err) {
3544 char *string_locale = _isds_utf82locale(string);
3545 if (err == IE_DATE) err = IE_ISDS;
3546 isds_printf_message(context,
3547 _("Could not convert ciEventTime as ISO time: %s"),
3548 string_locale);
3549 free(string_locale);
3550 goto leave;
3552 zfree(string);
3555 EXTRACT_STRING("isds:ciEventType", string);
3556 if (string) {
3557 err = string2isds_credit_event_type((xmlChar *)string,
3558 &(*event)->type);
3559 if (err) {
3560 if (err == IE_ENUM) {
3561 err = IE_ISDS;
3562 char *string_locale = _isds_utf82locale(string);
3563 isds_printf_message(context,
3564 _("Unknown isds:ciEventType value: %s"), string_locale);
3565 free(string_locale);
3567 goto leave;
3569 zfree(string);
3572 number_ptr = &((*event)->credit_change);
3573 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3574 number_ptr = &(*event)->new_credit;
3575 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3577 switch((*event)->type) {
3578 case ISDS_CREDIT_CHARGED:
3579 EXTRACT_STRING("isds:ciTransID",
3580 (*event)->details.charged.transaction);
3581 break;
3582 case ISDS_CREDIT_DISCHARGED:
3583 EXTRACT_STRING("isds:ciTransID",
3584 (*event)->details.discharged.transaction);
3585 break;
3586 case ISDS_CREDIT_MESSAGE_SENT:
3587 EXTRACT_STRING("isds:ciRecipientID",
3588 (*event)->details.message_sent.recipient);
3589 EXTRACT_STRING("isds:ciPDZID",
3590 (*event)->details.message_sent.message_id);
3591 break;
3592 case ISDS_CREDIT_STORAGE_SET:
3593 number_ptr = &((*event)->details.storage_set.new_capacity);
3594 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3595 EXTRACT_DATE("isds:ciNewFrom",
3596 (*event)->details.storage_set.new_valid_from);
3597 EXTRACT_DATE("isds:ciNewTo",
3598 (*event)->details.storage_set.new_valid_to);
3599 EXTRACT_LONGINT("isds:ciOldCapacity",
3600 (*event)->details.storage_set.old_capacity, 0);
3601 EXTRACT_DATE("isds:ciOldFrom",
3602 (*event)->details.storage_set.old_valid_from);
3603 EXTRACT_DATE("isds:ciOldTo",
3604 (*event)->details.storage_set.old_valid_to);
3605 EXTRACT_STRING("isds:ciDoneBy",
3606 (*event)->details.storage_set.initiator);
3607 break;
3608 case ISDS_CREDIT_EXPIRED:
3609 break;
3612 leave:
3613 if (err) isds_credit_event_free(event);
3614 free(string);
3615 xmlXPathFreeObject(result);
3616 return err;
3620 #endif /* HAVE_LIBCURL */
3623 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3624 * isds_envelope structure. The envelope is automatically allocated but not
3625 * reallocated. The date are just appended into envelope structure.
3626 * @context is ISDS context
3627 * @envelope is automatically allocated message envelope structure
3628 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3629 * In case of error @envelope will be freed. */
3630 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3631 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3632 isds_error err = IE_SUCCESS;
3633 xmlXPathObjectPtr result = NULL;
3635 if (!context) return IE_INVALID_CONTEXT;
3636 if (!envelope) return IE_INVAL;
3637 if (!xpath_ctx) return IE_INVAL;
3640 if (!*envelope) {
3641 /* Allocate envelope */
3642 *envelope = calloc(1, sizeof(**envelope));
3643 if (!*envelope) {
3644 err = IE_NOMEM;
3645 goto leave;
3647 } else {
3648 /* Else free former data */
3649 zfree((*envelope)->dmSenderOrgUnit);
3650 zfree((*envelope)->dmSenderOrgUnitNum);
3651 zfree((*envelope)->dbIDRecipient);
3652 zfree((*envelope)->dmRecipientOrgUnit);
3653 zfree((*envelope)->dmRecipientOrgUnitNum);
3654 zfree((*envelope)->dmToHands);
3655 zfree((*envelope)->dmAnnotation);
3656 zfree((*envelope)->dmRecipientRefNumber);
3657 zfree((*envelope)->dmSenderRefNumber);
3658 zfree((*envelope)->dmRecipientIdent);
3659 zfree((*envelope)->dmSenderIdent);
3660 zfree((*envelope)->dmLegalTitleLaw);
3661 zfree((*envelope)->dmLegalTitleYear);
3662 zfree((*envelope)->dmLegalTitleSect);
3663 zfree((*envelope)->dmLegalTitlePar);
3664 zfree((*envelope)->dmLegalTitlePoint);
3665 zfree((*envelope)->dmPersonalDelivery);
3666 zfree((*envelope)->dmAllowSubstDelivery);
3669 /* Extract envelope elements added by sender or ISDS
3670 * (XSD: gMessageEnvelopeSub type) */
3671 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3672 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3673 (*envelope)->dmSenderOrgUnitNum, 0);
3674 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3675 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3676 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3677 (*envelope)->dmRecipientOrgUnitNum, 0);
3678 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3679 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3680 EXTRACT_STRING("isds:dmRecipientRefNumber",
3681 (*envelope)->dmRecipientRefNumber);
3682 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3683 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3684 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3686 /* Extract envelope elements regarding law reference */
3687 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3688 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3689 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3690 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3691 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3693 /* Extract envelope other elements */
3694 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3695 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3696 (*envelope)->dmAllowSubstDelivery);
3698 leave:
3699 if (err) isds_envelope_free(envelope);
3700 xmlXPathFreeObject(result);
3701 return err;
3706 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3707 * isds_envelope structure. The envelope is automatically allocated but not
3708 * reallocated. The date are just appended into envelope structure.
3709 * @context is ISDS context
3710 * @envelope is automatically allocated message envelope structure
3711 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3712 * In case of error @envelope will be freed. */
3713 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3714 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3715 isds_error err = IE_SUCCESS;
3716 xmlXPathObjectPtr result = NULL;
3718 if (!context) return IE_INVALID_CONTEXT;
3719 if (!envelope) return IE_INVAL;
3720 if (!xpath_ctx) return IE_INVAL;
3723 if (!*envelope) {
3724 /* Allocate envelope */
3725 *envelope = calloc(1, sizeof(**envelope));
3726 if (!*envelope) {
3727 err = IE_NOMEM;
3728 goto leave;
3730 } else {
3731 /* Else free former data */
3732 zfree((*envelope)->dmID);
3733 zfree((*envelope)->dbIDSender);
3734 zfree((*envelope)->dmSender);
3735 zfree((*envelope)->dmSenderAddress);
3736 zfree((*envelope)->dmSenderType);
3737 zfree((*envelope)->dmRecipient);
3738 zfree((*envelope)->dmRecipientAddress);
3739 zfree((*envelope)->dmAmbiguousRecipient);
3742 /* Extract envelope elements added by ISDS
3743 * (XSD: gMessageEnvelope type) */
3744 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3745 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3746 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3747 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3748 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3749 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3750 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3751 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3752 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3753 (*envelope)->dmAmbiguousRecipient);
3755 /* Extract envelope elements added by sender and ISDS
3756 * (XSD: gMessageEnvelope type) */
3757 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3758 if (err) goto leave;
3760 leave:
3761 if (err) isds_envelope_free(envelope);
3762 xmlXPathFreeObject(result);
3763 return err;
3767 /* Convert other envelope elements from XML tree into isds_envelope structure:
3768 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3769 * The envelope is automatically allocated but not reallocated.
3770 * The data are just appended into envelope structure.
3771 * @context is ISDS context
3772 * @envelope is automatically allocated message envelope structure
3773 * @xpath_ctx is XPath context with current node as parent desired elements
3774 * In case of error @envelope will be freed. */
3775 static isds_error append_status_size_times(struct isds_ctx *context,
3776 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3777 isds_error err = IE_SUCCESS;
3778 xmlXPathObjectPtr result = NULL;
3779 char *string = NULL;
3780 unsigned long int *unumber = NULL;
3782 if (!context) return IE_INVALID_CONTEXT;
3783 if (!envelope) return IE_INVAL;
3784 if (!xpath_ctx) return IE_INVAL;
3787 if (!*envelope) {
3788 /* Allocate new */
3789 *envelope = calloc(1, sizeof(**envelope));
3790 if (!*envelope) {
3791 err = IE_NOMEM;
3792 goto leave;
3794 } else {
3795 /* Free old data */
3796 zfree((*envelope)->dmMessageStatus);
3797 zfree((*envelope)->dmAttachmentSize);
3798 zfree((*envelope)->dmDeliveryTime);
3799 zfree((*envelope)->dmAcceptanceTime);
3803 /* dmMessageStatus element is mandatory */
3804 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3805 if (!unumber) {
3806 isds_log_message(context,
3807 _("Missing mandatory sisds:dmMessageStatus integer"));
3808 err = IE_ISDS;
3809 goto leave;
3811 err = uint2isds_message_status(context, unumber,
3812 &((*envelope)->dmMessageStatus));
3813 if (err) {
3814 if (err == IE_ENUM) err = IE_ISDS;
3815 goto leave;
3817 free(unumber); unumber = NULL;
3819 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3822 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3823 if (string) {
3824 err = timestring2timeval((xmlChar *) string,
3825 &((*envelope)->dmDeliveryTime));
3826 if (err) {
3827 char *string_locale = _isds_utf82locale(string);
3828 if (err == IE_DATE) err = IE_ISDS;
3829 isds_printf_message(context,
3830 _("Could not convert dmDeliveryTime as ISO time: %s"),
3831 string_locale);
3832 free(string_locale);
3833 goto leave;
3835 zfree(string);
3838 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3839 if (string) {
3840 err = timestring2timeval((xmlChar *) string,
3841 &((*envelope)->dmAcceptanceTime));
3842 if (err) {
3843 char *string_locale = _isds_utf82locale(string);
3844 if (err == IE_DATE) err = IE_ISDS;
3845 isds_printf_message(context,
3846 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3847 string_locale);
3848 free(string_locale);
3849 goto leave;
3851 zfree(string);
3854 leave:
3855 if (err) isds_envelope_free(envelope);
3856 free(unumber);
3857 free(string);
3858 xmlXPathFreeObject(result);
3859 return err;
3863 /* Convert message type attribute of current element into isds_envelope
3864 * structure.
3865 * TODO: This function can be incorporated into append_status_size_times() as
3866 * they are called always together.
3867 * The envelope is automatically allocated but not reallocated.
3868 * The data are just appended into envelope structure.
3869 * @context is ISDS context
3870 * @envelope is automatically allocated message envelope structure
3871 * @xpath_ctx is XPath context with current node as parent of attribute
3872 * carrying message type
3873 * In case of error @envelope will be freed. */
3874 static isds_error append_message_type(struct isds_ctx *context,
3875 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3876 isds_error err = IE_SUCCESS;
3878 if (!context) return IE_INVALID_CONTEXT;
3879 if (!envelope) return IE_INVAL;
3880 if (!xpath_ctx) return IE_INVAL;
3883 if (!*envelope) {
3884 /* Allocate new */
3885 *envelope = calloc(1, sizeof(**envelope));
3886 if (!*envelope) {
3887 err = IE_NOMEM;
3888 goto leave;
3890 } else {
3891 /* Free old data */
3892 zfree((*envelope)->dmType);
3896 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3898 if (!(*envelope)->dmType) {
3899 /* Use default value */
3900 (*envelope)->dmType = strdup("V");
3901 if (!(*envelope)->dmType) {
3902 err = IE_NOMEM;
3903 goto leave;
3905 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3906 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3907 isds_printf_message(context,
3908 _("Message type in dmType attribute is not 1 character long: "
3909 "%s"),
3910 type_locale);
3911 free(type_locale);
3912 err = IE_ISDS;
3913 goto leave;
3916 leave:
3917 if (err) isds_envelope_free(envelope);
3918 return err;
3922 #if HAVE_LIBCURL
3923 /* Convert dmType isds_envelope member into XML attribute and append it to
3924 * current node.
3925 * @context is ISDS context
3926 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3927 * @dm_envelope is XML element the resulting attribute will be appended to.
3928 * @return error code, in case of error context' message is filled. */
3929 static isds_error insert_message_type(struct isds_ctx *context,
3930 const char *type, xmlNodePtr dm_envelope) {
3931 isds_error err = IE_SUCCESS;
3932 xmlAttrPtr attribute_node;
3934 if (!context) return IE_INVALID_CONTEXT;
3935 if (!dm_envelope) return IE_INVAL;
3937 /* Insert optional message type */
3938 if (type) {
3939 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3940 char *type_locale = _isds_utf82locale(type);
3941 isds_printf_message(context,
3942 _("Message type in envelope is not 1 character long: %s"),
3943 type_locale);
3944 free(type_locale);
3945 err = IE_INVAL;
3946 goto leave;
3948 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3951 leave:
3952 return err;
3954 #endif /* HAVE_LIBCURL */
3957 /* Extract message document into reallocated document structure
3958 * @context is ISDS context
3959 * @document is automatically reallocated message documents structure
3960 * @xpath_ctx is XPath context with current node as isds:dmFile
3961 * In case of error @document will be freed. */
3962 static isds_error extract_document(struct isds_ctx *context,
3963 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3964 isds_error err = IE_SUCCESS;
3965 xmlXPathObjectPtr result = NULL;
3966 xmlNodePtr file_node;
3967 char *string = NULL;
3969 if (!context) return IE_INVALID_CONTEXT;
3970 if (!document) return IE_INVAL;
3971 isds_document_free(document);
3972 if (!xpath_ctx) return IE_INVAL;
3973 file_node = xpath_ctx->node;
3975 *document = calloc(1, sizeof(**document));
3976 if (!*document) {
3977 err = IE_NOMEM;
3978 goto leave;
3981 /* Extract document meta data */
3982 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3983 if (context->normalize_mime_type) {
3984 const char *normalized_type =
3985 isds_normalize_mime_type((*document)->dmMimeType);
3986 if (NULL != normalized_type &&
3987 normalized_type != (*document)->dmMimeType) {
3988 char *new_type = strdup(normalized_type);
3989 if (NULL == new_type) {
3990 isds_printf_message(context,
3991 _("Not enough memory to normalize document MIME type"));
3992 err = IE_NOMEM;
3993 goto leave;
3995 free((*document)->dmMimeType);
3996 (*document)->dmMimeType = new_type;
4000 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
4001 err = string2isds_FileMetaType((xmlChar*)string,
4002 &((*document)->dmFileMetaType));
4003 if (err) {
4004 char *meta_type_locale = _isds_utf82locale(string);
4005 isds_printf_message(context,
4006 _("Document has invalid dmFileMetaType attribute value: %s"),
4007 meta_type_locale);
4008 free(meta_type_locale);
4009 err = IE_ISDS;
4010 goto leave;
4012 zfree(string);
4014 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
4015 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
4016 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
4017 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
4020 /* Extract document data.
4021 * Base64 encoded blob or XML subtree must be presented. */
4023 /* Check for dmEncodedContent */
4024 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
4025 xpath_ctx);
4026 if (!result) {
4027 err = IE_XML;
4028 goto leave;
4031 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4032 /* Here we have Base64 blob */
4033 (*document)->is_xml = 0;
4035 if (result->nodesetval->nodeNr > 1) {
4036 isds_printf_message(context,
4037 _("Document has more dmEncodedContent elements"));
4038 err = IE_ISDS;
4039 goto leave;
4042 xmlXPathFreeObject(result); result = NULL;
4043 EXTRACT_STRING("isds:dmEncodedContent", string);
4045 /* Decode non-empty document */
4046 if (string && string[0] != '\0') {
4047 (*document)->data_length =
4048 _isds_b64decode(string, &((*document)->data));
4049 if ((*document)->data_length == (size_t) -1) {
4050 isds_printf_message(context,
4051 _("Error while Base64-decoding document content"));
4052 err = IE_ERROR;
4053 goto leave;
4056 } else {
4057 /* No Base64 blob, try XML document */
4058 xmlXPathFreeObject(result); result = NULL;
4059 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4060 xpath_ctx);
4061 if (!result) {
4062 err = IE_XML;
4063 goto leave;
4066 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4067 /* Here we have XML document */
4068 (*document)->is_xml = 1;
4070 if (result->nodesetval->nodeNr > 1) {
4071 isds_printf_message(context,
4072 _("Document has more dmXMLContent elements"));
4073 err = IE_ISDS;
4074 goto leave;
4077 /* XXX: We cannot serialize the content simply because:
4078 * - XML document may point out of its scope (e.g. to message
4079 * envelope)
4080 * - isds:dmXMLContent can contain more elements, no element,
4081 * a text node only
4082 * - it's not the XML way
4083 * Thus we provide the only right solution: XML DOM. Let's
4084 * application to cope with this hot potato :) */
4085 (*document)->xml_node_list =
4086 result->nodesetval->nodeTab[0]->children;
4087 } else {
4088 /* No base64 blob, nor XML document */
4089 isds_printf_message(context,
4090 _("Document has no dmEncodedContent, nor dmXMLContent "
4091 "element"));
4092 err = IE_ISDS;
4093 goto leave;
4098 leave:
4099 if (err) isds_document_free(document);
4100 free(string);
4101 xmlXPathFreeObject(result);
4102 xpath_ctx->node = file_node;
4103 return err;
4108 /* Extract message documents into reallocated list of documents
4109 * @context is ISDS context
4110 * @documents is automatically reallocated message documents list structure
4111 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4112 * In case of error @documents will be freed. */
4113 static isds_error extract_documents(struct isds_ctx *context,
4114 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4115 isds_error err = IE_SUCCESS;
4116 xmlXPathObjectPtr result = NULL;
4117 xmlNodePtr files_node;
4118 struct isds_list *document, *prev_document = NULL;
4120 if (!context) return IE_INVALID_CONTEXT;
4121 if (!documents) return IE_INVAL;
4122 isds_list_free(documents);
4123 if (!xpath_ctx) return IE_INVAL;
4124 files_node = xpath_ctx->node;
4126 /* Find documents */
4127 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4128 if (!result) {
4129 err = IE_XML;
4130 goto leave;
4133 /* No match */
4134 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4135 isds_printf_message(context,
4136 _("Message does not contain any document"));
4137 err = IE_ISDS;
4138 goto leave;
4142 /* Iterate over documents */
4143 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4145 /* Allocate and append list item */
4146 document = calloc(1, sizeof(*document));
4147 if (!document) {
4148 err = IE_NOMEM;
4149 goto leave;
4151 document->destructor = (void (*)(void **))isds_document_free;
4152 if (i == 0) *documents = document;
4153 else prev_document->next = document;
4154 prev_document = document;
4156 /* Extract document */
4157 xpath_ctx->node = result->nodesetval->nodeTab[i];
4158 err = extract_document(context,
4159 (struct isds_document **) &(document->data), xpath_ctx);
4160 if (err) goto leave;
4164 leave:
4165 if (err) isds_list_free(documents);
4166 xmlXPathFreeObject(result);
4167 xpath_ctx->node = files_node;
4168 return err;
4172 #if HAVE_LIBCURL
4173 /* Convert isds:dmRecord XML tree into structure
4174 * @context is ISDS context
4175 * @envelope is automatically reallocated message envelope structure
4176 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4177 * In case of error @envelope will be freed. */
4178 static isds_error extract_DmRecord(struct isds_ctx *context,
4179 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4180 isds_error err = IE_SUCCESS;
4181 xmlXPathObjectPtr result = NULL;
4183 if (!context) return IE_INVALID_CONTEXT;
4184 if (!envelope) return IE_INVAL;
4185 isds_envelope_free(envelope);
4186 if (!xpath_ctx) return IE_INVAL;
4189 *envelope = calloc(1, sizeof(**envelope));
4190 if (!*envelope) {
4191 err = IE_NOMEM;
4192 goto leave;
4196 /* Extract tRecord data */
4197 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4199 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4200 * dmAcceptanceTime. */
4201 err = append_status_size_times(context, envelope, xpath_ctx);
4202 if (err) goto leave;
4204 /* Extract envelope elements added by sender and ISDS
4205 * (XSD: gMessageEnvelope type) */
4206 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4207 if (err) goto leave;
4209 /* Get message type */
4210 err = append_message_type(context, envelope, xpath_ctx);
4211 if (err) goto leave;
4214 leave:
4215 if (err) isds_envelope_free(envelope);
4216 xmlXPathFreeObject(result);
4217 return err;
4221 /* Convert XSD:tStateChangesRecord type XML tree into structure
4222 * @context is ISDS context
4223 * @changed_status is automatically reallocated message state change structure
4224 * @xpath_ctx is XPath context with current node as element of
4225 * XSD:tStateChangesRecord type
4226 * In case of error @changed_status will be freed. */
4227 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4228 struct isds_message_status_change **changed_status,
4229 xmlXPathContextPtr xpath_ctx) {
4230 isds_error err = IE_SUCCESS;
4231 xmlXPathObjectPtr result = NULL;
4232 unsigned long int *unumber = NULL;
4233 char *string = NULL;
4235 if (!context) return IE_INVALID_CONTEXT;
4236 if (!changed_status) return IE_INVAL;
4237 isds_message_status_change_free(changed_status);
4238 if (!xpath_ctx) return IE_INVAL;
4241 *changed_status = calloc(1, sizeof(**changed_status));
4242 if (!*changed_status) {
4243 err = IE_NOMEM;
4244 goto leave;
4248 /* Extract tGetStateChangesInput data */
4249 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4251 /* dmEventTime is mandatory */
4252 EXTRACT_STRING("isds:dmEventTime", string);
4253 if (string) {
4254 err = timestring2timeval((xmlChar *) string,
4255 &((*changed_status)->time));
4256 if (err) {
4257 char *string_locale = _isds_utf82locale(string);
4258 if (err == IE_DATE) err = IE_ISDS;
4259 isds_printf_message(context,
4260 _("Could not convert dmEventTime as ISO time: %s"),
4261 string_locale);
4262 free(string_locale);
4263 goto leave;
4265 zfree(string);
4268 /* dmMessageStatus element is mandatory */
4269 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4270 if (!unumber) {
4271 isds_log_message(context,
4272 _("Missing mandatory isds:dmMessageStatus integer"));
4273 err = IE_ISDS;
4274 goto leave;
4276 err = uint2isds_message_status(context, unumber,
4277 &((*changed_status)->dmMessageStatus));
4278 if (err) {
4279 if (err == IE_ENUM) err = IE_ISDS;
4280 goto leave;
4282 zfree(unumber);
4285 leave:
4286 free(unumber);
4287 free(string);
4288 if (err) isds_message_status_change_free(changed_status);
4289 xmlXPathFreeObject(result);
4290 return err;
4292 #endif /* HAVE_LIBCURL */
4295 /* Find and convert isds:dmHash XML tree into structure
4296 * @context is ISDS context
4297 * @envelope is automatically reallocated message hash structure
4298 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4299 * In case of error @hash will be freed. */
4300 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4301 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4302 isds_error err = IE_SUCCESS;
4303 xmlNodePtr old_ctx_node;
4304 xmlXPathObjectPtr result = NULL;
4305 char *string = NULL;
4307 if (!context) return IE_INVALID_CONTEXT;
4308 if (!hash) return IE_INVAL;
4309 isds_hash_free(hash);
4310 if (!xpath_ctx) return IE_INVAL;
4312 old_ctx_node = xpath_ctx->node;
4314 *hash = calloc(1, sizeof(**hash));
4315 if (!*hash) {
4316 err = IE_NOMEM;
4317 goto leave;
4320 /* Locate dmHash */
4321 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4322 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4323 err = IE_ISDS;
4324 goto leave;
4326 if (err) {
4327 err = IE_ERROR;
4328 goto leave;
4331 /* Get hash algorithm */
4332 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4333 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4334 if (err) {
4335 if (err == IE_ENUM) {
4336 char *string_locale = _isds_utf82locale(string);
4337 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4338 string_locale);
4339 free(string_locale);
4341 goto leave;
4343 zfree(string);
4345 /* Get hash value */
4346 EXTRACT_STRING(".", string);
4347 if (!string) {
4348 isds_printf_message(context,
4349 _("sisds:dmHash element is missing hash value"));
4350 err = IE_ISDS;
4351 goto leave;
4353 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4354 if ((*hash)->length == (size_t) -1) {
4355 isds_printf_message(context,
4356 _("Error while Base64-decoding hash value"));
4357 err = IE_ERROR;
4358 goto leave;
4361 leave:
4362 if (err) isds_hash_free(hash);
4363 free(string);
4364 xmlXPathFreeObject(result);
4365 xpath_ctx->node = old_ctx_node;
4366 return err;
4370 /* Find and append isds:dmQTimestamp XML tree into envelope.
4371 * Because one service is allowed to miss time-stamp content, and we think
4372 * other could too (flaw in specification), this function is deliberated and
4373 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4374 * @context is ISDS context
4375 * @envelope is automatically allocated envelope structure
4376 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4377 * child
4378 * In case of error @envelope will be freed. */
4379 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4380 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4381 isds_error err = IE_SUCCESS;
4382 xmlXPathObjectPtr result = NULL;
4383 char *string = NULL;
4385 if (!context) return IE_INVALID_CONTEXT;
4386 if (!envelope) return IE_INVAL;
4387 if (!xpath_ctx) {
4388 isds_envelope_free(envelope);
4389 return IE_INVAL;
4392 if (!*envelope) {
4393 *envelope = calloc(1, sizeof(**envelope));
4394 if (!*envelope) {
4395 err = IE_NOMEM;
4396 goto leave;
4398 } else {
4399 zfree((*envelope)->timestamp);
4400 (*envelope)->timestamp_length = 0;
4403 /* Get dmQTimestamp */
4404 EXTRACT_STRING("sisds:dmQTimestamp", string);
4405 if (!string) {
4406 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4407 goto leave;
4409 (*envelope)->timestamp_length =
4410 _isds_b64decode(string, &((*envelope)->timestamp));
4411 if ((*envelope)->timestamp_length == (size_t) -1) {
4412 isds_printf_message(context,
4413 _("Error while Base64-decoding time stamp value"));
4414 err = IE_ERROR;
4415 goto leave;
4418 leave:
4419 if (err) isds_envelope_free(envelope);
4420 free(string);
4421 xmlXPathFreeObject(result);
4422 return err;
4426 /* Convert XSD tReturnedMessage XML tree into message structure.
4427 * It does not store serialized XML tree into message->raw.
4428 * It does store (pointer to) parsed XML tree into message->xml if needed.
4429 * @context is ISDS context
4430 * @include_documents Use true if documents must be extracted
4431 * (tReturnedMessage XSD type), use false if documents shall be omitted
4432 * (tReturnedMessageEnvelope).
4433 * @message is automatically reallocated message structure
4434 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4435 * type
4436 * In case of error @message will be freed. */
4437 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4438 const _Bool include_documents, struct isds_message **message,
4439 xmlXPathContextPtr xpath_ctx) {
4440 isds_error err = IE_SUCCESS;
4441 xmlNodePtr message_node;
4443 if (!context) return IE_INVALID_CONTEXT;
4444 if (!message) return IE_INVAL;
4445 isds_message_free(message);
4446 if (!xpath_ctx) return IE_INVAL;
4449 *message = calloc(1, sizeof(**message));
4450 if (!*message) {
4451 err = IE_NOMEM;
4452 goto leave;
4455 /* Save message XPATH context node */
4456 message_node = xpath_ctx->node;
4459 /* Extract dmDM */
4460 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4461 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4462 if (err) { err = IE_ERROR; goto leave; }
4463 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4464 if (err) goto leave;
4466 if (include_documents) {
4467 struct isds_list *item;
4469 /* Extract dmFiles */
4470 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4471 xpath_ctx);
4472 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4473 err = IE_ISDS; goto leave;
4475 if (err) { err = IE_ERROR; goto leave; }
4476 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4477 if (err) goto leave;
4479 /* Store xmlDoc of this message if needed */
4480 /* Only if we got a XML document in all the documents. */
4481 for (item = (*message)->documents; item; item = item->next) {
4482 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4483 (*message)->xml = xpath_ctx->doc;
4484 break;
4490 /* Restore context to message */
4491 xpath_ctx->node = message_node;
4493 /* Extract dmHash */
4494 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4495 xpath_ctx);
4496 if (err) goto leave;
4498 /* Extract dmQTimestamp, */
4499 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4500 xpath_ctx);
4501 if (err) goto leave;
4503 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4504 * dmAcceptanceTime. */
4505 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4506 if (err) goto leave;
4508 /* Get message type */
4509 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4510 if (err) goto leave;
4512 leave:
4513 if (err) isds_message_free(message);
4514 return err;
4518 /* Extract message event into reallocated isds_event structure
4519 * @context is ISDS context
4520 * @event is automatically reallocated message event structure
4521 * @xpath_ctx is XPath context with current node as isds:dmEvent
4522 * In case of error @event will be freed. */
4523 static isds_error extract_event(struct isds_ctx *context,
4524 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4525 isds_error err = IE_SUCCESS;
4526 xmlXPathObjectPtr result = NULL;
4527 xmlNodePtr event_node;
4528 char *string = NULL;
4530 if (!context) return IE_INVALID_CONTEXT;
4531 if (!event) return IE_INVAL;
4532 isds_event_free(event);
4533 if (!xpath_ctx) return IE_INVAL;
4534 event_node = xpath_ctx->node;
4536 *event = calloc(1, sizeof(**event));
4537 if (!*event) {
4538 err = IE_NOMEM;
4539 goto leave;
4542 /* Extract event data.
4543 * All elements are optional according XSD. That's funny. */
4544 EXTRACT_STRING("sisds:dmEventTime", string);
4545 if (string) {
4546 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4547 if (err) {
4548 char *string_locale = _isds_utf82locale(string);
4549 if (err == IE_DATE) err = IE_ISDS;
4550 isds_printf_message(context,
4551 _("Could not convert dmEventTime as ISO time: %s"),
4552 string_locale);
4553 free(string_locale);
4554 goto leave;
4556 zfree(string);
4559 /* dmEventDescr element has prefix and the rest */
4560 EXTRACT_STRING("sisds:dmEventDescr", string);
4561 if (string) {
4562 err = eventstring2event((xmlChar *) string, *event);
4563 if (err) goto leave;
4564 zfree(string);
4567 leave:
4568 if (err) isds_event_free(event);
4569 free(string);
4570 xmlXPathFreeObject(result);
4571 xpath_ctx->node = event_node;
4572 return err;
4576 /* Convert element of XSD tEventsArray type from XML tree into
4577 * isds_list of isds_event's structure. The list is automatically reallocated.
4578 * @context is ISDS context
4579 * @events is automatically reallocated list of event structures
4580 * @xpath_ctx is XPath context with current node as tEventsArray
4581 * In case of error @events will be freed. */
4582 static isds_error extract_events(struct isds_ctx *context,
4583 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4584 isds_error err = IE_SUCCESS;
4585 xmlXPathObjectPtr result = NULL;
4586 xmlNodePtr events_node;
4587 struct isds_list *event, *prev_event = NULL;
4589 if (!context) return IE_INVALID_CONTEXT;
4590 if (!events) return IE_INVAL;
4591 if (!xpath_ctx) return IE_INVAL;
4592 events_node = xpath_ctx->node;
4594 /* Free old list */
4595 isds_list_free(events);
4597 /* Find events */
4598 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4599 if (!result) {
4600 err = IE_XML;
4601 goto leave;
4604 /* No match */
4605 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4606 isds_printf_message(context,
4607 _("Delivery info does not contain any event"));
4608 err = IE_ISDS;
4609 goto leave;
4613 /* Iterate over events */
4614 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4616 /* Allocate and append list item */
4617 event = calloc(1, sizeof(*event));
4618 if (!event) {
4619 err = IE_NOMEM;
4620 goto leave;
4622 event->destructor = (void (*)(void **))isds_event_free;
4623 if (i == 0) *events = event;
4624 else prev_event->next = event;
4625 prev_event = event;
4627 /* Extract event */
4628 xpath_ctx->node = result->nodesetval->nodeTab[i];
4629 err = extract_event(context,
4630 (struct isds_event **) &(event->data), xpath_ctx);
4631 if (err) goto leave;
4635 leave:
4636 if (err) isds_list_free(events);
4637 xmlXPathFreeObject(result);
4638 xpath_ctx->node = events_node;
4639 return err;
4643 #if HAVE_LIBCURL
4644 /* Insert Base64 encoded data as element with text child.
4645 * @context is session context
4646 * @parent is XML node to append @element with @data as child
4647 * @ns is XML namespace of @element, use NULL to inherit from @parent
4648 * @element is UTF-8 encoded name of new element
4649 * @data is bit stream to encode into @element
4650 * @length is size of @data in bytes
4651 * @return standard error code and fill long error message if needed */
4652 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4653 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4654 const void *data, size_t length) {
4655 isds_error err = IE_SUCCESS;
4656 xmlNodePtr node;
4658 if (!context) return IE_INVALID_CONTEXT;
4659 if (!data && length > 0) return IE_INVAL;
4660 if (!parent || !element) return IE_INVAL;
4662 xmlChar *base64data = NULL;
4663 base64data = (xmlChar *) _isds_b64encode(data, length);
4664 if (!base64data) {
4665 isds_printf_message(context,
4666 ngettext("Not enough memory to encode %zd byte into Base64",
4667 "Not enough memory to encode %zd bytes into Base64",
4668 length),
4669 length);
4670 err = IE_NOMEM;
4671 goto leave;
4673 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4675 leave:
4676 free(base64data);
4677 return err;
4681 /* Convert isds_document structure into XML tree and append to dmFiles node.
4682 * @context is session context
4683 * @document is ISDS document
4684 * @dm_files is XML element the resulting tree will be appended to as a child.
4685 * @return error code, in case of error context' message is filled. */
4686 static isds_error insert_document(struct isds_ctx *context,
4687 struct isds_document *document, xmlNodePtr dm_files) {
4688 isds_error err = IE_SUCCESS;
4689 xmlNodePtr new_file = NULL, file = NULL, node;
4690 xmlAttrPtr attribute_node;
4692 if (!context) return IE_INVALID_CONTEXT;
4693 if (!document || !dm_files) return IE_INVAL;
4695 /* Allocate new dmFile */
4696 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4697 if (!new_file) {
4698 isds_printf_message(context, _("Could not allocate main dmFile"));
4699 err = IE_ERROR;
4700 goto leave;
4702 /* Append the new dmFile.
4703 * XXX: Main document must go first */
4704 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4705 file = xmlAddPrevSibling(dm_files->children, new_file);
4706 else
4707 file = xmlAddChild(dm_files, new_file);
4709 if (!file) {
4710 xmlFreeNode(new_file); new_file = NULL;
4711 isds_printf_message(context, _("Could not add dmFile child to "
4712 "%s element"), dm_files->name);
4713 err = IE_ERROR;
4714 goto leave;
4717 /* @dmMimeType is required */
4718 if (!document->dmMimeType) {
4719 isds_log_message(context,
4720 _("Document is missing mandatory MIME type definition"));
4721 err = IE_INVAL;
4722 goto leave;
4724 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4726 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4727 if (!string) {
4728 isds_printf_message(context,
4729 _("Document has unknown dmFileMetaType: %ld"),
4730 document->dmFileMetaType);
4731 err = IE_ENUM;
4732 goto leave;
4734 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4736 if (document->dmFileGuid) {
4737 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4739 if (document->dmUpFileGuid) {
4740 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4743 /* @dmFileDescr is required */
4744 if (!document->dmFileDescr) {
4745 isds_log_message(context,
4746 _("Document is missing mandatory description (title)"));
4747 err = IE_INVAL;
4748 goto leave;
4750 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4752 if (document->dmFormat) {
4753 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4757 /* Insert content (body) of the document. */
4758 if (document->is_xml) {
4759 /* XML document requested */
4761 /* Allocate new dmXMLContent */
4762 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4763 if (!xmlcontent) {
4764 isds_printf_message(context,
4765 _("Could not allocate dmXMLContent element"));
4766 err = IE_ERROR;
4767 goto leave;
4769 /* Append it */
4770 node = xmlAddChild(file, xmlcontent);
4771 if (!node) {
4772 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4773 isds_printf_message(context,
4774 _("Could not add dmXMLContent child to %s element"),
4775 file->name);
4776 err = IE_ERROR;
4777 goto leave;
4780 /* Copy non-empty node list */
4781 if (document->xml_node_list) {
4782 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4783 document->xml_node_list);
4784 if (!content) {
4785 isds_printf_message(context,
4786 _("Not enough memory to copy XML document"));
4787 err = IE_NOMEM;
4788 goto leave;
4791 if (!xmlAddChildList(node, content)) {
4792 xmlFreeNodeList(content);
4793 isds_printf_message(context,
4794 _("Error while adding XML document into dmXMLContent"));
4795 err = IE_XML;
4796 goto leave;
4798 /* XXX: We cannot free the content here because it's part of node's
4799 * document since now. It will be freed with it automatically. */
4801 } else {
4802 /* Binary document requested */
4803 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4804 document->data, document->data_length);
4805 if (err) goto leave;
4808 leave:
4809 return err;
4813 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4814 * The copy must be preallocated, the date are just appended into structure.
4815 * @context is ISDS context
4816 * @copy is message copy structure
4817 * @xpath_ctx is XPath context with current node as tMStatus */
4818 static isds_error append_TMStatus(struct isds_ctx *context,
4819 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4820 isds_error err = IE_SUCCESS;
4821 xmlXPathObjectPtr result = NULL;
4822 char *code = NULL, *message = NULL;
4824 if (!context) return IE_INVALID_CONTEXT;
4825 if (!copy || !xpath_ctx) return IE_INVAL;
4827 /* Free old values */
4828 zfree(copy->dmStatus);
4829 zfree(copy->dmID);
4831 /* Get error specific to this copy */
4832 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4833 if (!code) {
4834 isds_log_message(context,
4835 _("Missing isds:dmStatusCode under "
4836 "XSD:tMStatus type element"));
4837 err = IE_ISDS;
4838 goto leave;
4841 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4842 /* This copy failed */
4843 copy->error = IE_ISDS;
4844 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4845 if (message) {
4846 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4847 if (!copy->dmStatus) {
4848 copy->dmStatus = code;
4849 code = NULL;
4851 } else {
4852 copy->dmStatus = code;
4853 code = NULL;
4855 } else {
4856 /* This copy succeeded. In this case only, message ID is valid */
4857 copy->error = IE_SUCCESS;
4859 EXTRACT_STRING("isds:dmID", copy->dmID);
4860 if (!copy->dmID) {
4861 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4862 "but did not returned assigned message ID\n"));
4863 err = IE_ISDS;
4867 leave:
4868 free(code);
4869 free(message);
4870 xmlXPathFreeObject(result);
4871 return err;
4875 /* Insert struct isds_approval data (box approval) into XML tree
4876 * @context is session context
4877 * @approval is libisds structure with approval description. NULL is
4878 * acceptable.
4879 * @parent is XML element to append @approval to */
4880 static isds_error insert_GExtApproval(struct isds_ctx *context,
4881 const struct isds_approval *approval, xmlNodePtr parent) {
4883 isds_error err = IE_SUCCESS;
4884 xmlNodePtr node;
4886 if (!context) return IE_INVALID_CONTEXT;
4887 if (!parent) return IE_INVAL;
4889 if (!approval) return IE_SUCCESS;
4891 /* Build XSD:gExtApproval */
4892 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4893 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4895 leave:
4896 return err;
4900 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4901 * code
4902 * @context is session context
4903 * @service_name is name of SERVICE_DB_ACCESS
4904 * @response is reallocated server SOAP body response as XML document
4905 * @raw_response is reallocated bit stream with response body. Use
4906 * NULL if you don't care
4907 * @raw_response_length is size of @raw_response in bytes
4908 * @code is reallocated ISDS status code
4909 * @status_message is reallocated ISDS status message
4910 * @return error coded from lower layer, context message will be set up
4911 * appropriately. */
4912 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4913 const xmlChar *service_name,
4914 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4915 xmlChar **code, xmlChar **status_message) {
4917 isds_error err = IE_SUCCESS;
4918 char *service_name_locale = NULL;
4919 xmlNodePtr request = NULL, node;
4920 xmlNsPtr isds_ns = NULL;
4922 if (!context) return IE_INVALID_CONTEXT;
4923 if (!service_name) return IE_INVAL;
4924 if (!response || !code || !status_message) return IE_INVAL;
4925 if (!raw_response_length && raw_response) return IE_INVAL;
4927 /* Free output argument */
4928 xmlFreeDoc(*response); *response = NULL;
4929 if (raw_response) zfree(*raw_response);
4930 zfree(*code);
4931 zfree(*status_message);
4934 /* Check if connection is established
4935 * TODO: This check should be done downstairs. */
4936 if (!context->curl) return IE_CONNECTION_CLOSED;
4938 service_name_locale = _isds_utf82locale((char*)service_name);
4939 if (!service_name_locale) {
4940 err = IE_NOMEM;
4941 goto leave;
4944 /* Build request */
4945 request = xmlNewNode(NULL, service_name);
4946 if (!request) {
4947 isds_printf_message(context,
4948 _("Could not build %s request"), service_name_locale);
4949 err = IE_ERROR;
4950 goto leave;
4952 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4953 if(!isds_ns) {
4954 isds_log_message(context, _("Could not create ISDS name space"));
4955 err = IE_ERROR;
4956 goto leave;
4958 xmlSetNs(request, isds_ns);
4961 /* Add XSD:tDummyInput child */
4962 INSERT_STRING(request, "dbDummy", NULL);
4965 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4966 service_name_locale);
4968 /* Send request */
4969 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4970 raw_response, raw_response_length);
4971 xmlFreeNode(request); request = NULL;
4973 if (err) {
4974 isds_log(ILF_ISDS, ILL_DEBUG,
4975 _("Processing ISDS response on %s request failed\n"),
4976 service_name_locale);
4977 goto leave;
4980 /* Check for response status */
4981 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4982 code, status_message, NULL);
4983 if (err) {
4984 isds_log(ILF_ISDS, ILL_DEBUG,
4985 _("ISDS response on %s request is missing status\n"),
4986 service_name_locale);
4987 goto leave;
4990 /* Request processed, but nothing found */
4991 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4992 char *code_locale = _isds_utf82locale((char*) *code);
4993 char *status_message_locale =
4994 _isds_utf82locale((char*) *status_message);
4995 isds_log(ILF_ISDS, ILL_DEBUG,
4996 _("Server refused %s request (code=%s, message=%s)\n"),
4997 service_name_locale, code_locale, status_message_locale);
4998 isds_log_message(context, status_message_locale);
4999 free(code_locale);
5000 free(status_message_locale);
5001 err = IE_ISDS;
5002 goto leave;
5005 leave:
5006 free(service_name_locale);
5007 xmlFreeNode(request);
5008 return err;
5010 #endif
5013 /* Get data about logged in user and his box.
5014 * @context is session context
5015 * @db_owner_info is reallocated box owner description. It will be freed on
5016 * error.
5017 * @return error code from lower layer, context message will be set up
5018 * appropriately. */
5019 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
5020 struct isds_DbOwnerInfo **db_owner_info) {
5021 isds_error err = IE_SUCCESS;
5022 #if HAVE_LIBCURL
5023 xmlDocPtr response = NULL;
5024 xmlChar *code = NULL, *message = NULL;
5025 xmlXPathContextPtr xpath_ctx = NULL;
5026 xmlXPathObjectPtr result = NULL;
5027 char *string = NULL;
5028 #endif
5030 if (!context) return IE_INVALID_CONTEXT;
5031 zfree(context->long_message);
5032 if (!db_owner_info) return IE_INVAL;
5033 isds_DbOwnerInfo_free(db_owner_info);
5035 #if HAVE_LIBCURL
5036 /* Check if connection is established */
5037 if (!context->curl) return IE_CONNECTION_CLOSED;
5040 /* Do request and check for success */
5041 err = build_send_check_dbdummy_request(context,
5042 BAD_CAST "GetOwnerInfoFromLogin",
5043 &response, NULL, NULL, &code, &message);
5044 if (err) goto leave;
5047 /* Extract data */
5048 /* Prepare structure */
5049 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5050 if (!*db_owner_info) {
5051 err = IE_NOMEM;
5052 goto leave;
5054 xpath_ctx = xmlXPathNewContext(response);
5055 if (!xpath_ctx) {
5056 err = IE_ERROR;
5057 goto leave;
5059 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5060 err = IE_ERROR;
5061 goto leave;
5064 /* Set context node */
5065 result = xmlXPathEvalExpression(BAD_CAST
5066 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5067 if (!result) {
5068 err = IE_ERROR;
5069 goto leave;
5071 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5072 isds_log_message(context, _("Missing dbOwnerInfo element"));
5073 err = IE_ISDS;
5074 goto leave;
5076 if (result->nodesetval->nodeNr > 1) {
5077 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5078 err = IE_ISDS;
5079 goto leave;
5081 xpath_ctx->node = result->nodesetval->nodeTab[0];
5082 xmlXPathFreeObject(result); result = NULL;
5084 /* Extract it */
5085 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5088 leave:
5089 if (err) {
5090 isds_DbOwnerInfo_free(db_owner_info);
5093 free(string);
5094 xmlXPathFreeObject(result);
5095 xmlXPathFreeContext(xpath_ctx);
5097 free(code);
5098 free(message);
5099 xmlFreeDoc(response);
5101 if (!err)
5102 isds_log(ILF_ISDS, ILL_DEBUG,
5103 _("GetOwnerInfoFromLogin request processed by server "
5104 "successfully.\n"));
5105 #else /* not HAVE_LIBCURL */
5106 err = IE_NOTSUP;
5107 #endif
5109 return err;
5113 /* Get data about logged in user. */
5114 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5115 struct isds_DbUserInfo **db_user_info) {
5116 isds_error err = IE_SUCCESS;
5117 #if HAVE_LIBCURL
5118 xmlDocPtr response = NULL;
5119 xmlChar *code = NULL, *message = NULL;
5120 xmlXPathContextPtr xpath_ctx = NULL;
5121 xmlXPathObjectPtr result = NULL;
5122 #endif
5124 if (!context) return IE_INVALID_CONTEXT;
5125 zfree(context->long_message);
5126 if (!db_user_info) return IE_INVAL;
5127 isds_DbUserInfo_free(db_user_info);
5129 #if HAVE_LIBCURL
5130 /* Check if connection is established */
5131 if (!context->curl) return IE_CONNECTION_CLOSED;
5134 /* Do request and check for success */
5135 err = build_send_check_dbdummy_request(context,
5136 BAD_CAST "GetUserInfoFromLogin",
5137 &response, NULL, NULL, &code, &message);
5138 if (err) goto leave;
5141 /* Extract data */
5142 /* Prepare structure */
5143 *db_user_info = calloc(1, sizeof(**db_user_info));
5144 if (!*db_user_info) {
5145 err = IE_NOMEM;
5146 goto leave;
5148 xpath_ctx = xmlXPathNewContext(response);
5149 if (!xpath_ctx) {
5150 err = IE_ERROR;
5151 goto leave;
5153 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5154 err = IE_ERROR;
5155 goto leave;
5158 /* Set context node */
5159 result = xmlXPathEvalExpression(BAD_CAST
5160 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5161 if (!result) {
5162 err = IE_ERROR;
5163 goto leave;
5165 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5166 isds_log_message(context, _("Missing dbUserInfo element"));
5167 err = IE_ISDS;
5168 goto leave;
5170 if (result->nodesetval->nodeNr > 1) {
5171 isds_log_message(context, _("Multiple dbUserInfo element"));
5172 err = IE_ISDS;
5173 goto leave;
5175 xpath_ctx->node = result->nodesetval->nodeTab[0];
5176 xmlXPathFreeObject(result); result = NULL;
5178 /* Extract it */
5179 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5181 leave:
5182 if (err) {
5183 isds_DbUserInfo_free(db_user_info);
5186 xmlXPathFreeObject(result);
5187 xmlXPathFreeContext(xpath_ctx);
5189 free(code);
5190 free(message);
5191 xmlFreeDoc(response);
5193 if (!err)
5194 isds_log(ILF_ISDS, ILL_DEBUG,
5195 _("GetUserInfoFromLogin request processed by server "
5196 "successfully.\n"));
5197 #else /* not HAVE_LIBCURL */
5198 err = IE_NOTSUP;
5199 #endif
5201 return err;
5205 /* Get expiration time of current password
5206 * @context is session context
5207 * @expiration is automatically reallocated time when password expires. If
5208 * password expiration is disabled, NULL will be returned. In case of error
5209 * it will be nulled too. */
5210 isds_error isds_get_password_expiration(struct isds_ctx *context,
5211 struct timeval **expiration) {
5212 isds_error err = IE_SUCCESS;
5213 #if HAVE_LIBCURL
5214 xmlDocPtr response = NULL;
5215 xmlChar *code = NULL, *message = NULL;
5216 xmlXPathContextPtr xpath_ctx = NULL;
5217 xmlXPathObjectPtr result = NULL;
5218 char *string = NULL;
5219 #endif
5221 if (!context) return IE_INVALID_CONTEXT;
5222 zfree(context->long_message);
5223 if (!expiration) return IE_INVAL;
5224 zfree(*expiration);
5226 #if HAVE_LIBCURL
5227 /* Check if connection is established */
5228 if (!context->curl) return IE_CONNECTION_CLOSED;
5231 /* Do request and check for success */
5232 err = build_send_check_dbdummy_request(context,
5233 BAD_CAST "GetPasswordInfo",
5234 &response, NULL, NULL, &code, &message);
5235 if (err) goto leave;
5238 /* Extract data */
5239 xpath_ctx = xmlXPathNewContext(response);
5240 if (!xpath_ctx) {
5241 err = IE_ERROR;
5242 goto leave;
5244 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5245 err = IE_ERROR;
5246 goto leave;
5249 /* Set context node */
5250 result = xmlXPathEvalExpression(BAD_CAST
5251 "/isds:GetPasswordInfoResponse", xpath_ctx);
5252 if (!result) {
5253 err = IE_ERROR;
5254 goto leave;
5256 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5257 isds_log_message(context,
5258 _("Missing GetPasswordInfoResponse element"));
5259 err = IE_ISDS;
5260 goto leave;
5262 if (result->nodesetval->nodeNr > 1) {
5263 isds_log_message(context,
5264 _("Multiple GetPasswordInfoResponse element"));
5265 err = IE_ISDS;
5266 goto leave;
5268 xpath_ctx->node = result->nodesetval->nodeTab[0];
5269 xmlXPathFreeObject(result); result = NULL;
5271 /* Extract expiration date */
5272 EXTRACT_STRING("isds:pswExpDate", string);
5273 if (string) {
5274 /* And convert it if any returned. Otherwise expiration is disabled. */
5275 err = timestring2timeval((xmlChar *) string, expiration);
5276 if (err) {
5277 char *string_locale = _isds_utf82locale(string);
5278 if (err == IE_DATE) err = IE_ISDS;
5279 isds_printf_message(context,
5280 _("Could not convert pswExpDate as ISO time: %s"),
5281 string_locale);
5282 free(string_locale);
5283 goto leave;
5287 leave:
5288 if (err) {
5289 if (*expiration) {
5290 zfree(*expiration);
5294 free(string);
5295 xmlXPathFreeObject(result);
5296 xmlXPathFreeContext(xpath_ctx);
5298 free(code);
5299 free(message);
5300 xmlFreeDoc(response);
5302 if (!err)
5303 isds_log(ILF_ISDS, ILL_DEBUG,
5304 _("GetPasswordInfo request processed by server "
5305 "successfully.\n"));
5306 #else /* not HAVE_LIBCURL */
5307 err = IE_NOTSUP;
5308 #endif
5310 return err;
5314 #if HAVE_LIBCURL
5315 /* Request delivering new TOTP code from ISDS through side channel before
5316 * changing password.
5317 * @context is session context
5318 * @password is current password.
5319 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5320 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5321 * function for more details.
5322 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5323 * NULL, if you don't care.
5324 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5325 * error code. */
5326 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5327 const char *password, struct isds_otp *otp, char **refnumber) {
5328 isds_error err = IE_SUCCESS;
5329 char *saved_url = NULL; /* No copy */
5330 #if HAVE_CURL_REAUTHORIZATION_BUG
5331 CURL *saved_curl = NULL; /* No copy */
5332 #endif
5333 xmlNsPtr isds_ns = NULL;
5334 xmlNodePtr request = NULL;
5335 xmlDocPtr response = NULL;
5336 xmlChar *code = NULL, *message = NULL;
5337 const xmlChar *codes[] = {
5338 BAD_CAST "2300",
5339 BAD_CAST "2301",
5340 BAD_CAST "2302"
5342 const char *meanings[] = {
5343 N_("Unexpected error"),
5344 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5345 N_("One-time code could not been sent. Try later again.")
5347 const isds_otp_resolution resolutions[] = {
5348 OTP_RESOLUTION_UNKNOWN,
5349 OTP_RESOLUTION_TO_FAST,
5350 OTP_RESOLUTION_TOTP_NOT_SENT
5353 if (NULL == context) return IE_INVALID_CONTEXT;
5354 zfree(context->long_message);
5355 if (NULL == password) {
5356 isds_log_message(context,
5357 _("Second argument (password) of isds_change_password() "
5358 "is NULL"));
5359 return IE_INVAL;
5362 /* Check if connection is established
5363 * TODO: This check should be done downstairs. */
5364 if (!context->curl) return IE_CONNECTION_CLOSED;
5366 if (!context->otp) {
5367 isds_log_message(context, _("This function requires OTP-authenticated "
5368 "context"));
5369 return IE_INVALID_CONTEXT;
5371 if (NULL == otp) {
5372 isds_log_message(context, _("If one-time password authentication "
5373 "method is in use, requesting new OTP code requires "
5374 "one-time credentials argument either"));
5375 return IE_INVAL;
5377 if (otp->method != OTP_TIME) {
5378 isds_log_message(context, _("Requesting new time-based OTP code from "
5379 "server requires one-time password authentication "
5380 "method"));
5381 return IE_INVAL;
5383 if (otp->otp_code != NULL) {
5384 isds_log_message(context, _("Requesting new time-based OTP code from "
5385 "server requires undefined OTP code member in "
5386 "one-time credentials argument"));
5387 return IE_INVAL;
5391 /* Build request */
5392 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5393 if (!request) {
5394 isds_log_message(context, _("Could not build SendSMSCode request"));
5395 return IE_ERROR;
5397 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5398 if(!isds_ns) {
5399 isds_log_message(context, _("Could not create ISDS name space"));
5400 xmlFreeNode(request);
5401 return IE_ERROR;
5403 xmlSetNs(request, isds_ns);
5405 /* Change URL temporarily for sending this request only */
5407 char *new_url = NULL;
5408 if ((err = _isds_build_url_from_context(context,
5409 "%1$.*2$sasws/changePassword", &new_url))) {
5410 goto leave;
5412 saved_url = context->url;
5413 context->url = new_url;
5416 /* Store credentials for sending this request only */
5417 context->otp_credentials = otp;
5418 _isds_discard_credentials(context, 0);
5419 if ((err = _isds_store_credentials(context, context->saved_username,
5420 password, NULL))) {
5421 _isds_discard_credentials(context, 0);
5422 goto leave;
5424 #if HAVE_CURL_REAUTHORIZATION_BUG
5425 saved_curl = context->curl;
5426 context->curl = curl_easy_init();
5427 if (NULL == context->curl) {
5428 err = IE_ERROR;
5429 goto leave;
5431 if (context->timeout) {
5432 err = isds_set_timeout(context, context->timeout);
5433 if (err) goto leave;
5435 #endif
5437 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5439 /* Sent request */
5440 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5442 /* Remove temporal credentials */
5443 _isds_discard_credentials(context, 0);
5444 /* Detach pointer to OTP credentials from context */
5445 context->otp_credentials = NULL;
5446 /* Keep context->otp true to keep signaling this is OTP session */
5448 /* Destroy request */
5449 xmlFreeNode(request); request = NULL;
5451 if (err) {
5452 isds_log(ILF_ISDS, ILL_DEBUG,
5453 _("Processing ISDS response on SendSMSCode request failed\n"));
5454 goto leave;
5457 /* Check for response status */
5458 err = isds_response_status(context, SERVICE_ASWS, response,
5459 &code, &message, (xmlChar **)refnumber);
5460 if (err) {
5461 isds_log(ILF_ISDS, ILL_DEBUG,
5462 _("ISDS response on SendSMSCode request is missing "
5463 "status\n"));
5464 goto leave;
5467 /* Check for error */
5468 if (xmlStrcmp(code, BAD_CAST "0000")) {
5469 char *code_locale = _isds_utf82locale((char*)code);
5470 char *message_locale = _isds_utf82locale((char*)message);
5471 size_t i;
5472 isds_log(ILF_ISDS, ILL_DEBUG,
5473 _("Server refused to send new code on SendSMSCode "
5474 "request (code=%s, message=%s)\n"),
5475 code_locale, message_locale);
5477 /* Check for known error codes */
5478 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5479 if (!xmlStrcmp(code, codes[i])) break;
5481 if (i < sizeof(codes)/sizeof(*codes)) {
5482 isds_log_message(context, _(meanings[i]));
5483 /* Mimic otp->resolution according to the code, specification does
5484 * prescribe OTP header to be available. */
5485 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5486 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5487 otp->resolution = resolutions[i];
5488 } else
5489 isds_log_message(context, message_locale);
5491 free(code_locale);
5492 free(message_locale);
5494 err = IE_ISDS;
5495 goto leave;
5498 /* Otherwise new code sent successfully */
5499 /* Mimic otp->resolution according to the code, specification does
5500 * prescribe OTP header to be available. */
5501 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5502 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5504 leave:
5505 if (NULL != saved_url) {
5506 /* Revert URL to original one */
5507 zfree(context->url);
5508 context->url = saved_url;
5510 #if HAVE_CURL_REAUTHORIZATION_BUG
5511 if (NULL != saved_curl) {
5512 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5513 context->curl = saved_curl;
5515 #endif
5517 free(code);
5518 free(message);
5519 xmlFreeDoc(response);
5520 xmlFreeNode(request);
5522 if (!err)
5523 isds_log(ILF_ISDS, ILL_DEBUG,
5524 _("New OTP code has been sent successfully on SendSMSCode "
5525 "request.\n"));
5526 return err;
5530 /* Convert response status code to isds_error code and set long message
5531 * @context is context to save long message to
5532 * @map is mapping from codes to errors and messages. Pass NULL for generic
5533 * handling.
5534 * @code is status code to translate
5535 * @message is non-localized status message to put into long message in case
5536 * of uknown error. It can be NULL if server did not provide any.
5537 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5538 * invalid invocation. */
5539 static isds_error statuscode2isds_error(struct isds_ctx *context,
5540 const struct code_map_isds_error *map,
5541 const xmlChar *code, const xmlChar *message) {
5542 if (NULL == code) {
5543 isds_log_message(context,
5544 _("NULL status code passed to statuscode2isds_error()"));
5545 return IE_INVAL;
5548 if (NULL != map) {
5549 /* Check for known error codes */
5550 for (int i=0; map->codes[i] != NULL; i++) {
5551 if (!xmlStrcmp(code, map->codes[i])) {
5552 isds_log_message(context, _(map->meanings[i]));
5553 return map->errors[i];
5558 /* Other error */
5559 if (xmlStrcmp(code, BAD_CAST "0000")) {
5560 char *message_locale = _isds_utf82locale((char*)message);
5561 if (NULL == message_locale)
5562 isds_log_message(context, _("ISDS server returned unknown error"));
5563 else
5564 isds_log_message(context, message_locale);
5565 free(message_locale);
5566 return IE_ISDS;
5569 return IE_SUCCESS;
5571 #endif
5574 /* Change user password in ISDS.
5575 * User must supply old password, new password will takes effect after some
5576 * time, current session can continue. Password must fulfill some constraints.
5577 * @context is session context
5578 * @old_password is current password.
5579 * @new_password is requested new password
5580 * @otp auxiliary data required if one-time password authentication is in use,
5581 * defines OTP code (if known) and returns fine grade resolution of OTP
5582 * procedure. Pass NULL, if one-time password authentication is not needed.
5583 * Please note the @otp argument must match OTP method used at log-in time. See
5584 * isds_login() function for more details.
5585 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5586 * NULL, if you don't care.
5587 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5588 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5589 * awaiting OTP code that has been delivered by side channel to the user. */
5590 isds_error isds_change_password(struct isds_ctx *context,
5591 const char *old_password, const char *new_password,
5592 struct isds_otp *otp, char **refnumber) {
5593 isds_error err = IE_SUCCESS;
5594 #if HAVE_LIBCURL
5595 char *saved_url = NULL; /* No copy */
5596 #if HAVE_CURL_REAUTHORIZATION_BUG
5597 CURL *saved_curl = NULL; /* No copy */
5598 #endif
5599 xmlNsPtr isds_ns = NULL;
5600 xmlNodePtr request = NULL, node;
5601 xmlDocPtr response = NULL;
5602 xmlChar *code = NULL, *message = NULL;
5603 const xmlChar *codes[] = {
5604 BAD_CAST "1066",
5605 BAD_CAST "1067",
5606 BAD_CAST "1079",
5607 BAD_CAST "1080",
5608 BAD_CAST "1081",
5609 BAD_CAST "1082",
5610 BAD_CAST "1083",
5611 BAD_CAST "1090",
5612 BAD_CAST "1091",
5613 BAD_CAST "2300",
5614 BAD_CAST "9204"
5616 const char *meanings[] = {
5617 N_("Password length must be between 8 and 32 characters"),
5618 N_("Password cannot be reused"), /* Server does not distinguish 1067
5619 and 1091 on ChangePasswordOTP */
5620 N_("Password contains forbidden character"),
5621 N_("Password must contain at least one upper-case letter, "
5622 "one lower-case, and one digit"),
5623 N_("Password cannot contain sequence of three identical characters"),
5624 N_("Password cannot contain user identifier"),
5625 N_("Password is too simmple"),
5626 N_("Old password is not valid"),
5627 N_("Password cannot be reused"),
5628 N_("Unexpected error"),
5629 N_("LDAP update error")
5631 #endif
5633 if (!context) return IE_INVALID_CONTEXT;
5634 zfree(context->long_message);
5635 if (NULL != refnumber)
5636 zfree(*refnumber);
5637 if (NULL == old_password) {
5638 isds_log_message(context,
5639 _("Second argument (old password) of isds_change_password() "
5640 "is NULL"));
5641 return IE_INVAL;
5643 if (NULL == otp && NULL == new_password) {
5644 isds_log_message(context,
5645 _("Third argument (new password) of isds_change_password() "
5646 "is NULL"));
5647 return IE_INVAL;
5650 #if HAVE_LIBCURL
5651 /* Check if connection is established
5652 * TODO: This check should be done downstairs. */
5653 if (!context->curl) return IE_CONNECTION_CLOSED;
5655 if (context->otp && NULL == otp) {
5656 isds_log_message(context, _("If one-time password authentication "
5657 "method is in use, changing password requires one-time "
5658 "credentials either"));
5659 return IE_INVAL;
5662 /* Build ChangeISDSPassword request */
5663 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5664 BAD_CAST "ChangePasswordOTP");
5665 if (!request) {
5666 isds_log_message(context, (NULL == otp) ?
5667 _("Could not build ChangeISDSPassword request") :
5668 _("Could not build ChangePasswordOTP request"));
5669 return IE_ERROR;
5671 isds_ns = xmlNewNs(request,
5672 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5673 NULL);
5674 if(!isds_ns) {
5675 isds_log_message(context, _("Could not create ISDS name space"));
5676 xmlFreeNode(request);
5677 return IE_ERROR;
5679 xmlSetNs(request, isds_ns);
5681 INSERT_STRING(request, "dbOldPassword", old_password);
5682 INSERT_STRING(request, "dbNewPassword", new_password);
5684 if (NULL != otp) {
5685 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5686 switch (otp->method) {
5687 case OTP_HMAC:
5688 isds_log(ILF_SEC, ILL_INFO,
5689 _("Selected authentication method: "
5690 "HMAC-based one-time password\n"));
5691 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5692 break;
5693 case OTP_TIME:
5694 isds_log(ILF_SEC, ILL_INFO,
5695 _("Selected authentication method: "
5696 "Time-based one-time password\n"));
5697 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5698 if (otp->otp_code == NULL) {
5699 isds_log(ILF_SEC, ILL_INFO,
5700 _("OTP code has not been provided by "
5701 "application, requesting server for "
5702 "new one.\n"));
5703 err = _isds_request_totp_code(context, old_password, otp,
5704 refnumber);
5705 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5706 goto leave;
5708 } else {
5709 isds_log(ILF_SEC, ILL_INFO,
5710 _("OTP code has been provided by "
5711 "application, not requesting server "
5712 "for new one.\n"));
5714 break;
5715 default:
5716 isds_log_message(context,
5717 _("Unknown one-time password authentication "
5718 "method requested by application"));
5719 err = IE_ENUM;
5720 goto leave;
5723 /* Change URL temporarily for sending this request only */
5725 char *new_url = NULL;
5726 if ((err = _isds_build_url_from_context(context,
5727 "%1$.*2$sasws/changePassword", &new_url))) {
5728 goto leave;
5730 saved_url = context->url;
5731 context->url = new_url;
5734 /* Store credentials for sending this request only */
5735 context->otp_credentials = otp;
5736 _isds_discard_credentials(context, 0);
5737 if ((err = _isds_store_credentials(context, context->saved_username,
5738 old_password, NULL))) {
5739 _isds_discard_credentials(context, 0);
5740 goto leave;
5742 #if HAVE_CURL_REAUTHORIZATION_BUG
5743 saved_curl = context->curl;
5744 context->curl = curl_easy_init();
5745 if (NULL == context->curl) {
5746 err = IE_ERROR;
5747 goto leave;
5749 if (context->timeout) {
5750 err = isds_set_timeout(context, context->timeout);
5751 if (err) goto leave;
5753 #endif
5756 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5757 _("Sending ChangeISDSPassword request to ISDS\n") :
5758 _("Sending ChangePasswordOTP request to ISDS\n"));
5760 /* Sent request */
5761 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5762 request, &response, NULL, NULL);
5764 if (otp) {
5765 /* Remove temporal credentials */
5766 _isds_discard_credentials(context, 0);
5767 /* Detach pointer to OTP credentials from context */
5768 context->otp_credentials = NULL;
5769 /* Keep context->otp true to keep signaling this is OTP session */
5772 /* Destroy request */
5773 xmlFreeNode(request); request = NULL;
5775 if (err) {
5776 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5777 _("Processing ISDS response on ChangeISDSPassword "
5778 "request failed\n") :
5779 _("Processing ISDS response on ChangePasswordOTP "
5780 "request failed\n"));
5781 goto leave;
5784 /* Check for response status */
5785 err = isds_response_status(context,
5786 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5787 &code, &message, (xmlChar **)refnumber);
5788 if (err) {
5789 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5790 _("ISDS response on ChangeISDSPassword request is missing "
5791 "status\n") :
5792 _("ISDS response on ChangePasswordOTP request is missing "
5793 "status\n"));
5794 goto leave;
5797 /* Check for known error codes */
5798 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5799 if (!xmlStrcmp(code, codes[i])) {
5800 char *code_locale = _isds_utf82locale((char*)code);
5801 char *message_locale = _isds_utf82locale((char*)message);
5802 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5803 _("Server refused to change password on ChangeISDSPassword "
5804 "request (code=%s, message=%s)\n") :
5805 _("Server refused to change password on ChangePasswordOTP "
5806 "request (code=%s, message=%s)\n"),
5807 code_locale, message_locale);
5808 free(code_locale);
5809 free(message_locale);
5810 isds_log_message(context, _(meanings[i]));
5811 err = IE_INVAL;
5812 goto leave;
5816 /* Other error */
5817 if (xmlStrcmp(code, BAD_CAST "0000")) {
5818 char *code_locale = _isds_utf82locale((char*)code);
5819 char *message_locale = _isds_utf82locale((char*)message);
5820 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5821 _("Server refused to change password on ChangeISDSPassword "
5822 "request (code=%s, message=%s)\n") :
5823 _("Server refused to change password on ChangePasswordOTP "
5824 "request (code=%s, message=%s)\n"),
5825 code_locale, message_locale);
5826 isds_log_message(context, message_locale);
5827 free(code_locale);
5828 free(message_locale);
5829 err = IE_ISDS;
5830 goto leave;
5833 /* Otherwise password changed successfully */
5835 leave:
5836 if (NULL != saved_url) {
5837 /* Revert URL to original one */
5838 zfree(context->url);
5839 context->url = saved_url;
5841 #if HAVE_CURL_REAUTHORIZATION_BUG
5842 if (NULL != saved_curl) {
5843 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5844 context->curl = saved_curl;
5846 #endif
5848 free(code);
5849 free(message);
5850 xmlFreeDoc(response);
5851 xmlFreeNode(request);
5853 if (!err)
5854 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5855 _("Password changed successfully on ChangeISDSPassword "
5856 "request.\n") :
5857 _("Password changed successfully on ChangePasswordOTP "
5858 "request.\n"));
5859 #else /* not HAVE_LIBCURL */
5860 err = IE_NOTSUP;
5861 #endif
5863 return err;
5867 #if HAVE_LIBCURL
5868 /* Generic middle part with request sending and response check.
5869 * It sends prepared request and checks for error code.
5870 * @context is ISDS session context.
5871 * @service is ISDS service handler
5872 * @service_name is name in scope of given @service
5873 * @request is XML tree with request. Will be freed to save memory.
5874 * @response is XML document outputting ISDS response.
5875 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5876 * @map is mapping from status code to library error. Pass NULL if no special
5877 * handling is requested.
5878 * NULL, if you don't care. */
5879 static isds_error send_destroy_request_check_response(
5880 struct isds_ctx *context,
5881 const isds_service service, const xmlChar *service_name,
5882 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5883 const struct code_map_isds_error *map) {
5884 isds_error err = IE_SUCCESS;
5885 char *service_name_locale = NULL;
5886 xmlChar *code = NULL, *message = NULL;
5889 if (!context) return IE_INVALID_CONTEXT;
5890 if (!service_name || *service_name == '\0' || !request || !*request ||
5891 !response)
5892 return IE_INVAL;
5894 /* Check if connection is established
5895 * TODO: This check should be done downstairs. */
5896 if (!context->curl) return IE_CONNECTION_CLOSED;
5898 service_name_locale = _isds_utf82locale((char*) service_name);
5899 if (!service_name_locale) {
5900 err = IE_NOMEM;
5901 goto leave;
5904 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5905 service_name_locale);
5907 /* Send request */
5908 err = _isds(context, service, *request, response, NULL, NULL);
5909 xmlFreeNode(*request); *request = NULL;
5911 if (err) {
5912 isds_log(ILF_ISDS, ILL_DEBUG,
5913 _("Processing ISDS response on %s request failed\n"),
5914 service_name_locale);
5915 goto leave;
5918 /* Check for response status */
5919 err = isds_response_status(context, service, *response,
5920 &code, &message, refnumber);
5921 if (err) {
5922 isds_log(ILF_ISDS, ILL_DEBUG,
5923 _("ISDS response on %s request is missing status\n"),
5924 service_name_locale);
5925 goto leave;
5928 err = statuscode2isds_error(context, map, code, message);
5930 /* Request processed, but server failed */
5931 if (xmlStrcmp(code, BAD_CAST "0000")) {
5932 char *code_locale = _isds_utf82locale((char*) code);
5933 char *message_locale = _isds_utf82locale((char*) message);
5934 isds_log(ILF_ISDS, ILL_DEBUG,
5935 _("Server refused %s request (code=%s, message=%s)\n"),
5936 service_name_locale, code_locale, message_locale);
5937 free(code_locale);
5938 free(message_locale);
5939 goto leave;
5943 leave:
5944 free(code);
5945 free(message);
5946 if (err && *response) {
5947 xmlFreeDoc(*response);
5948 *response = NULL;
5950 if (*request) {
5951 xmlFreeNode(*request);
5952 *request = NULL;
5954 free(service_name_locale);
5956 return err;
5960 /* Generic bottom half with request sending.
5961 * It sends prepared request, checks for error code, destroys response and
5962 * request and log success or failure.
5963 * @context is ISDS session context.
5964 * @service is ISDS service handler
5965 * @service_name is name in scope of given @service
5966 * @request is XML tree with request. Will be freed to save memory.
5967 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5968 * NULL, if you don't care. */
5969 static isds_error send_request_check_drop_response(
5970 struct isds_ctx *context,
5971 const isds_service service, const xmlChar *service_name,
5972 xmlNodePtr *request, xmlChar **refnumber) {
5973 isds_error err = IE_SUCCESS;
5974 xmlDocPtr response = NULL;
5977 if (!context) return IE_INVALID_CONTEXT;
5978 if (!service_name || *service_name == '\0' || !request || !*request)
5979 return IE_INVAL;
5981 /* Send request and check response*/
5982 err = send_destroy_request_check_response(context,
5983 service, service_name, request, &response, refnumber, NULL);
5985 xmlFreeDoc(response);
5987 if (*request) {
5988 xmlFreeNode(*request);
5989 *request = NULL;
5992 if (!err) {
5993 char *service_name_locale = _isds_utf82locale((char *) service_name);
5994 isds_log(ILF_ISDS, ILL_DEBUG,
5995 _("%s request processed by server successfully.\n"),
5996 service_name_locale);
5997 free(service_name_locale);
6000 return err;
6004 /* Insert isds_credentials_delivery structure into XML request if not NULL
6005 * @context is session context
6006 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6007 * credentials delivery. The email field is passed.
6008 * @parent is XML element where to insert */
6009 static isds_error insert_credentials_delivery(struct isds_ctx *context,
6010 const struct isds_credentials_delivery *credentials_delivery,
6011 xmlNodePtr parent) {
6012 isds_error err = IE_SUCCESS;
6013 xmlNodePtr node;
6015 if (!context) return IE_INVALID_CONTEXT;
6016 if (!parent) return IE_INVAL;
6018 if (credentials_delivery) {
6019 /* Following elements are valid only for services:
6020 * NewAccessData, AddDataBoxUser, CreateDataBox */
6021 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
6022 INSERT_STRING(parent, "email", credentials_delivery->email);
6025 leave:
6026 return err;
6030 /* Extract credentials delivery from ISDS response.
6031 * @context is session context
6032 * @credentials_delivery is pointer to valid structure to fill in returned
6033 * user's password (and new log-in name). If NULL, do not extract the data.
6034 * @response is pointer to XML document with ISDS response
6035 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6036 * @return IE_SUCCESS even if new user name has not been found because it's not
6037 * clear whether it's returned always. */
6038 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6039 struct isds_credentials_delivery *credentials_delivery,
6040 xmlDocPtr response, const char *request_name) {
6041 isds_error err = IE_SUCCESS;
6042 xmlXPathContextPtr xpath_ctx = NULL;
6043 xmlXPathObjectPtr result = NULL;
6044 char *xpath_query = NULL;
6046 if (!context) return IE_INVALID_CONTEXT;
6047 if (credentials_delivery) {
6048 zfree(credentials_delivery->token);
6049 zfree(credentials_delivery->new_user_name);
6051 if (!response || !request_name || !*request_name) return IE_INVAL;
6054 /* Extract optional token */
6055 if (credentials_delivery) {
6056 xpath_ctx = xmlXPathNewContext(response);
6057 if (!xpath_ctx) {
6058 err = IE_ERROR;
6059 goto leave;
6061 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6062 err = IE_ERROR;
6063 goto leave;
6066 /* Verify root element */
6067 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6068 request_name)) {
6069 err = IE_NOMEM;
6070 goto leave;
6072 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6073 if (!result) {
6074 err = IE_ERROR;
6075 goto leave;
6077 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6078 char *request_name_locale = _isds_utf82locale(request_name);
6079 isds_log(ILF_ISDS, ILL_WARNING,
6080 _("Wrong element in ISDS response for %s request "
6081 "while extracting credentials delivery details\n"),
6082 request_name_locale);
6083 free(request_name_locale);
6084 err = IE_ERROR;
6085 goto leave;
6087 xpath_ctx->node = result->nodesetval->nodeTab[0];
6090 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6091 * optional. */
6092 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6094 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6095 if (!credentials_delivery->token) {
6096 char *request_name_locale = _isds_utf82locale(request_name);
6097 isds_log(ILF_ISDS, ILL_ERR,
6098 _("ISDS did not return token on %s request "
6099 "even if requested\n"), request_name_locale);
6100 free(request_name_locale);
6101 err = IE_ERROR;
6105 leave:
6106 free(xpath_query);
6107 xmlXPathFreeObject(result);
6108 xmlXPathFreeContext(xpath_ctx);
6110 return err;
6114 /* Build XSD:tCreateDBInput request type for box creating.
6115 * @context is session context
6116 * @request outputs built XML tree
6117 * @service_name is request name of SERVICE_DB_MANIPULATION service
6118 * @box is box description to create including single primary user (in case of
6119 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6120 * ignored.
6121 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6122 * box, or contact address of PFO box owner)
6123 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6124 * @upper_box_id is optional ID of supper box if currently created box is
6125 * subordinated.
6126 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6127 * don't care.
6128 * @credentials_delivery is valid pointer if ISDS should return token that box
6129 * owner can use to obtain his new credentials in on-line way. Then valid email
6130 * member value should be supplied.
6131 * @approval is optional external approval of box manipulation */
6132 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6133 xmlNodePtr *request, const xmlChar *service_name,
6134 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6135 const xmlChar *former_names, const xmlChar *upper_box_id,
6136 const xmlChar *ceo_label,
6137 const struct isds_credentials_delivery *credentials_delivery,
6138 const struct isds_approval *approval) {
6139 isds_error err = IE_SUCCESS;
6140 xmlNsPtr isds_ns = NULL;
6141 xmlNodePtr node, dbPrimaryUsers;
6142 xmlChar *string = NULL;
6143 const struct isds_list *item;
6146 if (!context) return IE_INVALID_CONTEXT;
6147 if (!request || !service_name || service_name[0] == '\0' || !box)
6148 return IE_INVAL;
6151 /* Build CreateDataBox-similar request */
6152 *request = xmlNewNode(NULL, service_name);
6153 if (!*request) {
6154 char *service_name_locale = _isds_utf82locale((char*) service_name);
6155 isds_printf_message(context, _("Could build %s request"),
6156 service_name_locale);
6157 free(service_name_locale);
6158 return IE_ERROR;
6160 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6161 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6162 if (!isds_ns) {
6163 isds_log_message(context, _("Could not create ISDS1 name space"));
6164 xmlFreeNode(*request);
6165 return IE_ERROR;
6167 } else {
6168 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6169 if (!isds_ns) {
6170 isds_log_message(context, _("Could not create ISDS name space"));
6171 xmlFreeNode(*request);
6172 return IE_ERROR;
6175 xmlSetNs(*request, isds_ns);
6177 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6178 err = insert_DbOwnerInfo(context, box, 0, node);
6179 if (err) goto leave;
6181 /* Insert users */
6182 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6183 * verbose documentation allows none dbUserInfo */
6184 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6185 for (item = users; item; item = item->next) {
6186 if (item->data) {
6187 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6188 err = insert_DbUserInfo(context,
6189 (struct isds_DbUserInfo *) item->data, 1, node);
6190 if (err) goto leave;
6194 INSERT_STRING(*request, "dbFormerNames", former_names);
6195 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6196 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6198 err = insert_credentials_delivery(context, credentials_delivery, *request);
6199 if (err) goto leave;
6201 err = insert_GExtApproval(context, approval, *request);
6202 if (err) goto leave;
6204 leave:
6205 if (err) {
6206 xmlFreeNode(*request);
6207 *request = NULL;
6209 free(string);
6210 return err;
6212 #endif /* HAVE_LIBCURL */
6215 /* Create new box.
6216 * @context is session context
6217 * @box is box description to create including single primary user (in case of
6218 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6219 * ignored. It outputs box ID assigned by ISDS in dbID element.
6220 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6221 * box, or contact address of PFO box owner)
6222 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6223 * @upper_box_id is optional ID of supper box if currently created box is
6224 * subordinated.
6225 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6226 * @credentials_delivery is NULL if new password should be delivered off-line
6227 * to box owner. It is valid pointer if owner should obtain new password on-line
6228 * on dedicated web server. Then input @credentials_delivery.email value is
6229 * his e-mail address he must provide to dedicated web server together
6230 * with output reallocated @credentials_delivery.token member. Output
6231 * member @credentials_delivery.new_user_name is unused up on this call.
6232 * @approval is optional external approval of box manipulation
6233 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6234 * NULL, if you don't care.*/
6235 isds_error isds_add_box(struct isds_ctx *context,
6236 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6237 const char *former_names, const char *upper_box_id,
6238 const char *ceo_label,
6239 struct isds_credentials_delivery *credentials_delivery,
6240 const struct isds_approval *approval, char **refnumber) {
6241 isds_error err = IE_SUCCESS;
6242 #if HAVE_LIBCURL
6243 xmlNodePtr request = NULL;
6244 xmlDocPtr response = NULL;
6245 xmlXPathContextPtr xpath_ctx = NULL;
6246 xmlXPathObjectPtr result = NULL;
6247 #endif
6250 if (!context) return IE_INVALID_CONTEXT;
6251 zfree(context->long_message);
6252 if (credentials_delivery) {
6253 zfree(credentials_delivery->token);
6254 zfree(credentials_delivery->new_user_name);
6256 if (!box) return IE_INVAL;
6258 #if HAVE_LIBCURL
6259 /* Scratch box ID */
6260 zfree(box->dbID);
6262 /* Build CreateDataBox request */
6263 err = build_CreateDBInput_request(context,
6264 &request, BAD_CAST "CreateDataBox",
6265 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6266 (xmlChar *) ceo_label, credentials_delivery, approval);
6267 if (err) goto leave;
6269 /* Send it to server and process response */
6270 err = send_destroy_request_check_response(context,
6271 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6272 &response, (xmlChar **) refnumber, NULL);
6274 /* Extract box ID */
6275 xpath_ctx = xmlXPathNewContext(response);
6276 if (!xpath_ctx) {
6277 err = IE_ERROR;
6278 goto leave;
6280 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6281 err = IE_ERROR;
6282 goto leave;
6284 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6286 /* Extract optional token */
6287 err = extract_credentials_delivery(context, credentials_delivery, response,
6288 "CreateDataBox");
6290 leave:
6291 xmlXPathFreeObject(result);
6292 xmlXPathFreeContext(xpath_ctx);
6293 xmlFreeDoc(response);
6294 xmlFreeNode(request);
6296 if (!err) {
6297 isds_log(ILF_ISDS, ILL_DEBUG,
6298 _("CreateDataBox request processed by server successfully.\n"));
6300 #else /* not HAVE_LIBCURL */
6301 err = IE_NOTSUP;
6302 #endif
6304 return err;
6308 /* Notify ISDS about new PFO entity.
6309 * This function has no real effect.
6310 * @context is session context
6311 * @box is PFO description including single primary user. aifoIsds,
6312 * address->adCode, address->adDistrict members are ignored.
6313 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6314 * @former_names is optional undocumented string. Pass NULL if you don't care.
6315 * @upper_box_id is optional ID of supper box if currently created box is
6316 * subordinated.
6317 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6318 * @approval is optional external approval of box manipulation
6319 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6320 * NULL, if you don't care.*/
6321 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6322 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6323 const char *former_names, const char *upper_box_id,
6324 const char *ceo_label, const struct isds_approval *approval,
6325 char **refnumber) {
6326 isds_error err = IE_SUCCESS;
6327 #if HAVE_LIBCURL
6328 xmlNodePtr request = NULL;
6329 #endif
6331 if (!context) return IE_INVALID_CONTEXT;
6332 zfree(context->long_message);
6333 if (!box) return IE_INVAL;
6335 #if HAVE_LIBCURL
6336 /* Build CreateDataBoxPFOInfo request */
6337 err = build_CreateDBInput_request(context,
6338 &request, BAD_CAST "CreateDataBoxPFOInfo",
6339 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6340 (xmlChar *) ceo_label, NULL, approval);
6341 if (err) goto leave;
6343 /* Send it to server and process response */
6344 err = send_request_check_drop_response(context,
6345 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6346 (xmlChar **) refnumber);
6347 /* XXX: XML Schema names output dbID element but textual documentation
6348 * states no box identifier is returned. */
6349 leave:
6350 xmlFreeNode(request);
6351 #else /* not HAVE_LIBCURL */
6352 err = IE_NOTSUP;
6353 #endif
6354 return err;
6358 /* Common implementation for removing given box.
6359 * @context is session context
6360 * @service_name is UTF-8 encoded name fo ISDS service
6361 * @box is box description to delete. aifoIsds, address->adCode,
6362 * address->adDistrict members are ignored.
6363 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6364 * carry sane value. If NULL, do not inject this information into request.
6365 * @approval is optional external approval of box manipulation
6366 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6367 * NULL, if you don't care.*/
6368 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6369 const xmlChar *service_name,
6370 const struct isds_DbOwnerInfo *box, const struct tm *since,
6371 const struct isds_approval *approval, char **refnumber) {
6372 isds_error err = IE_SUCCESS;
6373 #if HAVE_LIBCURL
6374 xmlNsPtr isds_ns = NULL;
6375 xmlNodePtr request = NULL;
6376 xmlNodePtr node;
6377 xmlChar *string = NULL;
6378 #endif
6381 if (!context) return IE_INVALID_CONTEXT;
6382 zfree(context->long_message);
6383 if (!service_name || !*service_name || !box) return IE_INVAL;
6386 #if HAVE_LIBCURL
6387 /* Build DeleteDataBox(Promptly) request */
6388 request = xmlNewNode(NULL, service_name);
6389 if (!request) {
6390 char *service_name_locale = _isds_utf82locale((char*)service_name);
6391 isds_printf_message(context,
6392 _("Could build %s request"), service_name_locale);
6393 free(service_name_locale);
6394 return IE_ERROR;
6396 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6397 if(!isds_ns) {
6398 isds_log_message(context, _("Could not create ISDS name space"));
6399 xmlFreeNode(request);
6400 return IE_ERROR;
6402 xmlSetNs(request, isds_ns);
6404 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6405 err = insert_DbOwnerInfo(context, box, 0, node);
6406 if (err) goto leave;
6408 if (since) {
6409 err = tm2datestring(since, &string);
6410 if (err) {
6411 isds_log_message(context,
6412 _("Could not convert `since' argument to ISO date string"));
6413 goto leave;
6415 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6416 zfree(string);
6419 err = insert_GExtApproval(context, approval, request);
6420 if (err) goto leave;
6423 /* Send it to server and process response */
6424 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6425 service_name, &request, (xmlChar **) refnumber);
6427 leave:
6428 xmlFreeNode(request);
6429 free(string);
6430 #else /* not HAVE_LIBCURL */
6431 err = IE_NOTSUP;
6432 #endif
6433 return err;
6437 /* Remove given box permanently.
6438 * @context is session context
6439 * @box is box description to delete. aifoIsds, address->adCode,
6440 * address->adDistrict members are ignored.
6441 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6442 * carry sane value.
6443 * @approval is optional external approval of box manipulation
6444 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6445 * NULL, if you don't care.*/
6446 isds_error isds_delete_box(struct isds_ctx *context,
6447 const struct isds_DbOwnerInfo *box, const struct tm *since,
6448 const struct isds_approval *approval, char **refnumber) {
6449 if (!context) return IE_INVALID_CONTEXT;
6450 zfree(context->long_message);
6451 if (!box || !since) return IE_INVAL;
6453 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6454 box, since, approval, refnumber);
6458 /* Undocumented function.
6459 * @context is session context
6460 * @box is box description to delete. aifoIsds, address->adCode,
6461 * address->adDistrict members are ignored.
6462 * @approval is optional external approval of box manipulation
6463 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6464 * NULL, if you don't care.*/
6465 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6466 const struct isds_DbOwnerInfo *box,
6467 const struct isds_approval *approval, char **refnumber) {
6468 if (!context) return IE_INVALID_CONTEXT;
6469 zfree(context->long_message);
6470 if (!box) return IE_INVAL;
6472 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6473 box, NULL, approval, refnumber);
6477 /* Update data about given box.
6478 * @context is session context
6479 * @old_box current box description. aifoIsds, address->adCode,
6480 * address->adDistrict members are ignored.
6481 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6482 * address->adDistrict members are ignored.
6483 * @approval is optional external approval of box manipulation
6484 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6485 * NULL, if you don't care.*/
6486 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6487 const struct isds_DbOwnerInfo *old_box,
6488 const struct isds_DbOwnerInfo *new_box,
6489 const struct isds_approval *approval, char **refnumber) {
6490 isds_error err = IE_SUCCESS;
6491 #if HAVE_LIBCURL
6492 xmlNsPtr isds_ns = NULL;
6493 xmlNodePtr request = NULL;
6494 xmlNodePtr node;
6495 #endif
6498 if (!context) return IE_INVALID_CONTEXT;
6499 zfree(context->long_message);
6500 if (!old_box || !new_box) return IE_INVAL;
6503 #if HAVE_LIBCURL
6504 /* Build UpdateDataBoxDescr request */
6505 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6506 if (!request) {
6507 isds_log_message(context,
6508 _("Could build UpdateDataBoxDescr request"));
6509 return IE_ERROR;
6511 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6512 if(!isds_ns) {
6513 isds_log_message(context, _("Could not create ISDS name space"));
6514 xmlFreeNode(request);
6515 return IE_ERROR;
6517 xmlSetNs(request, isds_ns);
6519 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6520 err = insert_DbOwnerInfo(context, old_box, 0, node);
6521 if (err) goto leave;
6523 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6524 err = insert_DbOwnerInfo(context, new_box, 0, node);
6525 if (err) goto leave;
6527 err = insert_GExtApproval(context, approval, request);
6528 if (err) goto leave;
6531 /* Send it to server and process response */
6532 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6533 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6535 leave:
6536 xmlFreeNode(request);
6537 #else /* not HAVE_LIBCURL */
6538 err = IE_NOTSUP;
6539 #endif
6541 return err;
6545 #if HAVE_LIBCURL
6546 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6547 * code
6548 * @context is session context
6549 * @service is SOAP service
6550 * @service_name is name of request in @service
6551 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6552 * @box_id is box ID of interest
6553 * @approval is optional external approval of box manipulation
6554 * @response is server SOAP body response as XML document
6555 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6556 * NULL, if you don't care.
6557 * @return error coded from lower layer, context message will be set up
6558 * appropriately. */
6559 static isds_error build_send_dbid_request_check_response(
6560 struct isds_ctx *context, const isds_service service,
6561 const xmlChar *service_name, const xmlChar *box_id_element,
6562 const xmlChar *box_id, const struct isds_approval *approval,
6563 xmlDocPtr *response, xmlChar **refnumber) {
6565 isds_error err = IE_SUCCESS;
6566 char *service_name_locale = NULL, *box_id_locale = NULL;
6567 xmlNodePtr request = NULL, node;
6568 xmlNsPtr isds_ns = NULL;
6570 if (!context) return IE_INVALID_CONTEXT;
6571 if (!service_name || !box_id) return IE_INVAL;
6572 if (!response) return IE_INVAL;
6574 /* Free output argument */
6575 xmlFreeDoc(*response); *response = NULL;
6577 /* Prepare strings */
6578 service_name_locale = _isds_utf82locale((char*)service_name);
6579 if (!service_name_locale) {
6580 err = IE_NOMEM;
6581 goto leave;
6583 box_id_locale = _isds_utf82locale((char*)box_id);
6584 if (!box_id_locale) {
6585 err = IE_NOMEM;
6586 goto leave;
6589 /* Build request */
6590 request = xmlNewNode(NULL, service_name);
6591 if (!request) {
6592 isds_printf_message(context,
6593 _("Could not build %s request for %s box"), service_name_locale,
6594 box_id_locale);
6595 err = IE_ERROR;
6596 goto leave;
6598 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6599 if(!isds_ns) {
6600 isds_log_message(context, _("Could not create ISDS name space"));
6601 err = IE_ERROR;
6602 goto leave;
6604 xmlSetNs(request, isds_ns);
6606 /* Add XSD:tIdDbInput children */
6607 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6608 INSERT_STRING(request, box_id_element, box_id);
6609 err = insert_GExtApproval(context, approval, request);
6610 if (err) goto leave;
6612 /* Send request and check response*/
6613 err = send_destroy_request_check_response(context,
6614 service, service_name, &request, response, refnumber, NULL);
6616 leave:
6617 free(service_name_locale);
6618 free(box_id_locale);
6619 xmlFreeNode(request);
6620 return err;
6622 #endif /* HAVE_LIBCURL */
6625 /* Get data about all users assigned to given box.
6626 * @context is session context
6627 * @box_id is box ID
6628 * @users is automatically reallocated list of struct isds_DbUserInfo */
6629 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6630 struct isds_list **users) {
6631 isds_error err = IE_SUCCESS;
6632 #if HAVE_LIBCURL
6633 xmlDocPtr response = NULL;
6634 xmlXPathContextPtr xpath_ctx = NULL;
6635 xmlXPathObjectPtr result = NULL;
6636 int i;
6637 struct isds_list *item, *prev_item = NULL;
6638 #endif
6640 if (!context) return IE_INVALID_CONTEXT;
6641 zfree(context->long_message);
6642 if (!users || !box_id) return IE_INVAL;
6643 isds_list_free(users);
6646 #if HAVE_LIBCURL
6647 /* Do request and check for success */
6648 err = build_send_dbid_request_check_response(context,
6649 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6650 BAD_CAST box_id, NULL, &response, NULL);
6651 if (err) goto leave;
6654 /* Extract data */
6655 /* Prepare structure */
6656 xpath_ctx = xmlXPathNewContext(response);
6657 if (!xpath_ctx) {
6658 err = IE_ERROR;
6659 goto leave;
6661 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6662 err = IE_ERROR;
6663 goto leave;
6666 /* Set context node */
6667 result = xmlXPathEvalExpression(BAD_CAST
6668 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6669 xpath_ctx);
6670 if (!result) {
6671 err = IE_ERROR;
6672 goto leave;
6674 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6675 /* Iterate over all users */
6676 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6678 /* Prepare structure */
6679 item = calloc(1, sizeof(*item));
6680 if (!item) {
6681 err = IE_NOMEM;
6682 goto leave;
6684 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6685 if (i == 0) *users = item;
6686 else prev_item->next = item;
6687 prev_item = item;
6689 /* Extract it */
6690 xpath_ctx->node = result->nodesetval->nodeTab[i];
6691 err = extract_DbUserInfo(context,
6692 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6693 if (err) goto leave;
6697 leave:
6698 if (err) {
6699 isds_list_free(users);
6702 xmlXPathFreeObject(result);
6703 xmlXPathFreeContext(xpath_ctx);
6704 xmlFreeDoc(response);
6706 if (!err)
6707 isds_log(ILF_ISDS, ILL_DEBUG,
6708 _("GetDataBoxUsers request processed by server "
6709 "successfully.\n"));
6710 #else /* not HAVE_LIBCURL */
6711 err = IE_NOTSUP;
6712 #endif
6714 return err;
6718 /* Update data about user assigned to given box.
6719 * @context is session context
6720 * @box is box identification. aifoIsds, address->adCode,
6721 * address->adDistrict members are ignored.
6722 * @old_user identifies user to update, aifo_ticket member is ignored
6723 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6724 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6725 * NULL, if you don't care.*/
6726 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6727 const struct isds_DbOwnerInfo *box,
6728 const struct isds_DbUserInfo *old_user,
6729 const struct isds_DbUserInfo *new_user,
6730 char **refnumber) {
6731 isds_error err = IE_SUCCESS;
6732 #if HAVE_LIBCURL
6733 xmlNsPtr isds_ns = NULL;
6734 xmlNodePtr request = NULL;
6735 xmlNodePtr node;
6736 #endif
6739 if (!context) return IE_INVALID_CONTEXT;
6740 zfree(context->long_message);
6741 if (!box || !old_user || !new_user) return IE_INVAL;
6744 #if HAVE_LIBCURL
6745 /* Build UpdateDataBoxUser request */
6746 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6747 if (!request) {
6748 isds_log_message(context,
6749 _("Could build UpdateDataBoxUser request"));
6750 return IE_ERROR;
6752 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6753 if(!isds_ns) {
6754 isds_log_message(context, _("Could not create ISDS name space"));
6755 xmlFreeNode(request);
6756 return IE_ERROR;
6758 xmlSetNs(request, isds_ns);
6760 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6761 err = insert_DbOwnerInfo(context, box, 0, node);
6762 if (err) goto leave;
6764 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6765 err = insert_DbUserInfo(context, old_user, 0, node);
6766 if (err) goto leave;
6768 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6769 err = insert_DbUserInfo(context, new_user, 0, node);
6770 if (err) goto leave;
6772 /* Send it to server and process response */
6773 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6774 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6776 leave:
6777 xmlFreeNode(request);
6778 #else /* not HAVE_LIBCURL */
6779 err = IE_NOTSUP;
6780 #endif
6782 return err;
6786 /* Undocumented function.
6787 * @context is session context
6788 * @box_id is UTF-8 encoded box identifier
6789 * @token is UTF-8 encoded temporary password
6790 * @user_id outputs UTF-8 encoded reallocated user identifier
6791 * @password outpus UTF-8 encoded reallocated user password
6792 * Output arguments will be nulled in case of error */
6793 isds_error isds_activate(struct isds_ctx *context,
6794 const char *box_id, const char *token,
6795 char **user_id, char **password) {
6796 isds_error err = IE_SUCCESS;
6797 #if HAVE_LIBCURL
6798 xmlNsPtr isds_ns = NULL;
6799 xmlNodePtr request = NULL, node;
6800 xmlDocPtr response = NULL;
6801 xmlXPathContextPtr xpath_ctx = NULL;
6802 xmlXPathObjectPtr result = NULL;
6803 #endif
6806 if (!context) return IE_INVALID_CONTEXT;
6807 zfree(context->long_message);
6809 if (user_id) zfree(*user_id);
6810 if (password) zfree(*password);
6812 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6815 #if HAVE_LIBCURL
6816 /* Build Activate request */
6817 request = xmlNewNode(NULL, BAD_CAST "Activate");
6818 if (!request) {
6819 isds_log_message(context, _("Could build Activate request"));
6820 return IE_ERROR;
6822 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6823 if(!isds_ns) {
6824 isds_log_message(context, _("Could not create ISDS name space"));
6825 xmlFreeNode(request);
6826 return IE_ERROR;
6828 xmlSetNs(request, isds_ns);
6830 INSERT_STRING(request, "dbAccessDataId", token);
6831 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6832 INSERT_STRING(request, "dbID", box_id);
6835 /* Send request and check response*/
6836 err = send_destroy_request_check_response(context,
6837 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6838 &response, NULL, NULL);
6839 if (err) goto leave;
6842 /* Extract data */
6843 xpath_ctx = xmlXPathNewContext(response);
6844 if (!xpath_ctx) {
6845 err = IE_ERROR;
6846 goto leave;
6848 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6849 err = IE_ERROR;
6850 goto leave;
6852 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6853 xpath_ctx);
6854 if (!result) {
6855 err = IE_ERROR;
6856 goto leave;
6858 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6859 isds_log_message(context, _("Missing ActivateResponse element"));
6860 err = IE_ISDS;
6861 goto leave;
6863 if (result->nodesetval->nodeNr > 1) {
6864 isds_log_message(context, _("Multiple ActivateResponse element"));
6865 err = IE_ISDS;
6866 goto leave;
6868 xpath_ctx->node = result->nodesetval->nodeTab[0];
6869 xmlXPathFreeObject(result); result = NULL;
6871 EXTRACT_STRING("isds:userId", *user_id);
6872 if (!*user_id)
6873 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6874 "but did not return `userId' element.\n"));
6876 EXTRACT_STRING("isds:password", *password);
6877 if (!*password)
6878 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6879 "but did not return `password' element.\n"));
6881 leave:
6882 xmlXPathFreeObject(result);
6883 xmlXPathFreeContext(xpath_ctx);
6884 xmlFreeDoc(response);
6885 xmlFreeNode(request);
6887 if (!err)
6888 isds_log(ILF_ISDS, ILL_DEBUG,
6889 _("Activate request processed by server successfully.\n"));
6890 #else /* not HAVE_LIBCURL */
6891 err = IE_NOTSUP;
6892 #endif
6894 return err;
6898 /* Reset credentials of user assigned to given box.
6899 * @context is session context
6900 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6901 * members are ignored.
6902 * @user identifies user to reset password, aifo_ticket member is ignored
6903 * @fee_paid is true if fee has been paid, false otherwise
6904 * @approval is optional external approval of box manipulation
6905 * @credentials_delivery is NULL if new password should be delivered off-line
6906 * to the user. It is valid pointer if user should obtain new password on-line
6907 * on dedicated web server. Then input @credentials_delivery.email value is
6908 * user's e-mail address user must provide to dedicated web server together
6909 * with @credentials_delivery.token. The output reallocated token user needs
6910 * to use to authorize on the web server to view his new password. Output
6911 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6912 * ISDS changed up on this call. (No reason why server could change the name
6913 * is known now.)
6914 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6915 * NULL, if you don't care.*/
6916 isds_error isds_reset_password(struct isds_ctx *context,
6917 const struct isds_DbOwnerInfo *box,
6918 const struct isds_DbUserInfo *user,
6919 const _Bool fee_paid, const struct isds_approval *approval,
6920 struct isds_credentials_delivery *credentials_delivery,
6921 char **refnumber) {
6922 isds_error err = IE_SUCCESS;
6923 #if HAVE_LIBCURL
6924 xmlNsPtr isds_ns = NULL;
6925 xmlNodePtr request = NULL, node;
6926 xmlDocPtr response = NULL;
6927 #endif
6930 if (!context) return IE_INVALID_CONTEXT;
6931 zfree(context->long_message);
6933 if (credentials_delivery) {
6934 zfree(credentials_delivery->token);
6935 zfree(credentials_delivery->new_user_name);
6937 if (!box || !user) return IE_INVAL;
6940 #if HAVE_LIBCURL
6941 /* Build NewAccessData request */
6942 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6943 if (!request) {
6944 isds_log_message(context,
6945 _("Could build NewAccessData request"));
6946 return IE_ERROR;
6948 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6949 if(!isds_ns) {
6950 isds_log_message(context, _("Could not create ISDS name space"));
6951 xmlFreeNode(request);
6952 return IE_ERROR;
6954 xmlSetNs(request, isds_ns);
6956 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6957 err = insert_DbOwnerInfo(context, box, 0, node);
6958 if (err) goto leave;
6960 INSERT_ELEMENT(node, request, "dbUserInfo");
6961 err = insert_DbUserInfo(context, user, 0, node);
6962 if (err) goto leave;
6964 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6966 err = insert_credentials_delivery(context, credentials_delivery, request);
6967 if (err) goto leave;
6969 err = insert_GExtApproval(context, approval, request);
6970 if (err) goto leave;
6972 /* Send request and check response*/
6973 err = send_destroy_request_check_response(context,
6974 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6975 &response, (xmlChar **) refnumber, NULL);
6976 if (err) goto leave;
6979 /* Extract optional token */
6980 err = extract_credentials_delivery(context, credentials_delivery,
6981 response, "NewAccessData");
6983 leave:
6984 xmlFreeDoc(response);
6985 xmlFreeNode(request);
6987 if (!err)
6988 isds_log(ILF_ISDS, ILL_DEBUG,
6989 _("NewAccessData request processed by server "
6990 "successfully.\n"));
6991 #else /* not HAVE_LIBCURL */
6992 err = IE_NOTSUP;
6993 #endif
6995 return err;
6999 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7000 * code, destroy response and log success.
7001 * @context is ISDS session context.
7002 * @service_name is name of SERVICE_DB_MANIPULATION service
7003 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7004 * members are ignored.
7005 * @user identifies user to remove
7006 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7007 * @credentials_delivery is NULL if new user's password should be delivered
7008 * off-line to the user. It is valid pointer if user should obtain new
7009 * password on-line on dedicated web server. Then input
7010 * @credentials_delivery.email value is user's e-mail address user must
7011 * provide to dedicated web server together with @credentials_delivery.token.
7012 * The output reallocated token user needs to use to authorize on the web
7013 * server to view his new password. Output reallocated
7014 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7015 * assingned or changed up on this call.
7016 * @approval is optional external approval of box manipulation
7017 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7018 * NULL, if you don't care. */
7019 static isds_error build_send_manipulationboxuser_request_check_drop_response(
7020 struct isds_ctx *context, const xmlChar *service_name,
7021 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7022 _Bool honor_aifo_ticket,
7023 struct isds_credentials_delivery *credentials_delivery,
7024 const struct isds_approval *approval, xmlChar **refnumber) {
7025 isds_error err = IE_SUCCESS;
7026 #if HAVE_LIBCURL
7027 xmlNsPtr isds_ns = NULL;
7028 xmlNodePtr request = NULL, node;
7029 xmlDocPtr response = NULL;
7030 #endif
7033 if (!context) return IE_INVALID_CONTEXT;
7034 zfree(context->long_message);
7035 if (credentials_delivery) {
7036 zfree(credentials_delivery->token);
7037 zfree(credentials_delivery->new_user_name);
7039 if (!service_name || service_name[0] == '\0' || !box || !user)
7040 return IE_INVAL;
7043 #if HAVE_LIBCURL
7044 /* Build NewAccessData or similar request */
7045 request = xmlNewNode(NULL, service_name);
7046 if (!request) {
7047 char *service_name_locale = _isds_utf82locale((char *) service_name);
7048 isds_printf_message(context, _("Could not build %s request"),
7049 service_name_locale);
7050 free(service_name_locale);
7051 return IE_ERROR;
7053 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7054 if(!isds_ns) {
7055 isds_log_message(context, _("Could not create ISDS name space"));
7056 xmlFreeNode(request);
7057 return IE_ERROR;
7059 xmlSetNs(request, isds_ns);
7061 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7062 err = insert_DbOwnerInfo(context, box, 0, node);
7063 if (err) goto leave;
7065 INSERT_ELEMENT(node, request, "dbUserInfo");
7066 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7067 if (err) goto leave;
7069 err = insert_credentials_delivery(context, credentials_delivery, request);
7070 if (err) goto leave;
7072 err = insert_GExtApproval(context, approval, request);
7073 if (err) goto leave;
7076 /* Send request and check response*/
7077 err = send_destroy_request_check_response(context,
7078 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7079 refnumber, NULL);
7081 xmlFreeNode(request);
7082 request = NULL;
7084 /* Pick up credentials_delivery if requested */
7085 err = extract_credentials_delivery(context, credentials_delivery, response,
7086 (char *)service_name);
7088 leave:
7089 xmlFreeDoc(response);
7090 if (request) xmlFreeNode(request);
7092 if (!err) {
7093 char *service_name_locale = _isds_utf82locale((char *) service_name);
7094 isds_log(ILF_ISDS, ILL_DEBUG,
7095 _("%s request processed by server successfully.\n"),
7096 service_name_locale);
7097 free(service_name_locale);
7099 #else /* not HAVE_LIBCURL */
7100 err = IE_NOTSUP;
7101 #endif
7103 return err;
7107 /* Assign new user to given box.
7108 * @context is session context
7109 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7110 * members are ignored.
7111 * @user defines new user to add
7112 * @credentials_delivery is NULL if new user's password should be delivered
7113 * off-line to the user. It is valid pointer if user should obtain new
7114 * password on-line on dedicated web server. Then input
7115 * @credentials_delivery.email value is user's e-mail address user must
7116 * provide to dedicated web server together with @credentials_delivery.token.
7117 * The output reallocated token user needs to use to authorize on the web
7118 * server to view his new password. Output reallocated
7119 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7120 * assingned up on this call.
7121 * @approval is optional external approval of box manipulation
7122 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7123 * NULL, if you don't care.*/
7124 isds_error isds_add_user(struct isds_ctx *context,
7125 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7126 struct isds_credentials_delivery *credentials_delivery,
7127 const struct isds_approval *approval, char **refnumber) {
7128 return build_send_manipulationboxuser_request_check_drop_response(context,
7129 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7130 approval, (xmlChar **) refnumber);
7134 /* Remove user assigned to given box.
7135 * @context is session context
7136 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7137 * members are ignored.
7138 * @user identifies user to remove, aifo_ticket member is ignored
7139 * @approval is optional external approval of box manipulation
7140 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7141 * NULL, if you don't care.*/
7142 isds_error isds_delete_user(struct isds_ctx *context,
7143 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7144 const struct isds_approval *approval, char **refnumber) {
7145 return build_send_manipulationboxuser_request_check_drop_response(context,
7146 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7147 (xmlChar **) refnumber);
7151 /* Get list of boxes in ZIP archive.
7152 * @context is session context
7153 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7154 * System recognizes following values currently: ALL (all boxes), UPG
7155 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7156 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7157 * commercial messages). This argument is a string because specification
7158 * states new values can appear in the future. Not all list types are
7159 * available to all users.
7160 * @buffer is automatically reallocated memory to store the list of boxes. The
7161 * list is zipped CSV file.
7162 * @buffer_length is size of @buffer data in bytes.
7163 * In case of error @buffer will be freed and @buffer_length will be
7164 * undefined.*/
7165 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7166 const char *list_identifier, void **buffer, size_t *buffer_length) {
7167 isds_error err = IE_SUCCESS;
7168 #if HAVE_LIBCURL
7169 xmlNsPtr isds_ns = NULL;
7170 xmlNodePtr request = NULL, node;
7171 xmlDocPtr response = NULL;
7172 xmlXPathContextPtr xpath_ctx = NULL;
7173 xmlXPathObjectPtr result = NULL;
7174 char *string = NULL;
7175 #endif
7178 if (!context) return IE_INVALID_CONTEXT;
7179 zfree(context->long_message);
7180 if (buffer) zfree(*buffer);
7181 if (!buffer || !buffer_length) return IE_INVAL;
7184 #if HAVE_LIBCURL
7185 /* Check if connection is established
7186 * TODO: This check should be done downstairs. */
7187 if (!context->curl) return IE_CONNECTION_CLOSED;
7190 /* Build AuthenticateMessage request */
7191 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7192 if (!request) {
7193 isds_log_message(context,
7194 _("Could not build GetDataBoxList request"));
7195 return IE_ERROR;
7197 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7198 if(!isds_ns) {
7199 isds_log_message(context, _("Could not create ISDS name space"));
7200 xmlFreeNode(request);
7201 return IE_ERROR;
7203 xmlSetNs(request, isds_ns);
7204 INSERT_STRING(request, "dblType", list_identifier);
7206 /* Send request to server and process response */
7207 err = send_destroy_request_check_response(context,
7208 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7209 &response, NULL, NULL);
7210 if (err) goto leave;
7213 /* Extract Base-64 encoded ZIP file */
7214 xpath_ctx = xmlXPathNewContext(response);
7215 if (!xpath_ctx) {
7216 err = IE_ERROR;
7217 goto leave;
7219 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7220 err = IE_ERROR;
7221 goto leave;
7223 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7225 /* Decode non-empty archive */
7226 if (string && string[0] != '\0') {
7227 *buffer_length = _isds_b64decode(string, buffer);
7228 if (*buffer_length == (size_t) -1) {
7229 isds_printf_message(context,
7230 _("Error while Base64-decoding box list archive"));
7231 err = IE_ERROR;
7232 goto leave;
7237 leave:
7238 free(string);
7239 xmlXPathFreeObject(result);
7240 xmlXPathFreeContext(xpath_ctx);
7241 xmlFreeDoc(response);
7242 xmlFreeNode(request);
7244 if (!err) {
7245 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7246 "processed by server successfully.\n"));
7248 #else /* not HAVE_LIBCURL */
7249 err = IE_NOTSUP;
7250 #endif
7252 return err;
7256 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7257 * send it, check for error code, extract list of results, destroy response
7258 * and log success.
7259 * @context is ISDS session context.
7260 * @service_name is name of SERVICE_DB_SEARCH service
7261 * @pfo_service is false if tDbOwnerInfo request should be built from
7262 * @criteria and corresponding result extracted. It is true if
7263 * tDbPersonalOwnerInfoRequest request should be built. The request and
7264 * response differ subset of significant isds_DbOwnerInfo structure members.
7265 * @criteria is filter. You should fill in at least some members.
7266 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7267 * members will be ignored. If @pfo_service is true, dbType, ic,
7268 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7269 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7270 * ignored.
7271 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7272 * possibly empty. Input NULL or valid old structure. The same memebers as
7273 * in described for @criteria argument will be NULL according to @pfo_service
7274 * switch.
7275 * @return:
7276 * IE_SUCCESS if search succeeded, @boxes contains useful data
7277 * IE_NOEXIST if no such box exists, @boxes will be NULL
7278 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7279 * contains still valid data
7280 * other code if something bad happens. @boxes will be NULL. */
7281 static isds_error build_send_findbox_request_check_parse_drop_response(
7282 struct isds_ctx *context, const xmlChar *service_name,
7283 _Bool pfo_service, const struct isds_DbOwnerInfo *criteria,
7284 struct isds_list **boxes) {
7285 isds_error err = IE_SUCCESS;
7286 #if HAVE_LIBCURL
7287 char *service_name_locale = NULL;
7288 _Bool truncated = 0;
7289 xmlNsPtr isds_ns = NULL;
7290 xmlNodePtr request = NULL;
7291 xmlDocPtr response = NULL;
7292 xmlChar *code = NULL, *message = NULL;
7293 xmlNodePtr db_owner_info;
7294 xmlXPathContextPtr xpath_ctx = NULL;
7295 xmlXPathObjectPtr result = NULL;
7296 xmlChar *string = NULL;
7297 #endif
7300 if (!context) return IE_INVALID_CONTEXT;
7301 zfree(context->long_message);
7302 if (!boxes) return IE_INVAL;
7303 isds_list_free(boxes);
7305 if (!criteria) {
7306 return IE_INVAL;
7309 #if HAVE_LIBCURL
7310 /* Check if connection is established
7311 * TODO: This check should be done downstairs. */
7312 if (!context->curl) return IE_CONNECTION_CLOSED;
7313 service_name_locale = _isds_utf82locale((char *) service_name);
7315 /* Build request */
7316 request = xmlNewNode(NULL, service_name);
7317 if (!request) {
7318 isds_printf_message(context, _("Could not build %s request"),
7319 service_name_locale);
7320 free(service_name_locale);
7321 return IE_ERROR;
7323 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7324 if(!isds_ns) {
7325 isds_log_message(context, _("Could not create ISDS name space"));
7326 free(service_name_locale);
7327 xmlFreeNode(request);
7328 return IE_ERROR;
7330 xmlSetNs(request, isds_ns);
7331 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7332 if (!db_owner_info) {
7333 isds_printf_message(context,
7334 _("Could not add dbOwnerInfo child to %s element"),
7335 service_name_locale);
7336 free(service_name_locale);
7337 xmlFreeNode(request);
7338 return IE_ERROR;
7341 err = insert_DbOwnerInfo(context, criteria, pfo_service, db_owner_info);
7342 if (err) goto leave;
7345 /* Send request */
7346 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
7347 service_name_locale);
7348 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7350 /* Destroy request */
7351 xmlFreeNode(request); request = NULL;
7353 if (err) {
7354 isds_log(ILF_ISDS, ILL_DEBUG,
7355 _("Processing ISDS response on %s request failed\n"),
7356 service_name_locale);
7357 goto leave;
7360 /* Check for response status */
7361 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7362 &code, &message, NULL);
7363 if (err) {
7364 isds_log(ILF_ISDS, ILL_DEBUG,
7365 _("ISDS response on %s request is missing status\n"),
7366 service_name_locale);
7367 goto leave;
7370 /* Request processed, but nothing found */
7371 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7372 !xmlStrcmp(code, BAD_CAST "5001")) {
7373 char *code_locale = _isds_utf82locale((char*)code);
7374 char *message_locale = _isds_utf82locale((char*)message);
7375 isds_log(ILF_ISDS, ILL_DEBUG,
7376 _("Server did not find any box on %s request "
7377 "(code=%s, message=%s)\n"), service_name_locale,
7378 code_locale, message_locale);
7379 isds_log_message(context, message_locale);
7380 free(code_locale);
7381 free(message_locale);
7382 err = IE_NOEXIST;
7383 goto leave;
7386 /* Warning, not an error */
7387 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7388 char *code_locale = _isds_utf82locale((char*)code);
7389 char *message_locale = _isds_utf82locale((char*)message);
7390 isds_log(ILF_ISDS, ILL_DEBUG,
7391 _("Server truncated response on %s request "
7392 "(code=%s, message=%s)\n"), service_name_locale,
7393 code_locale, message_locale);
7394 isds_log_message(context, message_locale);
7395 free(code_locale);
7396 free(message_locale);
7397 truncated = 1;
7400 /* Other error */
7401 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7402 char *code_locale = _isds_utf82locale((char*)code);
7403 char *message_locale = _isds_utf82locale((char*)message);
7404 isds_log(ILF_ISDS, ILL_DEBUG,
7405 _("Server refused %s request (code=%s, message=%s)\n"),
7406 service_name_locale, code_locale, message_locale);
7407 isds_log_message(context, message_locale);
7408 free(code_locale);
7409 free(message_locale);
7410 err = IE_ISDS;
7411 goto leave;
7414 xpath_ctx = xmlXPathNewContext(response);
7415 if (!xpath_ctx) {
7416 err = IE_ERROR;
7417 goto leave;
7419 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7420 err = IE_ERROR;
7421 goto leave;
7424 /* Extract boxes if they present */
7425 if (-1 == isds_asprintf((char **)&string,
7426 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7427 service_name)) {
7428 err = IE_NOMEM;
7429 goto leave;
7431 result = xmlXPathEvalExpression(string, xpath_ctx);
7432 zfree(string);
7433 if (!result) {
7434 err = IE_ERROR;
7435 goto leave;
7437 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7438 struct isds_list *item, *prev_item = NULL;
7439 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7440 item = calloc(1, sizeof(*item));
7441 if (!item) {
7442 err = IE_NOMEM;
7443 goto leave;
7446 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7447 if (i == 0) *boxes = item;
7448 else prev_item->next = item;
7449 prev_item = item;
7451 xpath_ctx->node = result->nodesetval->nodeTab[i];
7452 err = extract_DbOwnerInfo(context,
7453 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7454 if (err) goto leave;
7458 leave:
7459 if (err) {
7460 isds_list_free(boxes);
7461 } else {
7462 if (truncated) err = IE_2BIG;
7465 free(string);
7466 xmlFreeNode(request);
7467 xmlXPathFreeObject(result);
7468 xmlXPathFreeContext(xpath_ctx);
7470 free(code);
7471 free(message);
7472 xmlFreeDoc(response);
7474 if (!err)
7475 isds_log(ILF_ISDS, ILL_DEBUG,
7476 _("%s request processed by server successfully.\n"),
7477 service_name_locale);
7478 free(service_name_locale);
7479 #else /* not HAVE_LIBCURL */
7480 err = IE_NOTSUP;
7481 #endif
7483 return err;
7487 /* Find boxes suiting given criteria.
7488 * @criteria is filter. You should fill in at least some members. aifoIsds,
7489 * address->adCode, address->adDistrict members are ignored.
7490 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7491 * possibly empty. Input NULL or valid old structure.
7492 * @return:
7493 * IE_SUCCESS if search succeeded, @boxes contains useful data
7494 * IE_NOEXIST if no such box exists, @boxes will be NULL
7495 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7496 * contains still valid data
7497 * other code if something bad happens. @boxes will be NULL. */
7498 isds_error isds_FindDataBox(struct isds_ctx *context,
7499 const struct isds_DbOwnerInfo *criteria,
7500 struct isds_list **boxes) {
7501 return build_send_findbox_request_check_parse_drop_response(context,
7502 BAD_CAST "FindDataBox", 0, criteria, boxes);
7506 #if HAVE_LIBCURL
7507 /* Convert a string with match markers into a plain string with list of
7508 * pointers to the matches
7509 * @string is an UTF-8 encoded non-constant string with match markers
7510 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7511 * The markers will be removed from the string.
7512 * @starts is a reallocated list of static pointers into the @string pointing
7513 * to places where match start markers occured.
7514 * @ends is a reallocated list of static pointers into the @string pointing
7515 * to places where match end markers occured.
7516 * @return IE_SUCCESS in case of no failure. */
7517 static isds_error interpret_matches(xmlChar *string,
7518 struct isds_list **starts, struct isds_list **ends) {
7519 isds_error err = IE_SUCCESS;
7520 xmlChar *pointer, *destination, *source;
7521 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7523 isds_list_free(starts);
7524 isds_list_free(ends);
7525 if (NULL == starts || NULL == ends) return IE_INVAL;
7526 if (NULL == string) return IE_SUCCESS;
7528 for (pointer = string; *pointer != '\0';) {
7529 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7530 /* Remove the start marker */
7531 for (source = pointer + 14, destination = pointer;
7532 *source != '\0'; source++, destination++) {
7533 *destination = *source;
7535 *destination = '\0';
7536 /* Append the pointer into the list */
7537 item = calloc(1, sizeof(*item));
7538 if (!item) {
7539 err = IE_NOMEM;
7540 goto leave;
7542 item->destructor = (void (*)(void **))NULL;
7543 item->data = pointer;
7544 if (NULL == prev_start) *starts = item;
7545 else prev_start->next = item;
7546 prev_start = item;
7547 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7548 /* Remove the end marker */
7549 for (source = pointer + 12, destination = pointer;
7550 *source != '\0'; source++, destination++) {
7551 *destination = *source;
7553 *destination = '\0';
7554 /* Append the pointer into the list */
7555 item = calloc(1, sizeof(*item));
7556 if (!item) {
7557 err = IE_NOMEM;
7558 goto leave;
7560 item->destructor = (void (*)(void **))NULL;
7561 item->data = pointer;
7562 if (NULL == prev_end) *ends = item;
7563 else prev_end->next = item;
7564 prev_end = item;
7565 } else {
7566 pointer++;
7570 leave:
7571 if (err) {
7572 isds_list_free(starts);
7573 isds_list_free(ends);
7575 return err;
7579 /* Convert isds:dbResult XML tree into structure
7580 * @context is ISDS context.
7581 * @fulltext_result is automatically reallocated found box structure.
7582 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7583 * @collect_matches is true to interpret match markers.
7584 * In case of error @result will be freed. */
7585 static isds_error extract_dbResult(struct isds_ctx *context,
7586 struct isds_fulltext_result **fulltext_result,
7587 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7588 isds_error err = IE_SUCCESS;
7589 xmlXPathObjectPtr result = NULL;
7590 char *string = NULL;
7592 if (NULL == context) return IE_INVALID_CONTEXT;
7593 if (NULL == fulltext_result) return IE_INVAL;
7594 isds_fulltext_result_free(fulltext_result);
7595 if (!xpath_ctx) return IE_INVAL;
7598 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7599 if (NULL == *fulltext_result) {
7600 err = IE_NOMEM;
7601 goto leave;
7604 /* Extract data */
7605 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7607 EXTRACT_STRING("isds:dbType", string);
7608 if (NULL == string) {
7609 err = IE_ISDS;
7610 isds_log_message(context, _("Empty isds:dbType element"));
7611 goto leave;
7613 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7614 if (err) {
7615 if (err == IE_ENUM) {
7616 err = IE_ISDS;
7617 char *string_locale = _isds_utf82locale(string);
7618 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7619 string_locale);
7620 free(string_locale);
7622 goto leave;
7624 zfree(string);
7626 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7627 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7629 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7630 if (err) goto leave;
7632 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7633 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7634 (*fulltext_result)->dbEffectiveOVM);
7636 EXTRACT_STRING("isds:dbSendOptions", string);
7637 if (NULL == string) {
7638 err = IE_ISDS;
7639 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7640 goto leave;
7642 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7643 (*fulltext_result)->active = 1;
7644 (*fulltext_result)->public_sending = 1;
7645 (*fulltext_result)->commercial_sending = 0;
7646 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7647 (*fulltext_result)->active = 1;
7648 (*fulltext_result)->public_sending = 1;
7649 (*fulltext_result)->commercial_sending = 1;
7650 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7651 (*fulltext_result)->active = 1;
7652 (*fulltext_result)->public_sending = 0;
7653 (*fulltext_result)->commercial_sending = 1;
7654 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7655 (*fulltext_result)->active = 1;
7656 (*fulltext_result)->public_sending = 0;
7657 (*fulltext_result)->commercial_sending = 0;
7658 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7659 (*fulltext_result)->active = 0;
7660 (*fulltext_result)->public_sending = 0;
7661 (*fulltext_result)->commercial_sending = 0;
7662 } else {
7663 err = IE_ISDS;
7664 char *string_locale = _isds_utf82locale(string);
7665 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7666 string_locale);
7667 free(string_locale);
7668 goto leave;
7670 zfree(string);
7672 /* Interpret match marks */
7673 if (collect_matches) {
7674 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7675 &((*fulltext_result)->name_match_start),
7676 &((*fulltext_result)->name_match_end));
7677 if (err) goto leave;
7678 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7679 &((*fulltext_result)->address_match_start),
7680 &((*fulltext_result)->address_match_end));
7681 if (err) goto leave;
7684 leave:
7685 if (err) isds_fulltext_result_free(fulltext_result);
7686 free(string);
7687 xmlXPathFreeObject(result);
7688 return err;
7690 #endif /* HAVE_LIBCURL */
7693 /* Find boxes matching a given full-text criteria.
7694 * @context is a session context
7695 * @query is a non-empty string which consists of words to search
7696 * @target selects box attributes to search for @query words. Pass NULL if you
7697 * don't care.
7698 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7699 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7700 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7701 * which is DBTYPE_SYSTEM.
7702 * @page_size defines count of boxes to constitute a response page. It counts
7703 * from zero. Pass NULL to let server to use a default value (50 now).
7704 * @page_number defines ordinar number of the response page to return. It
7705 * counts from zero. Pass NULL to let server to use a default value (0 now).
7706 * @track_matches points to true for marking @query words found in the box
7707 * attributes. It points to false for not marking. Pass NULL to let the server
7708 * to use default value (false now).
7709 * @total_matching_boxes outputs reallocated number of all boxes matching the
7710 * query. Will be pointer to NULL if server did not provide the value.
7711 * Pass NULL if you don't care.
7712 * @current_page_beginning outputs reallocated ordinar number of the first box
7713 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7714 * server did not provide the value. Pass NULL if you don't care.
7715 * @current_page_size outputs reallocated count of boxes in the this @boxes
7716 * page. It will be pointer to NULL if the server did not provide the value.
7717 * Pass NULL if you don't care.
7718 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7719 * is the last one, false if more boxes match, NULL if the server did not
7720 * provude the value. Pass NULL if you don't care.
7721 * @boxes outputs reallocated list of isds_fulltext_result structures,
7722 * possibly empty.
7723 * @return:
7724 * IE_SUCCESS if search succeeded
7725 * IE_2BIG if @page_size is too large
7726 * other code if something bad happens; output arguments will be NULL. */
7727 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7728 const char *query,
7729 const isds_fulltext_target *target,
7730 const isds_DbType *box_type,
7731 const unsigned long int *page_size,
7732 const unsigned long int *page_number,
7733 const _Bool *track_matches,
7734 unsigned long int **total_matching_boxes,
7735 unsigned long int **current_page_beginning,
7736 unsigned long int **current_page_size,
7737 _Bool **last_page,
7738 struct isds_list **boxes) {
7739 isds_error err = IE_SUCCESS;
7740 #if HAVE_LIBCURL
7741 xmlNsPtr isds_ns = NULL;
7742 xmlNodePtr request = NULL;
7743 xmlDocPtr response = NULL;
7744 xmlNodePtr node;
7745 xmlXPathContextPtr xpath_ctx = NULL;
7746 xmlXPathObjectPtr result = NULL;
7747 const xmlChar *static_string = NULL;
7748 xmlChar *string = NULL;
7750 const xmlChar *codes[] = {
7751 BAD_CAST "1004",
7752 BAD_CAST "1152",
7753 BAD_CAST "1153",
7754 BAD_CAST "1154",
7755 BAD_CAST "1155",
7756 BAD_CAST "1156",
7757 BAD_CAST "9002",
7758 NULL
7760 const char *meanings[] = {
7761 N_("You are not allowed to perform the search"),
7762 N_("The query string is empty"),
7763 N_("Searched box ID is malformed"),
7764 N_("Searched organization ID is malformed"),
7765 N_("Invalid input"),
7766 N_("Requested page size is too large"),
7767 N_("Search engine internal error")
7769 const isds_error errors[] = {
7770 IE_ISDS,
7771 IE_INVAL,
7772 IE_INVAL,
7773 IE_INVAL,
7774 IE_INVAL,
7775 IE_2BIG,
7776 IE_ISDS
7778 struct code_map_isds_error map = {
7779 .codes = codes,
7780 .meanings = meanings,
7781 .errors = errors
7783 #endif
7786 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7787 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7788 if (NULL != current_page_size) zfree(*current_page_size);
7789 if (NULL != last_page) zfree(*last_page);
7790 isds_list_free(boxes);
7792 if (NULL == context) return IE_INVALID_CONTEXT;
7793 zfree(context->long_message);
7795 if (NULL == boxes) return IE_INVAL;
7797 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7798 isds_log_message(context, _("Query string must be non-empty"));
7799 return IE_INVAL;
7802 #if HAVE_LIBCURL
7803 /* Check if connection is established
7804 * TODO: This check should be done downstairs. */
7805 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7807 /* Build FindDataBox request */
7808 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7809 if (NULL == request) {
7810 isds_log_message(context,
7811 _("Could not build ISDSSearch2 request"));
7812 return IE_ERROR;
7814 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7815 if(NULL == isds_ns) {
7816 isds_log_message(context, _("Could not create ISDS name space"));
7817 xmlFreeNode(request);
7818 return IE_ERROR;
7820 xmlSetNs(request, isds_ns);
7822 INSERT_STRING(request, "searchText", query);
7824 if (NULL != target) {
7825 static_string = isds_fulltext_target2string(*(target));
7826 if (NULL == static_string) {
7827 isds_printf_message(context, _("Invalid target value: %d"),
7828 *(target));
7829 err = IE_ENUM;
7830 goto leave;
7833 INSERT_STRING(request, "searchType", static_string);
7834 static_string = NULL;
7836 if (NULL != box_type) {
7837 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7838 if (DBTYPE_SYSTEM == *box_type) {
7839 static_string = BAD_CAST "ALL";
7840 } else if (DBTYPE_OVM_MAIN == *box_type) {
7841 static_string = BAD_CAST "OVM_MAIN";
7842 } else {
7843 static_string = isds_DbType2string(*(box_type));
7844 if (NULL == static_string) {
7845 isds_printf_message(context, _("Invalid box type value: %d"),
7846 *(box_type));
7847 err = IE_ENUM;
7848 goto leave;
7852 INSERT_STRING(request, "searchScope", static_string);
7853 static_string = NULL;
7855 INSERT_ULONGINT(request, "page", page_number, string);
7856 INSERT_ULONGINT(request, "pageSize", page_size, string);
7857 INSERT_BOOLEAN(request, "highlighting", track_matches);
7859 /* Send request and check response */
7860 err = send_destroy_request_check_response(context,
7861 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7862 &request, &response, NULL, &map);
7863 if (err) goto leave;
7865 /* Parse response */
7866 xpath_ctx = xmlXPathNewContext(response);
7867 if (NULL == xpath_ctx) {
7868 err = IE_ERROR;
7869 goto leave;
7871 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7872 err = IE_ERROR;
7873 goto leave;
7875 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7876 xpath_ctx);
7877 if (!result) {
7878 err = IE_ERROR;
7879 goto leave;
7881 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7882 isds_log_message(context, _("Missing ISDSSearch2 element"));
7883 err = IE_ISDS;
7884 goto leave;
7886 if (result->nodesetval->nodeNr > 1) {
7887 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7888 err = IE_ISDS;
7889 goto leave;
7891 xpath_ctx->node = result->nodesetval->nodeTab[0];
7892 xmlXPathFreeObject(result); result = NULL;
7895 /* Extract counters */
7896 if (NULL != total_matching_boxes) {
7897 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7899 if (NULL != current_page_size) {
7900 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7902 if (NULL != current_page_beginning) {
7903 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7905 if (NULL != last_page) {
7906 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7908 xmlXPathFreeObject(result); result = NULL;
7910 /* Extract boxes if they present */
7911 result = xmlXPathEvalExpression(BAD_CAST
7912 "isds:dbResults/isds:dbResult", xpath_ctx);
7913 if (NULL == result) {
7914 err = IE_ERROR;
7915 goto leave;
7917 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7918 struct isds_list *item, *prev_item = NULL;
7919 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7920 item = calloc(1, sizeof(*item));
7921 if (!item) {
7922 err = IE_NOMEM;
7923 goto leave;
7926 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7927 if (i == 0) *boxes = item;
7928 else prev_item->next = item;
7929 prev_item = item;
7931 xpath_ctx->node = result->nodesetval->nodeTab[i];
7932 err = extract_dbResult(context,
7933 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7934 (NULL == track_matches) ? 0 : *track_matches);
7935 if (err) goto leave;
7939 leave:
7940 if (err) {
7941 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7942 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7943 if (NULL != current_page_size) zfree(*current_page_size);
7944 if (NULL != last_page) zfree(*last_page);
7945 isds_list_free(boxes);
7948 free(string);
7949 xmlFreeNode(request);
7950 xmlXPathFreeObject(result);
7951 xmlXPathFreeContext(xpath_ctx);
7952 xmlFreeDoc(response);
7954 if (!err)
7955 isds_log(ILF_ISDS, ILL_DEBUG,
7956 _("ISDSSearch2 request processed by server successfully.\n"));
7957 #else /* not HAVE_LIBCURL */
7958 err = IE_NOTSUP;
7959 #endif
7961 return err;
7965 /* Get status of a box.
7966 * @context is ISDS session context.
7967 * @box_id is UTF-8 encoded box identifier as zero terminated string
7968 * @box_status is return value of box status.
7969 * @return:
7970 * IE_SUCCESS if box has been found and its status retrieved
7971 * IE_NOEXIST if box is not known to ISDS server
7972 * or other appropriate error.
7973 * You can use isds_DbState to enumerate box status. However out of enum
7974 * range value can be returned too. This is feature because ISDS
7975 * specification leaves the set of values open.
7976 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7977 * the box has been deleted, but ISDS still lists its former existence. */
7978 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7979 long int *box_status) {
7980 isds_error err = IE_SUCCESS;
7981 #if HAVE_LIBCURL
7982 xmlNsPtr isds_ns = NULL;
7983 xmlNodePtr request = NULL, db_id;
7984 xmlDocPtr response = NULL;
7985 xmlXPathContextPtr xpath_ctx = NULL;
7986 xmlXPathObjectPtr result = NULL;
7987 xmlChar *string = NULL;
7989 const xmlChar *codes[] = {
7990 BAD_CAST "5001",
7991 BAD_CAST "1007",
7992 BAD_CAST "2011",
7993 NULL
7995 const char *meanings[] = {
7996 "The box does not exist",
7997 "Box ID is malformed",
7998 "Box ID malformed",
8000 const isds_error errors[] = {
8001 IE_NOEXIST,
8002 IE_INVAL,
8003 IE_INVAL,
8005 struct code_map_isds_error map = {
8006 .codes = codes,
8007 .meanings = meanings,
8008 .errors = errors
8010 #endif
8012 if (!context) return IE_INVALID_CONTEXT;
8013 zfree(context->long_message);
8014 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
8016 #if HAVE_LIBCURL
8017 /* Check if connection is established
8018 * TODO: This check should be done downstairs. */
8019 if (!context->curl) return IE_CONNECTION_CLOSED;
8022 /* Build CheckDataBox request */
8023 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
8024 if (!request) {
8025 isds_log_message(context,
8026 _("Could build CheckDataBox request"));
8027 return IE_ERROR;
8029 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8030 if(!isds_ns) {
8031 isds_log_message(context, _("Could not create ISDS name space"));
8032 xmlFreeNode(request);
8033 return IE_ERROR;
8035 xmlSetNs(request, isds_ns);
8036 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
8037 if (!db_id) {
8038 isds_log_message(context, _("Could not add dbID child to "
8039 "CheckDataBox element"));
8040 xmlFreeNode(request);
8041 return IE_ERROR;
8045 /* Send request and check response*/
8046 err = send_destroy_request_check_response(context,
8047 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
8048 &request, &response, NULL, &map);
8049 if (err) goto leave;
8052 /* Extract data */
8053 xpath_ctx = xmlXPathNewContext(response);
8054 if (!xpath_ctx) {
8055 err = IE_ERROR;
8056 goto leave;
8058 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8059 err = IE_ERROR;
8060 goto leave;
8062 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
8063 xpath_ctx);
8064 if (!result) {
8065 err = IE_ERROR;
8066 goto leave;
8068 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8069 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
8070 err = IE_ISDS;
8071 goto leave;
8073 if (result->nodesetval->nodeNr > 1) {
8074 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
8075 err = IE_ISDS;
8076 goto leave;
8078 xpath_ctx->node = result->nodesetval->nodeTab[0];
8079 xmlXPathFreeObject(result); result = NULL;
8081 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8084 leave:
8085 free(string);
8086 xmlXPathFreeObject(result);
8087 xmlXPathFreeContext(xpath_ctx);
8089 xmlFreeDoc(response);
8091 if (!err)
8092 isds_log(ILF_ISDS, ILL_DEBUG,
8093 _("CheckDataBox request processed by server successfully.\n"));
8094 #else /* not HAVE_LIBCURL */
8095 err = IE_NOTSUP;
8096 #endif
8098 return err;
8102 /* Get list of permissions to send commercial messages.
8103 * @context is ISDS session context.
8104 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8105 * @permissions is a reallocated list of permissions (struct
8106 * isds_commercial_permission*) to send commercial messages from @box_id. The
8107 * order of permissions is significant as the server applies the permissions
8108 * and associated pre-paid credits in the order. Empty list means no
8109 * permission.
8110 * @return:
8111 * IE_SUCCESS if the list has been obtained correctly,
8112 * or other appropriate error. */
8113 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8114 const char *box_id, struct isds_list **permissions) {
8115 isds_error err = IE_SUCCESS;
8116 #if HAVE_LIBCURL
8117 xmlDocPtr response = NULL;
8118 xmlXPathContextPtr xpath_ctx = NULL;
8119 xmlXPathObjectPtr result = NULL;
8120 #endif
8122 if (!context) return IE_INVALID_CONTEXT;
8123 zfree(context->long_message);
8124 if (NULL == permissions) return IE_INVAL;
8125 isds_list_free(permissions);
8126 if (NULL == box_id) return IE_INVAL;
8128 #if HAVE_LIBCURL
8129 /* Check if connection is established */
8130 if (!context->curl) return IE_CONNECTION_CLOSED;
8132 /* Do request and check for success */
8133 err = build_send_dbid_request_check_response(context,
8134 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8135 BAD_CAST box_id, NULL, &response, NULL);
8136 if (!err) {
8137 isds_log(ILF_ISDS, ILL_DEBUG,
8138 _("PDZInfo request processed by server successfully.\n"));
8141 /* Extract data */
8142 /* Prepare structure */
8143 xpath_ctx = xmlXPathNewContext(response);
8144 if (!xpath_ctx) {
8145 err = IE_ERROR;
8146 goto leave;
8148 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8149 err = IE_ERROR;
8150 goto leave;
8153 /* Set context node */
8154 result = xmlXPathEvalExpression(BAD_CAST
8155 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8156 xpath_ctx);
8157 if (!result) {
8158 err = IE_ERROR;
8159 goto leave;
8161 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8162 struct isds_list *prev_item = NULL;
8164 /* Iterate over all permission records */
8165 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8166 struct isds_list *item;
8168 /* Prepare structure */
8169 item = calloc(1, sizeof(*item));
8170 if (!item) {
8171 err = IE_NOMEM;
8172 goto leave;
8174 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8175 if (i == 0) *permissions = item;
8176 else prev_item->next = item;
8177 prev_item = item;
8179 /* Extract it */
8180 xpath_ctx->node = result->nodesetval->nodeTab[i];
8181 err = extract_DbPDZRecord(context,
8182 (struct isds_commercial_permission **) (&item->data),
8183 xpath_ctx);
8184 if (err) goto leave;
8188 leave:
8189 if (err) {
8190 isds_list_free(permissions);
8193 xmlXPathFreeObject(result);
8194 xmlXPathFreeContext(xpath_ctx);
8195 xmlFreeDoc(response);
8197 #else /* not HAVE_LIBCURL */
8198 err = IE_NOTSUP;
8199 #endif
8201 return err;
8205 /* Get details about credit for sending pre-paid commercial messages.
8206 * @context is ISDS session context.
8207 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8208 * @from_date is first day of credit history to return in @history. Only
8209 * tm_year, tm_mon and tm_mday carry sane value.
8210 * @to_date is last day of credit history to return in @history. Only
8211 * tm_year, tm_mon and tm_mday carry sane value.
8212 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8213 * if you don't care. This and all other credit values are integers in
8214 * hundredths of Czech Crowns.
8215 * @email outputs notification e-mail address where notifications about credit
8216 * are sent. This is automatically reallocated string. Pass NULL if you don't
8217 * care. It can return NULL if no address is defined.
8218 * @history outputs auto-reallocated list of pointers to struct
8219 * isds_credit_event. Events in closed interval @from_time to @to_time are
8220 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8221 * are sorted by time.
8222 * @return:
8223 * IE_SUCCESS if the credit details have been obtained correctly,
8224 * or other appropriate error. Please note that server allows to retrieve
8225 * only limited history of events. */
8226 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8227 const char *box_id,
8228 const struct tm *from_date, const struct tm *to_date,
8229 long int *credit, char **email, struct isds_list **history) {
8230 isds_error err = IE_SUCCESS;
8231 #if HAVE_LIBCURL
8232 char *box_id_locale = NULL;
8233 xmlNodePtr request = NULL, node;
8234 xmlNsPtr isds_ns = NULL;
8235 xmlChar *string = NULL;
8237 xmlDocPtr response = NULL;
8238 xmlXPathContextPtr xpath_ctx = NULL;
8239 xmlXPathObjectPtr result = NULL;
8241 const xmlChar *codes[] = {
8242 BAD_CAST "1004",
8243 BAD_CAST "2011",
8244 BAD_CAST "1093",
8245 BAD_CAST "1137",
8246 BAD_CAST "1058",
8247 NULL
8249 const char *meanings[] = {
8250 "Insufficient priviledges for the box",
8251 "The box does not exist",
8252 "Date is too long (history is not available after 15 months)",
8253 "Interval is too long (limit is 3 months)",
8254 "Invalid date"
8256 const isds_error errors[] = {
8257 IE_ISDS,
8258 IE_NOEXIST,
8259 IE_DATE,
8260 IE_DATE,
8261 IE_DATE,
8263 struct code_map_isds_error map = {
8264 .codes = codes,
8265 .meanings = meanings,
8266 .errors = errors
8268 #endif
8270 if (!context) return IE_INVALID_CONTEXT;
8271 zfree(context->long_message);
8273 /* Free output argument */
8274 if (NULL != credit) *credit = 0;
8275 if (NULL != email) zfree(*email);
8276 isds_list_free(history);
8278 if (NULL == box_id) return IE_INVAL;
8280 #if HAVE_LIBCURL
8281 /* Check if connection is established */
8282 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8284 box_id_locale = _isds_utf82locale((char*)box_id);
8285 if (NULL == box_id_locale) {
8286 err = IE_NOMEM;
8287 goto leave;
8290 /* Build request */
8291 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8292 if (NULL == request) {
8293 isds_printf_message(context,
8294 _("Could not build DataBoxCreditInfo request for %s box"),
8295 box_id_locale);
8296 err = IE_ERROR;
8297 goto leave;
8299 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8300 if(!isds_ns) {
8301 isds_log_message(context, _("Could not create ISDS name space"));
8302 err = IE_ERROR;
8303 goto leave;
8305 xmlSetNs(request, isds_ns);
8307 /* Add mandatory XSD:tIdDbInput child */
8308 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8309 /* Add mandatory dates elements with optional values */
8310 if (from_date) {
8311 err = tm2datestring(from_date, &string);
8312 if (err) {
8313 isds_log_message(context,
8314 _("Could not convert `from_date' argument to ISO date "
8315 "string"));
8316 goto leave;
8318 INSERT_STRING(request, "ciFromDate", string);
8319 zfree(string);
8320 } else {
8321 INSERT_STRING(request, "ciFromDate", NULL);
8323 if (to_date) {
8324 err = tm2datestring(to_date, &string);
8325 if (err) {
8326 isds_log_message(context,
8327 _("Could not convert `to_date' argument to ISO date "
8328 "string"));
8329 goto leave;
8331 INSERT_STRING(request, "ciTodate", string);
8332 zfree(string);
8333 } else {
8334 INSERT_STRING(request, "ciTodate", NULL);
8337 /* Send request and check response*/
8338 err = send_destroy_request_check_response(context,
8339 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8340 &request, &response, NULL, &map);
8341 if (err) goto leave;
8344 /* Extract data */
8345 /* Set context to the root */
8346 xpath_ctx = xmlXPathNewContext(response);
8347 if (!xpath_ctx) {
8348 err = IE_ERROR;
8349 goto leave;
8351 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8352 err = IE_ERROR;
8353 goto leave;
8355 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8356 xpath_ctx);
8357 if (!result) {
8358 err = IE_ERROR;
8359 goto leave;
8361 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8362 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8363 err = IE_ISDS;
8364 goto leave;
8366 if (result->nodesetval->nodeNr > 1) {
8367 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8368 err = IE_ISDS;
8369 goto leave;
8371 xpath_ctx->node = result->nodesetval->nodeTab[0];
8372 xmlXPathFreeObject(result); result = NULL;
8374 /* Extract common data */
8375 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8376 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8378 /* Extract records */
8379 if (NULL == history) goto leave;
8380 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8381 xpath_ctx);
8382 if (!result) {
8383 err = IE_ERROR;
8384 goto leave;
8386 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8387 struct isds_list *prev_item = NULL;
8389 /* Iterate over all records */
8390 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8391 struct isds_list *item;
8393 /* Prepare structure */
8394 item = calloc(1, sizeof(*item));
8395 if (!item) {
8396 err = IE_NOMEM;
8397 goto leave;
8399 item->destructor = (void(*)(void**))isds_credit_event_free;
8400 if (i == 0) *history = item;
8401 else prev_item->next = item;
8402 prev_item = item;
8404 /* Extract it */
8405 xpath_ctx->node = result->nodesetval->nodeTab[i];
8406 err = extract_CiRecord(context,
8407 (struct isds_credit_event **) (&item->data),
8408 xpath_ctx);
8409 if (err) goto leave;
8413 leave:
8414 if (!err) {
8415 isds_log(ILF_ISDS, ILL_DEBUG,
8416 _("DataBoxCreditInfo request processed by server successfully.\n"));
8418 if (err) {
8419 isds_list_free(history);
8420 if (NULL != email) zfree(*email)
8423 free(box_id_locale);
8424 xmlXPathFreeObject(result);
8425 xmlXPathFreeContext(xpath_ctx);
8426 xmlFreeDoc(response);
8428 #else /* not HAVE_LIBCURL */
8429 err = IE_NOTSUP;
8430 #endif
8432 return err;
8436 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8437 * code, destroy response and log success.
8438 * @context is ISDS session context.
8439 * @service_name is name of SERVICE_DB_MANIPULATION service
8440 * @box_id is UTF-8 encoded box identifier as zero terminated string
8441 * @approval is optional external approval of box manipulation
8442 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8443 * NULL, if you don't care. */
8444 static isds_error build_send_manipulationdbid_request_check_drop_response(
8445 struct isds_ctx *context, const xmlChar *service_name,
8446 const xmlChar *box_id, const struct isds_approval *approval,
8447 xmlChar **refnumber) {
8448 isds_error err = IE_SUCCESS;
8449 #if HAVE_LIBCURL
8450 xmlDocPtr response = NULL;
8451 #endif
8453 if (!context) return IE_INVALID_CONTEXT;
8454 zfree(context->long_message);
8455 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8457 #if HAVE_LIBCURL
8458 /* Check if connection is established */
8459 if (!context->curl) return IE_CONNECTION_CLOSED;
8461 /* Do request and check for success */
8462 err = build_send_dbid_request_check_response(context,
8463 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8464 &response, refnumber);
8465 xmlFreeDoc(response);
8467 if (!err) {
8468 char *service_name_locale = _isds_utf82locale((char *) service_name);
8469 isds_log(ILF_ISDS, ILL_DEBUG,
8470 _("%s request processed by server successfully.\n"),
8471 service_name_locale);
8472 free(service_name_locale);
8474 #else /* not HAVE_LIBCURL */
8475 err = IE_NOTSUP;
8476 #endif
8478 return err;
8482 /* Switch box into state where box can receive commercial messages (off by
8483 * default)
8484 * @context is ISDS session context.
8485 * @box_id is UTF-8 encoded box identifier as zero terminated string
8486 * @allow is true for enable, false for disable commercial messages income
8487 * @approval is optional external approval of box manipulation
8488 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8489 * NULL, if you don't care. */
8490 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8491 const char *box_id, const _Bool allow,
8492 const struct isds_approval *approval, char **refnumber) {
8493 return build_send_manipulationdbid_request_check_drop_response(context,
8494 (allow) ? BAD_CAST "SetOpenAddressing" :
8495 BAD_CAST "ClearOpenAddressing",
8496 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8500 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8501 * message acceptance). This is just a box permission. Sender must apply
8502 * such role by sending each message.
8503 * @context is ISDS session context.
8504 * @box_id is UTF-8 encoded box identifier as zero terminated string
8505 * @allow is true for enable, false for disable OVM role permission
8506 * @approval is optional external approval of box manipulation
8507 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8508 * NULL, if you don't care. */
8509 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8510 const char *box_id, const _Bool allow,
8511 const struct isds_approval *approval, char **refnumber) {
8512 return build_send_manipulationdbid_request_check_drop_response(context,
8513 (allow) ? BAD_CAST "SetEffectiveOVM" :
8514 BAD_CAST "ClearEffectiveOVM",
8515 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8519 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8520 * code, destroy response and log success.
8521 * @context is ISDS session context.
8522 * @service_name is name of SERVICE_DB_MANIPULATION service
8523 * @owner is structure describing box. aifoIsds, address->adCode,
8524 * address->adDistrict members are ignored.
8525 * @approval is optional external approval of box manipulation
8526 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8527 * NULL, if you don't care. */
8528 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8529 struct isds_ctx *context, const xmlChar *service_name,
8530 const struct isds_DbOwnerInfo *owner,
8531 const struct isds_approval *approval, xmlChar **refnumber) {
8532 isds_error err = IE_SUCCESS;
8533 #if HAVE_LIBCURL
8534 char *service_name_locale = NULL;
8535 xmlNodePtr request = NULL, db_owner_info;
8536 xmlNsPtr isds_ns = NULL;
8537 #endif
8540 if (!context) return IE_INVALID_CONTEXT;
8541 zfree(context->long_message);
8542 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8544 #if HAVE_LIBCURL
8545 service_name_locale = _isds_utf82locale((char*)service_name);
8546 if (!service_name_locale) {
8547 err = IE_NOMEM;
8548 goto leave;
8551 /* Build request */
8552 request = xmlNewNode(NULL, service_name);
8553 if (!request) {
8554 isds_printf_message(context,
8555 _("Could not build %s request"), service_name_locale);
8556 err = IE_ERROR;
8557 goto leave;
8559 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8560 if(!isds_ns) {
8561 isds_log_message(context, _("Could not create ISDS name space"));
8562 err = IE_ERROR;
8563 goto leave;
8565 xmlSetNs(request, isds_ns);
8568 /* Add XSD:tOwnerInfoInput child*/
8569 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8570 err = insert_DbOwnerInfo(context, owner, 0, db_owner_info);
8571 if (err) goto leave;
8573 /* Add XSD:gExtApproval*/
8574 err = insert_GExtApproval(context, approval, request);
8575 if (err) goto leave;
8577 /* Send it to server and process response */
8578 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8579 service_name, &request, refnumber);
8581 leave:
8582 xmlFreeNode(request);
8583 free(service_name_locale);
8584 #else /* not HAVE_LIBCURL */
8585 err = IE_NOTSUP;
8586 #endif
8588 return err;
8592 /* Switch box accessibility state on request of box owner.
8593 * Despite the name, owner must do the request off-line. This function is
8594 * designed for such off-line meeting points (e.g. Czech POINT).
8595 * @context is ISDS session context.
8596 * @box identifies box to switch accessibility state. aifoIsds,
8597 * address->adCode, address->adDistrict members are ignored.
8598 * @allow is true for making accessible, false to disallow access.
8599 * @approval is optional external approval of box manipulation
8600 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8601 * NULL, if you don't care. */
8602 isds_error isds_switch_box_accessibility_on_owner_request(
8603 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8604 const _Bool allow, const struct isds_approval *approval,
8605 char **refnumber) {
8606 return build_send_manipulationdbowner_request_check_drop_response(context,
8607 (allow) ? BAD_CAST "EnableOwnDataBox" :
8608 BAD_CAST "DisableOwnDataBox",
8609 box, approval, (xmlChar **) refnumber);
8613 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8614 * date.
8615 * @context is ISDS session context.
8616 * @box identifies box to switch accessibility state. aifoIsds,
8617 * address->adCode, address->adDistrict members are ignored.
8618 * @since is date since accessibility has been denied. This can be past too.
8619 * Only tm_year, tm_mon and tm_mday carry sane value.
8620 * @approval is optional external approval of box manipulation
8621 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8622 * NULL, if you don't care. */
8623 isds_error isds_disable_box_accessibility_externaly(
8624 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8625 const struct tm *since, const struct isds_approval *approval,
8626 char **refnumber) {
8627 isds_error err = IE_SUCCESS;
8628 #if HAVE_LIBCURL
8629 char *service_name_locale = NULL;
8630 xmlNodePtr request = NULL, node;
8631 xmlNsPtr isds_ns = NULL;
8632 xmlChar *string = NULL;
8633 #endif
8636 if (!context) return IE_INVALID_CONTEXT;
8637 zfree(context->long_message);
8638 if (!box || !since) return IE_INVAL;
8640 #if HAVE_LIBCURL
8641 /* Build request */
8642 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8643 if (!request) {
8644 isds_printf_message(context,
8645 _("Could not build %s request"), "DisableDataBoxExternally");
8646 err = IE_ERROR;
8647 goto leave;
8649 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8650 if(!isds_ns) {
8651 isds_log_message(context, _("Could not create ISDS name space"));
8652 err = IE_ERROR;
8653 goto leave;
8655 xmlSetNs(request, isds_ns);
8658 /* Add @box identification */
8659 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8660 err = insert_DbOwnerInfo(context, box, 0, node);
8661 if (err) goto leave;
8663 /* Add @since date */
8664 err = tm2datestring(since, &string);
8665 if(err) {
8666 isds_log_message(context,
8667 _("Could not convert `since' argument to ISO date string"));
8668 goto leave;
8670 INSERT_STRING(request, "dbOwnerDisableDate", string);
8671 zfree(string);
8673 /* Add @approval */
8674 err = insert_GExtApproval(context, approval, request);
8675 if (err) goto leave;
8677 /* Send it to server and process response */
8678 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8679 BAD_CAST "DisableDataBoxExternally", &request,
8680 (xmlChar **) refnumber);
8682 leave:
8683 free(string);
8684 xmlFreeNode(request);
8685 free(service_name_locale);
8686 #else /* not HAVE_LIBCURL */
8687 err = IE_NOTSUP;
8688 #endif
8690 return err;
8694 #if HAVE_LIBCURL
8695 /* Insert struct isds_message data (envelope (recipient data optional) and
8696 * documents into XML tree
8697 * @context is session context
8698 * @outgoing_message is libisds structure with message data
8699 * @create_message is XML CreateMessage or CreateMultipleMessage element
8700 * @process_recipient true for recipient data serialization, false for no
8701 * serialization */
8702 static isds_error insert_envelope_files(struct isds_ctx *context,
8703 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8704 const _Bool process_recipient) {
8706 isds_error err = IE_SUCCESS;
8707 xmlNodePtr envelope, dm_files, node;
8708 xmlChar *string = NULL;
8710 if (!context) return IE_INVALID_CONTEXT;
8711 if (!outgoing_message || !create_message) return IE_INVAL;
8714 /* Build envelope */
8715 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8716 if (!envelope) {
8717 isds_printf_message(context, _("Could not add dmEnvelope child to "
8718 "%s element"), create_message->name);
8719 return IE_ERROR;
8722 if (!outgoing_message->envelope) {
8723 isds_log_message(context, _("Outgoing message is missing envelope"));
8724 err = IE_INVAL;
8725 goto leave;
8728 /* Insert optional message type */
8729 err = insert_message_type(context, outgoing_message->envelope->dmType,
8730 envelope);
8731 if (err) goto leave;
8733 INSERT_STRING(envelope, "dmSenderOrgUnit",
8734 outgoing_message->envelope->dmSenderOrgUnit);
8735 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8736 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8738 if (process_recipient) {
8739 if (!outgoing_message->envelope->dbIDRecipient) {
8740 isds_log_message(context,
8741 _("Outgoing message is missing recipient box identifier"));
8742 err = IE_INVAL;
8743 goto leave;
8745 INSERT_STRING(envelope, "dbIDRecipient",
8746 outgoing_message->envelope->dbIDRecipient);
8748 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8749 outgoing_message->envelope->dmRecipientOrgUnit);
8750 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8751 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8752 INSERT_STRING(envelope, "dmToHands",
8753 outgoing_message->envelope->dmToHands);
8756 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8757 "dmAnnotation");
8758 INSERT_STRING(envelope, "dmAnnotation",
8759 outgoing_message->envelope->dmAnnotation);
8761 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8762 0, 50, "dmRecipientRefNumber");
8763 INSERT_STRING(envelope, "dmRecipientRefNumber",
8764 outgoing_message->envelope->dmRecipientRefNumber);
8766 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8767 0, 50, "dmSenderRefNumber");
8768 INSERT_STRING(envelope, "dmSenderRefNumber",
8769 outgoing_message->envelope->dmSenderRefNumber);
8771 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8772 0, 50, "dmRecipientIdent");
8773 INSERT_STRING(envelope, "dmRecipientIdent",
8774 outgoing_message->envelope->dmRecipientIdent);
8776 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8777 0, 50, "dmSenderIdent");
8778 INSERT_STRING(envelope, "dmSenderIdent",
8779 outgoing_message->envelope->dmSenderIdent);
8781 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8782 outgoing_message->envelope->dmLegalTitleLaw, string);
8783 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8784 outgoing_message->envelope->dmLegalTitleYear, string);
8785 INSERT_STRING(envelope, "dmLegalTitleSect",
8786 outgoing_message->envelope->dmLegalTitleSect);
8787 INSERT_STRING(envelope, "dmLegalTitlePar",
8788 outgoing_message->envelope->dmLegalTitlePar);
8789 INSERT_STRING(envelope, "dmLegalTitlePoint",
8790 outgoing_message->envelope->dmLegalTitlePoint);
8792 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8793 outgoing_message->envelope->dmPersonalDelivery);
8794 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8795 outgoing_message->envelope->dmAllowSubstDelivery);
8797 /* ???: Should we require value for dbEffectiveOVM sender?
8798 * ISDS has default as true */
8799 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8800 INSERT_BOOLEAN(envelope, "dmOVM",
8801 outgoing_message->envelope->dmPublishOwnID);
8804 /* Append dmFiles */
8805 if (!outgoing_message->documents) {
8806 isds_log_message(context,
8807 _("Outgoing message is missing list of documents"));
8808 err = IE_INVAL;
8809 goto leave;
8811 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8812 if (!dm_files) {
8813 isds_printf_message(context, _("Could not add dmFiles child to "
8814 "%s element"), create_message->name);
8815 err = IE_ERROR;
8816 goto leave;
8819 /* Check for document hierarchy */
8820 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8821 if (err) goto leave;
8823 /* Process each document */
8824 for (struct isds_list *item =
8825 (struct isds_list *) outgoing_message->documents;
8826 item; item = item->next) {
8827 if (!item->data) {
8828 isds_log_message(context,
8829 _("List of documents contains empty item"));
8830 err = IE_INVAL;
8831 goto leave;
8833 /* FIXME: Check for dmFileMetaType and for document references.
8834 * Only first document can be of MAIN type */
8835 err = insert_document(context, (struct isds_document*) item->data,
8836 dm_files);
8838 if (err) goto leave;
8841 leave:
8842 free(string);
8843 return err;
8845 #endif /* HAVE_LIBCURL */
8848 /* Send a message via ISDS to a recipient
8849 * @context is session context
8850 * @outgoing_message is message to send; Some members are mandatory (like
8851 * dbIDRecipient), some are optional and some are irrelevant (especially data
8852 * about sender). Included pointer to isds_list documents must contain at
8853 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8854 * members will be filled with valid data from ISDS. Exact list of write
8855 * members is subject to change. Currently dmID is changed.
8856 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8857 isds_error isds_send_message(struct isds_ctx *context,
8858 struct isds_message *outgoing_message) {
8860 isds_error err = IE_SUCCESS;
8861 #if HAVE_LIBCURL
8862 xmlNsPtr isds_ns = NULL;
8863 xmlNodePtr request = NULL;
8864 xmlDocPtr response = NULL;
8865 xmlChar *code = NULL, *message = NULL;
8866 xmlXPathContextPtr xpath_ctx = NULL;
8867 xmlXPathObjectPtr result = NULL;
8868 /*_Bool message_is_complete = 0;*/
8869 #endif
8871 if (!context) return IE_INVALID_CONTEXT;
8872 zfree(context->long_message);
8873 if (!outgoing_message) return IE_INVAL;
8875 #if HAVE_LIBCURL
8876 /* Check if connection is established
8877 * TODO: This check should be done downstairs. */
8878 if (!context->curl) return IE_CONNECTION_CLOSED;
8881 /* Build CreateMessage request */
8882 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8883 if (!request) {
8884 isds_log_message(context,
8885 _("Could not build CreateMessage request"));
8886 return IE_ERROR;
8888 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8889 if(!isds_ns) {
8890 isds_log_message(context, _("Could not create ISDS name space"));
8891 xmlFreeNode(request);
8892 return IE_ERROR;
8894 xmlSetNs(request, isds_ns);
8896 /* Append envelope and files */
8897 err = insert_envelope_files(context, outgoing_message, request, 1);
8898 if (err) goto leave;
8901 /* Signal we can serialize message since now */
8902 /*message_is_complete = 1;*/
8905 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8907 /* Sent request */
8908 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8910 /* Don't' destroy request, we want to provide it to application later */
8912 if (err) {
8913 isds_log(ILF_ISDS, ILL_DEBUG,
8914 _("Processing ISDS response on CreateMessage "
8915 "request failed\n"));
8916 goto leave;
8919 /* Check for response status */
8920 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8921 &code, &message, NULL);
8922 if (err) {
8923 isds_log(ILF_ISDS, ILL_DEBUG,
8924 _("ISDS response on CreateMessage request "
8925 "is missing status\n"));
8926 goto leave;
8929 /* Request processed, but refused by server or server failed */
8930 if (xmlStrcmp(code, BAD_CAST "0000")) {
8931 char *box_id_locale =
8932 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8933 char *code_locale = _isds_utf82locale((char*)code);
8934 char *message_locale = _isds_utf82locale((char*)message);
8935 isds_log(ILF_ISDS, ILL_DEBUG,
8936 _("Server did not accept message for %s on CreateMessage "
8937 "request (code=%s, message=%s)\n"),
8938 box_id_locale, code_locale, message_locale);
8939 isds_log_message(context, message_locale);
8940 free(box_id_locale);
8941 free(code_locale);
8942 free(message_locale);
8943 err = IE_ISDS;
8944 goto leave;
8948 /* Extract data */
8949 xpath_ctx = xmlXPathNewContext(response);
8950 if (!xpath_ctx) {
8951 err = IE_ERROR;
8952 goto leave;
8954 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8955 err = IE_ERROR;
8956 goto leave;
8958 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8959 xpath_ctx);
8960 if (!result) {
8961 err = IE_ERROR;
8962 goto leave;
8964 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8965 isds_log_message(context, _("Missing CreateMessageResponse element"));
8966 err = IE_ISDS;
8967 goto leave;
8969 if (result->nodesetval->nodeNr > 1) {
8970 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8971 err = IE_ISDS;
8972 goto leave;
8974 xpath_ctx->node = result->nodesetval->nodeTab[0];
8975 xmlXPathFreeObject(result); result = NULL;
8977 if (outgoing_message->envelope->dmID) {
8978 free(outgoing_message->envelope->dmID);
8979 outgoing_message->envelope->dmID = NULL;
8981 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8982 if (!outgoing_message->envelope->dmID) {
8983 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8984 "but did not return assigned message ID\n"));
8987 leave:
8988 /* TODO: Serialize message into structure member raw */
8989 /* XXX: Each web service transport message in different format.
8990 * Therefore it's not possible to save them directly.
8991 * To save them, one must figure out common format.
8992 * We can leave it on application, or we can implement the ESS format. */
8993 /*if (message_is_complete) {
8994 if (outgoing_message->envelope->dmID) {
8996 /* Add assigned message ID as first child*/
8997 /*xmlNodePtr dmid_text = xmlNewText(
8998 (xmlChar *) outgoing_message->envelope->dmID);
8999 if (!dmid_text) goto serialization_failed;
9001 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9002 BAD_CAST "dmID");
9003 if (!dmid_element) {
9004 xmlFreeNode(dmid_text);
9005 goto serialization_failed;
9008 xmlNodePtr dmid_element_with_text =
9009 xmlAddChild(dmid_element, dmid_text);
9010 if (!dmid_element_with_text) {
9011 xmlFreeNode(dmid_element);
9012 xmlFreeNode(dmid_text);
9013 goto serialization_failed;
9016 node = xmlAddPrevSibling(envelope->childern,
9017 dmid_element_with_text);
9018 if (!node) {
9019 xmlFreeNodeList(dmid_element_with_text);
9020 goto serialization_failed;
9024 /* Serialize message with ID into raw */
9025 /*buffer = serialize_element(envelope)*/
9026 /* }
9028 serialization_failed:
9032 /* Clean up */
9033 xmlXPathFreeObject(result);
9034 xmlXPathFreeContext(xpath_ctx);
9036 free(code);
9037 free(message);
9038 xmlFreeDoc(response);
9039 xmlFreeNode(request);
9041 if (!err)
9042 isds_log(ILF_ISDS, ILL_DEBUG,
9043 _("CreateMessage request processed by server "
9044 "successfully.\n"));
9045 #else /* not HAVE_LIBCURL */
9046 err = IE_NOTSUP;
9047 #endif
9049 return err;
9053 /* Send a message via ISDS to a multiple recipients
9054 * @context is session context
9055 * @outgoing_message is message to send; Some members are mandatory,
9056 * some are optional and some are irrelevant (especially data
9057 * about sender). Data about recipient will be substituted by ISDS from
9058 * @copies. Included pointer to isds_list documents must
9059 * contain at least one document of FILEMETATYPE_MAIN.
9060 * @copies is list of isds_message_copy structures addressing all desired
9061 * recipients. This is read-write structure, some members will be filled with
9062 * valid data from ISDS (message IDs, error codes, error descriptions).
9063 * @return
9064 * ISDS_SUCCESS if all messages have been sent
9065 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9066 * succeeded messages can be identified by copies->data->error),
9067 * or other error code if something other goes wrong. */
9068 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
9069 const struct isds_message *outgoing_message,
9070 struct isds_list *copies) {
9072 isds_error err = IE_SUCCESS;
9073 #if HAVE_LIBCURL
9074 isds_error append_err;
9075 xmlNsPtr isds_ns = NULL;
9076 xmlNodePtr request = NULL, recipients, recipient, node;
9077 struct isds_list *item;
9078 struct isds_message_copy *copy;
9079 xmlDocPtr response = NULL;
9080 xmlChar *code = NULL, *message = NULL;
9081 xmlXPathContextPtr xpath_ctx = NULL;
9082 xmlXPathObjectPtr result = NULL;
9083 xmlChar *string = NULL;
9084 int i;
9085 #endif
9087 if (!context) return IE_INVALID_CONTEXT;
9088 zfree(context->long_message);
9089 if (!outgoing_message || !copies) return IE_INVAL;
9091 #if HAVE_LIBCURL
9092 /* Check if connection is established
9093 * TODO: This check should be done downstairs. */
9094 if (!context->curl) return IE_CONNECTION_CLOSED;
9097 /* Build CreateMultipleMessage request */
9098 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9099 if (!request) {
9100 isds_log_message(context,
9101 _("Could not build CreateMultipleMessage request"));
9102 return IE_ERROR;
9104 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9105 if(!isds_ns) {
9106 isds_log_message(context, _("Could not create ISDS name space"));
9107 xmlFreeNode(request);
9108 return IE_ERROR;
9110 xmlSetNs(request, isds_ns);
9113 /* Build recipients */
9114 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9115 if (!recipients) {
9116 isds_log_message(context, _("Could not add dmRecipients child to "
9117 "CreateMultipleMessage element"));
9118 xmlFreeNode(request);
9119 return IE_ERROR;
9122 /* Insert each recipient */
9123 for (item = copies; item; item = item->next) {
9124 copy = (struct isds_message_copy *) item->data;
9125 if (!copy) {
9126 isds_log_message(context,
9127 _("`copies' list item contains empty data"));
9128 err = IE_INVAL;
9129 goto leave;
9132 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9133 if (!recipient) {
9134 isds_log_message(context, _("Could not add dmRecipient child to "
9135 "dmRecipients element"));
9136 err = IE_ERROR;
9137 goto leave;
9140 if (!copy->dbIDRecipient) {
9141 isds_log_message(context,
9142 _("Message copy is missing recipient box identifier"));
9143 err = IE_INVAL;
9144 goto leave;
9146 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9147 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9148 copy->dmRecipientOrgUnit);
9149 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9150 copy->dmRecipientOrgUnitNum, string);
9151 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9154 /* Append envelope and files */
9155 err = insert_envelope_files(context, outgoing_message, request, 0);
9156 if (err) goto leave;
9159 isds_log(ILF_ISDS, ILL_DEBUG,
9160 _("Sending CreateMultipleMessage request to ISDS\n"));
9162 /* Sent request */
9163 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9164 if (err) {
9165 isds_log(ILF_ISDS, ILL_DEBUG,
9166 _("Processing ISDS response on CreateMultipleMessage "
9167 "request failed\n"));
9168 goto leave;
9171 /* Check for response status */
9172 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9173 &code, &message, NULL);
9174 if (err) {
9175 isds_log(ILF_ISDS, ILL_DEBUG,
9176 _("ISDS response on CreateMultipleMessage request "
9177 "is missing status\n"));
9178 goto leave;
9181 /* Request processed, but some copies failed */
9182 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9183 char *box_id_locale =
9184 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9185 char *code_locale = _isds_utf82locale((char*)code);
9186 char *message_locale = _isds_utf82locale((char*)message);
9187 isds_log(ILF_ISDS, ILL_DEBUG,
9188 _("Server did accept message for multiple recipients "
9189 "on CreateMultipleMessage request but delivery to "
9190 "some of them failed (code=%s, message=%s)\n"),
9191 box_id_locale, code_locale, message_locale);
9192 isds_log_message(context, message_locale);
9193 free(box_id_locale);
9194 free(code_locale);
9195 free(message_locale);
9196 err = IE_PARTIAL_SUCCESS;
9199 /* Request refused by server as whole */
9200 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9201 char *box_id_locale =
9202 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9203 char *code_locale = _isds_utf82locale((char*)code);
9204 char *message_locale = _isds_utf82locale((char*)message);
9205 isds_log(ILF_ISDS, ILL_DEBUG,
9206 _("Server did not accept message for multiple recipients "
9207 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9208 box_id_locale, code_locale, message_locale);
9209 isds_log_message(context, message_locale);
9210 free(box_id_locale);
9211 free(code_locale);
9212 free(message_locale);
9213 err = IE_ISDS;
9214 goto leave;
9218 /* Extract data */
9219 xpath_ctx = xmlXPathNewContext(response);
9220 if (!xpath_ctx) {
9221 err = IE_ERROR;
9222 goto leave;
9224 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9225 err = IE_ERROR;
9226 goto leave;
9228 result = xmlXPathEvalExpression(
9229 BAD_CAST "/isds:CreateMultipleMessageResponse"
9230 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9231 xpath_ctx);
9232 if (!result) {
9233 err = IE_ERROR;
9234 goto leave;
9236 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9237 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9238 err = IE_ISDS;
9239 goto leave;
9242 /* Extract message ID and delivery status for each copy */
9243 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9244 item = item->next, i++) {
9245 copy = (struct isds_message_copy *) item->data;
9246 xpath_ctx->node = result->nodesetval->nodeTab[i];
9248 append_err = append_TMStatus(context, copy, xpath_ctx);
9249 if (append_err) {
9250 err = append_err;
9251 goto leave;
9254 if (item || i < result->nodesetval->nodeNr) {
9255 isds_printf_message(context, _("ISDS returned unexpected number of "
9256 "message copy delivery states: %d"),
9257 result->nodesetval->nodeNr);
9258 err = IE_ISDS;
9259 goto leave;
9263 leave:
9264 /* Clean up */
9265 free(string);
9266 xmlXPathFreeObject(result);
9267 xmlXPathFreeContext(xpath_ctx);
9269 free(code);
9270 free(message);
9271 xmlFreeDoc(response);
9272 xmlFreeNode(request);
9274 if (!err)
9275 isds_log(ILF_ISDS, ILL_DEBUG,
9276 _("CreateMultipleMessageResponse request processed by server "
9277 "successfully.\n"));
9278 #else /* not HAVE_LIBCURL */
9279 err = IE_NOTSUP;
9280 #endif
9282 return err;
9286 /* Get list of messages. This is common core for getting sent or received
9287 * messages.
9288 * Any criterion argument can be NULL, if you don't care about it.
9289 * @context is session context. Must not be NULL.
9290 * @outgoing_direction is true if you want list of outgoing messages,
9291 * it's false if you want incoming messages.
9292 * @from_time is minimal time and date of message sending inclusive.
9293 * @to_time is maximal time and date of message sending inclusive
9294 * @organization_unit_number is number of sender/recipient respectively.
9295 * @status_filter is bit field of isds_message_status values. Use special
9296 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9297 * all values, you can use bit-wise arithmetic if you want.)
9298 * @offset is index of first message we are interested in. First message is 1.
9299 * Set to 0 (or 1) if you don't care.
9300 * @number is maximal length of list you want to get as input value, outputs
9301 * number of messages matching these criteria. Can be NULL if you don't care
9302 * (applies to output value either).
9303 * @messages is automatically reallocated list of isds_message's. Be ware that
9304 * it returns only brief overview (envelope and some other fields) about each
9305 * message, not the complete message. FIXME: Specify exact fields.
9306 * The list is sorted by delivery time in ascending order.
9307 * Use NULL if you don't care about don't need the data (useful if you want to
9308 * know only the @number). If you provide &NULL, list will be allocated on
9309 * heap, if you provide pointer to non-NULL, list will be freed automatically
9310 * at first. Also in case of error the list will be NULLed.
9311 * @return IE_SUCCESS or appropriate error code. */
9312 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9313 _Bool outgoing_direction,
9314 const struct timeval *from_time, const struct timeval *to_time,
9315 const long int *organization_unit_number,
9316 const unsigned int status_filter,
9317 const unsigned long int offset, unsigned long int *number,
9318 struct isds_list **messages) {
9320 isds_error err = IE_SUCCESS;
9321 #if HAVE_LIBCURL
9322 xmlNsPtr isds_ns = NULL;
9323 xmlNodePtr request = NULL, node;
9324 xmlDocPtr response = NULL;
9325 xmlChar *code = NULL, *message = NULL;
9326 xmlXPathContextPtr xpath_ctx = NULL;
9327 xmlXPathObjectPtr result = NULL;
9328 xmlChar *string = NULL;
9329 int count = 0;
9330 #endif
9332 if (!context) return IE_INVALID_CONTEXT;
9333 zfree(context->long_message);
9335 /* Free former message list if any */
9336 if (messages) isds_list_free(messages);
9338 #if HAVE_LIBCURL
9339 /* Check if connection is established
9340 * TODO: This check should be done downstairs. */
9341 if (!context->curl) return IE_CONNECTION_CLOSED;
9343 /* Build GetListOf*Messages request */
9344 request = xmlNewNode(NULL,
9345 (outgoing_direction) ?
9346 BAD_CAST "GetListOfSentMessages" :
9347 BAD_CAST "GetListOfReceivedMessages"
9349 if (!request) {
9350 isds_log_message(context,
9351 (outgoing_direction) ?
9352 _("Could not build GetListOfSentMessages request") :
9353 _("Could not build GetListOfReceivedMessages request")
9355 return IE_ERROR;
9357 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9358 if(!isds_ns) {
9359 isds_log_message(context, _("Could not create ISDS name space"));
9360 xmlFreeNode(request);
9361 return IE_ERROR;
9363 xmlSetNs(request, isds_ns);
9366 if (from_time) {
9367 err = timeval2timestring(from_time, &string);
9368 if (err) goto leave;
9370 INSERT_STRING(request, "dmFromTime", string);
9371 free(string); string = NULL;
9373 if (to_time) {
9374 err = timeval2timestring(to_time, &string);
9375 if (err) goto leave;
9377 INSERT_STRING(request, "dmToTime", string);
9378 free(string); string = NULL;
9380 if (outgoing_direction) {
9381 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9382 organization_unit_number, string);
9383 } else {
9384 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9385 organization_unit_number, string);
9388 if (status_filter > MESSAGESTATE_ANY) {
9389 isds_printf_message(context,
9390 _("Invalid message state filter value: %ld"), status_filter);
9391 err = IE_INVAL;
9392 goto leave;
9394 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9396 if (offset > 0 ) {
9397 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9398 } else {
9399 INSERT_STRING(request, "dmOffset", "1");
9402 /* number 0 means no limit */
9403 if (number && *number == 0) {
9404 INSERT_STRING(request, "dmLimit", NULL);
9405 } else {
9406 INSERT_ULONGINT(request, "dmLimit", number, string);
9410 isds_log(ILF_ISDS, ILL_DEBUG,
9411 (outgoing_direction) ?
9412 _("Sending GetListOfSentMessages request to ISDS\n") :
9413 _("Sending GetListOfReceivedMessages request to ISDS\n")
9416 /* Sent request */
9417 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9418 xmlFreeNode(request); request = NULL;
9420 if (err) {
9421 isds_log(ILF_ISDS, ILL_DEBUG,
9422 (outgoing_direction) ?
9423 _("Processing ISDS response on GetListOfSentMessages "
9424 "request failed\n") :
9425 _("Processing ISDS response on GetListOfReceivedMessages "
9426 "request failed\n")
9428 goto leave;
9431 /* Check for response status */
9432 err = isds_response_status(context, SERVICE_DM_INFO, response,
9433 &code, &message, NULL);
9434 if (err) {
9435 isds_log(ILF_ISDS, ILL_DEBUG,
9436 (outgoing_direction) ?
9437 _("ISDS response on GetListOfSentMessages request "
9438 "is missing status\n") :
9439 _("ISDS response on GetListOfReceivedMessages request "
9440 "is missing status\n")
9442 goto leave;
9445 /* Request processed, but nothing found */
9446 if (xmlStrcmp(code, BAD_CAST "0000")) {
9447 char *code_locale = _isds_utf82locale((char*)code);
9448 char *message_locale = _isds_utf82locale((char*)message);
9449 isds_log(ILF_ISDS, ILL_DEBUG,
9450 (outgoing_direction) ?
9451 _("Server refused GetListOfSentMessages request "
9452 "(code=%s, message=%s)\n") :
9453 _("Server refused GetListOfReceivedMessages request "
9454 "(code=%s, message=%s)\n"),
9455 code_locale, message_locale);
9456 isds_log_message(context, message_locale);
9457 free(code_locale);
9458 free(message_locale);
9459 err = IE_ISDS;
9460 goto leave;
9464 /* Extract data */
9465 xpath_ctx = xmlXPathNewContext(response);
9466 if (!xpath_ctx) {
9467 err = IE_ERROR;
9468 goto leave;
9470 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9471 err = IE_ERROR;
9472 goto leave;
9474 result = xmlXPathEvalExpression(
9475 (outgoing_direction) ?
9476 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9477 "isds:dmRecords/isds:dmRecord" :
9478 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9479 "isds:dmRecords/isds:dmRecord",
9480 xpath_ctx);
9481 if (!result) {
9482 err = IE_ERROR;
9483 goto leave;
9486 /* Fill output arguments in */
9487 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9488 struct isds_envelope *envelope;
9489 struct isds_list *item = NULL, *last_item = NULL;
9491 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9492 /* Create new message */
9493 item = calloc(1, sizeof(*item));
9494 if (!item) {
9495 err = IE_NOMEM;
9496 goto leave;
9498 item->destructor = (void(*)(void**)) &isds_message_free;
9499 item->data = calloc(1, sizeof(struct isds_message));
9500 if (!item->data) {
9501 isds_list_free(&item);
9502 err = IE_NOMEM;
9503 goto leave;
9506 /* Extract envelope data */
9507 xpath_ctx->node = result->nodesetval->nodeTab[count];
9508 envelope = NULL;
9509 err = extract_DmRecord(context, &envelope, xpath_ctx);
9510 if (err) {
9511 isds_list_free(&item);
9512 goto leave;
9515 /* Attach extracted envelope */
9516 ((struct isds_message *) item->data)->envelope = envelope;
9518 /* Append new message into the list */
9519 if (!*messages) {
9520 *messages = last_item = item;
9521 } else {
9522 last_item->next = item;
9523 last_item = item;
9527 if (number) *number = count;
9529 leave:
9530 if (err) {
9531 isds_list_free(messages);
9534 free(string);
9535 xmlXPathFreeObject(result);
9536 xmlXPathFreeContext(xpath_ctx);
9538 free(code);
9539 free(message);
9540 xmlFreeDoc(response);
9541 xmlFreeNode(request);
9543 if (!err)
9544 isds_log(ILF_ISDS, ILL_DEBUG,
9545 (outgoing_direction) ?
9546 _("GetListOfSentMessages request processed by server "
9547 "successfully.\n") :
9548 _("GetListOfReceivedMessages request processed by server "
9549 "successfully.\n")
9551 #else /* not HAVE_LIBCURL */
9552 err = IE_NOTSUP;
9553 #endif
9554 return err;
9558 /* Get list of outgoing (already sent) messages.
9559 * Any criterion argument can be NULL, if you don't care about it.
9560 * @context is session context. Must not be NULL.
9561 * @from_time is minimal time and date of message sending inclusive.
9562 * @to_time is maximal time and date of message sending inclusive
9563 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9564 * @status_filter is bit field of isds_message_status values. Use special
9565 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9566 * all values, you can use bit-wise arithmetic if you want.)
9567 * @offset is index of first message we are interested in. First message is 1.
9568 * Set to 0 (or 1) if you don't care.
9569 * @number is maximal length of list you want to get as input value, outputs
9570 * number of messages matching these criteria. Can be NULL if you don't care
9571 * (applies to output value either).
9572 * @messages is automatically reallocated list of isds_message's. Be ware that
9573 * it returns only brief overview (envelope and some other fields) about each
9574 * message, not the complete message. FIXME: Specify exact fields.
9575 * The list is sorted by delivery time in ascending order.
9576 * Use NULL if you don't care about the meta data (useful if you want to know
9577 * only the @number). If you provide &NULL, list will be allocated on heap,
9578 * if you provide pointer to non-NULL, list will be freed automatically at
9579 * first. Also in case of error the list will be NULLed.
9580 * @return IE_SUCCESS or appropriate error code. */
9581 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9582 const struct timeval *from_time, const struct timeval *to_time,
9583 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9584 const unsigned long int offset, unsigned long int *number,
9585 struct isds_list **messages) {
9587 return isds_get_list_of_messages(
9588 context, 1,
9589 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9590 offset, number,
9591 messages);
9595 /* Get list of incoming (addressed to you) messages.
9596 * Any criterion argument can be NULL, if you don't care about it.
9597 * @context is session context. Must not be NULL.
9598 * @from_time is minimal time and date of message sending inclusive.
9599 * @to_time is maximal time and date of message sending inclusive
9600 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9601 * @status_filter is bit field of isds_message_status values. Use special
9602 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9603 * all values, you can use bit-wise arithmetic if you want.)
9604 * @offset is index of first message we are interested in. First message is 1.
9605 * Set to 0 (or 1) if you don't care.
9606 * @number is maximal length of list you want to get as input value, outputs
9607 * number of messages matching these criteria. Can be NULL if you don't care
9608 * (applies to output value either).
9609 * @messages is automatically reallocated list of isds_message's. Be ware that
9610 * it returns only brief overview (envelope and some other fields) about each
9611 * message, not the complete message. FIXME: Specify exact fields.
9612 * Use NULL if you don't care about the meta data (useful if you want to know
9613 * only the @number). If you provide &NULL, list will be allocated on heap,
9614 * if you provide pointer to non-NULL, list will be freed automatically at
9615 * first. Also in case of error the list will be NULLed.
9616 * @return IE_SUCCESS or appropriate error code. */
9617 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9618 const struct timeval *from_time, const struct timeval *to_time,
9619 const long int *dmRecipientOrgUnitNum,
9620 const unsigned int status_filter,
9621 const unsigned long int offset, unsigned long int *number,
9622 struct isds_list **messages) {
9624 return isds_get_list_of_messages(
9625 context, 0,
9626 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9627 offset, number,
9628 messages);
9632 /* Get list of sent message state changes.
9633 * Any criterion argument can be NULL, if you don't care about it.
9634 * @context is session context. Must not be NULL.
9635 * @from_time is minimal time and date of status changes inclusive
9636 * @to_time is maximal time and date of status changes inclusive
9637 * @changed_states is automatically reallocated list of
9638 * isds_message_status_change's. If you provide &NULL, list will be allocated
9639 * on heap, if you provide pointer to non-NULL, list will be freed
9640 * automatically at first. Also in case of error the list will be NULLed.
9641 * XXX: The list item ordering is not specified.
9642 * XXX: Server provides only `recent' changes.
9643 * @return IE_SUCCESS or appropriate error code. */
9644 isds_error isds_get_list_of_sent_message_state_changes(
9645 struct isds_ctx *context,
9646 const struct timeval *from_time, const struct timeval *to_time,
9647 struct isds_list **changed_states) {
9649 isds_error err = IE_SUCCESS;
9650 #if HAVE_LIBCURL
9651 xmlNsPtr isds_ns = NULL;
9652 xmlNodePtr request = NULL, node;
9653 xmlDocPtr response = NULL;
9654 xmlXPathContextPtr xpath_ctx = NULL;
9655 xmlXPathObjectPtr result = NULL;
9656 xmlChar *string = NULL;
9657 int count = 0;
9658 #endif
9660 if (!context) return IE_INVALID_CONTEXT;
9661 zfree(context->long_message);
9663 /* Free former message list if any */
9664 isds_list_free(changed_states);
9666 #if HAVE_LIBCURL
9667 /* Check if connection is established
9668 * TODO: This check should be done downstairs. */
9669 if (!context->curl) return IE_CONNECTION_CLOSED;
9671 /* Build GetMessageStateChanges request */
9672 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9673 if (!request) {
9674 isds_log_message(context,
9675 _("Could not build GetMessageStateChanges request"));
9676 return IE_ERROR;
9678 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9679 if(!isds_ns) {
9680 isds_log_message(context, _("Could not create ISDS name space"));
9681 xmlFreeNode(request);
9682 return IE_ERROR;
9684 xmlSetNs(request, isds_ns);
9687 if (from_time) {
9688 err = timeval2timestring(from_time, &string);
9689 if (err) goto leave;
9691 INSERT_STRING(request, "dmFromTime", string);
9692 zfree(string);
9694 if (to_time) {
9695 err = timeval2timestring(to_time, &string);
9696 if (err) goto leave;
9698 INSERT_STRING(request, "dmToTime", string);
9699 zfree(string);
9702 /* Sent request */
9703 err = send_destroy_request_check_response(context,
9704 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9705 &response, NULL, NULL);
9706 if (err) goto leave;
9709 /* Extract data */
9710 xpath_ctx = xmlXPathNewContext(response);
9711 if (!xpath_ctx) {
9712 err = IE_ERROR;
9713 goto leave;
9715 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9716 err = IE_ERROR;
9717 goto leave;
9719 result = xmlXPathEvalExpression(
9720 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9721 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9722 if (!result) {
9723 err = IE_ERROR;
9724 goto leave;
9727 /* Fill output arguments in */
9728 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9729 struct isds_list *item = NULL, *last_item = NULL;
9731 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9732 /* Create new status change */
9733 item = calloc(1, sizeof(*item));
9734 if (!item) {
9735 err = IE_NOMEM;
9736 goto leave;
9738 item->destructor =
9739 (void(*)(void**)) &isds_message_status_change_free;
9741 /* Extract message status change */
9742 xpath_ctx->node = result->nodesetval->nodeTab[count];
9743 err = extract_StateChangesRecord(context,
9744 (struct isds_message_status_change **) &item->data,
9745 xpath_ctx);
9746 if (err) {
9747 isds_list_free(&item);
9748 goto leave;
9751 /* Append new message status change into the list */
9752 if (!*changed_states) {
9753 *changed_states = last_item = item;
9754 } else {
9755 last_item->next = item;
9756 last_item = item;
9761 leave:
9762 if (err) {
9763 isds_list_free(changed_states);
9766 free(string);
9767 xmlXPathFreeObject(result);
9768 xmlXPathFreeContext(xpath_ctx);
9769 xmlFreeDoc(response);
9770 xmlFreeNode(request);
9772 if (!err)
9773 isds_log(ILF_ISDS, ILL_DEBUG,
9774 _("GetMessageStateChanges request processed by server "
9775 "successfully.\n"));
9776 #else /* not HAVE_LIBCURL */
9777 err = IE_NOTSUP;
9778 #endif
9779 return err;
9783 #if HAVE_LIBCURL
9784 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9785 * code
9786 * @context is session context
9787 * @service is ISDS WS service handler
9788 * @service_name is name of SERVICE_DM_OPERATIONS
9789 * @message_id is message ID to send as service argument to ISDS
9790 * @response is reallocated server SOAP body response as XML document
9791 * @raw_response is reallocated bit stream with response body. Use
9792 * NULL if you don't care
9793 * @raw_response_length is size of @raw_response in bytes
9794 * @code is reallocated ISDS status code
9795 * @status_message is reallocated ISDS status message
9796 * @return error coded from lower layer, context message will be set up
9797 * appropriately. */
9798 static isds_error build_send_check_message_request(struct isds_ctx *context,
9799 const isds_service service, const xmlChar *service_name,
9800 const char *message_id,
9801 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9802 xmlChar **code, xmlChar **status_message) {
9804 isds_error err = IE_SUCCESS;
9805 char *service_name_locale = NULL, *message_id_locale = NULL;
9806 xmlNodePtr request = NULL, node;
9807 xmlNsPtr isds_ns = NULL;
9809 if (!context) return IE_INVALID_CONTEXT;
9810 if (!service_name || !message_id) return IE_INVAL;
9811 if (!response || !code || !status_message) return IE_INVAL;
9812 if (!raw_response_length && raw_response) return IE_INVAL;
9814 /* Free output argument */
9815 xmlFreeDoc(*response); *response = NULL;
9816 if (raw_response) zfree(*raw_response);
9817 zfree(*code);
9818 zfree(*status_message);
9821 /* Check if connection is established
9822 * TODO: This check should be done downstairs. */
9823 if (!context->curl) return IE_CONNECTION_CLOSED;
9825 service_name_locale = _isds_utf82locale((char*)service_name);
9826 message_id_locale = _isds_utf82locale(message_id);
9827 if (!service_name_locale || !message_id_locale) {
9828 err = IE_NOMEM;
9829 goto leave;
9832 /* Build request */
9833 request = xmlNewNode(NULL, service_name);
9834 if (!request) {
9835 isds_printf_message(context,
9836 _("Could not build %s request for %s message ID"),
9837 service_name_locale, message_id_locale);
9838 err = IE_ERROR;
9839 goto leave;
9841 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9842 if(!isds_ns) {
9843 isds_log_message(context, _("Could not create ISDS name space"));
9844 err = IE_ERROR;
9845 goto leave;
9847 xmlSetNs(request, isds_ns);
9850 /* Add requested ID */
9851 err = validate_message_id_length(context, (xmlChar *) message_id);
9852 if (err) goto leave;
9853 INSERT_STRING(request, "dmID", message_id);
9856 isds_log(ILF_ISDS, ILL_DEBUG,
9857 _("Sending %s request for %s message ID to ISDS\n"),
9858 service_name_locale, message_id_locale);
9860 /* Send request */
9861 err = _isds(context, service, request, response,
9862 raw_response, raw_response_length);
9863 xmlFreeNode(request); request = NULL;
9865 if (err) {
9866 isds_log(ILF_ISDS, ILL_DEBUG,
9867 _("Processing ISDS response on %s request failed\n"),
9868 service_name_locale);
9869 goto leave;
9872 /* Check for response status */
9873 err = isds_response_status(context, service, *response,
9874 code, status_message, NULL);
9875 if (err) {
9876 isds_log(ILF_ISDS, ILL_DEBUG,
9877 _("ISDS response on %s request is missing status\n"),
9878 service_name_locale);
9879 goto leave;
9882 /* Request processed, but nothing found */
9883 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9884 char *code_locale = _isds_utf82locale((char*) *code);
9885 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9886 isds_log(ILF_ISDS, ILL_DEBUG,
9887 _("Server refused %s request for %s message ID "
9888 "(code=%s, message=%s)\n"),
9889 service_name_locale, message_id_locale,
9890 code_locale, status_message_locale);
9891 isds_log_message(context, status_message_locale);
9892 free(code_locale);
9893 free(status_message_locale);
9894 err = IE_ISDS;
9895 goto leave;
9898 leave:
9899 free(message_id_locale);
9900 free(service_name_locale);
9901 xmlFreeNode(request);
9902 return err;
9906 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9907 * signed data and free ISDS response.
9908 * @context is session context
9909 * @message_id is UTF-8 encoded message ID for logging purpose
9910 * @response is parsed XML document. It will be freed and NULLed in the middle
9911 * of function run to save memory. This is not guaranteed in case of error.
9912 * @request_name is name of ISDS request used to construct response root
9913 * element name and for logging purpose.
9914 * @raw is reallocated output buffer with DER encoded CMS data
9915 * @raw_length is size of @raw buffer in bytes
9916 * @returns standard error codes, in case of error, @raw will be freed and
9917 * NULLed, @response sometimes. */
9918 static isds_error find_extract_signed_data_free_response(
9919 struct isds_ctx *context, const xmlChar *message_id,
9920 xmlDocPtr *response, const xmlChar *request_name,
9921 void **raw, size_t *raw_length) {
9923 isds_error err = IE_SUCCESS;
9924 char *xpath_expression = NULL;
9925 xmlXPathContextPtr xpath_ctx = NULL;
9926 xmlXPathObjectPtr result = NULL;
9927 char *encoded_structure = NULL;
9929 if (!context) return IE_INVALID_CONTEXT;
9930 if (!raw) return IE_INVAL;
9931 zfree(*raw);
9932 if (!message_id || !response || !*response || !request_name || !raw_length)
9933 return IE_INVAL;
9935 /* Build XPath expression */
9936 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9937 "Response/isds:dmSignature");
9938 if (!xpath_expression) return IE_NOMEM;
9940 /* Extract data */
9941 xpath_ctx = xmlXPathNewContext(*response);
9942 if (!xpath_ctx) {
9943 err = IE_ERROR;
9944 goto leave;
9946 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9947 err = IE_ERROR;
9948 goto leave;
9950 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9951 if (!result) {
9952 err = IE_ERROR;
9953 goto leave;
9955 /* Empty response */
9956 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9957 char *message_id_locale = _isds_utf82locale((char*) message_id);
9958 isds_printf_message(context,
9959 _("Server did not return any signed data for message ID `%s' "
9960 "on %s request"),
9961 message_id_locale, request_name);
9962 free(message_id_locale);
9963 err = IE_ISDS;
9964 goto leave;
9966 /* More responses */
9967 if (result->nodesetval->nodeNr > 1) {
9968 char *message_id_locale = _isds_utf82locale((char*) message_id);
9969 isds_printf_message(context,
9970 _("Server did return more signed data for message ID `%s' "
9971 "on %s request"),
9972 message_id_locale, request_name);
9973 free(message_id_locale);
9974 err = IE_ISDS;
9975 goto leave;
9977 /* One response */
9978 xpath_ctx->node = result->nodesetval->nodeTab[0];
9980 /* Extract PKCS#7 structure */
9981 EXTRACT_STRING(".", encoded_structure);
9982 if (!encoded_structure) {
9983 isds_log_message(context, _("dmSignature element is empty"));
9986 /* Here we have delivery info as standalone CMS in encoded_structure.
9987 * We don't need any other data, free them: */
9988 xmlXPathFreeObject(result); result = NULL;
9989 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9990 xmlFreeDoc(*response); *response = NULL;
9993 /* Decode PKCS#7 to DER format */
9994 *raw_length = _isds_b64decode(encoded_structure, raw);
9995 if (*raw_length == (size_t) -1) {
9996 isds_log_message(context,
9997 _("Error while Base64-decoding PKCS#7 structure"));
9998 err = IE_ERROR;
9999 goto leave;
10002 leave:
10003 if (err) {
10004 zfree(*raw);
10005 raw_length = 0;
10008 free(encoded_structure);
10009 xmlXPathFreeObject(result);
10010 xmlXPathFreeContext(xpath_ctx);
10011 free(xpath_expression);
10013 return err;
10015 #endif /* HAVE_LIBCURL */
10018 /* Download incoming message envelope identified by ID.
10019 * @context is session context
10020 * @message_id is message identifier (you can get them from
10021 * isds_get_list_of_received_messages())
10022 * @message is automatically reallocated message retrieved from ISDS.
10023 * It will miss documents per se. Use isds_get_received_message(), if you are
10024 * interested in documents (content) too.
10025 * Returned hash and timestamp require documents to be verifiable. */
10026 isds_error isds_get_received_envelope(struct isds_ctx *context,
10027 const char *message_id, struct isds_message **message) {
10029 isds_error err = IE_SUCCESS;
10030 #if HAVE_LIBCURL
10031 xmlDocPtr response = NULL;
10032 xmlChar *code = NULL, *status_message = NULL;
10033 xmlXPathContextPtr xpath_ctx = NULL;
10034 xmlXPathObjectPtr result = NULL;
10035 #endif
10037 if (!context) return IE_INVALID_CONTEXT;
10038 zfree(context->long_message);
10040 /* Free former message if any */
10041 if (!message) return IE_INVAL;
10042 isds_message_free(message);
10044 #if HAVE_LIBCURL
10045 /* Do request and check for success */
10046 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10047 BAD_CAST "MessageEnvelopeDownload", message_id,
10048 &response, NULL, NULL, &code, &status_message);
10049 if (err) goto leave;
10051 /* Extract data */
10052 xpath_ctx = xmlXPathNewContext(response);
10053 if (!xpath_ctx) {
10054 err = IE_ERROR;
10055 goto leave;
10057 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10058 err = IE_ERROR;
10059 goto leave;
10061 result = xmlXPathEvalExpression(
10062 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
10063 "isds:dmReturnedMessageEnvelope",
10064 xpath_ctx);
10065 if (!result) {
10066 err = IE_ERROR;
10067 goto leave;
10069 /* Empty response */
10070 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10071 char *message_id_locale = _isds_utf82locale((char*) message_id);
10072 isds_printf_message(context,
10073 _("Server did not return any envelope for ID `%s' "
10074 "on MessageEnvelopeDownload request"), message_id_locale);
10075 free(message_id_locale);
10076 err = IE_ISDS;
10077 goto leave;
10079 /* More envelops */
10080 if (result->nodesetval->nodeNr > 1) {
10081 char *message_id_locale = _isds_utf82locale((char*) message_id);
10082 isds_printf_message(context,
10083 _("Server did return more envelopes for ID `%s' "
10084 "on MessageEnvelopeDownload request"), message_id_locale);
10085 free(message_id_locale);
10086 err = IE_ISDS;
10087 goto leave;
10089 /* One message */
10090 xpath_ctx->node = result->nodesetval->nodeTab[0];
10092 /* Extract the envelope (= message without documents, hence 0) */
10093 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10094 if (err) goto leave;
10096 /* Save XML blob */
10097 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10098 &(*message)->raw_length);
10100 leave:
10101 if (err) {
10102 isds_message_free(message);
10105 xmlXPathFreeObject(result);
10106 xmlXPathFreeContext(xpath_ctx);
10108 free(code);
10109 free(status_message);
10110 if (!*message || !(*message)->xml) {
10111 xmlFreeDoc(response);
10114 if (!err)
10115 isds_log(ILF_ISDS, ILL_DEBUG,
10116 _("MessageEnvelopeDownload request processed by server "
10117 "successfully.\n")
10119 #else /* not HAVE_LIBCURL */
10120 err = IE_NOTSUP;
10121 #endif
10122 return err;
10126 /* Load delivery info of any format from buffer.
10127 * @context is session context
10128 * @raw_type advertises format of @buffer content. Only delivery info types
10129 * are accepted.
10130 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10131 * retrieve such data from message->raw after calling
10132 * isds_get_signed_delivery_info().
10133 * @length is length of buffer in bytes.
10134 * @message is automatically reallocated message parsed from @buffer.
10135 * @strategy selects how buffer will be attached into raw isds_message member.
10136 * */
10137 isds_error isds_load_delivery_info(struct isds_ctx *context,
10138 const isds_raw_type raw_type,
10139 const void *buffer, const size_t length,
10140 struct isds_message **message, const isds_buffer_strategy strategy) {
10142 isds_error err = IE_SUCCESS;
10143 message_ns_type message_ns;
10144 xmlDocPtr message_doc = NULL;
10145 xmlXPathContextPtr xpath_ctx = NULL;
10146 xmlXPathObjectPtr result = NULL;
10147 void *xml_stream = NULL;
10148 size_t xml_stream_length = 0;
10150 if (!context) return IE_INVALID_CONTEXT;
10151 zfree(context->long_message);
10152 if (!message) return IE_INVAL;
10153 isds_message_free(message);
10154 if (!buffer) return IE_INVAL;
10157 /* Select buffer format and extract XML from CMS*/
10158 switch (raw_type) {
10159 case RAWTYPE_DELIVERYINFO:
10160 message_ns = MESSAGE_NS_UNSIGNED;
10161 xml_stream = (void *) buffer;
10162 xml_stream_length = length;
10163 break;
10165 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10166 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10167 xml_stream = (void *) buffer;
10168 xml_stream_length = length;
10169 break;
10171 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10172 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10173 err = _isds_extract_cms_data(context, buffer, length,
10174 &xml_stream, &xml_stream_length);
10175 if (err) goto leave;
10176 break;
10178 default:
10179 isds_log_message(context, _("Bad raw delivery representation type"));
10180 return IE_INVAL;
10181 break;
10184 isds_log(ILF_ISDS, ILL_DEBUG,
10185 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10186 xml_stream_length, xml_stream);
10188 /* Convert delivery info XML stream into XPath context */
10189 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10190 if (!message_doc) {
10191 err = IE_XML;
10192 goto leave;
10194 xpath_ctx = xmlXPathNewContext(message_doc);
10195 if (!xpath_ctx) {
10196 err = IE_ERROR;
10197 goto leave;
10199 /* XXX: Name spaces mangled for signed delivery info:
10200 * http://isds.czechpoint.cz/v20/delivery:
10202 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10203 * <q:dmDelivery>
10204 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10205 * <p:dmID>170272</p:dmID>
10206 * ...
10207 * </p:dmDm>
10208 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10209 * ...
10210 * </q:dmEvents>...</q:dmEvents>
10211 * </q:dmDelivery>
10212 * </q:GetDeliveryInfoResponse>
10213 * */
10214 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10215 err = IE_ERROR;
10216 goto leave;
10218 result = xmlXPathEvalExpression(
10219 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10220 xpath_ctx);
10221 if (!result) {
10222 err = IE_ERROR;
10223 goto leave;
10225 /* Empty delivery info */
10226 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10227 isds_printf_message(context,
10228 _("XML document is not sisds:dmDelivery document"));
10229 err = IE_ISDS;
10230 goto leave;
10232 /* More delivery info's */
10233 if (result->nodesetval->nodeNr > 1) {
10234 isds_printf_message(context,
10235 _("XML document has more sisds:dmDelivery elements"));
10236 err = IE_ISDS;
10237 goto leave;
10239 /* One delivery info */
10240 xpath_ctx->node = result->nodesetval->nodeTab[0];
10242 /* Extract the envelope (= message without documents, hence 0).
10243 * XXX: extract_TReturnedMessage() can obtain attachments size,
10244 * but delivery info carries none. It's coded as option elements,
10245 * so it should work. */
10246 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10247 if (err) goto leave;
10249 /* Extract events */
10250 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10251 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10252 if (err) { err = IE_ERROR; goto leave; }
10253 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10254 if (err) goto leave;
10256 /* Append raw CMS structure into message */
10257 (*message)->raw_type = raw_type;
10258 switch (strategy) {
10259 case BUFFER_DONT_STORE:
10260 break;
10261 case BUFFER_COPY:
10262 (*message)->raw = malloc(length);
10263 if (!(*message)->raw) {
10264 err = IE_NOMEM;
10265 goto leave;
10267 memcpy((*message)->raw, buffer, length);
10268 (*message)->raw_length = length;
10269 break;
10270 case BUFFER_MOVE:
10271 (*message)->raw = (void *) buffer;
10272 (*message)->raw_length = length;
10273 break;
10274 default:
10275 err = IE_ENUM;
10276 goto leave;
10279 leave:
10280 if (err) {
10281 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10282 isds_message_free(message);
10285 xmlXPathFreeObject(result);
10286 xmlXPathFreeContext(xpath_ctx);
10287 if (!*message || !(*message)->xml) {
10288 xmlFreeDoc(message_doc);
10290 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10292 if (!err)
10293 isds_log(ILF_ISDS, ILL_DEBUG,
10294 _("Delivery info loaded successfully.\n"));
10295 return err;
10299 /* Download signed delivery info-sheet of given message identified by ID.
10300 * @context is session context
10301 * @message_id is message identifier (you can get them from
10302 * isds_get_list_of_{sent,received}_messages())
10303 * @message is automatically reallocated message retrieved from ISDS.
10304 * It will miss documents per se. Use isds_get_signed_received_message(),
10305 * if you are interested in documents (content). OTOH, only this function
10306 * can get list events message has gone through. */
10307 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10308 const char *message_id, struct isds_message **message) {
10310 isds_error err = IE_SUCCESS;
10311 #if HAVE_LIBCURL
10312 xmlDocPtr response = NULL;
10313 xmlChar *code = NULL, *status_message = NULL;
10314 void *raw = NULL;
10315 size_t raw_length = 0;
10316 #endif
10318 if (!context) return IE_INVALID_CONTEXT;
10319 zfree(context->long_message);
10321 /* Free former message if any */
10322 if (!message) return IE_INVAL;
10323 isds_message_free(message);
10325 #if HAVE_LIBCURL
10326 /* Do request and check for success */
10327 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10328 BAD_CAST "GetSignedDeliveryInfo", message_id,
10329 &response, NULL, NULL, &code, &status_message);
10330 if (err) goto leave;
10332 /* Find signed delivery info, extract it into raw and maybe free
10333 * response */
10334 err = find_extract_signed_data_free_response(context,
10335 (xmlChar *)message_id, &response,
10336 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10337 if (err) goto leave;
10339 /* Parse delivery info */
10340 err = isds_load_delivery_info(context,
10341 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10342 message, BUFFER_MOVE);
10343 if (err) goto leave;
10345 raw = NULL;
10347 leave:
10348 if (err) {
10349 isds_message_free(message);
10352 free(raw);
10353 free(code);
10354 free(status_message);
10355 xmlFreeDoc(response);
10357 if (!err)
10358 isds_log(ILF_ISDS, ILL_DEBUG,
10359 _("GetSignedDeliveryInfo request processed by server "
10360 "successfully.\n")
10362 #else /* not HAVE_LIBCURL */
10363 err = IE_NOTSUP;
10364 #endif
10365 return err;
10369 /* Download delivery info-sheet of given message identified by ID.
10370 * @context is session context
10371 * @message_id is message identifier (you can get them from
10372 * isds_get_list_of_{sent,received}_messages())
10373 * @message is automatically reallocated message retrieved from ISDS.
10374 * It will miss documents per se. Use isds_get_received_message(), if you are
10375 * interested in documents (content). OTOH, only this function can get list
10376 * of events message has gone through. */
10377 isds_error isds_get_delivery_info(struct isds_ctx *context,
10378 const char *message_id, struct isds_message **message) {
10380 isds_error err = IE_SUCCESS;
10381 #if HAVE_LIBCURL
10382 xmlDocPtr response = NULL;
10383 xmlChar *code = NULL, *status_message = NULL;
10384 xmlNodePtr delivery_node = NULL;
10385 void *raw = NULL;
10386 size_t raw_length = 0;
10387 #endif
10389 if (!context) return IE_INVALID_CONTEXT;
10390 zfree(context->long_message);
10392 /* Free former message if any */
10393 if (!message) return IE_INVAL;
10394 isds_message_free(message);
10396 #if HAVE_LIBCURL
10397 /* Do request and check for success */
10398 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10399 BAD_CAST "GetDeliveryInfo", message_id,
10400 &response, NULL, NULL, &code, &status_message);
10401 if (err) goto leave;
10404 /* Serialize delivery info */
10405 delivery_node = xmlDocGetRootElement(response);
10406 if (!delivery_node) {
10407 char *message_id_locale = _isds_utf82locale((char*) message_id);
10408 isds_printf_message(context,
10409 _("Server did not return any delivery info for ID `%s' "
10410 "on GetDeliveryInfo request"), message_id_locale);
10411 free(message_id_locale);
10412 err = IE_ISDS;
10413 goto leave;
10415 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10416 if (err) goto leave;
10418 /* Parse delivery info */
10419 /* TODO: Here we parse the response second time. We could single delivery
10420 * parser from isds_load_delivery_info() to make things faster. */
10421 err = isds_load_delivery_info(context,
10422 RAWTYPE_DELIVERYINFO, raw, raw_length,
10423 message, BUFFER_MOVE);
10424 if (err) goto leave;
10426 raw = NULL;
10429 leave:
10430 if (err) {
10431 isds_message_free(message);
10434 free(raw);
10435 free(code);
10436 free(status_message);
10437 xmlFreeDoc(response);
10439 if (!err)
10440 isds_log(ILF_ISDS, ILL_DEBUG,
10441 _("GetDeliveryInfo request processed by server "
10442 "successfully.\n")
10444 #else /* not HAVE_LIBCURL */
10445 err = IE_NOTSUP;
10446 #endif
10447 return err;
10451 /* Download incoming message identified by ID.
10452 * @context is session context
10453 * @message_id is message identifier (you can get them from
10454 * isds_get_list_of_received_messages())
10455 * @message is automatically reallocated message retrieved from ISDS */
10456 isds_error isds_get_received_message(struct isds_ctx *context,
10457 const char *message_id, struct isds_message **message) {
10459 isds_error err = IE_SUCCESS;
10460 #if HAVE_LIBCURL
10461 xmlDocPtr response = NULL;
10462 void *xml_stream = NULL;
10463 size_t xml_stream_length;
10464 xmlChar *code = NULL, *status_message = NULL;
10465 xmlXPathContextPtr xpath_ctx = NULL;
10466 xmlXPathObjectPtr result = NULL;
10467 char *phys_path = NULL;
10468 size_t phys_start, phys_end;
10469 #endif
10471 if (!context) return IE_INVALID_CONTEXT;
10472 zfree(context->long_message);
10474 /* Free former message if any */
10475 if (NULL == message) return IE_INVAL;
10476 if (message) isds_message_free(message);
10478 #if HAVE_LIBCURL
10479 /* Do request and check for success */
10480 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10481 BAD_CAST "MessageDownload", message_id,
10482 &response, &xml_stream, &xml_stream_length,
10483 &code, &status_message);
10484 if (err) goto leave;
10486 /* Extract data */
10487 xpath_ctx = xmlXPathNewContext(response);
10488 if (!xpath_ctx) {
10489 err = IE_ERROR;
10490 goto leave;
10492 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10493 err = IE_ERROR;
10494 goto leave;
10496 result = xmlXPathEvalExpression(
10497 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10498 xpath_ctx);
10499 if (!result) {
10500 err = IE_ERROR;
10501 goto leave;
10503 /* Empty response */
10504 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10505 char *message_id_locale = _isds_utf82locale((char*) message_id);
10506 isds_printf_message(context,
10507 _("Server did not return any message for ID `%s' "
10508 "on MessageDownload request"), message_id_locale);
10509 free(message_id_locale);
10510 err = IE_ISDS;
10511 goto leave;
10513 /* More messages */
10514 if (result->nodesetval->nodeNr > 1) {
10515 char *message_id_locale = _isds_utf82locale((char*) message_id);
10516 isds_printf_message(context,
10517 _("Server did return more messages for ID `%s' "
10518 "on MessageDownload request"), message_id_locale);
10519 free(message_id_locale);
10520 err = IE_ISDS;
10521 goto leave;
10523 /* One message */
10524 xpath_ctx->node = result->nodesetval->nodeTab[0];
10526 /* Extract the message */
10527 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10528 if (err) goto leave;
10530 /* Locate raw XML blob */
10531 phys_path = strdup(
10532 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10533 PHYSXML_ELEMENT_SEPARATOR
10534 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10535 PHYSXML_ELEMENT_SEPARATOR
10536 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10538 if (!phys_path) {
10539 err = IE_NOMEM;
10540 goto leave;
10542 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10543 phys_path, &phys_start, &phys_end);
10544 zfree(phys_path);
10545 if (err) {
10546 isds_log_message(context,
10547 _("Substring with isds:MessageDownloadResponse element "
10548 "could not be located in raw SOAP message"));
10549 goto leave;
10551 /* Save XML blob */
10552 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10553 &(*message)->raw_length);*/
10554 /* TODO: Store name space declarations from ancestors */
10555 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10556 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10557 (*message)->raw_length = phys_end - phys_start + 1;
10558 (*message)->raw = malloc((*message)->raw_length);
10559 if (!(*message)->raw) {
10560 err = IE_NOMEM;
10561 goto leave;
10563 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10566 leave:
10567 if (err) {
10568 isds_message_free(message);
10571 free(phys_path);
10573 xmlXPathFreeObject(result);
10574 xmlXPathFreeContext(xpath_ctx);
10576 free(code);
10577 free(status_message);
10578 free(xml_stream);
10579 if (!*message || !(*message)->xml) {
10580 xmlFreeDoc(response);
10583 if (!err)
10584 isds_log(ILF_ISDS, ILL_DEBUG,
10585 _("MessageDownload request processed by server "
10586 "successfully.\n")
10588 #else /* not HAVE_LIBCURL */
10589 err = IE_NOTSUP;
10590 #endif
10591 return err;
10595 /* Load message of any type from buffer.
10596 * @context is session context
10597 * @raw_type defines content type of @buffer. Only message types are allowed.
10598 * @buffer is message raw representation. Format (CMS, plain signed,
10599 * message direction) is defined in @raw_type. You can retrieve such data
10600 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10601 * @length is length of buffer in bytes.
10602 * @message is automatically reallocated message parsed from @buffer.
10603 * @strategy selects how buffer will be attached into raw isds_message member.
10604 * */
10605 isds_error isds_load_message(struct isds_ctx *context,
10606 const isds_raw_type raw_type, const void *buffer, const size_t length,
10607 struct isds_message **message, const isds_buffer_strategy strategy) {
10609 isds_error err = IE_SUCCESS;
10610 void *xml_stream = NULL;
10611 size_t xml_stream_length = 0;
10612 message_ns_type message_ns;
10613 xmlDocPtr message_doc = NULL;
10614 xmlXPathContextPtr xpath_ctx = NULL;
10615 xmlXPathObjectPtr result = NULL;
10617 if (!context) return IE_INVALID_CONTEXT;
10618 zfree(context->long_message);
10619 if (!message) return IE_INVAL;
10620 isds_message_free(message);
10621 if (!buffer) return IE_INVAL;
10624 /* Select buffer format and extract XML from CMS*/
10625 switch (raw_type) {
10626 case RAWTYPE_INCOMING_MESSAGE:
10627 message_ns = MESSAGE_NS_UNSIGNED;
10628 xml_stream = (void *) buffer;
10629 xml_stream_length = length;
10630 break;
10632 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10633 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10634 xml_stream = (void *) buffer;
10635 xml_stream_length = length;
10636 break;
10638 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10639 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10640 err = _isds_extract_cms_data(context, buffer, length,
10641 &xml_stream, &xml_stream_length);
10642 if (err) goto leave;
10643 break;
10645 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10646 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10647 xml_stream = (void *) buffer;
10648 xml_stream_length = length;
10649 break;
10651 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10652 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10653 err = _isds_extract_cms_data(context, buffer, length,
10654 &xml_stream, &xml_stream_length);
10655 if (err) goto leave;
10656 break;
10658 default:
10659 isds_log_message(context, _("Bad raw message representation type"));
10660 return IE_INVAL;
10661 break;
10664 isds_log(ILF_ISDS, ILL_DEBUG,
10665 _("Loading message:\n%.*s\nEnd of message\n"),
10666 xml_stream_length, xml_stream);
10668 /* Convert messages XML stream into XPath context */
10669 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10670 if (!message_doc) {
10671 err = IE_XML;
10672 goto leave;
10674 xpath_ctx = xmlXPathNewContext(message_doc);
10675 if (!xpath_ctx) {
10676 err = IE_ERROR;
10677 goto leave;
10679 /* XXX: Standard name space for unsigned incoming direction:
10680 * http://isds.czechpoint.cz/v20/
10682 * XXX: Name spaces mangled for signed outgoing direction:
10683 * http://isds.czechpoint.cz/v20/SentMessage:
10685 * <q:MessageDownloadResponse
10686 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10687 * <q:dmReturnedMessage>
10688 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10689 * <p:dmID>151916</p:dmID>
10690 * ...
10691 * </p:dmDm>
10692 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10693 * ...
10694 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10695 * </q:dmReturnedMessage>
10696 * </q:MessageDownloadResponse>
10698 * XXX: Name spaces mangled for signed incoming direction:
10699 * http://isds.czechpoint.cz/v20/message:
10701 * <q:MessageDownloadResponse
10702 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10703 * <q:dmReturnedMessage>
10704 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10705 * <p:dmID>151916</p:dmID>
10706 * ...
10707 * </p:dmDm>
10708 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10709 * ...
10710 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10711 * </q:dmReturnedMessage>
10712 * </q:MessageDownloadResponse>
10714 * Stupidity of ISDS developers is unlimited */
10715 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10716 err = IE_ERROR;
10717 goto leave;
10719 result = xmlXPathEvalExpression(
10720 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10721 xpath_ctx);
10722 if (!result) {
10723 err = IE_ERROR;
10724 goto leave;
10726 /* Empty message */
10727 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10728 isds_printf_message(context,
10729 _("XML document does not contain "
10730 "sisds:dmReturnedMessage element"));
10731 err = IE_ISDS;
10732 goto leave;
10734 /* More messages */
10735 if (result->nodesetval->nodeNr > 1) {
10736 isds_printf_message(context,
10737 _("XML document has more sisds:dmReturnedMessage elements"));
10738 err = IE_ISDS;
10739 goto leave;
10741 /* One message */
10742 xpath_ctx->node = result->nodesetval->nodeTab[0];
10744 /* Extract the message */
10745 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10746 if (err) goto leave;
10748 /* Append raw buffer into message */
10749 (*message)->raw_type = raw_type;
10750 switch (strategy) {
10751 case BUFFER_DONT_STORE:
10752 break;
10753 case BUFFER_COPY:
10754 (*message)->raw = malloc(length);
10755 if (!(*message)->raw) {
10756 err = IE_NOMEM;
10757 goto leave;
10759 memcpy((*message)->raw, buffer, length);
10760 (*message)->raw_length = length;
10761 break;
10762 case BUFFER_MOVE:
10763 (*message)->raw = (void *) buffer;
10764 (*message)->raw_length = length;
10765 break;
10766 default:
10767 err = IE_ENUM;
10768 goto leave;
10772 leave:
10773 if (err) {
10774 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10775 isds_message_free(message);
10778 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10779 xmlXPathFreeObject(result);
10780 xmlXPathFreeContext(xpath_ctx);
10781 if (!*message || !(*message)->xml) {
10782 xmlFreeDoc(message_doc);
10785 if (!err)
10786 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10787 return err;
10791 /* Determine type of raw message or delivery info according some heuristics.
10792 * It does not validate the raw blob.
10793 * @context is session context
10794 * @raw_type returns content type of @buffer. Valid only if exit code of this
10795 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10796 * reallocated memory.
10797 * @buffer is message raw representation.
10798 * @length is length of buffer in bytes. */
10799 isds_error isds_guess_raw_type(struct isds_ctx *context,
10800 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10801 isds_error err;
10802 void *xml_stream = NULL;
10803 size_t xml_stream_length = 0;
10804 xmlDocPtr document = NULL;
10805 xmlNodePtr root = NULL;
10807 if (!context) return IE_INVALID_CONTEXT;
10808 zfree(context->long_message);
10809 if (length == 0 || !buffer) return IE_INVAL;
10810 if (!raw_type) return IE_INVAL;
10812 /* Try CMS */
10813 err = _isds_extract_cms_data(context, buffer, length,
10814 &xml_stream, &xml_stream_length);
10815 if (err) {
10816 xml_stream = (void *) buffer;
10817 xml_stream_length = (size_t) length;
10818 err = IE_SUCCESS;
10821 /* Try XML */
10822 document = xmlParseMemory(xml_stream, xml_stream_length);
10823 if (!document) {
10824 isds_printf_message(context,
10825 _("Could not parse data as XML document"));
10826 err = IE_NOTSUP;
10827 goto leave;
10830 /* Get root element */
10831 root = xmlDocGetRootElement(document);
10832 if (!root) {
10833 isds_printf_message(context,
10834 _("XML document is missing root element"));
10835 err = IE_XML;
10836 goto leave;
10839 if (!root->ns || !root->ns->href) {
10840 isds_printf_message(context,
10841 _("Root element does not belong to any name space"));
10842 err = IE_NOTSUP;
10843 goto leave;
10846 /* Test name space */
10847 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10848 if (xml_stream == buffer)
10849 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10850 else
10851 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10852 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10853 if (xml_stream == buffer)
10854 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10855 else
10856 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10857 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10858 if (xml_stream == buffer)
10859 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10860 else
10861 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10862 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10863 if (xml_stream != buffer) {
10864 isds_printf_message(context,
10865 _("Document in ISDS name space is encapsulated into CMS" ));
10866 err = IE_NOTSUP;
10867 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10868 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10869 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10870 *raw_type = RAWTYPE_DELIVERYINFO;
10871 else {
10872 isds_printf_message(context,
10873 _("Unknown root element in ISDS name space"));
10874 err = IE_NOTSUP;
10876 } else {
10877 isds_printf_message(context,
10878 _("Unknown name space"));
10879 err = IE_NOTSUP;
10882 leave:
10883 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10884 xmlFreeDoc(document);
10885 return err;
10889 /* Download signed incoming/outgoing message identified by ID.
10890 * @context is session context
10891 * @output is true for outgoing message, false for incoming message
10892 * @message_id is message identifier (you can get them from
10893 * isds_get_list_of_{sent,received}_messages())
10894 * @message is automatically reallocated message retrieved from ISDS. The raw
10895 * member will be filled with PKCS#7 structure in DER format. */
10896 static isds_error isds_get_signed_message(struct isds_ctx *context,
10897 const _Bool outgoing, const char *message_id,
10898 struct isds_message **message) {
10900 isds_error err = IE_SUCCESS;
10901 #if HAVE_LIBCURL
10902 xmlDocPtr response = NULL;
10903 xmlChar *code = NULL, *status_message = NULL;
10904 xmlXPathContextPtr xpath_ctx = NULL;
10905 xmlXPathObjectPtr result = NULL;
10906 char *encoded_structure = NULL;
10907 void *raw = NULL;
10908 size_t raw_length = 0;
10909 #endif
10911 if (!context) return IE_INVALID_CONTEXT;
10912 zfree(context->long_message);
10913 if (!message) return IE_INVAL;
10914 isds_message_free(message);
10916 #if HAVE_LIBCURL
10917 /* Do request and check for success */
10918 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10919 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10920 BAD_CAST "SignedMessageDownload",
10921 message_id, &response, NULL, NULL, &code, &status_message);
10922 if (err) goto leave;
10924 /* Find signed message, extract it into raw and maybe free
10925 * response */
10926 err = find_extract_signed_data_free_response(context,
10927 (xmlChar *)message_id, &response,
10928 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10929 BAD_CAST "SignedMessageDownload",
10930 &raw, &raw_length);
10931 if (err) goto leave;
10933 /* Parse message */
10934 err = isds_load_message(context,
10935 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10936 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10937 raw, raw_length, message, BUFFER_MOVE);
10938 if (err) goto leave;
10940 raw = NULL;
10942 leave:
10943 if (err) {
10944 isds_message_free(message);
10947 free(encoded_structure);
10948 xmlXPathFreeObject(result);
10949 xmlXPathFreeContext(xpath_ctx);
10950 free(raw);
10952 free(code);
10953 free(status_message);
10954 xmlFreeDoc(response);
10956 if (!err)
10957 isds_log(ILF_ISDS, ILL_DEBUG,
10958 (outgoing) ?
10959 _("SignedSentMessageDownload request processed by server "
10960 "successfully.\n") :
10961 _("SignedMessageDownload request processed by server "
10962 "successfully.\n")
10964 #else /* not HAVE_LIBCURL */
10965 err = IE_NOTSUP;
10966 #endif
10967 return err;
10971 /* Download signed incoming message identified by ID.
10972 * @context is session context
10973 * @message_id is message identifier (you can get them from
10974 * isds_get_list_of_received_messages())
10975 * @message is automatically reallocated message retrieved from ISDS. The raw
10976 * member will be filled with PKCS#7 structure in DER format. */
10977 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10978 const char *message_id, struct isds_message **message) {
10979 return isds_get_signed_message(context, 0, message_id, message);
10983 /* Download signed outgoing message identified by ID.
10984 * @context is session context
10985 * @message_id is message identifier (you can get them from
10986 * isds_get_list_of_sent_messages())
10987 * @message is automatically reallocated message retrieved from ISDS. The raw
10988 * member will be filled with PKCS#7 structure in DER format. */
10989 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10990 const char *message_id, struct isds_message **message) {
10991 return isds_get_signed_message(context, 1, message_id, message);
10995 /* Get type and name of user who sent a message identified by ID.
10996 * @context is session context
10997 * @message_id is message identifier
10998 * @sender_type is pointer to automatically allocated type of sender detected
10999 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11000 * library or to the server, NULL will be returned. Pass NULL if you don't
11001 * care about it.
11002 * @raw_sender_type is automatically reallocated UTF-8 string describing
11003 * sender type or NULL if not known to server. Pass NULL if you don't care.
11004 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11005 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11006 isds_error isds_get_message_sender(struct isds_ctx *context,
11007 const char *message_id, isds_sender_type **sender_type,
11008 char **raw_sender_type, char **sender_name) {
11009 isds_error err = IE_SUCCESS;
11010 #if HAVE_LIBCURL
11011 xmlDocPtr response = NULL;
11012 xmlChar *code = NULL, *status_message = NULL;
11013 xmlXPathContextPtr xpath_ctx = NULL;
11014 xmlXPathObjectPtr result = NULL;
11015 char *type_string = NULL;
11016 #endif
11018 if (!context) return IE_INVALID_CONTEXT;
11019 zfree(context->long_message);
11020 if (sender_type) zfree(*sender_type);
11021 if (raw_sender_type) zfree(*raw_sender_type);
11022 if (sender_name) zfree(*sender_name);
11023 if (!message_id) return IE_INVAL;
11025 #if HAVE_LIBCURL
11026 /* Do request and check for success */
11027 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11028 BAD_CAST "GetMessageAuthor",
11029 message_id, &response, NULL, NULL, &code, &status_message);
11030 if (err) goto leave;
11032 /* Extract data */
11033 xpath_ctx = xmlXPathNewContext(response);
11034 if (!xpath_ctx) {
11035 err = IE_ERROR;
11036 goto leave;
11038 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11039 err = IE_ERROR;
11040 goto leave;
11042 result = xmlXPathEvalExpression(
11043 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
11044 if (!result) {
11045 err = IE_ERROR;
11046 goto leave;
11048 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11049 isds_log_message(context,
11050 _("Missing GetMessageAuthorResponse element"));
11051 err = IE_ISDS;
11052 goto leave;
11054 if (result->nodesetval->nodeNr > 1) {
11055 isds_log_message(context,
11056 _("Multiple GetMessageAuthorResponse element"));
11057 err = IE_ISDS;
11058 goto leave;
11060 xpath_ctx->node = result->nodesetval->nodeTab[0];
11061 xmlXPathFreeObject(result); result = NULL;
11063 /* Fill output arguments in */
11064 EXTRACT_STRING("isds:userType", type_string);
11065 if (NULL != type_string) {
11066 if (NULL != sender_type) {
11067 *sender_type = calloc(1, sizeof(**sender_type));
11068 if (NULL == *sender_type) {
11069 err = IE_NOMEM;
11070 goto leave;
11073 err = string2isds_sender_type((xmlChar *)type_string,
11074 *sender_type);
11075 if (err) {
11076 zfree(*sender_type);
11077 if (err == IE_ENUM) {
11078 err = IE_SUCCESS;
11079 char *type_string_locale = _isds_utf82locale(type_string);
11080 isds_log(ILF_ISDS, ILL_WARNING,
11081 _("Unknown isds:userType value: %s"),
11082 type_string_locale);
11083 free(type_string_locale);
11088 if (NULL != sender_name)
11089 EXTRACT_STRING("isds:authorName", *sender_name);
11091 leave:
11092 if (err) {
11093 if (NULL != sender_type) zfree(*sender_type);
11094 zfree(type_string);
11095 if (NULL != sender_name) zfree(*sender_name);
11097 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11099 xmlXPathFreeObject(result);
11100 xmlXPathFreeContext(xpath_ctx);
11102 free(code);
11103 free(status_message);
11104 xmlFreeDoc(response);
11106 if (!err)
11107 isds_log(ILF_ISDS, ILL_DEBUG,
11108 _("GetMessageAuthor request processed by server "
11109 "successfully.\n"));
11110 #else /* not HAVE_LIBCURL */
11111 err = IE_NOTSUP;
11112 #endif
11113 return err;
11117 /* Retrieve hash of message identified by ID stored in ISDS.
11118 * @context is session context
11119 * @message_id is message identifier
11120 * @hash is automatically reallocated message hash downloaded from ISDS.
11121 * Message must exist in system and must not be deleted. */
11122 isds_error isds_download_message_hash(struct isds_ctx *context,
11123 const char *message_id, struct isds_hash **hash) {
11125 isds_error err = IE_SUCCESS;
11126 #if HAVE_LIBCURL
11127 xmlDocPtr response = NULL;
11128 xmlChar *code = NULL, *status_message = NULL;
11129 xmlXPathContextPtr xpath_ctx = NULL;
11130 xmlXPathObjectPtr result = NULL;
11131 #endif
11133 if (!context) return IE_INVALID_CONTEXT;
11134 zfree(context->long_message);
11136 isds_hash_free(hash);
11138 #if HAVE_LIBCURL
11139 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11140 BAD_CAST "VerifyMessage", message_id,
11141 &response, NULL, NULL, &code, &status_message);
11142 if (err) goto leave;
11145 /* Extract data */
11146 xpath_ctx = xmlXPathNewContext(response);
11147 if (!xpath_ctx) {
11148 err = IE_ERROR;
11149 goto leave;
11151 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11152 err = IE_ERROR;
11153 goto leave;
11155 result = xmlXPathEvalExpression(
11156 BAD_CAST "/isds:VerifyMessageResponse",
11157 xpath_ctx);
11158 if (!result) {
11159 err = IE_ERROR;
11160 goto leave;
11162 /* Empty response */
11163 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11164 char *message_id_locale = _isds_utf82locale((char*) message_id);
11165 isds_printf_message(context,
11166 _("Server did not return any response for ID `%s' "
11167 "on VerifyMessage request"), message_id_locale);
11168 free(message_id_locale);
11169 err = IE_ISDS;
11170 goto leave;
11172 /* More responses */
11173 if (result->nodesetval->nodeNr > 1) {
11174 char *message_id_locale = _isds_utf82locale((char*) message_id);
11175 isds_printf_message(context,
11176 _("Server did return more responses for ID `%s' "
11177 "on VerifyMessage request"), message_id_locale);
11178 free(message_id_locale);
11179 err = IE_ISDS;
11180 goto leave;
11182 /* One response */
11183 xpath_ctx->node = result->nodesetval->nodeTab[0];
11185 /* Extract the hash */
11186 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11188 leave:
11189 if (err) {
11190 isds_hash_free(hash);
11193 xmlXPathFreeObject(result);
11194 xmlXPathFreeContext(xpath_ctx);
11196 free(code);
11197 free(status_message);
11198 xmlFreeDoc(response);
11200 if (!err)
11201 isds_log(ILF_ISDS, ILL_DEBUG,
11202 _("VerifyMessage request processed by server "
11203 "successfully.\n")
11205 #else /* not HAVE_LIBCURL */
11206 err = IE_NOTSUP;
11207 #endif
11208 return err;
11212 /* Erase message specified by @message_id from long term storage. Other
11213 * message cannot be erased on user request.
11214 * @context is session context
11215 * @message_id is message identifier.
11216 * @incoming is true for incoming message, false for outgoing message.
11217 * @return
11218 * IE_SUCCESS if message has ben removed
11219 * IE_INVAL if message does not exist in long term storage or message
11220 * belongs to different box
11221 * TODO: IE_NOEPRM if user has no permission to erase a message */
11222 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11223 const char *message_id, _Bool incoming) {
11224 isds_error err = IE_SUCCESS;
11225 #if HAVE_LIBCURL
11226 xmlNodePtr request = NULL, node;
11227 xmlNsPtr isds_ns = NULL;
11228 xmlDocPtr response = NULL;
11229 xmlChar *code = NULL, *status_message = NULL;
11230 #endif
11232 if (!context) return IE_INVALID_CONTEXT;
11233 zfree(context->long_message);
11234 if (NULL == message_id) return IE_INVAL;
11236 /* Check if connection is established
11237 * TODO: This check should be done downstairs. */
11238 if (!context->curl) return IE_CONNECTION_CLOSED;
11240 #if HAVE_LIBCURL
11241 /* Build request */
11242 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11243 if (!request) {
11244 isds_log_message(context,
11245 _("Could build EraseMessage request"));
11246 return IE_ERROR;
11248 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11249 if(!isds_ns) {
11250 isds_log_message(context, _("Could not create ISDS name space"));
11251 xmlFreeNode(request);
11252 return IE_ERROR;
11254 xmlSetNs(request, isds_ns);
11256 err = validate_message_id_length(context, (xmlChar *) message_id);
11257 if (err) goto leave;
11258 INSERT_STRING(request, "dmID", message_id);
11260 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11263 /* Send request */
11264 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11265 "message ID %s to ISDS\n"), message_id);
11266 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11267 xmlFreeNode(request); request = NULL;
11269 if (err) {
11270 isds_log(ILF_ISDS, ILL_DEBUG,
11271 _("Processing ISDS response on EraseMessage request "
11272 "failed\n"));
11273 goto leave;
11276 /* Check for response status */
11277 err = isds_response_status(context, SERVICE_DM_INFO, response,
11278 &code, &status_message, NULL);
11279 if (err) {
11280 isds_log(ILF_ISDS, ILL_DEBUG,
11281 _("ISDS response on EraseMessage request is missing "
11282 "status\n"));
11283 goto leave;
11286 /* Check server status code */
11287 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11288 isds_log_message(context, _("Message to erase belongs to other box"));
11289 err = IE_INVAL;
11290 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11291 isds_log_message(context, _("Message to erase is not saved in "
11292 "long term storage or the direction does not match"));
11293 err = IE_INVAL;
11294 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11295 char *code_locale = _isds_utf82locale((char*) code);
11296 char *message_locale = _isds_utf82locale((char*) status_message);
11297 isds_log(ILF_ISDS, ILL_DEBUG,
11298 _("Server refused EraseMessage request "
11299 "(code=%s, message=%s)\n"),
11300 code_locale, message_locale);
11301 isds_log_message(context, message_locale);
11302 free(code_locale);
11303 free(message_locale);
11304 err = IE_ISDS;
11305 goto leave;
11308 leave:
11309 free(code);
11310 free(status_message);
11311 xmlFreeDoc(response);
11312 xmlFreeNode(request);
11314 if (!err)
11315 isds_log(ILF_ISDS, ILL_DEBUG,
11316 _("EraseMessage request processed by server "
11317 "successfully.\n")
11319 #else /* not HAVE_LIBCURL */
11320 err = IE_NOTSUP;
11321 #endif
11322 return err;
11326 /* Mark message as read. This is a transactional commit function to acknowledge
11327 * to ISDS the message has been downloaded and processed by client properly.
11328 * @context is session context
11329 * @message_id is message identifier. */
11330 isds_error isds_mark_message_read(struct isds_ctx *context,
11331 const char *message_id) {
11333 isds_error err = IE_SUCCESS;
11334 #if HAVE_LIBCURL
11335 xmlDocPtr response = NULL;
11336 xmlChar *code = NULL, *status_message = NULL;
11337 #endif
11339 if (!context) return IE_INVALID_CONTEXT;
11340 zfree(context->long_message);
11342 #if HAVE_LIBCURL
11343 /* Do request and check for success */
11344 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11345 BAD_CAST "MarkMessageAsDownloaded", message_id,
11346 &response, NULL, NULL, &code, &status_message);
11348 free(code);
11349 free(status_message);
11350 xmlFreeDoc(response);
11352 if (!err)
11353 isds_log(ILF_ISDS, ILL_DEBUG,
11354 _("MarkMessageAsDownloaded request processed by server "
11355 "successfully.\n")
11357 #else /* not HAVE_LIBCURL */
11358 err = IE_NOTSUP;
11359 #endif
11360 return err;
11364 /* Mark message as received by recipient. This is applicable only to
11365 * commercial message. Use envelope->dmType message member to distinguish
11366 * commercial message from government message. Government message is
11367 * received automatically (by law), commercial message on recipient request.
11368 * @context is session context
11369 * @message_id is message identifier. */
11370 isds_error isds_mark_message_received(struct isds_ctx *context,
11371 const char *message_id) {
11373 isds_error err = IE_SUCCESS;
11374 #if HAVE_LIBCURL
11375 xmlDocPtr response = NULL;
11376 xmlChar *code = NULL, *status_message = NULL;
11377 #endif
11379 if (!context) return IE_INVALID_CONTEXT;
11380 zfree(context->long_message);
11382 #if HAVE_LIBCURL
11383 /* Do request and check for success */
11384 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11385 BAD_CAST "ConfirmDelivery", message_id,
11386 &response, NULL, NULL, &code, &status_message);
11388 free(code);
11389 free(status_message);
11390 xmlFreeDoc(response);
11392 if (!err)
11393 isds_log(ILF_ISDS, ILL_DEBUG,
11394 _("ConfirmDelivery request processed by server "
11395 "successfully.\n")
11397 #else /* not HAVE_LIBCURL */
11398 err = IE_NOTSUP;
11399 #endif
11400 return err;
11404 /* Send document for authorized conversion into Czech POINT system.
11405 * This is public anonymous service, no log-in necessary. Special context is
11406 * used to reuse keep-a-live HTTPS connection.
11407 * @context is Czech POINT session context. DO NOT use context connected to
11408 * ISDS server. Use new context or context used by this function previously.
11409 * @document is document to convert. Only data, data_length, dmFileDescr and
11410 * is_xml members are significant. Be ware that not all document formats can be
11411 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11412 * @id is reallocated identifier assigned by Czech POINT system to
11413 * your document on submit. Use is to tell it to Czech POINT officer.
11414 * @date is reallocated document submit date (submitted documents
11415 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11416 * value. */
11417 isds_error czp_convert_document(struct isds_ctx *context,
11418 const struct isds_document *document,
11419 char **id, struct tm **date) {
11420 isds_error err = IE_SUCCESS;
11421 #if HAVE_LIBCURL
11422 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11423 xmlNodePtr request = NULL, node;
11424 xmlDocPtr response = NULL;
11426 xmlXPathContextPtr xpath_ctx = NULL;
11427 xmlXPathObjectPtr result = NULL;
11428 long int status = -1;
11429 long int *status_ptr = &status;
11430 char *string = NULL;
11431 #endif
11434 if (!context) return IE_INVALID_CONTEXT;
11435 zfree(context->long_message);
11436 if (!document || !id || !date) return IE_INVAL;
11438 if (document->is_xml) {
11439 isds_log_message(context,
11440 _("XML documents cannot be submitted to conversion"));
11441 return IE_NOTSUP;
11444 /* Free output arguments */
11445 zfree(*id);
11446 zfree(*date);
11448 #if HAVE_LIBCURL
11449 /* Store configuration */
11450 context->type = CTX_TYPE_CZP;
11451 free(context->url);
11452 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11453 if (!(context->url))
11454 return IE_NOMEM;
11456 /* Prepare CURL handle if not yet connected */
11457 if (!context->curl) {
11458 context->curl = curl_easy_init();
11459 if (!(context->curl))
11460 return IE_ERROR;
11463 /* Build conversion request */
11464 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11465 if (!request) {
11466 isds_log_message(context,
11467 _("Could not build Czech POINT conversion request"));
11468 return IE_ERROR;
11470 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11471 if(!deposit_ns) {
11472 isds_log_message(context,
11473 _("Could not create Czech POINT deposit name space"));
11474 xmlFreeNode(request);
11475 return IE_ERROR;
11477 xmlSetNs(request, deposit_ns);
11479 /* Insert children. They are in empty namespace! */
11480 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11481 if(!empty_ns) {
11482 isds_log_message(context, _("Could not create empty name space"));
11483 err = IE_ERROR;
11484 goto leave;
11486 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11487 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11488 document->dmFileDescr);
11490 /* Document encoded in Base64 */
11491 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11492 document->data, document->data_length);
11493 if (err) goto leave;
11495 isds_log(ILF_ISDS, ILL_DEBUG,
11496 _("Submitting document for conversion into Czech POINT deposit"));
11498 /* Send conversion request */
11499 err = _czp_czpdeposit(context, request, &response);
11500 xmlFreeNode(request); request = NULL;
11502 if (err) {
11503 czp_do_close_connection(context);
11504 goto leave;
11508 /* Extract response */
11509 xpath_ctx = xmlXPathNewContext(response);
11510 if (!xpath_ctx) {
11511 err = IE_ERROR;
11512 goto leave;
11514 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11515 err = IE_ERROR;
11516 goto leave;
11518 result = xmlXPathEvalExpression(
11519 BAD_CAST "/deposit:saveDocumentResponse/return",
11520 xpath_ctx);
11521 if (!result) {
11522 err = IE_ERROR;
11523 goto leave;
11525 /* Empty response */
11526 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11527 isds_printf_message(context,
11528 _("Missing `return' element in Czech POINT deposit response"));
11529 err = IE_ISDS;
11530 goto leave;
11532 /* More responses */
11533 if (result->nodesetval->nodeNr > 1) {
11534 isds_printf_message(context,
11535 _("Multiple `return' element in Czech POINT deposit response"));
11536 err = IE_ISDS;
11537 goto leave;
11539 /* One response */
11540 xpath_ctx->node = result->nodesetval->nodeTab[0];
11542 /* Get status */
11543 EXTRACT_LONGINT("status", status_ptr, 1);
11544 if (status) {
11545 EXTRACT_STRING("statusMsg", string);
11546 char *string_locale = _isds_utf82locale(string);
11547 isds_printf_message(context,
11548 _("Czech POINT deposit refused document for conversion "
11549 "(code=%ld, message=%s)"),
11550 status, string_locale);
11551 free(string_locale);
11552 err = IE_ISDS;
11553 goto leave;
11556 /* Get document ID */
11557 EXTRACT_STRING("documentID", *id);
11559 /* Get submit date */
11560 EXTRACT_STRING("dateInserted", string);
11561 if (string) {
11562 *date = calloc(1, sizeof(**date));
11563 if (!*date) {
11564 err = IE_NOMEM;
11565 goto leave;
11567 err = _isds_datestring2tm((xmlChar *)string, *date);
11568 if (err) {
11569 if (err == IE_NOTSUP) {
11570 err = IE_ISDS;
11571 char *string_locale = _isds_utf82locale(string);
11572 isds_printf_message(context,
11573 _("Invalid dateInserted value: %s"), string_locale);
11574 free(string_locale);
11576 goto leave;
11580 leave:
11581 free(string);
11582 xmlXPathFreeObject(result);
11583 xmlXPathFreeContext(xpath_ctx);
11585 xmlFreeDoc(response);
11586 xmlFreeNode(request);
11588 if (!err) {
11589 char *id_locale = _isds_utf82locale((char *) *id);
11590 isds_log(ILF_ISDS, ILL_DEBUG,
11591 _("Document %s has been submitted for conversion "
11592 "to server successfully\n"), id_locale);
11593 free(id_locale);
11595 #else /* not HAVE_LIBCURL */
11596 err = IE_NOTSUP;
11597 #endif
11598 return err;
11602 /* Close possibly opened connection to Czech POINT document deposit.
11603 * @context is Czech POINT session context. */
11604 isds_error czp_close_connection(struct isds_ctx *context) {
11605 if (!context) return IE_INVALID_CONTEXT;
11606 zfree(context->long_message);
11607 #if HAVE_LIBCURL
11608 return czp_do_close_connection(context);
11609 #else
11610 return IE_NOTSUP;
11611 #endif
11615 /* Send request for new box creation in testing ISDS instance.
11616 * It's not possible to request for a production box currently, as it
11617 * communicates via e-mail.
11618 * XXX: This function does not work either. Server complains about invalid
11619 * e-mail address.
11620 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11621 * this function
11622 * @context is special session context for box creation request. DO NOT use
11623 * standard context as it could reveal your password. Use fresh new context or
11624 * context previously used by this function.
11625 * @box is box description to create including single primary user (in case of
11626 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11627 * ignored. It outputs box ID assigned by ISDS in dbID element.
11628 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11629 * box, or contact address of PFO box owner). The email member is mandatory as
11630 * it will be used to deliver credentials.
11631 * @former_names is former name of box owner. Pass NULL if you don't care.
11632 * @approval is optional external approval of box manipulation
11633 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11634 * NULL, if you don't care.*/
11635 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11636 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11637 const char *former_names, const struct isds_approval *approval,
11638 char **refnumber) {
11639 isds_error err = IE_SUCCESS;
11640 #if HAVE_LIBCURL
11641 xmlNodePtr request = NULL;
11642 xmlDocPtr response = NULL;
11643 xmlXPathContextPtr xpath_ctx = NULL;
11644 xmlXPathObjectPtr result = NULL;
11645 #endif
11648 if (!context) return IE_INVALID_CONTEXT;
11649 zfree(context->long_message);
11650 if (!box) return IE_INVAL;
11652 #if HAVE_LIBCURL
11653 if (!box->email || box->email[0] == '\0') {
11654 isds_log_message(context, _("E-mail field is mandatory"));
11655 return IE_INVAL;
11658 /* Scratch box ID */
11659 zfree(box->dbID);
11661 /* Store configuration */
11662 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11663 free(context->url);
11664 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11665 if (!(context->url))
11666 return IE_NOMEM;
11668 /* Prepare CURL handle if not yet connected */
11669 if (!context->curl) {
11670 context->curl = curl_easy_init();
11671 if (!(context->curl))
11672 return IE_ERROR;
11675 /* Build CreateDataBox request */
11676 err = build_CreateDBInput_request(context,
11677 &request, BAD_CAST "CreateDataBox",
11678 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11679 if (err) goto leave;
11681 /* Send it to server and process response */
11682 err = send_destroy_request_check_response(context,
11683 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11684 &response, (xmlChar **) refnumber, NULL);
11685 if (err) goto leave;
11687 /* Extract box ID */
11688 xpath_ctx = xmlXPathNewContext(response);
11689 if (!xpath_ctx) {
11690 err = IE_ERROR;
11691 goto leave;
11693 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11694 err = IE_ERROR;
11695 goto leave;
11697 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11699 leave:
11700 xmlXPathFreeObject(result);
11701 xmlXPathFreeContext(xpath_ctx);
11702 xmlFreeDoc(response);
11703 xmlFreeNode(request);
11705 if (!err) {
11706 isds_log(ILF_ISDS, ILL_DEBUG,
11707 _("CreateDataBox request processed by server successfully.\n"));
11709 #else /* not HAVE_LIBCURL */
11710 err = IE_NOTSUP;
11711 #endif
11713 return err;
11717 /* Submit CMS signed message to ISDS to verify its originality. This is
11718 * stronger form of isds_verify_message_hash() because ISDS does more checks
11719 * than simple one (potentialy old weak) hash comparison.
11720 * @context is session context
11721 * @message is memory with raw CMS signed message bit stream
11722 * @length is @message size in bytes
11723 * @return
11724 * IE_SUCCESS if message originates in ISDS
11725 * IE_NOTEQUAL if message is unknown to ISDS
11726 * other code for other errors */
11727 isds_error isds_authenticate_message(struct isds_ctx *context,
11728 const void *message, size_t length) {
11729 isds_error err = IE_SUCCESS;
11730 #if HAVE_LIBCURL
11731 xmlNsPtr isds_ns = NULL;
11732 xmlNodePtr request = NULL;
11733 xmlDocPtr response = NULL;
11734 xmlXPathContextPtr xpath_ctx = NULL;
11735 xmlXPathObjectPtr result = NULL;
11736 _Bool *authentic = NULL;
11737 #endif
11739 if (!context) return IE_INVALID_CONTEXT;
11740 zfree(context->long_message);
11741 if (!message || length == 0) return IE_INVAL;
11743 #if HAVE_LIBCURL
11744 /* Check if connection is established
11745 * TODO: This check should be done downstairs. */
11746 if (!context->curl) return IE_CONNECTION_CLOSED;
11749 /* Build AuthenticateMessage request */
11750 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11751 if (!request) {
11752 isds_log_message(context,
11753 _("Could not build AuthenticateMessage request"));
11754 return IE_ERROR;
11756 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11757 if(!isds_ns) {
11758 isds_log_message(context, _("Could not create ISDS name space"));
11759 xmlFreeNode(request);
11760 return IE_ERROR;
11762 xmlSetNs(request, isds_ns);
11764 /* Insert Base64 encoded message */
11765 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11766 message, length);
11767 if (err) goto leave;
11769 /* Send request to server and process response */
11770 err = send_destroy_request_check_response(context,
11771 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11772 &response, NULL, NULL);
11773 if (err) goto leave;
11776 /* ISDS has decided */
11777 xpath_ctx = xmlXPathNewContext(response);
11778 if (!xpath_ctx) {
11779 err = IE_ERROR;
11780 goto leave;
11782 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11783 err = IE_ERROR;
11784 goto leave;
11787 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11789 if (!authentic) {
11790 isds_log_message(context,
11791 _("Server did not return any response on "
11792 "AuthenticateMessage request"));
11793 err = IE_ISDS;
11794 goto leave;
11796 if (*authentic) {
11797 isds_log(ILF_ISDS, ILL_DEBUG,
11798 _("ISDS authenticated the message successfully\n"));
11799 } else {
11800 isds_log_message(context, _("ISDS does not know the message"));
11801 err = IE_NOTEQUAL;
11805 leave:
11806 free(authentic);
11807 xmlXPathFreeObject(result);
11808 xmlXPathFreeContext(xpath_ctx);
11810 xmlFreeDoc(response);
11811 xmlFreeNode(request);
11812 #else /* not HAVE_LIBCURL */
11813 err = IE_NOTSUP;
11814 #endif
11816 return err;
11820 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11821 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11822 * be re-signed.
11823 * @context is session context
11824 * @input_data is memory with raw CMS signed message or delivery info bit
11825 * stream to re-sign
11826 * @input_length is @input_data size in bytes
11827 * @output_data is pointer to auto-allocated memory where to store re-signed
11828 * input data blob. Caller must free it.
11829 * @output_data is pointer where to store @output_data size in bytes
11830 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11831 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11832 * @return
11833 * IE_SUCCESS if CMS blob has been re-signed successfully
11834 * other code for other errors */
11835 isds_error isds_resign_message(struct isds_ctx *context,
11836 const void *input_data, size_t input_length,
11837 void **output_data, size_t *output_length, struct tm **valid_to) {
11838 isds_error err = IE_SUCCESS;
11839 #if HAVE_LIBCURL
11840 xmlNsPtr isds_ns = NULL;
11841 xmlNodePtr request = NULL;
11842 xmlDocPtr response = NULL;
11843 xmlXPathContextPtr xpath_ctx = NULL;
11844 xmlXPathObjectPtr result = NULL;
11845 char *string = NULL;
11846 const xmlChar *codes[] = {
11847 BAD_CAST "2200",
11848 BAD_CAST "2201",
11849 BAD_CAST "2204",
11850 BAD_CAST "2207",
11851 NULL
11853 const char *meanings[] = {
11854 "Message is bad",
11855 "Message is not original",
11856 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11857 "Time stamp could not been generated in time"
11859 const isds_error errors[] = {
11860 IE_INVAL,
11861 IE_NOTUNIQ,
11862 IE_INVAL,
11863 IE_ISDS,
11865 struct code_map_isds_error map = {
11866 .codes = codes,
11867 .meanings = meanings,
11868 .errors = errors
11870 #endif
11872 if (NULL != output_data) *output_data = NULL;
11873 if (NULL != output_length) *output_length = 0;
11874 if (NULL != valid_to) *valid_to = NULL;
11876 if (NULL == context) return IE_INVALID_CONTEXT;
11877 zfree(context->long_message);
11878 if (NULL == input_data || 0 == input_length) {
11879 isds_log_message(context, _("Empty CMS blob on input"));
11880 return IE_INVAL;
11882 if (NULL == output_data || NULL == output_length) {
11883 isds_log_message(context,
11884 _("NULL pointer provided for output CMS blob"));
11885 return IE_INVAL;
11888 #if HAVE_LIBCURL
11889 /* Check if connection is established
11890 * TODO: This check should be done downstairs. */
11891 if (!context->curl) return IE_CONNECTION_CLOSED;
11894 /* Build Re-signISDSDocument request */
11895 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11896 if (!request) {
11897 isds_log_message(context,
11898 _("Could not build Re-signISDSDocument request"));
11899 return IE_ERROR;
11901 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11902 if(!isds_ns) {
11903 isds_log_message(context, _("Could not create ISDS name space"));
11904 xmlFreeNode(request);
11905 return IE_ERROR;
11907 xmlSetNs(request, isds_ns);
11909 /* Insert Base64 encoded CMS blob */
11910 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11911 input_data, input_length);
11912 if (err) goto leave;
11914 /* Send request to server and process response */
11915 err = send_destroy_request_check_response(context,
11916 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11917 &response, NULL, &map);
11918 if (err) goto leave;
11921 /* Extract re-signed data */
11922 xpath_ctx = xmlXPathNewContext(response);
11923 if (!xpath_ctx) {
11924 err = IE_ERROR;
11925 goto leave;
11927 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11928 err = IE_ERROR;
11929 goto leave;
11931 result = xmlXPathEvalExpression(
11932 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11933 if (!result) {
11934 err = IE_ERROR;
11935 goto leave;
11937 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11938 isds_log_message(context,
11939 _("Missing Re-signISDSDocumentResponse element"));
11940 err = IE_ISDS;
11941 goto leave;
11943 if (result->nodesetval->nodeNr > 1) {
11944 isds_log_message(context,
11945 _("Multiple Re-signISDSDocumentResponse element"));
11946 err = IE_ISDS;
11947 goto leave;
11949 xpath_ctx->node = result->nodesetval->nodeTab[0];
11950 xmlXPathFreeObject(result); result = NULL;
11952 EXTRACT_STRING("isds:dmResultDoc", string);
11953 /* Decode non-empty data */
11954 if (NULL != string && string[0] != '\0') {
11955 *output_length = _isds_b64decode(string, output_data);
11956 if (*output_length == (size_t) -1) {
11957 isds_log_message(context,
11958 _("Error while Base64-decoding re-signed data"));
11959 err = IE_ERROR;
11960 goto leave;
11962 } else {
11963 isds_log_message(context, _("Server did not send re-signed data"));
11964 err = IE_ISDS;
11965 goto leave;
11967 zfree(string);
11969 if (NULL != valid_to) {
11970 /* Get time stamp expiration date */
11971 EXTRACT_STRING("isds:dmValidTo", string);
11972 if (NULL != string) {
11973 *valid_to = calloc(1, sizeof(**valid_to));
11974 if (!*valid_to) {
11975 err = IE_NOMEM;
11976 goto leave;
11978 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11979 if (err) {
11980 if (err == IE_NOTSUP) {
11981 err = IE_ISDS;
11982 char *string_locale = _isds_utf82locale(string);
11983 isds_printf_message(context,
11984 _("Invalid dmValidTo value: %s"), string_locale);
11985 free(string_locale);
11987 goto leave;
11992 leave:
11993 free(string);
11995 xmlXPathFreeObject(result);
11996 xmlXPathFreeContext(xpath_ctx);
11998 xmlFreeDoc(response);
11999 xmlFreeNode(request);
12000 #else /* not HAVE_LIBCURL */
12001 err = IE_NOTSUP;
12002 #endif
12004 return err;
12007 #undef INSERT_ELEMENT
12008 #undef CHECK_FOR_STRING_LENGTH
12009 #undef INSERT_STRING_ATTRIBUTE
12010 #undef INSERT_ULONGINTNOPTR
12011 #undef INSERT_ULONGINT
12012 #undef INSERT_LONGINT
12013 #undef INSERT_BOOLEAN
12014 #undef INSERT_SCALAR_BOOLEAN
12015 #undef INSERT_STRING
12016 #undef INSERT_STRING_WITH_NS
12017 #undef EXTRACT_STRING_ATTRIBUTE
12018 #undef EXTRACT_ULONGINT
12019 #undef EXTRACT_LONGINT
12020 #undef EXTRACT_BOOLEAN
12021 #undef EXTRACT_STRING
12024 /* Compute hash of message from raw representation and store it into envelope.
12025 * Original hash structure will be destroyed in envelope.
12026 * @context is session context
12027 * @message is message carrying raw XML message blob
12028 * @algorithm is desired hash algorithm to use */
12029 isds_error isds_compute_message_hash(struct isds_ctx *context,
12030 struct isds_message *message, const isds_hash_algorithm algorithm) {
12031 isds_error err = IE_SUCCESS;
12032 const char *nsuri;
12033 void *xml_stream = NULL;
12034 size_t xml_stream_length;
12035 size_t phys_start, phys_end;
12036 char *phys_path = NULL;
12037 struct isds_hash *new_hash = NULL;
12040 if (!context) return IE_INVALID_CONTEXT;
12041 zfree(context->long_message);
12042 if (!message) return IE_INVAL;
12044 if (!message->raw) {
12045 isds_log_message(context,
12046 _("Message does not carry raw representation"));
12047 return IE_INVAL;
12050 switch (message->raw_type) {
12051 case RAWTYPE_INCOMING_MESSAGE:
12052 nsuri = ISDS_NS;
12053 xml_stream = message->raw;
12054 xml_stream_length = message->raw_length;
12055 break;
12057 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
12058 nsuri = SISDS_INCOMING_NS;
12059 xml_stream = message->raw;
12060 xml_stream_length = message->raw_length;
12061 break;
12063 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
12064 nsuri = SISDS_INCOMING_NS;
12065 err = _isds_extract_cms_data(context,
12066 message->raw, message->raw_length,
12067 &xml_stream, &xml_stream_length);
12068 if (err) goto leave;
12069 break;
12071 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
12072 nsuri = SISDS_OUTGOING_NS;
12073 xml_stream = message->raw;
12074 xml_stream_length = message->raw_length;
12075 break;
12077 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12078 nsuri = SISDS_OUTGOING_NS;
12079 err = _isds_extract_cms_data(context,
12080 message->raw, message->raw_length,
12081 &xml_stream, &xml_stream_length);
12082 if (err) goto leave;
12083 break;
12085 default:
12086 isds_log_message(context, _("Bad raw representation type"));
12087 return IE_INVAL;
12088 break;
12092 /* XXX: Hash is computed from original string representing isds:dmDm
12093 * subtree. That means no encoding, white space, xmlns attributes changes.
12094 * In other words, input for hash can be invalid XML stream. */
12095 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12096 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12097 PHYSXML_ELEMENT_SEPARATOR,
12098 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12099 PHYSXML_ELEMENT_SEPARATOR
12100 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12101 err = IE_NOMEM;
12102 goto leave;
12104 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12105 phys_path, &phys_start, &phys_end);
12106 zfree(phys_path);
12107 if (err) {
12108 isds_log_message(context,
12109 _("Substring with isds:dmDM element could not be located "
12110 "in raw message"));
12111 goto leave;
12115 /* Compute hash */
12116 new_hash = calloc(1, sizeof(*new_hash));
12117 if (!new_hash) {
12118 err = IE_NOMEM;
12119 goto leave;
12121 new_hash->algorithm = algorithm;
12122 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12123 new_hash);
12124 if (err) {
12125 isds_log_message(context, _("Could not compute message hash"));
12126 goto leave;
12129 /* Save computed hash */
12130 if (!message->envelope) {
12131 message->envelope = calloc(1, sizeof(*message->envelope));
12132 if (!message->envelope) {
12133 err = IE_NOMEM;
12134 goto leave;
12137 isds_hash_free(&message->envelope->hash);
12138 message->envelope->hash = new_hash;
12140 leave:
12141 if (err) {
12142 isds_hash_free(&new_hash);
12145 free(phys_path);
12146 if (xml_stream != message->raw) free(xml_stream);
12147 return err;
12151 /* Compare two hashes.
12152 * @h1 is first hash
12153 * @h2 is another hash
12154 * @return
12155 * IE_SUCCESS if hashes equal
12156 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12157 * IE_ENUM if not comparable, but both structures defined
12158 * IE_INVAL if some of the structures are undefined (NULL)
12159 * IE_ERROR if internal error occurs */
12160 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12161 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12162 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12163 if (h1->length != h2->length) return IE_ERROR;
12164 if (h1->length > 0 && !h1->value) return IE_ERROR;
12165 if (h2->length > 0 && !h2->value) return IE_ERROR;
12167 for (size_t i = 0; i < h1->length; i++) {
12168 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12169 return IE_NOTEQUAL;
12171 return IE_SUCCESS;
12175 /* Check message has gone through ISDS by comparing message hash stored in
12176 * ISDS and locally computed hash. You must provide message with valid raw
12177 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12178 * This is convenient wrapper for isds_download_message_hash(),
12179 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12180 * @context is session context
12181 * @message is message with valid raw and envelope member; envelope->hash
12182 * member will be changed during function run. Use envelope on heap only.
12183 * @return
12184 * IE_SUCCESS if message originates in ISDS
12185 * IE_NOTEQUAL if message is unknown to ISDS
12186 * other code for other errors */
12187 isds_error isds_verify_message_hash(struct isds_ctx *context,
12188 struct isds_message *message) {
12189 isds_error err = IE_SUCCESS;
12190 struct isds_hash *downloaded_hash = NULL;
12192 if (!context) return IE_INVALID_CONTEXT;
12193 zfree(context->long_message);
12194 if (!message) return IE_INVAL;
12196 if (!message->envelope) {
12197 isds_log_message(context,
12198 _("Given message structure is missing envelope"));
12199 return IE_INVAL;
12201 if (!message->raw) {
12202 isds_log_message(context,
12203 _("Given message structure is missing raw representation"));
12204 return IE_INVAL;
12207 err = isds_download_message_hash(context, message->envelope->dmID,
12208 &downloaded_hash);
12209 if (err) goto leave;
12211 err = isds_compute_message_hash(context, message,
12212 downloaded_hash->algorithm);
12213 if (err) goto leave;
12215 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12217 leave:
12218 isds_hash_free(&downloaded_hash);
12219 return err;
12223 /* Search for document by document ID in list of documents. IDs are compared
12224 * as UTF-8 string.
12225 * @documents is list of isds_documents
12226 * @id is document identifier
12227 * @return first matching document or NULL. */
12228 const struct isds_document *isds_find_document_by_id(
12229 const struct isds_list *documents, const char *id) {
12230 const struct isds_list *item;
12231 const struct isds_document *document;
12233 for (item = documents; item; item = item->next) {
12234 document = (struct isds_document *) item->data;
12235 if (!document) continue;
12237 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12238 return document;
12241 return NULL;
12245 /* Normalize @mime_type to be proper MIME type.
12246 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12247 * guess regular MIME type (e.g. "application/pdf").
12248 * @mime_type is UTF-8 encoded MIME type to fix
12249 * @return original @mime_type if no better interpretation exists, or
12250 * constant static UTF-8 encoded string with proper MIME type. */
12251 const char *isds_normalize_mime_type(const char *mime_type) {
12252 if (!mime_type) return NULL;
12254 for (size_t offset = 0;
12255 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12256 offset += 2) {
12257 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12258 extension_map_mime[offset]))
12259 return (const char *) extension_map_mime[offset + 1];
12262 return mime_type;
12266 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12267 struct isds_message **message);
12268 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12269 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12270 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12271 struct isds_address **address);
12273 int isds_message_free(struct isds_message **message);
12274 int isds_address_free(struct isds_address **address);
12278 /* Makes known all relevant namespaces to given XPath context
12279 * @xpath_ctx is XPath context
12280 * @message_ns selects proper message name space. Unsigned and signed
12281 * messages and delivery info's differ in prefix and URI. */
12282 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12283 const message_ns_type message_ns) {
12284 const xmlChar *message_namespace = NULL;
12286 if (!xpath_ctx) return IE_ERROR;
12288 switch(message_ns) {
12289 case MESSAGE_NS_1:
12290 message_namespace = BAD_CAST ISDS1_NS; break;
12291 case MESSAGE_NS_UNSIGNED:
12292 message_namespace = BAD_CAST ISDS_NS; break;
12293 case MESSAGE_NS_SIGNED_INCOMING:
12294 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12295 case MESSAGE_NS_SIGNED_OUTGOING:
12296 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12297 case MESSAGE_NS_SIGNED_DELIVERY:
12298 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12299 default:
12300 return IE_ENUM;
12303 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12304 return IE_ERROR;
12305 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12306 return IE_ERROR;
12307 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12308 return IE_ERROR;
12309 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12310 return IE_ERROR;
12311 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12312 return IE_ERROR;
12313 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12314 return IE_ERROR;
12315 return IE_SUCCESS;