Added RECEIVER and GUARDIAN user types.
[libisds.git] / src / isds.c
bloba6e33a2ac6a1811b2c2faecd1f51cdae34febb5f
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include <inttypes.h> /* For PRIdMAX formatting macro */
10 #include "utils.h"
11 #if HAVE_LIBCURL
12 #include "soap.h"
13 #endif
14 #include "validator.h"
15 #include "crypto.h"
16 #include "physxml.h"
17 #include "system.h"
19 /* Global variables.
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities;
22 isds_log_level log_level;
23 isds_log_callback log_callback;
24 void *log_callback_data;
25 const char *version_gpgme = N_("n/a");
26 const char *version_gcrypt = N_("n/a");
27 const char *version_openssl = N_("n/a");
28 const char *version_expat = N_("n/a");
30 /* Locators */
31 /* Base URL of production ISDS instance */
32 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
33 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
34 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
36 /* Base URL of production ISDS instance */
37 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
38 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
39 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
41 /* Extension to MIME type map */
42 static const xmlChar *extension_map_mime[] = {
43 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
45 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
46 BAD_CAST "doc", BAD_CAST "application/msword",
47 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
48 "wordprocessingml.document",
49 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
50 BAD_CAST "prj", BAD_CAST "application/octet-stream",
51 BAD_CAST "qix", BAD_CAST "application/octet-stream",
52 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
53 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
54 BAD_CAST "shp", BAD_CAST "application/octet-stream",
55 BAD_CAST "shx", BAD_CAST "application/octet-stream",
56 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
57 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
58 BAD_CAST "edi", BAD_CAST "application/edifact",
59 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
60 BAD_CAST "gfs", BAD_CAST "application/xml",
61 BAD_CAST "gml", BAD_CAST "application/xml",
62 BAD_CAST "gif", BAD_CAST "image/gif",
63 BAD_CAST "htm", BAD_CAST "text/html",
64 BAD_CAST "html", BAD_CAST "text/html",
65 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
66 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
67 BAD_CAST "jfif", BAD_CAST "image/jpeg",
68 BAD_CAST "jpg", BAD_CAST "image/jpeg",
69 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
70 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
71 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
72 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
73 BAD_CAST "mpg", BAD_CAST "video/mpeg",
74 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
75 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
76 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
77 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
78 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
79 BAD_CAST "pdf", BAD_CAST "application/pdf",
80 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
81 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
82 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
83 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
84 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
85 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
86 BAD_CAST "png", BAD_CAST "image/png",
87 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
88 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
89 "presentationml.presentation",
90 BAD_CAST "rtf", BAD_CAST "application/rtf",
91 BAD_CAST "tif", BAD_CAST "image/tiff",
92 BAD_CAST "tiff", BAD_CAST "image/tiff",
93 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
94 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
95 BAD_CAST "txt", BAD_CAST "text/plain",
96 BAD_CAST "wav", BAD_CAST "audio/wav",
97 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
98 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
99 "spreadsheetml.sheet",
100 BAD_CAST "xml", BAD_CAST "application/xml",
101 BAD_CAST "xsd", BAD_CAST "application/xml",
102 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
105 /* Structure type to hold conversion table from status code to isds_error and
106 * long message */
107 struct code_map_isds_error {
108 const xmlChar **codes; /* NULL terminated array of status codes */
109 const char **meanings; /* Mapping to non-localized long messages */
110 const isds_error *errors; /* Mapping to isds_error code */
113 /* Deallocate structure isds_pki_credentials and NULL it.
114 * Pass-phrase is discarded.
115 * @pki credentials to to free */
116 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
117 if(!pki || !*pki) return;
119 free((*pki)->engine);
120 free((*pki)->certificate);
121 free((*pki)->key);
123 if ((*pki)->passphrase) {
124 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
125 free((*pki)->passphrase);
128 zfree((*pki));
132 /* Free isds_list with all member data.
133 * @list list to free, on return will be NULL */
134 void isds_list_free(struct isds_list **list) {
135 struct isds_list *item, *next_item;
137 if (!list || !*list) return;
139 for(item = *list; item; item = next_item) {
140 if (item->destructor) (item->destructor)(&(item->data));
141 next_item = item->next;
142 free(item);
145 *list = NULL;
149 /* Deallocate structure isds_hash and NULL it.
150 * @hash hash to to free */
151 void isds_hash_free(struct isds_hash **hash) {
152 if(!hash || !*hash) return;
153 free((*hash)->value);
154 zfree((*hash));
158 /* Deallocate structure isds_PersonName recursively and NULL it */
159 void isds_PersonName_free(struct isds_PersonName **person_name) {
160 if (!person_name || !*person_name) return;
162 free((*person_name)->pnFirstName);
163 free((*person_name)->pnMiddleName);
164 free((*person_name)->pnLastName);
165 free((*person_name)->pnLastNameAtBirth);
167 free(*person_name);
168 *person_name = NULL;
172 /* Deallocate structure isds_BirthInfo recursively and NULL it */
173 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
174 if (!birth_info || !*birth_info) return;
176 free((*birth_info)->biDate);
177 free((*birth_info)->biCity);
178 free((*birth_info)->biCounty);
179 free((*birth_info)->biState);
181 free(*birth_info);
182 *birth_info = NULL;
186 /* Deallocate structure isds_Address recursively and NULL it */
187 void isds_Address_free(struct isds_Address **address) {
188 if (!address || !*address) return;
190 free((*address)->adCode);
191 free((*address)->adCity);
192 free((*address)->adDistrict);
193 free((*address)->adStreet);
194 free((*address)->adNumberInStreet);
195 free((*address)->adNumberInMunicipality);
196 free((*address)->adZipCode);
197 free((*address)->adState);
199 free(*address);
200 *address = NULL;
204 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
205 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
206 if (!db_owner_info || !*db_owner_info) return;
208 free((*db_owner_info)->dbID);
209 free((*db_owner_info)->dbType);
210 free((*db_owner_info)->ic);
211 isds_PersonName_free(&((*db_owner_info)->personName));
212 free((*db_owner_info)->firmName);
213 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
214 isds_Address_free(&((*db_owner_info)->address));
215 free((*db_owner_info)->nationality);
216 free((*db_owner_info)->email);
217 free((*db_owner_info)->telNumber);
218 free((*db_owner_info)->identifier);
219 free((*db_owner_info)->aifoIsds);
220 free((*db_owner_info)->registryCode);
221 free((*db_owner_info)->dbState);
222 free((*db_owner_info)->dbEffectiveOVM);
223 free((*db_owner_info)->dbOpenAddressing);
225 free(*db_owner_info);
226 *db_owner_info = NULL;
229 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
230 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
231 if (!db_user_info || !*db_user_info) return;
233 free((*db_user_info)->userID);
234 free((*db_user_info)->userType);
235 free((*db_user_info)->userPrivils);
236 isds_PersonName_free(&((*db_user_info)->personName));
237 isds_Address_free(&((*db_user_info)->address));
238 free((*db_user_info)->biDate);
239 free((*db_user_info)->ic);
240 free((*db_user_info)->firmName);
241 free((*db_user_info)->caStreet);
242 free((*db_user_info)->caCity);
243 free((*db_user_info)->caZipCode);
244 free((*db_user_info)->caState);
245 free((*db_user_info)->aifo_ticket);
247 zfree(*db_user_info);
251 /* Deallocate struct isds_event recursively and NULL it */
252 void isds_event_free(struct isds_event **event) {
253 if (!event || !*event) return;
255 free((*event)->time);
256 free((*event)->type);
257 free((*event)->description);
258 zfree(*event);
262 /* Deallocate struct isds_envelope recursively and NULL it */
263 void isds_envelope_free(struct isds_envelope **envelope) {
264 if (!envelope || !*envelope) return;
266 free((*envelope)->dmID);
267 free((*envelope)->dbIDSender);
268 free((*envelope)->dmSender);
269 free((*envelope)->dmSenderAddress);
270 free((*envelope)->dmSenderType);
271 free((*envelope)->dmRecipient);
272 free((*envelope)->dmRecipientAddress);
273 free((*envelope)->dmAmbiguousRecipient);
274 free((*envelope)->dmType);
276 free((*envelope)->dmOrdinal);
277 free((*envelope)->dmMessageStatus);
278 free((*envelope)->dmDeliveryTime);
279 free((*envelope)->dmAcceptanceTime);
280 isds_hash_free(&(*envelope)->hash);
281 free((*envelope)->timestamp);
282 isds_list_free(&(*envelope)->events);
284 free((*envelope)->dmSenderOrgUnit);
285 free((*envelope)->dmSenderOrgUnitNum);
286 free((*envelope)->dbIDRecipient);
287 free((*envelope)->dmRecipientOrgUnit);
288 free((*envelope)->dmRecipientOrgUnitNum);
289 free((*envelope)->dmToHands);
290 free((*envelope)->dmAnnotation);
291 free((*envelope)->dmRecipientRefNumber);
292 free((*envelope)->dmSenderRefNumber);
293 free((*envelope)->dmRecipientIdent);
294 free((*envelope)->dmSenderIdent);
296 free((*envelope)->dmLegalTitleLaw);
297 free((*envelope)->dmLegalTitleYear);
298 free((*envelope)->dmLegalTitleSect);
299 free((*envelope)->dmLegalTitlePar);
300 free((*envelope)->dmLegalTitlePoint);
302 free((*envelope)->dmPersonalDelivery);
303 free((*envelope)->dmAllowSubstDelivery);
305 free((*envelope)->dmOVM);
306 free((*envelope)->dmPublishOwnID);
308 free(*envelope);
309 *envelope = NULL;
313 /* Deallocate struct isds_message recursively and NULL it */
314 void isds_message_free(struct isds_message **message) {
315 if (!message || !*message) return;
317 free((*message)->raw);
318 isds_envelope_free(&((*message)->envelope));
319 isds_list_free(&((*message)->documents));
320 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
322 free(*message);
323 *message = NULL;
327 /* Deallocate struct isds_document recursively and NULL it */
328 void isds_document_free(struct isds_document **document) {
329 if (!document || !*document) return;
331 if (!(*document)->is_xml) {
332 free((*document)->data);
334 free((*document)->dmMimeType);
335 free((*document)->dmFileGuid);
336 free((*document)->dmUpFileGuid);
337 free((*document)->dmFileDescr);
338 free((*document)->dmFormat);
340 free(*document);
341 *document = NULL;
345 /* Deallocate struct isds_message_copy recursively and NULL it */
346 void isds_message_copy_free(struct isds_message_copy **copy) {
347 if (!copy || !*copy) return;
349 free((*copy)->dbIDRecipient);
350 free((*copy)->dmRecipientOrgUnit);
351 free((*copy)->dmRecipientOrgUnitNum);
352 free((*copy)->dmToHands);
354 free((*copy)->dmStatus);
355 free((*copy)->dmID);
357 zfree(*copy);
361 /* Deallocate struct isds_message_status_change recursively and NULL it */
362 void isds_message_status_change_free(
363 struct isds_message_status_change **message_status_change) {
364 if (!message_status_change || !*message_status_change) return;
366 free((*message_status_change)->dmID);
367 free((*message_status_change)->time);
368 free((*message_status_change)->dmMessageStatus);
370 zfree(*message_status_change);
374 /* Deallocate struct isds_approval recursively and NULL it */
375 void isds_approval_free(struct isds_approval **approval) {
376 if (!approval || !*approval) return;
378 free((*approval)->refference);
380 zfree(*approval);
384 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
385 * The email string is deallocated too. */
386 void isds_credentials_delivery_free(
387 struct isds_credentials_delivery **credentials_delivery) {
388 if (!credentials_delivery || !*credentials_delivery) return;
390 free((*credentials_delivery)->email);
391 free((*credentials_delivery)->token);
392 free((*credentials_delivery)->new_user_name);
394 zfree(*credentials_delivery);
398 /* Deallocate struct isds_commercial_permission recursively and NULL it */
399 void isds_commercial_permission_free(
400 struct isds_commercial_permission **permission) {
401 if (NULL == permission || NULL == *permission) return;
403 free((*permission)->recipient);
404 free((*permission)->payer);
405 free((*permission)->expiration);
406 free((*permission)->count);
407 free((*permission)->reply_identifier);
409 zfree(*permission);
413 /* Deallocate struct isds_credit_event recursively and NULL it */
414 void isds_credit_event_free(struct isds_credit_event **event) {
415 if (NULL == event || NULL == *event) return;
417 free((*event)->time);
418 switch ((*event)->type) {
419 case ISDS_CREDIT_CHARGED:
420 free((*event)->details.charged.transaction);
421 break;
422 case ISDS_CREDIT_DISCHARGED:
423 free((*event)->details.discharged.transaction);
424 break;
425 case ISDS_CREDIT_MESSAGE_SENT:
426 free((*event)->details.message_sent.recipient);
427 free((*event)->details.message_sent.message_id);
428 break;
429 case ISDS_CREDIT_STORAGE_SET:
430 free((*event)->details.storage_set.new_valid_from);
431 free((*event)->details.storage_set.new_valid_to);
432 free((*event)->details.storage_set.old_capacity);
433 free((*event)->details.storage_set.old_valid_from);
434 free((*event)->details.storage_set.old_valid_to);
435 free((*event)->details.storage_set.initiator);
436 break;
437 case ISDS_CREDIT_EXPIRED:
438 break;
441 zfree(*event);
445 /* Deallocate struct isds_fulltext_result recursively and NULL it */
446 void isds_fulltext_result_free(
447 struct isds_fulltext_result **result) {
448 if (NULL == result || NULL == *result) return;
450 free((*result)->dbID);
451 free((*result)->name);
452 isds_list_free(&((*result)->name_match_start));
453 isds_list_free(&((*result)->name_match_end));
454 free((*result)->address);
455 isds_list_free(&((*result)->address_match_start));
456 isds_list_free(&((*result)->address_match_end));
457 free((*result)->ic);
458 free((*result)->biDate);
460 zfree(*result);
464 /* Deallocate struct isds_box_state_period recursively and NULL it */
465 void isds_box_state_period_free(struct isds_box_state_period **period) {
466 if (NULL == period || NULL == *period) return;
467 zfree(*period);
471 /* *DUP_OR_ERROR macros needs error label */
472 #define STRDUP_OR_ERROR(new, template) { \
473 if (!template) { \
474 (new) = NULL; \
475 } else { \
476 (new) = strdup(template); \
477 if (!new) goto error; \
481 #define FLATDUP_OR_ERROR(new, template) { \
482 if (!template) { \
483 (new) = NULL; \
484 } else { \
485 (new) = malloc(sizeof(*(new))); \
486 if (!new) goto error; \
487 memcpy((new), (template), sizeof(*(template))); \
491 /* Copy structure isds_pki_credentials recursively. */
492 struct isds_pki_credentials *isds_pki_credentials_duplicate(
493 const struct isds_pki_credentials *template) {
494 struct isds_pki_credentials *new = NULL;
496 if(!template) return NULL;
498 new = calloc(1, sizeof(*new));
499 if (!new) return NULL;
501 STRDUP_OR_ERROR(new->engine, template->engine);
502 new->certificate_format = template->certificate_format;
503 STRDUP_OR_ERROR(new->certificate, template->certificate);
504 new->key_format = template->key_format;
505 STRDUP_OR_ERROR(new->key, template->key);
506 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
508 return new;
510 error:
511 isds_pki_credentials_free(&new);
512 return NULL;
516 /* Copy structure isds_PersonName recursively */
517 struct isds_PersonName *isds_PersonName_duplicate(
518 const struct isds_PersonName *src) {
519 struct isds_PersonName *new = NULL;
521 if (!src) return NULL;
523 new = calloc(1, sizeof(*new));
524 if (!new) return NULL;
526 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
527 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
528 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
529 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
531 return new;
533 error:
534 isds_PersonName_free(&new);
535 return NULL;
539 /* Copy structure isds_BirthInfo recursively */
540 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
541 const struct isds_BirthInfo *template) {
542 struct isds_BirthInfo *new = NULL;
544 if (!template) return NULL;
546 new = calloc(1, sizeof(*new));
547 if (!new) return NULL;
549 FLATDUP_OR_ERROR(new->biDate, template->biDate);
550 STRDUP_OR_ERROR(new->biCity, template->biCity);
551 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
552 STRDUP_OR_ERROR(new->biState, template->biState);
554 return new;
556 error:
557 isds_BirthInfo_free(&new);
558 return NULL;
562 /* Copy structure isds_Address recursively */
563 struct isds_Address *isds_Address_duplicate(
564 const struct isds_Address *src) {
565 struct isds_Address *new = NULL;
567 if (!src) return NULL;
569 new = calloc(1, sizeof(*new));
570 if (!new) return NULL;
572 FLATDUP_OR_ERROR(new->adCode, src->adCode);
573 STRDUP_OR_ERROR(new->adCity, src->adCity);
574 STRDUP_OR_ERROR(new->adDistrict, src->adDistrict);
575 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
576 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
577 STRDUP_OR_ERROR(new->adNumberInMunicipality,
578 src->adNumberInMunicipality);
579 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
580 STRDUP_OR_ERROR(new->adState, src->adState);
582 return new;
584 error:
585 isds_Address_free(&new);
586 return NULL;
590 /* Copy structure isds_DbOwnerInfo recursively */
591 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
592 const struct isds_DbOwnerInfo *src) {
593 struct isds_DbOwnerInfo *new = NULL;
594 if (!src) return NULL;
596 new = calloc(1, sizeof(*new));
597 if (!new) return NULL;
599 STRDUP_OR_ERROR(new->dbID, src->dbID);
600 FLATDUP_OR_ERROR(new->dbType, src->dbType);
601 STRDUP_OR_ERROR(new->ic, src->ic);
603 if (src->personName) {
604 if (!(new->personName =
605 isds_PersonName_duplicate(src->personName)))
606 goto error;
609 STRDUP_OR_ERROR(new->firmName, src->firmName);
611 if (src->birthInfo) {
612 if (!(new->birthInfo =
613 isds_BirthInfo_duplicate(src->birthInfo)))
614 goto error;
617 if (src->address) {
618 if (!(new->address = isds_Address_duplicate(src->address)))
619 goto error;
622 STRDUP_OR_ERROR(new->nationality, src->nationality);
623 STRDUP_OR_ERROR(new->email, src->email);
624 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
625 STRDUP_OR_ERROR(new->identifier, src->identifier);
626 FLATDUP_OR_ERROR(new->aifoIsds, src->aifoIsds);
627 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
628 FLATDUP_OR_ERROR(new->dbState, src->dbState);
629 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
630 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
632 return new;
634 error:
635 isds_DbOwnerInfo_free(&new);
636 return NULL;
640 /* Copy structure isds_DbUserInfo recursively */
641 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
642 const struct isds_DbUserInfo *src) {
643 struct isds_DbUserInfo *new = NULL;
644 if (!src) return NULL;
646 new = calloc(1, sizeof(*new));
647 if (!new) return NULL;
649 STRDUP_OR_ERROR(new->userID, src->userID);
650 FLATDUP_OR_ERROR(new->userType, src->userType);
651 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
653 if (src->personName) {
654 if (!(new->personName =
655 isds_PersonName_duplicate(src->personName)))
656 goto error;
659 if (src->address) {
660 if (!(new->address = isds_Address_duplicate(src->address)))
661 goto error;
664 FLATDUP_OR_ERROR(new->biDate, src->biDate);
665 STRDUP_OR_ERROR(new->ic, src->ic);
666 STRDUP_OR_ERROR(new->firmName, src->firmName);
667 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
668 STRDUP_OR_ERROR(new->caCity, src->caCity);
669 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
670 STRDUP_OR_ERROR(new->caState, src->caState);
671 STRDUP_OR_ERROR(new->aifo_ticket, src->aifo_ticket);
673 return new;
675 error:
676 isds_DbUserInfo_free(&new);
677 return NULL;
681 /* Copy structure isds_box_state_period recursively */
682 struct isds_box_state_period *isds_box_state_period_duplicate(
683 const struct isds_box_state_period *src) {
684 struct isds_box_state_period *new = NULL;
685 if (!src) return NULL;
687 new = calloc(1, sizeof(*new));
688 if (!new) return NULL;
690 memcpy(&new->from, &src->from, sizeof(src->from));
691 memcpy(&new->to, &src->to, sizeof(src->to));
692 new->dbState = src->dbState;
694 return new;
697 #undef FLATDUP_OR_ERROR
698 #undef STRDUP_OR_ERROR
701 /* Logs libxml2 errors. Should be registered to libxml2 library.
702 * @ctx is unused currently
703 * @msg is printf-like formated message from libxml2 (UTF-8?)
704 * @... are variadic arguments for @msg */
705 static void log_xml(void *ctx, const char *msg, ...) {
706 va_list ap;
707 char *text = NULL;
709 /* Silent warning for unused function argument.
710 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
711 (void)ctx;
713 if (!msg) return;
715 va_start(ap, msg);
716 isds_vasprintf(&text, msg, ap);
717 va_end(ap);
719 if (text)
720 isds_log(ILF_XML, ILL_ERR, "%s", text);
721 free(text);
725 /* Initialize ISDS library.
726 * Global function, must be called before other functions.
727 * If it fails you can not use ISDS library and must call isds_cleanup() to
728 * free partially initialized global variables. */
729 isds_error isds_init(void) {
730 /* NULL global variables */
731 log_facilities = ILF_ALL;
732 log_level = ILL_WARNING;
733 log_callback = NULL;
734 log_callback_data = NULL;
736 #if ENABLE_NLS
737 /* Initialize gettext */
738 bindtextdomain(PACKAGE, LOCALEDIR);
739 #endif
741 #if HAVE_LIBCURL
742 /* Initialize CURL */
743 if (curl_global_init(CURL_GLOBAL_ALL)) {
744 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
745 return IE_ERROR;
747 #endif /* HAVE_LIBCURL */
749 /* Initialise cryptographic back-ends. */
750 if (IE_SUCCESS != _isds_init_crypto()) {
751 isds_log(ILF_ISDS, ILL_CRIT,
752 _("Initialization of cryptographic back-end failed\n"));
753 return IE_ERROR;
756 /* This can _exit() current program. Find not so assertive check. */
757 LIBXML_TEST_VERSION;
758 xmlSetGenericErrorFunc(NULL, log_xml);
760 /* Check expat */
761 if (_isds_init_expat(&version_expat)) {
762 isds_log(ILF_ISDS, ILL_CRIT,
763 _("expat library initialization failed\n"));
764 return IE_ERROR;
767 /* Allocate global variables */
770 return IE_SUCCESS;
774 /* Deinitialize ISDS library.
775 * Global function, must be called as last library function. */
776 isds_error isds_cleanup(void) {
777 /* XML */
778 xmlCleanupParser();
780 #if HAVE_LIBCURL
781 /* Curl */
782 curl_global_cleanup();
783 #endif
785 return IE_SUCCESS;
789 /* Return version string of this library. Version of dependencies can be
790 * embedded. Do no try to parse it. You must free it. */
791 char *isds_version(void) {
792 char *buffer = NULL;
794 isds_asprintf(&buffer,
795 #if HAVE_LIBCURL
796 # ifndef USE_OPENSSL_BACKEND
797 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
798 # else
799 _("%s (%s, %s, %s, libxml2 %s)"),
800 # endif
801 #else
802 # ifndef USE_OPENSSL_BACKEND
803 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
804 # else
805 _("%s (%s, %s, libxml2 %s)"),
806 # endif
807 #endif
808 PACKAGE_VERSION,
809 #if HAVE_LIBCURL
810 curl_version(),
811 #endif
812 #ifndef USE_OPENSSL_BACKEND
813 version_gpgme, version_gcrypt,
814 #else
815 version_openssl,
816 #endif
817 version_expat, xmlParserVersion);
818 return buffer;
822 /* Return text description of ISDS error */
823 const char *isds_strerror(const isds_error error) {
824 switch (error) {
825 case IE_SUCCESS:
826 return(_("Success")); break;
827 case IE_ERROR:
828 return(_("Unspecified error")); break;
829 case IE_NOTSUP:
830 return(_("Not supported")); break;
831 case IE_INVAL:
832 return(_("Invalid value")); break;
833 case IE_INVALID_CONTEXT:
834 return(_("Invalid context")); break;
835 case IE_NOT_LOGGED_IN:
836 return(_("Not logged in")); break;
837 case IE_CONNECTION_CLOSED:
838 return(_("Connection closed")); break;
839 case IE_TIMED_OUT:
840 return(_("Timed out")); break;
841 case IE_NOEXIST:
842 return(_("Not exist")); break;
843 case IE_NOMEM:
844 return(_("Out of memory")); break;
845 case IE_NETWORK:
846 return(_("Network problem")); break;
847 case IE_HTTP:
848 return(_("HTTP problem")); break;
849 case IE_SOAP:
850 return(_("SOAP problem")); break;
851 case IE_XML:
852 return(_("XML problem")); break;
853 case IE_ISDS:
854 return(_("ISDS server problem")); break;
855 case IE_ENUM:
856 return(_("Invalid enum value")); break;
857 case IE_DATE:
858 return(_("Invalid date value")); break;
859 case IE_2BIG:
860 return(_("Too big")); break;
861 case IE_2SMALL:
862 return(_("Too small")); break;
863 case IE_NOTUNIQ:
864 return(_("Value not unique")); break;
865 case IE_NOTEQUAL:
866 return(_("Values not equal")); break;
867 case IE_PARTIAL_SUCCESS:
868 return(_("Some suboperations failed")); break;
869 case IE_ABORTED:
870 return(_("Operation aborted")); break;
871 case IE_SECURITY:
872 return(_("Security problem")); break;
873 default:
874 return(_("Unknown error"));
879 /* Create ISDS context.
880 * Each context can be used for different sessions to (possibly) different
881 * ISDS server with different credentials. */
882 struct isds_ctx *isds_ctx_create(void) {
883 struct isds_ctx *context;
884 context = malloc(sizeof(*context));
885 if (context) memset(context, 0, sizeof(*context));
886 return context;
889 #if HAVE_LIBCURL
890 /* Close possibly opened connection to Czech POINT document deposit without
891 * resetting long_message buffer.
892 * XXX: Do not use czp_close_connection() if you do not want to destroy log
893 * message.
894 * @context is Czech POINT session context. */
895 static isds_error czp_do_close_connection(struct isds_ctx *context) {
896 if (!context) return IE_INVALID_CONTEXT;
897 _isds_close_connection(context);
898 return IE_SUCCESS;
902 /* Discard credentials.
903 * @context is ISDS context
904 * @discard_saved_username is true for removing saved username, false for
905 * keeping it.
906 * Only that. It does not cause log out, connection close or similar. */
907 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
908 _Bool discard_saved_username) {
909 if(!context) return IE_INVALID_CONTEXT;
911 if (context->username) {
912 memset(context->username, 0, strlen(context->username));
913 zfree(context->username);
915 if (context->password) {
916 memset(context->password, 0, strlen(context->password));
917 zfree(context->password);
919 isds_pki_credentials_free(&context->pki_credentials);
920 if (discard_saved_username && context->saved_username) {
921 memset(context->saved_username, 0, strlen(context->saved_username));
922 zfree(context->saved_username);
925 return IE_SUCCESS;
927 #endif /* HAVE_LIBCURL */
930 /* Destroy ISDS context and free memory.
931 * @context will be NULLed on success. */
932 isds_error isds_ctx_free(struct isds_ctx **context) {
933 if (!context || !*context) {
934 return IE_INVALID_CONTEXT;
937 #if HAVE_LIBCURL
938 /* Discard credentials and close connection */
939 switch ((*context)->type) {
940 case CTX_TYPE_NONE: break;
941 case CTX_TYPE_ISDS: isds_logout(*context); break;
942 case CTX_TYPE_CZP:
943 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
944 czp_do_close_connection(*context); break;
947 /* For sure */
948 _isds_discard_credentials(*context, 1);
950 /* Free other structures */
951 free((*context)->url);
952 free((*context)->tls_verify_server);
953 free((*context)->tls_ca_file);
954 free((*context)->tls_ca_dir);
955 free((*context)->tls_crl_file);
956 #endif /* HAVE_LIBCURL */
957 free((*context)->long_message);
959 free(*context);
960 *context = NULL;
961 return IE_SUCCESS;
965 /* Return long message text produced by library function, e.g. detailed error
966 * message. Returned pointer is only valid until new library function is
967 * called for the same context. Could be NULL, especially if NULL context is
968 * supplied. Return string is locale encoded. */
969 char *isds_long_message(const struct isds_ctx *context) {
970 if (!context) return NULL;
971 return context->long_message;
975 /* Stores message into context' long_message buffer.
976 * Application can pick the message up using isds_long_message().
977 * NULL @message truncates the buffer but does not deallocate it.
978 * @message is coded in locale encoding */
979 _hidden isds_error isds_log_message(struct isds_ctx *context,
980 const char *message) {
981 char *buffer;
982 size_t length;
984 if (!context) return IE_INVALID_CONTEXT;
986 /* FIXME: Check for integer overflow */
987 length = 1 + ((message) ? strlen(message) : 0);
988 buffer = realloc(context->long_message, length);
989 if (!buffer) return IE_NOMEM;
991 if (message)
992 strcpy(buffer, message);
993 else
994 *buffer = '\0';
996 context->long_message = buffer;
997 return IE_SUCCESS;
1001 /* Appends message into context' long_message buffer.
1002 * Application can pick the message up using isds_long_message().
1003 * NULL message has void effect. */
1004 _hidden isds_error isds_append_message(struct isds_ctx *context,
1005 const char *message) {
1006 char *buffer;
1007 size_t old_length, length;
1009 if (!context) return IE_INVALID_CONTEXT;
1010 if (!message) return IE_SUCCESS;
1011 if (!context->long_message)
1012 return isds_log_message(context, message);
1014 old_length = strlen(context->long_message);
1015 /* FIXME: Check for integer overflow */
1016 length = 1 + old_length + strlen(message);
1017 buffer = realloc(context->long_message, length);
1018 if (!buffer) return IE_NOMEM;
1020 strcpy(buffer + old_length, message);
1022 context->long_message = buffer;
1023 return IE_SUCCESS;
1027 /* Stores formatted message into context' long_message buffer.
1028 * Application can pick the message up using isds_long_message(). */
1029 _hidden isds_error isds_printf_message(struct isds_ctx *context,
1030 const char *format, ...) {
1031 va_list ap;
1032 int length;
1034 if (!context) return IE_INVALID_CONTEXT;
1035 va_start(ap, format);
1036 length = isds_vasprintf(&(context->long_message), format, ap);
1037 va_end(ap);
1039 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1043 /* Set logging up.
1044 * @facilities is bit mask of isds_log_facility values,
1045 * @level is verbosity level. */
1046 void isds_set_logging(const unsigned int facilities,
1047 const isds_log_level level) {
1048 log_facilities = facilities;
1049 log_level = level;
1053 /* Register callback function libisds calls when new global log message is
1054 * produced by library. Library logs to stderr by default.
1055 * @callback is function provided by application libisds will call. See type
1056 * definition for @callback argument explanation. Pass NULL to revert logging to
1057 * default behaviour.
1058 * @data is application specific data @callback gets as last argument */
1059 void isds_set_log_callback(isds_log_callback callback, void *data) {
1060 log_callback = callback;
1061 log_callback_data = data;
1065 /* Log @message in class @facility with log @level into global log. @message
1066 * is printf(3) formatting string, variadic arguments may be necessary.
1067 * For debugging purposes. */
1068 _hidden isds_error isds_log(const isds_log_facility facility,
1069 const isds_log_level level, const char *message, ...) {
1070 va_list ap;
1071 char *buffer = NULL;
1072 int length;
1074 if (level > log_level) return IE_SUCCESS;
1075 if (!(log_facilities & facility)) return IE_SUCCESS;
1076 if (!message) return IE_INVAL;
1078 if (log_callback) {
1079 /* Pass message to application supplied callback function */
1080 va_start(ap, message);
1081 length = isds_vasprintf(&buffer, message, ap);
1082 va_end(ap);
1084 if (length == -1) {
1085 return IE_ERROR;
1087 if (length > 0) {
1088 log_callback(facility, level, buffer, length, log_callback_data);
1090 free(buffer);
1091 } else {
1092 /* Default: Log it to stderr */
1093 va_start(ap, message);
1094 vfprintf(stderr, message, ap);
1095 va_end(ap);
1096 /* Line buffered printf is default.
1097 * fflush(stderr);*/
1100 return IE_SUCCESS;
1104 /* Set timeout in milliseconds for each network job like connecting to server
1105 * or sending message. Use 0 to disable timeout limits. */
1106 isds_error isds_set_timeout(struct isds_ctx *context,
1107 const unsigned int timeout) {
1108 if (!context) return IE_INVALID_CONTEXT;
1109 zfree(context->long_message);
1111 #if HAVE_LIBCURL
1112 context->timeout = timeout;
1114 if (context->curl) {
1115 CURLcode curl_err;
1117 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1118 if (!curl_err)
1119 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1120 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1121 context->timeout);
1122 #else
1123 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1124 context->timeout / 1000);
1125 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1126 if (curl_err) return IE_ERROR;
1129 return IE_SUCCESS;
1130 #else /* not HAVE_LIBCURL */
1131 return IE_NOTSUP;
1132 #endif
1136 /* Register callback function libisds calls periodically during HTTP data
1137 * transfer.
1138 * @context is session context
1139 * @callback is function provided by application libisds will call. See type
1140 * definition for @callback argument explanation.
1141 * @data is application specific data @callback gets as last argument */
1142 isds_error isds_set_progress_callback(struct isds_ctx *context,
1143 isds_progress_callback callback, void *data) {
1144 if (!context) return IE_INVALID_CONTEXT;
1145 zfree(context->long_message);
1147 #if HAVE_LIBCURL
1148 context->progress_callback = callback;
1149 context->progress_callback_data = data;
1151 return IE_SUCCESS;
1152 #else /* not HAVE_LIBCURL */
1153 return IE_NOTSUP;
1154 #endif
1158 /* Change context settings.
1159 * @context is context which setting will be applied to
1160 * @option is name of option. It determines the type of last argument. See
1161 * isds_option definition for more info.
1162 * @... is value of new setting. Type is determined by @option
1163 * */
1164 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1165 ...) {
1166 isds_error err = IE_SUCCESS;
1167 va_list ap;
1168 #if HAVE_LIBCURL
1169 char *pointer, *string;
1170 #endif
1172 if (!context) return IE_INVALID_CONTEXT;
1173 zfree(context->long_message);
1175 va_start(ap, option);
1177 #define REPLACE_VA_BOOLEAN(destination) { \
1178 if (!(destination)) { \
1179 (destination) = malloc(sizeof(*(destination))); \
1180 if (!(destination)) { \
1181 err = IE_NOMEM; goto leave; \
1184 *(destination) = (_Bool) !!va_arg(ap, int); \
1187 #define REPLACE_VA_STRING(destination) { \
1188 string = va_arg(ap, char *); \
1189 if (string) { \
1190 pointer = realloc((destination), 1 + strlen(string)); \
1191 if (!pointer) { err = IE_NOMEM; goto leave; } \
1192 strcpy(pointer, string); \
1193 (destination) = pointer; \
1194 } else { \
1195 free(destination); \
1196 (destination) = NULL; \
1200 switch (option) {
1201 case IOPT_TLS_VERIFY_SERVER:
1202 #if HAVE_LIBCURL
1203 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1204 #else
1205 err = IE_NOTSUP; goto leave;
1206 #endif
1207 break;
1208 case IOPT_TLS_CA_FILE:
1209 #if HAVE_LIBCURL
1210 REPLACE_VA_STRING(context->tls_ca_file);
1211 #else
1212 err = IE_NOTSUP; goto leave;
1213 #endif
1214 break;
1215 case IOPT_TLS_CA_DIRECTORY:
1216 #if HAVE_LIBCURL
1217 REPLACE_VA_STRING(context->tls_ca_dir);
1218 #else
1219 err = IE_NOTSUP; goto leave;
1220 #endif
1221 break;
1222 case IOPT_TLS_CRL_FILE:
1223 #if HAVE_LIBCURL
1224 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1225 REPLACE_VA_STRING(context->tls_crl_file);
1226 #else
1227 isds_log_message(context,
1228 _("Curl library does not support CRL definition"));
1229 err = IE_NOTSUP;
1230 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1231 #else
1232 err = IE_NOTSUP; goto leave;
1233 #endif /* not HAVE_LIBCURL */
1234 break;
1235 case IOPT_NORMALIZE_MIME_TYPE:
1236 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1237 break;
1239 default:
1240 err = IE_ENUM; goto leave;
1243 #undef REPLACE_VA_STRING
1244 #undef REPLACE_VA_BOOLEAN
1246 leave:
1247 va_end(ap);
1248 return err;
1252 #if HAVE_LIBCURL
1253 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1254 * Destination for NULL argument will not be touched.
1255 * Destination pointers must be freed before calling this function.
1256 * If @username is @context->saved_username, the saved_username will not be
1257 * replaced. The saved_username is clobbered only if context has set otp
1258 * member.
1259 * Return IE_SUCCESS on success. */
1260 static isds_error _isds_store_credentials(struct isds_ctx *context,
1261 const char *username, const char *password,
1262 const struct isds_pki_credentials *pki_credentials) {
1263 if (NULL == context) return IE_INVALID_CONTEXT;
1265 /* FIXME: mlock password
1266 * (I have a library) */
1268 if (username) {
1269 context->username = strdup(username);
1270 if (context->otp && context->saved_username != username)
1271 context->saved_username = strdup(username);
1273 if (password) {
1274 if (NULL == context->otp_credentials)
1275 context->password = strdup(password);
1276 else
1277 context->password = _isds_astrcat(password,
1278 context->otp_credentials->otp_code);
1280 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1282 if ((NULL != username && NULL == context->username) ||
1283 (NULL != password && NULL == context->password) ||
1284 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1285 (context->otp && NULL != context->username &&
1286 NULL == context->saved_username)) {
1287 return IE_NOMEM;
1290 return IE_SUCCESS;
1292 #endif
1295 /* Connect and log into ISDS server.
1296 * All required arguments will be copied, you do not have to keep them after
1297 * that.
1298 * ISDS supports six different authentication methods. Exact method is
1299 * selected on @username, @password, @pki_credentials, and @otp arguments:
1300 * - If @pki_credentials == NULL, @username and @password must be supplied
1301 * and then
1302 * - If @otp == NULL, simple authentication by username and password will
1303 * be proceeded.
1304 * - If @otp != NULL, authentication by username and password and OTP
1305 * will be used.
1306 * - If @pki_credentials != NULL, then
1307 * - If @username == NULL, only certificate will be used
1308 * - If @username != NULL, then
1309 * - If @password == NULL, then certificate will be used and
1310 * @username shifts meaning to box ID. This is used for hosted
1311 * services.
1312 * - Otherwise all three arguments will be used.
1313 * Please note, that different cases require different certificate type
1314 * (system qualified one or commercial non qualified one). This library
1315 * does not check such political issues. Please see ISDS Specification
1316 * for more details.
1317 * @url is base address of ISDS web service. Pass extern isds_locator
1318 * variable to use production ISDS instance without client certificate
1319 * authentication (or extern isds_cert_locator with client certificate
1320 * authentication or extern isds_otp_locators with OTP authentication).
1321 * Passing NULL has the same effect, autoselection between isds_locator,
1322 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1323 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1324 * isds_otp_testing_locator) variable to select testing instance.
1325 * @username is user name of ISDS user or box ID
1326 * @password is user's secret password
1327 * @pki_credentials defines public key cryptographic material to use in client
1328 * authentication.
1329 * @otp selects one-time password authentication method to use, defines OTP
1330 * code (if known) and returns fine grade resolution of OTP procedure.
1331 * @return:
1332 * IE_SUCCESS if authentication succeeds
1333 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1334 * requested, fine grade reason will be set into @otp->resolution. Error
1335 * message from server can be obtained by isds_long_message() call.
1336 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1337 * server has sent OTP code through side channel. Application is expected to
1338 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1339 * this call to complete second phase of TOTP authentication;
1340 * or other appropriate error. */
1341 isds_error isds_login(struct isds_ctx *context, const char *url,
1342 const char *username, const char *password,
1343 const struct isds_pki_credentials *pki_credentials,
1344 struct isds_otp *otp) {
1345 #if HAVE_LIBCURL
1346 isds_error err = IE_NOT_LOGGED_IN;
1347 isds_error soap_err;
1348 xmlNsPtr isds_ns = NULL;
1349 xmlNodePtr request = NULL;
1350 #endif /* HAVE_LIBCURL */
1352 if (!context) return IE_INVALID_CONTEXT;
1353 zfree(context->long_message);
1355 #if HAVE_LIBCURL
1356 /* Close connection if already logged in */
1357 if (context->curl) {
1358 _isds_close_connection(context);
1361 /* Store configuration */
1362 context->type = CTX_TYPE_ISDS;
1363 zfree(context->url);
1365 /* Mangle base URI according to requested authentication method */
1366 if (NULL == pki_credentials) {
1367 isds_log(ILF_SEC, ILL_INFO,
1368 _("Selected authentication method: no certificate, "
1369 "username and password\n"));
1370 if (!username || !password) {
1371 isds_log_message(context,
1372 _("Both username and password must be supplied"));
1373 return IE_INVAL;
1375 context->otp_credentials = otp;
1376 context->otp = (NULL != context->otp_credentials);
1378 if (!context->otp) {
1379 /* Default locator is official system (without certificate or
1380 * OTP) */
1381 context->url = strdup((NULL != url) ? url : isds_locator);
1382 } else {
1383 const char *authenticator_uri = NULL;
1384 if (!url) url = isds_otp_locator;
1385 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1386 switch (context->otp_credentials->method) {
1387 case OTP_HMAC:
1388 isds_log(ILF_SEC, ILL_INFO,
1389 _("Selected authentication method: "
1390 "HMAC-based one-time password\n"));
1391 authenticator_uri =
1392 "%sas/processLogin?type=hotp&uri=%sapps/";
1393 break;
1394 case OTP_TIME:
1395 isds_log(ILF_SEC, ILL_INFO,
1396 _("Selected authentication method: "
1397 "Time-based one-time password\n"));
1398 if (context->otp_credentials->otp_code == NULL) {
1399 isds_log(ILF_SEC, ILL_INFO,
1400 _("OTP code has not been provided by "
1401 "application, requesting server for "
1402 "new one.\n"));
1403 authenticator_uri =
1404 "%sas/processLogin?type=totp&sendSms=true&"
1405 "uri=%sapps/";
1406 } else {
1407 isds_log(ILF_SEC, ILL_INFO,
1408 _("OTP code has been provided by "
1409 "application, not requesting server "
1410 "for new one.\n"));
1411 authenticator_uri =
1412 "%sas/processLogin?type=totp&"
1413 "uri=%sapps/";
1415 break;
1416 default:
1417 isds_log_message(context,
1418 _("Unknown one-time password authentication "
1419 "method requested by application"));
1420 return IE_ENUM;
1422 if (-1 == isds_asprintf(&context->url, authenticator_uri, url, url))
1423 return IE_NOMEM;
1425 } else {
1426 /* Default locator is official system (with client certificate) */
1427 context->otp = 0;
1428 context->otp_credentials = NULL;
1429 if (!url) url = isds_cert_locator;
1431 if (!username) {
1432 isds_log(ILF_SEC, ILL_INFO,
1433 _("Selected authentication method: system certificate, "
1434 "no username and no password\n"));
1435 password = NULL;
1436 context->url = _isds_astrcat(url, "cert/");
1437 } else {
1438 if (!password) {
1439 isds_log(ILF_SEC, ILL_INFO,
1440 _("Selected authentication method: system certificate, "
1441 "box ID and no password\n"));
1442 context->url = _isds_astrcat(url, "hspis/");
1443 } else {
1444 isds_log(ILF_SEC, ILL_INFO,
1445 _("Selected authentication method: commercial "
1446 "certificate, username and password\n"));
1447 context->url = _isds_astrcat(url, "certds/");
1451 if (!(context->url))
1452 return IE_NOMEM;
1454 /* Prepare CURL handle */
1455 context->curl = curl_easy_init();
1456 if (!(context->curl))
1457 return IE_ERROR;
1459 /* Build log-in request */
1460 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1461 if (!request) {
1462 isds_log_message(context, _("Could not build ISDS log-in request"));
1463 return IE_ERROR;
1465 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1466 if(!isds_ns) {
1467 isds_log_message(context, _("Could not create ISDS name space"));
1468 xmlFreeNode(request);
1469 return IE_ERROR;
1471 xmlSetNs(request, isds_ns);
1473 /* Store credentials */
1474 _isds_discard_credentials(context, 1);
1475 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1476 _isds_discard_credentials(context, 1);
1477 xmlFreeNode(request);
1478 return IE_NOMEM;
1481 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1482 username, url);
1484 /* XXX: ISDS documentation does not specify response body for
1485 * DummyOperation request. However real server sends back
1486 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1487 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1488 * SOAP body content, e.g. the dmStatus element. */
1490 /* Send log-in request */
1491 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1493 if (context->otp) {
1494 /* Revert context URL from OTP authentication service URL to OTP web
1495 * service base URL for subsequent calls. Potenial isds_login() retry
1496 * will re-set context URL again. */
1497 zfree(context->url);
1498 context->url = _isds_astrcat(url, "apps/");
1499 if (context->url == NULL) {
1500 soap_err = IE_NOMEM;
1502 /* Detach pointer to OTP credentials from context */
1503 context->otp_credentials = NULL;
1506 /* Remove credentials */
1507 _isds_discard_credentials(context, 0);
1509 /* Destroy log-in request */
1510 xmlFreeNode(request);
1512 if (soap_err) {
1513 _isds_close_connection(context);
1514 return soap_err;
1517 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1518 * authentication succeeded if soap_err == IE_SUCCESS */
1519 err = IE_SUCCESS;
1521 if (!err)
1522 isds_log(ILF_ISDS, ILL_DEBUG,
1523 _("User %s has been logged into server %s successfully\n"),
1524 username, url);
1525 return err;
1526 #else /* not HAVE_LIBCURL */
1527 return IE_NOTSUP;
1528 #endif
1532 /* Log out from ISDS server discards credentials and connection configuration. */
1533 isds_error isds_logout(struct isds_ctx *context) {
1534 if (!context) return IE_INVALID_CONTEXT;
1535 zfree(context->long_message);
1537 #if HAVE_LIBCURL
1538 if (context->curl) {
1539 if (context->otp) {
1540 isds_error err = _isds_invalidate_otp_cookie(context);
1541 if (err) return err;
1544 /* Close connection */
1545 _isds_close_connection(context);
1547 /* Discard credentials for sure. They should not survive isds_login(),
1548 * even successful .*/
1549 _isds_discard_credentials(context, 1);
1551 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1552 } else {
1553 _isds_discard_credentials(context, 1);
1555 zfree(context->url);
1556 return IE_SUCCESS;
1557 #else /* not HAVE_LIBCURL */
1558 return IE_NOTSUP;
1559 #endif
1563 /* Verify connection to ISDS is alive and server is responding.
1564 * Send dummy request to ISDS and expect dummy response. */
1565 isds_error isds_ping(struct isds_ctx *context) {
1566 #if HAVE_LIBCURL
1567 isds_error soap_err;
1568 xmlNsPtr isds_ns = NULL;
1569 xmlNodePtr request = NULL;
1570 #endif /* HAVE_LIBCURL */
1572 if (!context) return IE_INVALID_CONTEXT;
1573 zfree(context->long_message);
1575 #if HAVE_LIBCURL
1576 /* Check if connection is established */
1577 if (!context->curl) return IE_CONNECTION_CLOSED;
1580 /* Build dummy request */
1581 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1582 if (!request) {
1583 isds_log_message(context, _("Could build ISDS dummy request"));
1584 return IE_ERROR;
1586 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1587 if(!isds_ns) {
1588 isds_log_message(context, _("Could not create ISDS name space"));
1589 xmlFreeNode(request);
1590 return IE_ERROR;
1592 xmlSetNs(request, isds_ns);
1594 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1596 /* XXX: ISDS documentation does not specify response body for
1597 * DummyOperation request. However real server sends back
1598 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1599 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1600 * SOAP body content, e.g. the dmStatus element. */
1602 /* Send dummy request */
1603 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1605 /* Destroy log-in request */
1606 xmlFreeNode(request);
1608 if (soap_err) {
1609 isds_log(ILF_ISDS, ILL_DEBUG,
1610 _("ISDS server could not be contacted\n"));
1611 return soap_err;
1614 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1615 * authentication succeeded if soap_err == IE_SUCCESS */
1618 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1620 return IE_SUCCESS;
1621 #else /* not HAVE_LIBCURL */
1622 return IE_NOTSUP;
1623 #endif
1627 /* Send bogus request to ISDS.
1628 * Just for test purposes */
1629 isds_error isds_bogus_request(struct isds_ctx *context) {
1630 #if HAVE_LIBCURL
1631 isds_error err;
1632 xmlNsPtr isds_ns = NULL;
1633 xmlNodePtr request = NULL;
1634 xmlDocPtr response = NULL;
1635 xmlChar *code = NULL, *message = NULL;
1636 #endif
1638 if (!context) return IE_INVALID_CONTEXT;
1639 zfree(context->long_message);
1641 #if HAVE_LIBCURL
1642 /* Check if connection is established */
1643 if (!context->curl) {
1644 /* Testing printf message */
1645 isds_printf_message(context, "%s", _("I said connection closed"));
1646 return IE_CONNECTION_CLOSED;
1650 /* Build dummy request */
1651 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1652 if (!request) {
1653 isds_log_message(context, _("Could build ISDS bogus request"));
1654 return IE_ERROR;
1656 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1657 if(!isds_ns) {
1658 isds_log_message(context, _("Could not create ISDS name space"));
1659 xmlFreeNode(request);
1660 return IE_ERROR;
1662 xmlSetNs(request, isds_ns);
1664 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1666 /* Sent bogus request */
1667 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1669 /* Destroy request */
1670 xmlFreeNode(request);
1672 if (err) {
1673 isds_log(ILF_ISDS, ILL_DEBUG,
1674 _("Processing ISDS response on bogus request failed\n"));
1675 xmlFreeDoc(response);
1676 return err;
1679 /* Check for response status */
1680 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1681 &code, &message, NULL);
1682 if (err) {
1683 isds_log(ILF_ISDS, ILL_DEBUG,
1684 _("ISDS response on bogus request is missing status\n"));
1685 free(code);
1686 free(message);
1687 xmlFreeDoc(response);
1688 return err;
1690 if (xmlStrcmp(code, BAD_CAST "0000")) {
1691 char *code_locale = _isds_utf82locale((char*)code);
1692 char *message_locale = _isds_utf82locale((char*)message);
1693 isds_log(ILF_ISDS, ILL_DEBUG,
1694 _("Server refused bogus request (code=%s, message=%s)\n"),
1695 code_locale, message_locale);
1696 /* XXX: Literal error messages from ISDS are Czech messages
1697 * (English sometimes) in UTF-8. It's hard to catch them for
1698 * translation. Successfully gettextized would return in locale
1699 * encoding, unsuccessfully translated would pass in UTF-8. */
1700 isds_log_message(context, message_locale);
1701 free(code_locale);
1702 free(message_locale);
1703 free(code);
1704 free(message);
1705 xmlFreeDoc(response);
1706 return IE_ISDS;
1710 free(code);
1711 free(message);
1712 xmlFreeDoc(response);
1714 isds_log(ILF_ISDS, ILL_DEBUG,
1715 _("Bogus message accepted by server. This should not happen.\n"));
1717 return IE_SUCCESS;
1718 #else /* not HAVE_LIBCURL */
1719 return IE_NOTSUP;
1720 #endif
1724 #if HAVE_LIBCURL
1725 /* Serialize XML subtree to buffer preserving XML indentation.
1726 * @context is session context
1727 * @subtree is XML element to be serialized (with children)
1728 * @buffer is automatically reallocated buffer where serialize to
1729 * @length is size of serialized stream in bytes
1730 * @return standard error code, free @buffer in case of error */
1731 static isds_error serialize_subtree(struct isds_ctx *context,
1732 xmlNodePtr subtree, void **buffer, size_t *length) {
1733 isds_error err = IE_SUCCESS;
1734 xmlBufferPtr xml_buffer = NULL;
1735 xmlSaveCtxtPtr save_ctx = NULL;
1736 xmlDocPtr subtree_doc = NULL;
1737 xmlNodePtr subtree_copy;
1738 xmlNsPtr isds_ns;
1739 void *new_buffer;
1741 if (!context) return IE_INVALID_CONTEXT;
1742 if (!buffer) return IE_INVAL;
1743 zfree(*buffer);
1744 if (!subtree || !length) return IE_INVAL;
1746 /* Make temporary XML document with @subtree root element */
1747 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1748 * It can result in not well-formed on invalid XML tree (e.g. name space
1749 * prefix definition can miss. */
1750 /*FIXME */
1752 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1753 if (!subtree_doc) {
1754 isds_log_message(context, _("Could not build temporary document"));
1755 err = IE_ERROR;
1756 goto leave;
1759 /* XXX: Copy subtree and attach the copy to document.
1760 * One node can not bee attached into more document at the same time.
1761 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1762 * automatically.
1763 * XXX: Check xmlSaveTree() too. */
1764 subtree_copy = xmlCopyNodeList(subtree);
1765 if (!subtree_copy) {
1766 isds_log_message(context, _("Could not copy subtree"));
1767 err = IE_ERROR;
1768 goto leave;
1770 xmlDocSetRootElement(subtree_doc, subtree_copy);
1772 /* Only this way we get namespace definition as @xmlns:isds,
1773 * otherwise we get namespace prefix without definition */
1774 /* FIXME: Don't overwrite original default namespace */
1775 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1776 if(!isds_ns) {
1777 isds_log_message(context, _("Could not create ISDS name space"));
1778 err = IE_ERROR;
1779 goto leave;
1781 xmlSetNs(subtree_copy, isds_ns);
1784 /* Serialize the document into buffer */
1785 xml_buffer = xmlBufferCreate();
1786 if (!xml_buffer) {
1787 isds_log_message(context, _("Could not create xmlBuffer"));
1788 err = IE_ERROR;
1789 goto leave;
1791 /* Last argument 0 means to not format the XML tree */
1792 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1793 if (!save_ctx) {
1794 isds_log_message(context, _("Could not create XML serializer"));
1795 err = IE_ERROR;
1796 goto leave;
1798 /* XXX: According LibXML documentation, this function does not return
1799 * meaningful value yet */
1800 xmlSaveDoc(save_ctx, subtree_doc);
1801 if (-1 == xmlSaveFlush(save_ctx)) {
1802 isds_log_message(context,
1803 _("Could not serialize XML subtree"));
1804 err = IE_ERROR;
1805 goto leave;
1807 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1808 * even after xmlSaveFlush(). Thus close it here */
1809 xmlSaveClose(save_ctx); save_ctx = NULL;
1812 /* Store and detach buffer from xml_buffer */
1813 *buffer = xml_buffer->content;
1814 *length = xml_buffer->use;
1815 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1817 /* Shrink buffer */
1818 new_buffer = realloc(*buffer, *length);
1819 if (new_buffer) *buffer = new_buffer;
1821 leave:
1822 if (err) {
1823 zfree(*buffer);
1824 *length = 0;
1827 xmlSaveClose(save_ctx);
1828 xmlBufferFree(xml_buffer);
1829 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1830 return err;
1832 #endif /* HAVE_LIBCURL */
1835 #if 0
1836 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1837 * @context is session context
1838 * @document is original document where @nodeset points to
1839 * @nodeset is XPath node set to dump (recursively)
1840 * @buffer is automatically reallocated buffer where serialize to
1841 * @length is size of serialized stream in bytes
1842 * @return standard error code, free @buffer in case of error */
1843 static isds_error dump_nodeset(struct isds_ctx *context,
1844 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1845 void **buffer, size_t *length) {
1846 isds_error err = IE_SUCCESS;
1847 xmlBufferPtr xml_buffer = NULL;
1848 void *new_buffer;
1850 if (!context) return IE_INVALID_CONTEXT;
1851 if (!buffer) return IE_INVAL;
1852 zfree(*buffer);
1853 if (!document || !nodeset || !length) return IE_INVAL;
1854 *length = 0;
1856 /* Empty node set results into NULL buffer */
1857 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1858 goto leave;
1861 /* Resulting the document into buffer */
1862 xml_buffer = xmlBufferCreate();
1863 if (!xml_buffer) {
1864 isds_log_message(context, _("Could not create xmlBuffer"));
1865 err = IE_ERROR;
1866 goto leave;
1869 /* Iterate over all nodes */
1870 for (int i = 0; i < nodeset->nodeNr; i++) {
1871 /* Serialize node.
1872 * XXX: xmlNodeDump() appends to xml_buffer. */
1873 if (-1 ==
1874 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1875 isds_log_message(context, _("Could not dump XML node"));
1876 err = IE_ERROR;
1877 goto leave;
1881 /* Store and detach buffer from xml_buffer */
1882 *buffer = xml_buffer->content;
1883 *length = xml_buffer->use;
1884 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1886 /* Shrink buffer */
1887 new_buffer = realloc(*buffer, *length);
1888 if (new_buffer) *buffer = new_buffer;
1891 leave:
1892 if (err) {
1893 zfree(*buffer);
1894 *length = 0;
1897 xmlBufferFree(xml_buffer);
1898 return err;
1900 #endif
1902 #if 0
1903 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1904 * @context is session context
1905 * @document is original document where @nodeset points to
1906 * @nodeset is XPath node set to dump (recursively)
1907 * @buffer is automatically reallocated buffer where serialize to
1908 * @length is size of serialized stream in bytes
1909 * @return standard error code, free @buffer in case of error */
1910 static isds_error dump_nodeset(struct isds_ctx *context,
1911 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1912 void **buffer, size_t *length) {
1913 isds_error err = IE_SUCCESS;
1914 xmlBufferPtr xml_buffer = NULL;
1915 xmlSaveCtxtPtr save_ctx = NULL;
1916 void *new_buffer;
1918 if (!context) return IE_INVALID_CONTEXT;
1919 if (!buffer) return IE_INVAL;
1920 zfree(*buffer);
1921 if (!document || !nodeset || !length) return IE_INVAL;
1922 *length = 0;
1924 /* Empty node set results into NULL buffer */
1925 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1926 goto leave;
1929 /* Resulting the document into buffer */
1930 xml_buffer = xmlBufferCreate();
1931 if (!xml_buffer) {
1932 isds_log_message(context, _("Could not create xmlBuffer"));
1933 err = IE_ERROR;
1934 goto leave;
1936 if (xmlSubstituteEntitiesDefault(1)) {
1937 isds_log_message(context, _("Could not disable attribute escaping"));
1938 err = IE_ERROR;
1939 goto leave;
1941 /* Last argument means:
1942 * 0 to not format the XML tree
1943 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1944 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1945 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1946 if (!save_ctx) {
1947 isds_log_message(context, _("Could not create XML serializer"));
1948 err = IE_ERROR;
1949 goto leave;
1951 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1952 isds_log_message(context, _("Could not disable attribute escaping"));
1953 err = IE_ERROR;
1954 goto leave;
1958 /* Iterate over all nodes */
1959 for (int i = 0; i < nodeset->nodeNr; i++) {
1960 /* Serialize node.
1961 * XXX: xmlNodeDump() appends to xml_buffer. */
1962 /*if (-1 ==
1963 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1965 /* XXX: According LibXML documentation, this function does not return
1966 * meaningful value yet */
1967 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1968 if (-1 == xmlSaveFlush(save_ctx)) {
1969 isds_log_message(context,
1970 _("Could not serialize XML subtree"));
1971 err = IE_ERROR;
1972 goto leave;
1976 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1977 * even after xmlSaveFlush(). Thus close it here */
1978 xmlSaveClose(save_ctx); save_ctx = NULL;
1980 /* Store and detach buffer from xml_buffer */
1981 *buffer = xml_buffer->content;
1982 *length = xml_buffer->use;
1983 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1985 /* Shrink buffer */
1986 new_buffer = realloc(*buffer, *length);
1987 if (new_buffer) *buffer = new_buffer;
1989 leave:
1990 if (err) {
1991 zfree(*buffer);
1992 *length = 0;
1995 xmlSaveClose(save_ctx);
1996 xmlBufferFree(xml_buffer);
1997 return err;
1999 #endif
2002 #if HAVE_LIBCURL
2003 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
2004 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
2005 if (!string || !type) return IE_INVAL;
2007 if (!xmlStrcmp(string, BAD_CAST "FO"))
2008 *type = DBTYPE_FO;
2009 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
2010 *type = DBTYPE_PFO;
2011 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
2012 *type = DBTYPE_PFO_ADVOK;
2013 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
2014 *type = DBTYPE_PFO_DANPOR;
2015 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
2016 *type = DBTYPE_PFO_INSSPR;
2017 else if (!xmlStrcmp(string, BAD_CAST "PO"))
2018 *type = DBTYPE_PO;
2019 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
2020 *type = DBTYPE_PO_ZAK;
2021 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
2022 *type = DBTYPE_PO_REQ;
2023 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
2024 *type = DBTYPE_OVM;
2025 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
2026 *type = DBTYPE_OVM_NOTAR;
2027 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
2028 *type = DBTYPE_OVM_EXEKUT;
2029 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2030 *type = DBTYPE_OVM_REQ;
2031 else
2032 return IE_ENUM;
2033 return IE_SUCCESS;
2037 /* Convert ISDS dbType enum @type to UTF-8 string.
2038 * @Return pointer to static string, or NULL if unknown enum value */
2039 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2040 switch(type) {
2041 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2042 * of view of generic public SOAP interface. */
2043 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2044 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2045 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2046 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2047 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2048 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2049 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2050 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2051 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2052 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2053 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2054 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2055 default: return NULL; break;
2060 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2061 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2062 if (!string || !type) return IE_INVAL;
2064 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2065 *type = USERTYPE_PRIMARY;
2066 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2067 *type = USERTYPE_ENTRUSTED;
2068 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2069 *type = USERTYPE_ADMINISTRATOR;
2070 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2071 *type = USERTYPE_OFFICIAL;
2072 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2073 *type = USERTYPE_OFFICIAL_CERT;
2074 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2075 *type = USERTYPE_LIQUIDATOR;
2076 else if (!xmlStrcmp(string, BAD_CAST "RECEIVER"))
2077 *type = USERTYPE_RECEIVER;
2078 else if (!xmlStrcmp(string, BAD_CAST "GUARDIAN"))
2079 *type = USERTYPE_GUARDIAN;
2080 else
2081 return IE_ENUM;
2082 return IE_SUCCESS;
2086 /* Convert ISDS userType enum @type to UTF-8 string.
2087 * @Return pointer to static string, or NULL if unknown enum value */
2088 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2089 switch(type) {
2090 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2091 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2092 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2093 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2094 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2095 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2096 case USERTYPE_RECEIVER: return(BAD_CAST "RECEIVER"); break;
2097 case USERTYPE_GUARDIAN: return(BAD_CAST "GUARDIAN"); break;
2098 default: return NULL; break;
2103 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2104 static isds_error string2isds_sender_type(const xmlChar *string,
2105 isds_sender_type *type) {
2106 if (!string || !type) return IE_INVAL;
2108 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2109 *type = SENDERTYPE_PRIMARY;
2110 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2111 *type = SENDERTYPE_ENTRUSTED;
2112 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2113 *type = SENDERTYPE_ADMINISTRATOR;
2114 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2115 *type = SENDERTYPE_OFFICIAL;
2116 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2117 *type = SENDERTYPE_VIRTUAL;
2118 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2119 *type = SENDERTYPE_OFFICIAL_CERT;
2120 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2121 *type = SENDERTYPE_LIQUIDATOR;
2122 else if (!xmlStrcmp(string, BAD_CAST "RECEIVER"))
2123 *type = SENDERTYPE_RECEIVER;
2124 else if (!xmlStrcmp(string, BAD_CAST "GUARDIAN"))
2125 *type = SENDERTYPE_GUARDIAN;
2126 else
2127 return IE_ENUM;
2128 return IE_SUCCESS;
2132 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2133 static isds_error string2isds_payment_type(const xmlChar *string,
2134 isds_payment_type *type) {
2135 if (!string || !type) return IE_INVAL;
2137 if (!xmlStrcmp(string, BAD_CAST "K"))
2138 *type = PAYMENT_SENDER;
2139 else if (!xmlStrcmp(string, BAD_CAST "O"))
2140 *type = PAYMENT_RESPONSE;
2141 else if (!xmlStrcmp(string, BAD_CAST "G"))
2142 *type = PAYMENT_SPONSOR;
2143 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2144 *type = PAYMENT_SPONSOR_LIMITED;
2145 else if (!xmlStrcmp(string, BAD_CAST "D"))
2146 *type = PAYMENT_SPONSOR_EXTERNAL;
2147 else if (!xmlStrcmp(string, BAD_CAST "E"))
2148 *type = PAYMENT_STAMP;
2149 else
2150 return IE_ENUM;
2151 return IE_SUCCESS;
2155 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2156 * ciEventType is integer but we convert it from string representation
2157 * directly. */
2158 static isds_error string2isds_credit_event_type(const xmlChar *string,
2159 isds_credit_event_type *type) {
2160 if (!string || !type) return IE_INVAL;
2162 if (!xmlStrcmp(string, BAD_CAST "1"))
2163 *type = ISDS_CREDIT_CHARGED;
2164 else if (!xmlStrcmp(string, BAD_CAST "2"))
2165 *type = ISDS_CREDIT_DISCHARGED;
2166 else if (!xmlStrcmp(string, BAD_CAST "3"))
2167 *type = ISDS_CREDIT_MESSAGE_SENT;
2168 else if (!xmlStrcmp(string, BAD_CAST "4"))
2169 *type = ISDS_CREDIT_STORAGE_SET;
2170 else if (!xmlStrcmp(string, BAD_CAST "5"))
2171 *type = ISDS_CREDIT_EXPIRED;
2172 else
2173 return IE_ENUM;
2174 return IE_SUCCESS;
2178 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2179 * @Return pointer to static string, or NULL if unknown enum value */
2180 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2181 switch(type) {
2182 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2183 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2184 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2185 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2186 default: return NULL; break;
2191 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2192 * ISDSSearch2/searchType value.
2193 * @Return pointer to static string, or NULL if unknown enum value */
2194 static const xmlChar *isds_fulltext_target2string(
2195 const isds_fulltext_target type) {
2196 switch(type) {
2197 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2198 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2199 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2200 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2201 default: return NULL; break;
2204 #endif /* HAVE_LIBCURL */
2207 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2208 * @Return IE_ENUM if @string is not valid enum member */
2209 static isds_error string2isds_FileMetaType(const xmlChar *string,
2210 isds_FileMetaType *type) {
2211 if (!string || !type) return IE_INVAL;
2213 if (!xmlStrcmp(string, BAD_CAST "main"))
2214 *type = FILEMETATYPE_MAIN;
2215 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2216 *type = FILEMETATYPE_ENCLOSURE;
2217 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2218 *type = FILEMETATYPE_SIGNATURE;
2219 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2220 *type = FILEMETATYPE_META;
2221 else
2222 return IE_ENUM;
2223 return IE_SUCCESS;
2227 /* Convert UTF-8 @string to ISDS hash @algorithm.
2228 * @Return IE_ENUM if @string is not valid enum member */
2229 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2230 isds_hash_algorithm *algorithm) {
2231 if (!string || !algorithm) return IE_INVAL;
2233 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2234 *algorithm = HASH_ALGORITHM_MD5;
2235 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2236 *algorithm = HASH_ALGORITHM_SHA_1;
2237 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2238 *algorithm = HASH_ALGORITHM_SHA_224;
2239 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2240 *algorithm = HASH_ALGORITHM_SHA_256;
2241 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2242 *algorithm = HASH_ALGORITHM_SHA_384;
2243 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2244 *algorithm = HASH_ALGORITHM_SHA_512;
2245 else
2246 return IE_ENUM;
2247 return IE_SUCCESS;
2251 #if HAVE_LIBCURL
2252 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2253 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2254 if (!time || !string) return IE_INVAL;
2256 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2257 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2258 return IE_ERROR;
2260 return IE_SUCCESS;
2264 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2265 * respects the @time microseconds too. */
2266 static isds_error timeval2timestring(const struct timeval *time,
2267 xmlChar **string) {
2268 struct tm broken;
2269 time_t seconds_as_time_t;
2271 if (!time || !string) return IE_INVAL;
2273 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2274 * 32-bit long in Microsoft API. Convert value to the type expected by
2275 * gmtime_r(). */
2276 seconds_as_time_t = time->tv_sec;
2277 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2278 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2280 /* TODO: small negative year should be formatted as "-0012". This is not
2281 * true for glibc "%04d". We should implement it.
2282 * time->tv_usec type is su_seconds_t which is required to be signed
2283 * integer to accomodate values from range [-1, 1000000].
2284 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2285 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2286 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2287 * of the range check above. */
2288 if (-1 == isds_asprintf((char **) string,
2289 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2290 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2291 broken.tm_hour, broken.tm_min, broken.tm_sec,
2292 (int32_t)time->tv_usec))
2293 return IE_ERROR;
2295 return IE_SUCCESS;
2297 #endif /* HAVE_LIBCURL */
2300 /* Convert UTF-8 ISO 8601 date-time @string to static struct timeval.
2301 * It respects microseconds too. Microseconds are rounded half up.
2302 * In case of error, @time will be undefined. */
2303 static isds_error timestring2static_timeval(const xmlChar *string,
2304 struct timeval *time) {
2305 struct tm broken;
2306 char *offset, *delim, *endptr;
2307 const int subsecond_resolution = 6;
2308 char subseconds[subsecond_resolution + 1];
2309 _Bool round_up = 0;
2310 int offset_hours, offset_minutes;
2311 int i;
2312 long int long_number;
2313 #ifdef _WIN32
2314 int tmp;
2315 #endif
2317 if (!time) return IE_INVAL;
2318 if (!string) {
2319 return IE_INVAL;
2322 memset(&broken, 0, sizeof(broken));
2323 memset(time, 0, sizeof(*time));
2326 /* xsd:date is ISO 8601 string, thus ASCII */
2327 /*TODO: negative year */
2329 #ifdef _WIN32
2330 i = 0;
2331 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2332 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2333 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2334 &i)) < 6) {
2335 return IE_DATE;
2338 broken.tm_year -= 1900;
2339 broken.tm_mon--;
2340 broken.tm_isdst = -1;
2341 offset = (char*)string + i;
2342 #else
2343 /* Parse date and time without subseconds and offset */
2344 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2345 if (!offset) {
2346 return IE_DATE;
2348 #endif
2350 /* Get subseconds */
2351 if (*offset == '.' ) {
2352 offset++;
2354 /* Copy first 6 digits, pad it with zeros.
2355 * Current server implementation uses only millisecond resolution. */
2356 /* TODO: isdigit() is locale sensitive */
2357 for (i = 0;
2358 i < subsecond_resolution && isdigit(*offset);
2359 i++, offset++) {
2360 subseconds[i] = *offset;
2362 if (subsecond_resolution == i && isdigit(*offset)) {
2363 /* Check 7th digit for rounding */
2364 if (*offset >= '5') round_up = 1;
2365 offset++;
2367 for (; i < subsecond_resolution; i++) {
2368 subseconds[i] = '0';
2370 subseconds[subsecond_resolution] = '\0';
2372 /* Convert it into integer */
2373 long_number = strtol(subseconds, &endptr, 10);
2374 if (*endptr != '\0' || long_number == LONG_MIN ||
2375 long_number == LONG_MAX) {
2376 return IE_DATE;
2378 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2379 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2380 * microseconds" and "the type shall be a signed integer capable of
2381 * storing values at least in the range [-1, 1000000]. */
2382 if (long_number < -1 || long_number >= 1000000) {
2383 return IE_DATE;
2385 time->tv_usec = long_number;
2387 /* Round the subseconds */
2388 if (round_up) {
2389 if (999999 == time->tv_usec) {
2390 time->tv_usec = 0;
2391 broken.tm_sec++;
2392 } else {
2393 time->tv_usec++;
2397 /* move to the zone offset delimiter or signal NULL*/
2398 delim = strchr(offset, '-');
2399 if (!delim)
2400 delim = strchr(offset, '+');
2401 if (!delim)
2402 delim = strchr(offset, 'Z');
2403 offset = delim;
2406 /* Get zone offset */
2407 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2408 * "" equals to "Z" and it means UTC zone. */
2409 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2410 * colon separator */
2411 if (offset && (*offset == '-' || *offset == '+')) {
2412 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2413 return IE_DATE;
2415 if (*offset == '+') {
2416 broken.tm_hour -= offset_hours;
2417 broken.tm_min -= offset_minutes;
2418 } else {
2419 broken.tm_hour += offset_hours;
2420 broken.tm_min += offset_minutes;
2424 /* Convert to time_t */
2425 time->tv_sec = _isds_timegm(&broken);
2426 if (time->tv_sec == (time_t) -1) {
2427 return IE_DATE;
2430 return IE_SUCCESS;
2434 /* Convert UTF-8 ISO 8601 date-time @string to reallocated struct timeval.
2435 * It respects microseconds too. Microseconds are rounded half up.
2436 * In case of error, @time will be freed. */
2437 static isds_error timestring2timeval(const xmlChar *string,
2438 struct timeval **time) {
2439 isds_error error;
2441 if (!time) return IE_INVAL;
2442 if (!string) {
2443 zfree(*time);
2444 return IE_INVAL;
2447 if (!*time) {
2448 *time = calloc(1, sizeof(**time));
2449 if (!*time) return IE_NOMEM;
2450 } else {
2451 memset(*time, 0, sizeof(**time));
2454 error = timestring2static_timeval(string, *time);
2455 if (error) {
2456 zfree(*time);
2459 return error;
2463 /* Convert unsigned int into isds_message_status.
2464 * @context is session context
2465 * @number is pointer to number value. NULL will be treated as invalid value.
2466 * @status is automatically reallocated status
2467 * @return IE_SUCCESS, or error code and free status */
2468 static isds_error uint2isds_message_status(struct isds_ctx *context,
2469 const unsigned long int *number, isds_message_status **status) {
2470 if (!context) return IE_INVALID_CONTEXT;
2471 if (!status) return IE_INVAL;
2473 free(*status); *status = NULL;
2474 if (!number) return IE_INVAL;
2476 if (*number < 1 || *number > 10) {
2477 isds_printf_message(context, _("Invalid message status value: %lu"),
2478 *number);
2479 return IE_ENUM;
2482 *status = malloc(sizeof(**status));
2483 if (!*status) return IE_NOMEM;
2485 **status = 1 << *number;
2486 return IE_SUCCESS;
2490 /* Convert event description string into isds_event members type and
2491 * description
2492 * @string is raw event description starting with event prefix
2493 * @event is structure where to store type and stripped description to
2494 * @return standard error code, unknown prefix is not classified as an error.
2495 * */
2496 static isds_error eventstring2event(const xmlChar *string,
2497 struct isds_event* event) {
2498 const xmlChar *known_prefixes[] = {
2499 BAD_CAST "EV0:",
2500 BAD_CAST "EV1:",
2501 BAD_CAST "EV2:",
2502 BAD_CAST "EV3:",
2503 BAD_CAST "EV4:",
2504 BAD_CAST "EV5:",
2505 BAD_CAST "EV11:",
2506 BAD_CAST "EV12:",
2507 BAD_CAST "EV13:"
2509 const isds_event_type types[] = {
2510 EVENT_ENTERED_SYSTEM,
2511 EVENT_ACCEPTED_BY_RECIPIENT,
2512 EVENT_ACCEPTED_BY_FICTION,
2513 EVENT_UNDELIVERABLE,
2514 EVENT_COMMERCIAL_ACCEPTED,
2515 EVENT_DELIVERED,
2516 EVENT_PRIMARY_LOGIN,
2517 EVENT_ENTRUSTED_LOGIN,
2518 EVENT_SYSCERT_LOGIN
2520 unsigned int index;
2521 size_t length;
2523 if (!string || !event) return IE_INVAL;
2525 if (!event->type) {
2526 event->type = malloc(sizeof(*event->type));
2527 if (!(event->type)) return IE_NOMEM;
2529 zfree(event->description);
2531 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2532 index++) {
2533 length = xmlUTF8Strlen(known_prefixes[index]);
2535 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2536 /* Prefix is known */
2537 *event->type = types[index];
2539 /* Strip prefix from description and spaces */
2540 /* TODO: Recognize all white spaces from UCS blank class and
2541 * operate on UTF-8 chars. */
2542 for (; string[length] != '\0' && string[length] == ' '; length++);
2543 event->description = strdup((char *) (string + length));
2544 if (!(event->description)) return IE_NOMEM;
2546 return IE_SUCCESS;
2550 /* Unknown event prefix.
2551 * XSD allows any string */
2552 char *string_locale = _isds_utf82locale((char *) string);
2553 isds_log(ILF_ISDS, ILL_WARNING,
2554 _("Unknown delivery info event prefix: %s\n"), string_locale);
2555 free(string_locale);
2557 *event->type = EVENT_UKNOWN;
2558 event->description = strdup((char *) string);
2559 if (!(event->description)) return IE_NOMEM;
2561 return IE_SUCCESS;
2565 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2566 * and leave label */
2567 #define EXTRACT_STRING(element, string) { \
2568 xmlXPathFreeObject(result); \
2569 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2570 if (NULL == (result)) { \
2571 err = IE_ERROR; \
2572 goto leave; \
2574 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2575 if (result->nodesetval->nodeNr > 1) { \
2576 isds_printf_message(context, _("Multiple %s element"), element); \
2577 err = IE_ERROR; \
2578 goto leave; \
2580 (string) = (char *) \
2581 xmlXPathCastNodeSetToString(result->nodesetval); \
2582 if (NULL == (string)) { \
2583 err = IE_ERROR; \
2584 goto leave; \
2589 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2591 char *string = NULL; \
2592 EXTRACT_STRING(element, string); \
2594 if (string) { \
2595 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2596 if (!(booleanPtr)) { \
2597 free(string); \
2598 err = IE_NOMEM; \
2599 goto leave; \
2602 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2603 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2604 *(booleanPtr) = 1; \
2605 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2606 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2607 *(booleanPtr) = 0; \
2608 else { \
2609 char *string_locale = _isds_utf82locale((char*)string); \
2610 isds_printf_message(context, \
2611 _("%s value is not valid boolean: %s"), \
2612 element, string_locale); \
2613 free(string_locale); \
2614 free(string); \
2615 err = IE_ERROR; \
2616 goto leave; \
2619 free(string); \
2623 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2625 char *string = NULL; \
2626 EXTRACT_STRING(element, string); \
2628 if (NULL == string) { \
2629 isds_printf_message(context, _("%s element is empty"), element); \
2630 err = IE_ERROR; \
2631 goto leave; \
2633 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2634 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2635 (boolean) = 1; \
2636 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2637 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2638 (boolean) = 0; \
2639 else { \
2640 char *string_locale = _isds_utf82locale((char*)string); \
2641 isds_printf_message(context, \
2642 _("%s value is not valid boolean: %s"), \
2643 element, string_locale); \
2644 free(string_locale); \
2645 free(string); \
2646 err = IE_ERROR; \
2647 goto leave; \
2650 free(string); \
2653 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2655 char *string = NULL; \
2656 EXTRACT_STRING(element, string); \
2657 if (string) { \
2658 long int number; \
2659 char *endptr; \
2661 number = strtol((char*)string, &endptr, 10); \
2663 if (*endptr != '\0') { \
2664 char *string_locale = _isds_utf82locale((char *)string); \
2665 isds_printf_message(context, \
2666 _("%s is not valid integer: %s"), \
2667 element, string_locale); \
2668 free(string_locale); \
2669 free(string); \
2670 err = IE_ISDS; \
2671 goto leave; \
2674 if (number == LONG_MIN || number == LONG_MAX) { \
2675 char *string_locale = _isds_utf82locale((char *)string); \
2676 isds_printf_message(context, \
2677 _("%s value out of range of long int: %s"), \
2678 element, string_locale); \
2679 free(string_locale); \
2680 free(string); \
2681 err = IE_ERROR; \
2682 goto leave; \
2685 free(string); string = NULL; \
2687 if (!(preallocated)) { \
2688 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2689 if (!(longintPtr)) { \
2690 err = IE_NOMEM; \
2691 goto leave; \
2694 *(longintPtr) = number; \
2698 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2700 char *string = NULL; \
2701 EXTRACT_STRING(element, string); \
2702 if (string) { \
2703 long int number; \
2704 char *endptr; \
2706 number = strtol((char*)string, &endptr, 10); \
2708 if (*endptr != '\0') { \
2709 char *string_locale = _isds_utf82locale((char *)string); \
2710 isds_printf_message(context, \
2711 _("%s is not valid integer: %s"), \
2712 element, string_locale); \
2713 free(string_locale); \
2714 free(string); \
2715 err = IE_ISDS; \
2716 goto leave; \
2719 if (number == LONG_MIN || number == LONG_MAX) { \
2720 char *string_locale = _isds_utf82locale((char *)string); \
2721 isds_printf_message(context, \
2722 _("%s value out of range of long int: %s"), \
2723 element, string_locale); \
2724 free(string_locale); \
2725 free(string); \
2726 err = IE_ERROR; \
2727 goto leave; \
2730 free(string); string = NULL; \
2731 if (number < 0) { \
2732 isds_printf_message(context, \
2733 _("%s value is negative: %ld"), element, number); \
2734 err = IE_ERROR; \
2735 goto leave; \
2738 if (!(preallocated)) { \
2739 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2740 if (!(ulongintPtr)) { \
2741 err = IE_NOMEM; \
2742 goto leave; \
2745 *(ulongintPtr) = number; \
2749 #define EXTRACT_DATE(element, tmPtr) { \
2750 char *string = NULL; \
2751 EXTRACT_STRING(element, string); \
2752 if (NULL != string) { \
2753 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2754 if (NULL == (tmPtr)) { \
2755 free(string); \
2756 err = IE_NOMEM; \
2757 goto leave; \
2759 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2760 if (err) { \
2761 if (err == IE_NOTSUP) { \
2762 err = IE_ISDS; \
2763 char *string_locale = _isds_utf82locale(string); \
2764 char *element_locale = _isds_utf82locale(element); \
2765 isds_printf_message(context, _("Invalid %s value: %s"), \
2766 element_locale, string_locale); \
2767 free(string_locale); \
2768 free(element_locale); \
2770 free(string); \
2771 goto leave; \
2773 free(string); \
2777 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2778 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2779 NULL); \
2780 if ((required) && (!string)) { \
2781 char *attribute_locale = _isds_utf82locale(attribute); \
2782 char *element_locale = \
2783 _isds_utf82locale((char *)xpath_ctx->node->name); \
2784 isds_printf_message(context, \
2785 _("Could not extract required %s attribute value from " \
2786 "%s element"), attribute_locale, element_locale); \
2787 free(element_locale); \
2788 free(attribute_locale); \
2789 err = IE_ERROR; \
2790 goto leave; \
2795 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2797 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2798 (xmlChar *) (string)); \
2799 if (!node) { \
2800 isds_printf_message(context, \
2801 _("Could not add %s child to %s element"), \
2802 element, (parent)->name); \
2803 err = IE_ERROR; \
2804 goto leave; \
2808 #define INSERT_STRING(parent, element, string) \
2809 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2811 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2813 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2814 else { INSERT_STRING(parent, element, "false"); } \
2817 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2819 if (booleanPtr) { \
2820 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2821 } else { \
2822 INSERT_STRING(parent, element, NULL); \
2826 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2827 if ((longintPtr)) { \
2828 /* FIXME: locale sensitive */ \
2829 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2830 err = IE_NOMEM; \
2831 goto leave; \
2833 INSERT_STRING(parent, element, buffer) \
2834 free(buffer); (buffer) = NULL; \
2835 } else { INSERT_STRING(parent, element, NULL) } \
2838 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2839 if ((ulongintPtr)) { \
2840 /* FIXME: locale sensitive */ \
2841 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2842 err = IE_NOMEM; \
2843 goto leave; \
2845 INSERT_STRING(parent, element, buffer) \
2846 free(buffer); (buffer) = NULL; \
2847 } else { INSERT_STRING(parent, element, NULL) } \
2850 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2852 /* FIXME: locale sensitive */ \
2853 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2854 err = IE_NOMEM; \
2855 goto leave; \
2857 INSERT_STRING(parent, element, buffer) \
2858 free(buffer); (buffer) = NULL; \
2861 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2862 * new attribute. */
2863 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2865 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2866 (xmlChar *) (string)); \
2867 if (!attribute_node) { \
2868 isds_printf_message(context, _("Could not add %s " \
2869 "attribute to %s element"), \
2870 (attribute), (parent)->name); \
2871 err = IE_ERROR; \
2872 goto leave; \
2876 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2877 if (string) { \
2878 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2879 if (length > (maximum)) { \
2880 isds_printf_message(context, \
2881 ngettext("%s has more than %d characters", \
2882 "%s has more than %d characters", (maximum)), \
2883 (name), (maximum)); \
2884 err = IE_2BIG; \
2885 goto leave; \
2887 if (length < (minimum)) { \
2888 isds_printf_message(context, \
2889 ngettext("%s has less than %d characters", \
2890 "%s has less than %d characters", (minimum)), \
2891 (name), (minimum)); \
2892 err = IE_2SMALL; \
2893 goto leave; \
2898 #define INSERT_ELEMENT(child, parent, element) \
2900 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2901 if (!(child)) { \
2902 isds_printf_message(context, \
2903 _("Could not add %s child to %s element"), \
2904 (element), (parent)->name); \
2905 err = IE_ERROR; \
2906 goto leave; \
2911 /* Find child element by name in given XPath context and switch context onto
2912 * it. The child must be uniq and must exist. Otherwise fails.
2913 * @context is ISDS context
2914 * @child is child element name
2915 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2916 * into it child. In error case, the @xpath_ctx keeps original value. */
2917 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2918 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2919 isds_error err = IE_SUCCESS;
2920 xmlXPathObjectPtr result = NULL;
2922 if (!context) return IE_INVALID_CONTEXT;
2923 if (!child || !xpath_ctx) return IE_INVAL;
2925 /* Find child */
2926 result = xmlXPathEvalExpression(child, xpath_ctx);
2927 if (!result) {
2928 err = IE_XML;
2929 goto leave;
2932 /* No match */
2933 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2934 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2935 char *child_locale = _isds_utf82locale((char*) child);
2936 isds_printf_message(context,
2937 _("%s element does not contain %s child"),
2938 parent_locale, child_locale);
2939 free(child_locale);
2940 free(parent_locale);
2941 err = IE_NOEXIST;
2942 goto leave;
2945 /* More matches */
2946 if (result->nodesetval->nodeNr > 1) {
2947 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2948 char *child_locale = _isds_utf82locale((char*) child);
2949 isds_printf_message(context,
2950 _("%s element contains multiple %s children"),
2951 parent_locale, child_locale);
2952 free(child_locale);
2953 free(parent_locale);
2954 err = IE_NOTUNIQ;
2955 goto leave;
2958 /* Switch context */
2959 xpath_ctx->node = result->nodesetval->nodeTab[0];
2961 leave:
2962 xmlXPathFreeObject(result);
2963 return err;
2968 #if HAVE_LIBCURL
2969 /* Find and convert XSD:gPersonName group in current node into structure
2970 * @context is ISDS context
2971 * @personName is automatically reallocated person name structure. If no member
2972 * value is found, will be freed.
2973 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2974 * elements
2975 * In case of error @personName will be freed. */
2976 static isds_error extract_gPersonName(struct isds_ctx *context,
2977 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2978 isds_error err = IE_SUCCESS;
2979 xmlXPathObjectPtr result = NULL;
2981 if (!context) return IE_INVALID_CONTEXT;
2982 if (!personName) return IE_INVAL;
2983 isds_PersonName_free(personName);
2984 if (!xpath_ctx) return IE_INVAL;
2987 *personName = calloc(1, sizeof(**personName));
2988 if (!*personName) {
2989 err = IE_NOMEM;
2990 goto leave;
2993 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2994 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2995 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2996 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2998 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2999 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
3000 isds_PersonName_free(personName);
3002 leave:
3003 if (err) isds_PersonName_free(personName);
3004 xmlXPathFreeObject(result);
3005 return err;
3009 /* Find and convert XSD:gAddress group extended with relevant
3010 * tdbPersonalOwnerinfo members in current node into structure
3011 * @context is ISDS context
3012 * @address is automatically reallocated address structure. If no member
3013 * value is found, will be freed.
3014 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
3015 * elements
3016 * In case of error @address will be freed. */
3017 static isds_error extract_gAddress(struct isds_ctx *context,
3018 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
3019 isds_error err = IE_SUCCESS;
3020 xmlXPathObjectPtr result = NULL;
3022 if (!context) return IE_INVALID_CONTEXT;
3023 if (!address) return IE_INVAL;
3024 isds_Address_free(address);
3025 if (!xpath_ctx) return IE_INVAL;
3028 *address = calloc(1, sizeof(**address));
3029 if (!*address) {
3030 err = IE_NOMEM;
3031 goto leave;
3034 EXTRACT_LONGINT("isds:adCode", (*address)->adCode, 0);
3035 EXTRACT_STRING("isds:adCity", (*address)->adCity);
3036 EXTRACT_STRING("isds:adDistrict", (*address)->adDistrict);
3037 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
3038 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
3039 EXTRACT_STRING("isds:adNumberInMunicipality",
3040 (*address)->adNumberInMunicipality);
3041 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
3042 EXTRACT_STRING("isds:adState", (*address)->adState);
3044 if (!(*address)->adCity && !(*address)->adStreet &&
3045 !(*address)->adNumberInStreet &&
3046 !(*address)->adNumberInMunicipality &&
3047 !(*address)->adZipCode && !(*address)->adState)
3048 isds_Address_free(address);
3050 leave:
3051 if (err) isds_Address_free(address);
3052 xmlXPathFreeObject(result);
3053 return err;
3057 /* Find and convert isds:biDate element in current node into structure
3058 * @context is ISDS context
3059 * @biDate is automatically reallocated birth date structure. If no member
3060 * value is found, will be freed.
3061 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3062 * element
3063 * In case of error @biDate will be freed. */
3064 static isds_error extract_BiDate(struct isds_ctx *context,
3065 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3066 isds_error err = IE_SUCCESS;
3067 xmlXPathObjectPtr result = NULL;
3068 char *string = NULL;
3070 if (!context) return IE_INVALID_CONTEXT;
3071 if (!biDate) return IE_INVAL;
3072 zfree(*biDate);
3073 if (!xpath_ctx) return IE_INVAL;
3075 EXTRACT_STRING("isds:biDate", string);
3076 if (string) {
3077 *biDate = calloc(1, sizeof(**biDate));
3078 if (!*biDate) {
3079 err = IE_NOMEM;
3080 goto leave;
3082 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3083 if (err) {
3084 if (err == IE_NOTSUP) {
3085 err = IE_ISDS;
3086 char *string_locale = _isds_utf82locale(string);
3087 isds_printf_message(context,
3088 _("Invalid isds:biDate value: %s"), string_locale);
3089 free(string_locale);
3091 goto leave;
3095 leave:
3096 if (err) zfree(*biDate);
3097 free(string);
3098 xmlXPathFreeObject(result);
3099 return err;
3103 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3104 * @context is ISDS context
3105 * @db_owner_info is automatically reallocated box owner info structure
3106 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3107 * XSD:tdbPersonalOwenerInfo element
3108 * In case of error @db_owner_info will be freed. */
3109 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3110 struct isds_DbOwnerInfo **db_owner_info,
3111 xmlXPathContextPtr xpath_ctx) {
3112 isds_error err = IE_SUCCESS;
3113 xmlXPathObjectPtr result = NULL;
3114 char *string = NULL;
3116 if (!context) return IE_INVALID_CONTEXT;
3117 if (!db_owner_info) return IE_INVAL;
3118 isds_DbOwnerInfo_free(db_owner_info);
3119 if (!xpath_ctx) return IE_INVAL;
3122 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3123 if (!*db_owner_info) {
3124 err = IE_NOMEM;
3125 goto leave;
3128 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3130 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info)->aifoIsds);
3132 EXTRACT_STRING("isds:dbType", string);
3133 if (string) {
3134 (*db_owner_info)->dbType =
3135 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3136 if (!(*db_owner_info)->dbType) {
3137 err = IE_NOMEM;
3138 goto leave;
3140 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3141 if (err) {
3142 zfree((*db_owner_info)->dbType);
3143 if (err == IE_ENUM) {
3144 err = IE_ISDS;
3145 char *string_locale = _isds_utf82locale(string);
3146 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3147 string_locale);
3148 free(string_locale);
3150 goto leave;
3152 zfree(string);
3155 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3157 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3158 xpath_ctx);
3159 if (err) goto leave;
3161 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3163 (*db_owner_info)->birthInfo =
3164 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3165 if (!(*db_owner_info)->birthInfo) {
3166 err = IE_NOMEM;
3167 goto leave;
3169 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3170 xpath_ctx);
3171 if (err) goto leave;
3172 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3173 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3174 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3175 if (!(*db_owner_info)->birthInfo->biDate &&
3176 !(*db_owner_info)->birthInfo->biCity &&
3177 !(*db_owner_info)->birthInfo->biCounty &&
3178 !(*db_owner_info)->birthInfo->biState)
3179 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3181 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3182 if (err) goto leave;
3184 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3185 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3186 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3187 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3188 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3190 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3192 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3193 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3194 (*db_owner_info)->dbOpenAddressing);
3196 leave:
3197 if (err) isds_DbOwnerInfo_free(db_owner_info);
3198 free(string);
3199 xmlXPathFreeObject(result);
3200 return err;
3204 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3205 * @context is session context
3206 * @owner is libisds structure with box description.
3207 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3208 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3209 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3210 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3211 * ignored.
3212 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3213 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3214 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3215 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3216 * type. */
3217 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3218 const struct isds_DbOwnerInfo *owner, _Bool pfo_subtype,
3219 xmlNodePtr db_owner_info) {
3221 isds_error err = IE_SUCCESS;
3222 xmlNodePtr node;
3223 xmlChar *string = NULL;
3224 const xmlChar *type_string = NULL;
3226 if (!context) return IE_INVALID_CONTEXT;
3227 if (!owner || !db_owner_info) return IE_INVAL;
3230 /* XXX: All the elements except email and telNumber are mandatory. */
3231 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3232 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3234 if (pfo_subtype) {
3235 INSERT_BOOLEAN(db_owner_info, "aifoIsds", owner->aifoIsds);
3238 if (!pfo_subtype) {
3239 /* dbType */
3240 if (owner->dbType) {
3241 type_string = isds_DbType2string(*(owner->dbType));
3242 if (!type_string) {
3243 isds_printf_message(context, _("Invalid dbType value: %d"),
3244 *(owner->dbType));
3245 err = IE_ENUM;
3246 goto leave;
3249 INSERT_STRING(db_owner_info, "dbType", type_string);
3251 INSERT_STRING(db_owner_info, "ic", owner->ic);
3254 INSERT_STRING(db_owner_info, "pnFirstName",
3255 (NULL == owner->personName) ? NULL: owner->personName->pnFirstName);
3256 INSERT_STRING(db_owner_info, "pnMiddleName",
3257 (NULL == owner->personName) ? NULL: owner->personName->pnMiddleName);
3258 INSERT_STRING(db_owner_info, "pnLastName",
3259 (NULL == owner->personName) ? NULL: owner->personName->pnLastName);
3260 if (!pfo_subtype) {
3261 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3262 (NULL == owner->personName) ? NULL:
3263 owner->personName->pnLastNameAtBirth);
3265 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3268 if (NULL != owner->birthInfo && NULL != owner->birthInfo->biDate) {
3269 err = tm2datestring(owner->birthInfo->biDate, &string);
3270 if (err) goto leave;
3272 INSERT_STRING(db_owner_info, "biDate", string);
3273 zfree(string);
3275 INSERT_STRING(db_owner_info, "biCity",
3276 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCity);
3277 INSERT_STRING(db_owner_info, "biCounty",
3278 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCounty);
3279 INSERT_STRING(db_owner_info, "biState",
3280 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biState);
3282 if (pfo_subtype) {
3283 INSERT_LONGINT(db_owner_info, "adCode",
3284 (NULL == owner->address) ? NULL : owner->address->adCode,
3285 string);
3287 INSERT_STRING(db_owner_info, "adCity",
3288 (NULL == owner->address) ? NULL: owner->address->adCity);
3289 if (pfo_subtype) {
3290 INSERT_STRING(db_owner_info, "adDistrict",
3291 (NULL == owner->address) ? NULL: owner->address->adDistrict);
3293 INSERT_STRING(db_owner_info, "adStreet",
3294 (NULL == owner->address) ? NULL: owner->address->adStreet);
3295 INSERT_STRING(db_owner_info, "adNumberInStreet",
3296 (NULL == owner->address) ? NULL: owner->address->adNumberInStreet);
3297 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3298 (NULL == owner->address) ? NULL: owner->address->adNumberInMunicipality);
3299 INSERT_STRING(db_owner_info, "adZipCode",
3300 (NULL == owner->address) ? NULL: owner->address->adZipCode);
3301 INSERT_STRING(db_owner_info, "adState",
3302 (NULL == owner->address) ? NULL: owner->address->adState);
3304 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3306 if (!pfo_subtype) {
3307 INSERT_STRING(db_owner_info, "email", owner->email);
3308 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3310 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3311 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3313 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3314 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3316 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3318 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3319 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3320 owner->dbOpenAddressing);
3323 leave:
3324 free(string);
3325 return err;
3329 /* Convert XSD:tDbUserInfo XML tree into structure
3330 * @context is ISDS context
3331 * @db_user_info is automatically reallocated user info structure
3332 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3333 * In case of error @db_user_info will be freed. */
3334 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3335 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3336 isds_error err = IE_SUCCESS;
3337 xmlXPathObjectPtr result = NULL;
3338 char *string = NULL;
3340 if (!context) return IE_INVALID_CONTEXT;
3341 if (!db_user_info) return IE_INVAL;
3342 isds_DbUserInfo_free(db_user_info);
3343 if (!xpath_ctx) return IE_INVAL;
3346 *db_user_info = calloc(1, sizeof(**db_user_info));
3347 if (!*db_user_info) {
3348 err = IE_NOMEM;
3349 goto leave;
3352 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3354 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3356 EXTRACT_STRING("isds:userType", string);
3357 if (string) {
3358 (*db_user_info)->userType =
3359 calloc(1, sizeof(*((*db_user_info)->userType)));
3360 if (!(*db_user_info)->userType) {
3361 err = IE_NOMEM;
3362 goto leave;
3364 err = string2isds_UserType((xmlChar *)string,
3365 (*db_user_info)->userType);
3366 if (err) {
3367 zfree((*db_user_info)->userType);
3368 if (err == IE_ENUM) {
3369 err = IE_ISDS;
3370 char *string_locale = _isds_utf82locale(string);
3371 isds_printf_message(context,
3372 _("Unknown isds:userType value: %s"), string_locale);
3373 free(string_locale);
3375 goto leave;
3377 zfree(string);
3380 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3382 (*db_user_info)->personName =
3383 calloc(1, sizeof(*((*db_user_info)->personName)));
3384 if (!(*db_user_info)->personName) {
3385 err = IE_NOMEM;
3386 goto leave;
3389 err = extract_gPersonName(context, &(*db_user_info)->personName,
3390 xpath_ctx);
3391 if (err) goto leave;
3393 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3394 if (err) goto leave;
3396 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3397 if (err) goto leave;
3399 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3400 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3402 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3403 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3404 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3406 /* ???: Default value is "CZ" according specification. Should we provide
3407 * it? */
3408 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3410 leave:
3411 if (err) isds_DbUserInfo_free(db_user_info);
3412 free(string);
3413 xmlXPathFreeObject(result);
3414 return err;
3418 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3419 * @context is session context
3420 * @user is libisds structure with user description
3421 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3422 * @db_user_info is XML element of XSD:tDbUserInfo */
3423 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3424 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3425 xmlNodePtr db_user_info) {
3427 isds_error err = IE_SUCCESS;
3428 xmlNodePtr node;
3429 xmlAttrPtr attribute_node;
3430 xmlChar *string = NULL;
3432 if (!context) return IE_INVALID_CONTEXT;
3433 if (!user || !db_user_info) return IE_INVAL;
3435 /* Build XSD:tDbUserInfo */
3437 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3438 * allow it everywhere. */
3439 if (honor_aifo_ticket && user->aifo_ticket) {
3440 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3443 if (user->personName) {
3444 INSERT_STRING(db_user_info, "pnFirstName",
3445 user->personName->pnFirstName);
3446 INSERT_STRING(db_user_info, "pnMiddleName",
3447 user->personName->pnMiddleName);
3448 INSERT_STRING(db_user_info, "pnLastName",
3449 user->personName->pnLastName);
3450 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3451 user->personName->pnLastNameAtBirth);
3453 if (user->address) {
3454 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3455 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3456 INSERT_STRING(db_user_info, "adNumberInStreet",
3457 user->address->adNumberInStreet);
3458 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3459 user->address->adNumberInMunicipality);
3460 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3461 INSERT_STRING(db_user_info, "adState", user->address->adState);
3463 if (user->biDate) {
3464 if (!tm2datestring(user->biDate, &string))
3465 INSERT_STRING(db_user_info, "biDate", string);
3466 zfree(string);
3468 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3469 INSERT_STRING(db_user_info, "userID", user->userID);
3471 /* userType */
3472 if (user->userType) {
3473 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3474 if (!type_string) {
3475 isds_printf_message(context, _("Invalid userType value: %d"),
3476 *(user->userType));
3477 err = IE_ENUM;
3478 goto leave;
3480 INSERT_STRING(db_user_info, "userType", type_string);
3483 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3484 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3485 INSERT_STRING(db_user_info, "ic", user->ic);
3486 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3487 INSERT_STRING(db_user_info, "firmName", user->firmName);
3488 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3489 INSERT_STRING(db_user_info, "caCity", user->caCity);
3490 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3491 INSERT_STRING(db_user_info, "caState", user->caState);
3493 leave:
3494 free(string);
3495 return err;
3499 /* Convert XSD:tPDZRec XML tree into structure
3500 * @context is ISDS context
3501 * @permission is automatically reallocated commercial permission structure
3502 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3503 * In case of error @permission will be freed. */
3504 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3505 struct isds_commercial_permission **permission,
3506 xmlXPathContextPtr xpath_ctx) {
3507 isds_error err = IE_SUCCESS;
3508 xmlXPathObjectPtr result = NULL;
3509 char *string = NULL;
3511 if (!context) return IE_INVALID_CONTEXT;
3512 if (!permission) return IE_INVAL;
3513 isds_commercial_permission_free(permission);
3514 if (!xpath_ctx) return IE_INVAL;
3517 *permission = calloc(1, sizeof(**permission));
3518 if (!*permission) {
3519 err = IE_NOMEM;
3520 goto leave;
3523 EXTRACT_STRING("isds:PDZType", string);
3524 if (string) {
3525 err = string2isds_payment_type((xmlChar *)string,
3526 &(*permission)->type);
3527 if (err) {
3528 if (err == IE_ENUM) {
3529 err = IE_ISDS;
3530 char *string_locale = _isds_utf82locale(string);
3531 isds_printf_message(context,
3532 _("Unknown isds:PDZType value: %s"), string_locale);
3533 free(string_locale);
3535 goto leave;
3537 zfree(string);
3540 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3541 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3543 EXTRACT_STRING("isds:PDZExpire", string);
3544 if (string) {
3545 err = timestring2timeval((xmlChar *) string,
3546 &((*permission)->expiration));
3547 if (err) {
3548 char *string_locale = _isds_utf82locale(string);
3549 if (err == IE_DATE) err = IE_ISDS;
3550 isds_printf_message(context,
3551 _("Could not convert PDZExpire as ISO time: %s"),
3552 string_locale);
3553 free(string_locale);
3554 goto leave;
3556 zfree(string);
3559 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3560 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3562 leave:
3563 if (err) isds_commercial_permission_free(permission);
3564 free(string);
3565 xmlXPathFreeObject(result);
3566 return err;
3570 /* Convert XSD:tCiRecord XML tree into structure
3571 * @context is ISDS context
3572 * @event is automatically reallocated commercial credit event structure
3573 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3574 * In case of error @event will be freed. */
3575 static isds_error extract_CiRecord(struct isds_ctx *context,
3576 struct isds_credit_event **event,
3577 xmlXPathContextPtr xpath_ctx) {
3578 isds_error err = IE_SUCCESS;
3579 xmlXPathObjectPtr result = NULL;
3580 char *string = NULL;
3581 long int *number_ptr;
3583 if (!context) return IE_INVALID_CONTEXT;
3584 if (!event) return IE_INVAL;
3585 isds_credit_event_free(event);
3586 if (!xpath_ctx) return IE_INVAL;
3589 *event = calloc(1, sizeof(**event));
3590 if (!*event) {
3591 err = IE_NOMEM;
3592 goto leave;
3595 EXTRACT_STRING("isds:ciEventTime", string);
3596 if (string) {
3597 err = timestring2timeval((xmlChar *) string,
3598 &(*event)->time);
3599 if (err) {
3600 char *string_locale = _isds_utf82locale(string);
3601 if (err == IE_DATE) err = IE_ISDS;
3602 isds_printf_message(context,
3603 _("Could not convert ciEventTime as ISO time: %s"),
3604 string_locale);
3605 free(string_locale);
3606 goto leave;
3608 zfree(string);
3611 EXTRACT_STRING("isds:ciEventType", string);
3612 if (string) {
3613 err = string2isds_credit_event_type((xmlChar *)string,
3614 &(*event)->type);
3615 if (err) {
3616 if (err == IE_ENUM) {
3617 err = IE_ISDS;
3618 char *string_locale = _isds_utf82locale(string);
3619 isds_printf_message(context,
3620 _("Unknown isds:ciEventType value: %s"), string_locale);
3621 free(string_locale);
3623 goto leave;
3625 zfree(string);
3628 number_ptr = &((*event)->credit_change);
3629 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3630 number_ptr = &(*event)->new_credit;
3631 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3633 switch((*event)->type) {
3634 case ISDS_CREDIT_CHARGED:
3635 EXTRACT_STRING("isds:ciTransID",
3636 (*event)->details.charged.transaction);
3637 break;
3638 case ISDS_CREDIT_DISCHARGED:
3639 EXTRACT_STRING("isds:ciTransID",
3640 (*event)->details.discharged.transaction);
3641 break;
3642 case ISDS_CREDIT_MESSAGE_SENT:
3643 EXTRACT_STRING("isds:ciRecipientID",
3644 (*event)->details.message_sent.recipient);
3645 EXTRACT_STRING("isds:ciPDZID",
3646 (*event)->details.message_sent.message_id);
3647 break;
3648 case ISDS_CREDIT_STORAGE_SET:
3649 number_ptr = &((*event)->details.storage_set.new_capacity);
3650 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3651 EXTRACT_DATE("isds:ciNewFrom",
3652 (*event)->details.storage_set.new_valid_from);
3653 EXTRACT_DATE("isds:ciNewTo",
3654 (*event)->details.storage_set.new_valid_to);
3655 EXTRACT_LONGINT("isds:ciOldCapacity",
3656 (*event)->details.storage_set.old_capacity, 0);
3657 EXTRACT_DATE("isds:ciOldFrom",
3658 (*event)->details.storage_set.old_valid_from);
3659 EXTRACT_DATE("isds:ciOldTo",
3660 (*event)->details.storage_set.old_valid_to);
3661 EXTRACT_STRING("isds:ciDoneBy",
3662 (*event)->details.storage_set.initiator);
3663 break;
3664 case ISDS_CREDIT_EXPIRED:
3665 break;
3668 leave:
3669 if (err) isds_credit_event_free(event);
3670 free(string);
3671 xmlXPathFreeObject(result);
3672 return err;
3676 #endif /* HAVE_LIBCURL */
3679 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3680 * isds_envelope structure. The envelope is automatically allocated but not
3681 * reallocated. The date are just appended into envelope structure.
3682 * @context is ISDS context
3683 * @envelope is automatically allocated message envelope structure
3684 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3685 * In case of error @envelope will be freed. */
3686 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3687 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3688 isds_error err = IE_SUCCESS;
3689 xmlXPathObjectPtr result = NULL;
3691 if (!context) return IE_INVALID_CONTEXT;
3692 if (!envelope) return IE_INVAL;
3693 if (!xpath_ctx) return IE_INVAL;
3696 if (!*envelope) {
3697 /* Allocate envelope */
3698 *envelope = calloc(1, sizeof(**envelope));
3699 if (!*envelope) {
3700 err = IE_NOMEM;
3701 goto leave;
3703 } else {
3704 /* Else free former data */
3705 zfree((*envelope)->dmSenderOrgUnit);
3706 zfree((*envelope)->dmSenderOrgUnitNum);
3707 zfree((*envelope)->dbIDRecipient);
3708 zfree((*envelope)->dmRecipientOrgUnit);
3709 zfree((*envelope)->dmRecipientOrgUnitNum);
3710 zfree((*envelope)->dmToHands);
3711 zfree((*envelope)->dmAnnotation);
3712 zfree((*envelope)->dmRecipientRefNumber);
3713 zfree((*envelope)->dmSenderRefNumber);
3714 zfree((*envelope)->dmRecipientIdent);
3715 zfree((*envelope)->dmSenderIdent);
3716 zfree((*envelope)->dmLegalTitleLaw);
3717 zfree((*envelope)->dmLegalTitleYear);
3718 zfree((*envelope)->dmLegalTitleSect);
3719 zfree((*envelope)->dmLegalTitlePar);
3720 zfree((*envelope)->dmLegalTitlePoint);
3721 zfree((*envelope)->dmPersonalDelivery);
3722 zfree((*envelope)->dmAllowSubstDelivery);
3725 /* Extract envelope elements added by sender or ISDS
3726 * (XSD: gMessageEnvelopeSub type) */
3727 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3728 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3729 (*envelope)->dmSenderOrgUnitNum, 0);
3730 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3731 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3732 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3733 (*envelope)->dmRecipientOrgUnitNum, 0);
3734 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3735 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3736 EXTRACT_STRING("isds:dmRecipientRefNumber",
3737 (*envelope)->dmRecipientRefNumber);
3738 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3739 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3740 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3742 /* Extract envelope elements regarding law reference */
3743 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3744 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3745 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3746 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3747 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3749 /* Extract envelope other elements */
3750 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3751 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3752 (*envelope)->dmAllowSubstDelivery);
3754 leave:
3755 if (err) isds_envelope_free(envelope);
3756 xmlXPathFreeObject(result);
3757 return err;
3762 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3763 * isds_envelope structure. The envelope is automatically allocated but not
3764 * reallocated. The date are just appended into envelope structure.
3765 * @context is ISDS context
3766 * @envelope is automatically allocated message envelope structure
3767 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3768 * In case of error @envelope will be freed. */
3769 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3770 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3771 isds_error err = IE_SUCCESS;
3772 xmlXPathObjectPtr result = NULL;
3774 if (!context) return IE_INVALID_CONTEXT;
3775 if (!envelope) return IE_INVAL;
3776 if (!xpath_ctx) return IE_INVAL;
3779 if (!*envelope) {
3780 /* Allocate envelope */
3781 *envelope = calloc(1, sizeof(**envelope));
3782 if (!*envelope) {
3783 err = IE_NOMEM;
3784 goto leave;
3786 } else {
3787 /* Else free former data */
3788 zfree((*envelope)->dmID);
3789 zfree((*envelope)->dbIDSender);
3790 zfree((*envelope)->dmSender);
3791 zfree((*envelope)->dmSenderAddress);
3792 zfree((*envelope)->dmSenderType);
3793 zfree((*envelope)->dmRecipient);
3794 zfree((*envelope)->dmRecipientAddress);
3795 zfree((*envelope)->dmAmbiguousRecipient);
3798 /* Extract envelope elements added by ISDS
3799 * (XSD: gMessageEnvelope type) */
3800 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3801 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3802 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3803 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3804 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3805 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3806 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3807 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3808 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3809 (*envelope)->dmAmbiguousRecipient);
3811 /* Extract envelope elements added by sender and ISDS
3812 * (XSD: gMessageEnvelope type) */
3813 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3814 if (err) goto leave;
3816 leave:
3817 if (err) isds_envelope_free(envelope);
3818 xmlXPathFreeObject(result);
3819 return err;
3823 /* Convert other envelope elements from XML tree into isds_envelope structure:
3824 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3825 * The envelope is automatically allocated but not reallocated.
3826 * The data are just appended into envelope structure.
3827 * @context is ISDS context
3828 * @envelope is automatically allocated message envelope structure
3829 * @xpath_ctx is XPath context with current node as parent desired elements
3830 * In case of error @envelope will be freed. */
3831 static isds_error append_status_size_times(struct isds_ctx *context,
3832 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3833 isds_error err = IE_SUCCESS;
3834 xmlXPathObjectPtr result = NULL;
3835 char *string = NULL;
3836 unsigned long int *unumber = NULL;
3838 if (!context) return IE_INVALID_CONTEXT;
3839 if (!envelope) return IE_INVAL;
3840 if (!xpath_ctx) return IE_INVAL;
3843 if (!*envelope) {
3844 /* Allocate new */
3845 *envelope = calloc(1, sizeof(**envelope));
3846 if (!*envelope) {
3847 err = IE_NOMEM;
3848 goto leave;
3850 } else {
3851 /* Free old data */
3852 zfree((*envelope)->dmMessageStatus);
3853 zfree((*envelope)->dmAttachmentSize);
3854 zfree((*envelope)->dmDeliveryTime);
3855 zfree((*envelope)->dmAcceptanceTime);
3859 /* dmMessageStatus element is mandatory */
3860 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3861 if (!unumber) {
3862 isds_log_message(context,
3863 _("Missing mandatory sisds:dmMessageStatus integer"));
3864 err = IE_ISDS;
3865 goto leave;
3867 err = uint2isds_message_status(context, unumber,
3868 &((*envelope)->dmMessageStatus));
3869 if (err) {
3870 if (err == IE_ENUM) err = IE_ISDS;
3871 goto leave;
3873 free(unumber); unumber = NULL;
3875 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3878 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3879 if (string) {
3880 err = timestring2timeval((xmlChar *) string,
3881 &((*envelope)->dmDeliveryTime));
3882 if (err) {
3883 char *string_locale = _isds_utf82locale(string);
3884 if (err == IE_DATE) err = IE_ISDS;
3885 isds_printf_message(context,
3886 _("Could not convert dmDeliveryTime as ISO time: %s"),
3887 string_locale);
3888 free(string_locale);
3889 goto leave;
3891 zfree(string);
3894 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3895 if (string) {
3896 err = timestring2timeval((xmlChar *) string,
3897 &((*envelope)->dmAcceptanceTime));
3898 if (err) {
3899 char *string_locale = _isds_utf82locale(string);
3900 if (err == IE_DATE) err = IE_ISDS;
3901 isds_printf_message(context,
3902 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3903 string_locale);
3904 free(string_locale);
3905 goto leave;
3907 zfree(string);
3910 leave:
3911 if (err) isds_envelope_free(envelope);
3912 free(unumber);
3913 free(string);
3914 xmlXPathFreeObject(result);
3915 return err;
3919 /* Convert message type attribute of current element into isds_envelope
3920 * structure.
3921 * TODO: This function can be incorporated into append_status_size_times() as
3922 * they are called always together.
3923 * The envelope is automatically allocated but not reallocated.
3924 * The data are just appended into envelope structure.
3925 * @context is ISDS context
3926 * @envelope is automatically allocated message envelope structure
3927 * @xpath_ctx is XPath context with current node as parent of attribute
3928 * carrying message type
3929 * In case of error @envelope will be freed. */
3930 static isds_error append_message_type(struct isds_ctx *context,
3931 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3932 isds_error err = IE_SUCCESS;
3934 if (!context) return IE_INVALID_CONTEXT;
3935 if (!envelope) return IE_INVAL;
3936 if (!xpath_ctx) return IE_INVAL;
3939 if (!*envelope) {
3940 /* Allocate new */
3941 *envelope = calloc(1, sizeof(**envelope));
3942 if (!*envelope) {
3943 err = IE_NOMEM;
3944 goto leave;
3946 } else {
3947 /* Free old data */
3948 zfree((*envelope)->dmType);
3952 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3954 if (!(*envelope)->dmType) {
3955 /* Use default value */
3956 (*envelope)->dmType = strdup("V");
3957 if (!(*envelope)->dmType) {
3958 err = IE_NOMEM;
3959 goto leave;
3961 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3962 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3963 isds_printf_message(context,
3964 _("Message type in dmType attribute is not 1 character long: "
3965 "%s"),
3966 type_locale);
3967 free(type_locale);
3968 err = IE_ISDS;
3969 goto leave;
3972 leave:
3973 if (err) isds_envelope_free(envelope);
3974 return err;
3978 #if HAVE_LIBCURL
3979 /* Convert dmType isds_envelope member into XML attribute and append it to
3980 * current node.
3981 * @context is ISDS context
3982 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3983 * @dm_envelope is XML element the resulting attribute will be appended to.
3984 * @return error code, in case of error context' message is filled. */
3985 static isds_error insert_message_type(struct isds_ctx *context,
3986 const char *type, xmlNodePtr dm_envelope) {
3987 isds_error err = IE_SUCCESS;
3988 xmlAttrPtr attribute_node;
3990 if (!context) return IE_INVALID_CONTEXT;
3991 if (!dm_envelope) return IE_INVAL;
3993 /* Insert optional message type */
3994 if (type) {
3995 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3996 char *type_locale = _isds_utf82locale(type);
3997 isds_printf_message(context,
3998 _("Message type in envelope is not 1 character long: %s"),
3999 type_locale);
4000 free(type_locale);
4001 err = IE_INVAL;
4002 goto leave;
4004 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
4007 leave:
4008 return err;
4010 #endif /* HAVE_LIBCURL */
4013 /* Extract message document into reallocated document structure
4014 * @context is ISDS context
4015 * @document is automatically reallocated message documents structure
4016 * @xpath_ctx is XPath context with current node as isds:dmFile
4017 * In case of error @document will be freed. */
4018 static isds_error extract_document(struct isds_ctx *context,
4019 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
4020 isds_error err = IE_SUCCESS;
4021 xmlXPathObjectPtr result = NULL;
4022 xmlNodePtr file_node;
4023 char *string = NULL;
4025 if (!context) return IE_INVALID_CONTEXT;
4026 if (!document) return IE_INVAL;
4027 isds_document_free(document);
4028 if (!xpath_ctx) return IE_INVAL;
4029 file_node = xpath_ctx->node;
4031 *document = calloc(1, sizeof(**document));
4032 if (!*document) {
4033 err = IE_NOMEM;
4034 goto leave;
4037 /* Extract document meta data */
4038 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
4039 if (context->normalize_mime_type) {
4040 const char *normalized_type =
4041 isds_normalize_mime_type((*document)->dmMimeType);
4042 if (NULL != normalized_type &&
4043 normalized_type != (*document)->dmMimeType) {
4044 char *new_type = strdup(normalized_type);
4045 if (NULL == new_type) {
4046 isds_printf_message(context,
4047 _("Not enough memory to normalize document MIME type"));
4048 err = IE_NOMEM;
4049 goto leave;
4051 free((*document)->dmMimeType);
4052 (*document)->dmMimeType = new_type;
4056 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
4057 err = string2isds_FileMetaType((xmlChar*)string,
4058 &((*document)->dmFileMetaType));
4059 if (err) {
4060 char *meta_type_locale = _isds_utf82locale(string);
4061 isds_printf_message(context,
4062 _("Document has invalid dmFileMetaType attribute value: %s"),
4063 meta_type_locale);
4064 free(meta_type_locale);
4065 err = IE_ISDS;
4066 goto leave;
4068 zfree(string);
4070 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
4071 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
4072 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
4073 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
4076 /* Extract document data.
4077 * Base64 encoded blob or XML subtree must be presented. */
4079 /* Check for dmEncodedContent */
4080 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
4081 xpath_ctx);
4082 if (!result) {
4083 err = IE_XML;
4084 goto leave;
4087 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4088 /* Here we have Base64 blob */
4089 (*document)->is_xml = 0;
4091 if (result->nodesetval->nodeNr > 1) {
4092 isds_printf_message(context,
4093 _("Document has more dmEncodedContent elements"));
4094 err = IE_ISDS;
4095 goto leave;
4098 xmlXPathFreeObject(result); result = NULL;
4099 EXTRACT_STRING("isds:dmEncodedContent", string);
4101 /* Decode non-empty document */
4102 if (string && string[0] != '\0') {
4103 (*document)->data_length =
4104 _isds_b64decode(string, &((*document)->data));
4105 if ((*document)->data_length == (size_t) -1) {
4106 isds_printf_message(context,
4107 _("Error while Base64-decoding document content"));
4108 err = IE_ERROR;
4109 goto leave;
4112 } else {
4113 /* No Base64 blob, try XML document */
4114 xmlXPathFreeObject(result); result = NULL;
4115 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4116 xpath_ctx);
4117 if (!result) {
4118 err = IE_XML;
4119 goto leave;
4122 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4123 /* Here we have XML document */
4124 (*document)->is_xml = 1;
4126 if (result->nodesetval->nodeNr > 1) {
4127 isds_printf_message(context,
4128 _("Document has more dmXMLContent elements"));
4129 err = IE_ISDS;
4130 goto leave;
4133 /* XXX: We cannot serialize the content simply because:
4134 * - XML document may point out of its scope (e.g. to message
4135 * envelope)
4136 * - isds:dmXMLContent can contain more elements, no element,
4137 * a text node only
4138 * - it's not the XML way
4139 * Thus we provide the only right solution: XML DOM. Let's
4140 * application to cope with this hot potato :) */
4141 (*document)->xml_node_list =
4142 result->nodesetval->nodeTab[0]->children;
4143 } else {
4144 /* No base64 blob, nor XML document */
4145 isds_printf_message(context,
4146 _("Document has no dmEncodedContent, nor dmXMLContent "
4147 "element"));
4148 err = IE_ISDS;
4149 goto leave;
4154 leave:
4155 if (err) isds_document_free(document);
4156 free(string);
4157 xmlXPathFreeObject(result);
4158 xpath_ctx->node = file_node;
4159 return err;
4164 /* Extract message documents into reallocated list of documents
4165 * @context is ISDS context
4166 * @documents is automatically reallocated message documents list structure
4167 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4168 * In case of error @documents will be freed. */
4169 static isds_error extract_documents(struct isds_ctx *context,
4170 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4171 isds_error err = IE_SUCCESS;
4172 xmlXPathObjectPtr result = NULL;
4173 xmlNodePtr files_node;
4174 struct isds_list *document, *prev_document = NULL;
4176 if (!context) return IE_INVALID_CONTEXT;
4177 if (!documents) return IE_INVAL;
4178 isds_list_free(documents);
4179 if (!xpath_ctx) return IE_INVAL;
4180 files_node = xpath_ctx->node;
4182 /* Find documents */
4183 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4184 if (!result) {
4185 err = IE_XML;
4186 goto leave;
4189 /* No match */
4190 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4191 isds_printf_message(context,
4192 _("Message does not contain any document"));
4193 err = IE_ISDS;
4194 goto leave;
4198 /* Iterate over documents */
4199 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4201 /* Allocate and append list item */
4202 document = calloc(1, sizeof(*document));
4203 if (!document) {
4204 err = IE_NOMEM;
4205 goto leave;
4207 document->destructor = (void (*)(void **))isds_document_free;
4208 if (i == 0) *documents = document;
4209 else prev_document->next = document;
4210 prev_document = document;
4212 /* Extract document */
4213 xpath_ctx->node = result->nodesetval->nodeTab[i];
4214 err = extract_document(context,
4215 (struct isds_document **) &(document->data), xpath_ctx);
4216 if (err) goto leave;
4220 leave:
4221 if (err) isds_list_free(documents);
4222 xmlXPathFreeObject(result);
4223 xpath_ctx->node = files_node;
4224 return err;
4228 #if HAVE_LIBCURL
4229 /* Convert isds:dmRecord XML tree into structure
4230 * @context is ISDS context
4231 * @envelope is automatically reallocated message envelope structure
4232 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4233 * In case of error @envelope will be freed. */
4234 static isds_error extract_DmRecord(struct isds_ctx *context,
4235 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4236 isds_error err = IE_SUCCESS;
4237 xmlXPathObjectPtr result = NULL;
4239 if (!context) return IE_INVALID_CONTEXT;
4240 if (!envelope) return IE_INVAL;
4241 isds_envelope_free(envelope);
4242 if (!xpath_ctx) return IE_INVAL;
4245 *envelope = calloc(1, sizeof(**envelope));
4246 if (!*envelope) {
4247 err = IE_NOMEM;
4248 goto leave;
4252 /* Extract tRecord data */
4253 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4255 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4256 * dmAcceptanceTime. */
4257 err = append_status_size_times(context, envelope, xpath_ctx);
4258 if (err) goto leave;
4260 /* Extract envelope elements added by sender and ISDS
4261 * (XSD: gMessageEnvelope type) */
4262 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4263 if (err) goto leave;
4265 /* Get message type */
4266 err = append_message_type(context, envelope, xpath_ctx);
4267 if (err) goto leave;
4270 leave:
4271 if (err) isds_envelope_free(envelope);
4272 xmlXPathFreeObject(result);
4273 return err;
4277 /* Convert XSD:tStateChangesRecord type XML tree into structure
4278 * @context is ISDS context
4279 * @changed_status is automatically reallocated message state change structure
4280 * @xpath_ctx is XPath context with current node as element of
4281 * XSD:tStateChangesRecord type
4282 * In case of error @changed_status will be freed. */
4283 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4284 struct isds_message_status_change **changed_status,
4285 xmlXPathContextPtr xpath_ctx) {
4286 isds_error err = IE_SUCCESS;
4287 xmlXPathObjectPtr result = NULL;
4288 unsigned long int *unumber = NULL;
4289 char *string = NULL;
4291 if (!context) return IE_INVALID_CONTEXT;
4292 if (!changed_status) return IE_INVAL;
4293 isds_message_status_change_free(changed_status);
4294 if (!xpath_ctx) return IE_INVAL;
4297 *changed_status = calloc(1, sizeof(**changed_status));
4298 if (!*changed_status) {
4299 err = IE_NOMEM;
4300 goto leave;
4304 /* Extract tGetStateChangesInput data */
4305 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4307 /* dmEventTime is mandatory */
4308 EXTRACT_STRING("isds:dmEventTime", string);
4309 if (string) {
4310 err = timestring2timeval((xmlChar *) string,
4311 &((*changed_status)->time));
4312 if (err) {
4313 char *string_locale = _isds_utf82locale(string);
4314 if (err == IE_DATE) err = IE_ISDS;
4315 isds_printf_message(context,
4316 _("Could not convert dmEventTime as ISO time: %s"),
4317 string_locale);
4318 free(string_locale);
4319 goto leave;
4321 zfree(string);
4324 /* dmMessageStatus element is mandatory */
4325 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4326 if (!unumber) {
4327 isds_log_message(context,
4328 _("Missing mandatory isds:dmMessageStatus integer"));
4329 err = IE_ISDS;
4330 goto leave;
4332 err = uint2isds_message_status(context, unumber,
4333 &((*changed_status)->dmMessageStatus));
4334 if (err) {
4335 if (err == IE_ENUM) err = IE_ISDS;
4336 goto leave;
4338 zfree(unumber);
4341 leave:
4342 free(unumber);
4343 free(string);
4344 if (err) isds_message_status_change_free(changed_status);
4345 xmlXPathFreeObject(result);
4346 return err;
4348 #endif /* HAVE_LIBCURL */
4351 /* Find and convert isds:dmHash XML tree into structure
4352 * @context is ISDS context
4353 * @envelope is automatically reallocated message hash structure
4354 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4355 * In case of error @hash will be freed. */
4356 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4357 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4358 isds_error err = IE_SUCCESS;
4359 xmlNodePtr old_ctx_node;
4360 xmlXPathObjectPtr result = NULL;
4361 char *string = NULL;
4363 if (!context) return IE_INVALID_CONTEXT;
4364 if (!hash) return IE_INVAL;
4365 isds_hash_free(hash);
4366 if (!xpath_ctx) return IE_INVAL;
4368 old_ctx_node = xpath_ctx->node;
4370 *hash = calloc(1, sizeof(**hash));
4371 if (!*hash) {
4372 err = IE_NOMEM;
4373 goto leave;
4376 /* Locate dmHash */
4377 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4378 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4379 err = IE_ISDS;
4380 goto leave;
4382 if (err) {
4383 err = IE_ERROR;
4384 goto leave;
4387 /* Get hash algorithm */
4388 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4389 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4390 if (err) {
4391 if (err == IE_ENUM) {
4392 char *string_locale = _isds_utf82locale(string);
4393 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4394 string_locale);
4395 free(string_locale);
4397 goto leave;
4399 zfree(string);
4401 /* Get hash value */
4402 EXTRACT_STRING(".", string);
4403 if (!string) {
4404 isds_printf_message(context,
4405 _("sisds:dmHash element is missing hash value"));
4406 err = IE_ISDS;
4407 goto leave;
4409 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4410 if ((*hash)->length == (size_t) -1) {
4411 isds_printf_message(context,
4412 _("Error while Base64-decoding hash value"));
4413 err = IE_ERROR;
4414 goto leave;
4417 leave:
4418 if (err) isds_hash_free(hash);
4419 free(string);
4420 xmlXPathFreeObject(result);
4421 xpath_ctx->node = old_ctx_node;
4422 return err;
4426 /* Find and append isds:dmQTimestamp XML tree into envelope.
4427 * Because one service is allowed to miss time-stamp content, and we think
4428 * other could too (flaw in specification), this function is deliberated and
4429 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4430 * @context is ISDS context
4431 * @envelope is automatically allocated envelope structure
4432 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4433 * child
4434 * In case of error @envelope will be freed. */
4435 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4436 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4437 isds_error err = IE_SUCCESS;
4438 xmlXPathObjectPtr result = NULL;
4439 char *string = NULL;
4441 if (!context) return IE_INVALID_CONTEXT;
4442 if (!envelope) return IE_INVAL;
4443 if (!xpath_ctx) {
4444 isds_envelope_free(envelope);
4445 return IE_INVAL;
4448 if (!*envelope) {
4449 *envelope = calloc(1, sizeof(**envelope));
4450 if (!*envelope) {
4451 err = IE_NOMEM;
4452 goto leave;
4454 } else {
4455 zfree((*envelope)->timestamp);
4456 (*envelope)->timestamp_length = 0;
4459 /* Get dmQTimestamp */
4460 EXTRACT_STRING("sisds:dmQTimestamp", string);
4461 if (!string) {
4462 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4463 goto leave;
4465 (*envelope)->timestamp_length =
4466 _isds_b64decode(string, &((*envelope)->timestamp));
4467 if ((*envelope)->timestamp_length == (size_t) -1) {
4468 isds_printf_message(context,
4469 _("Error while Base64-decoding time stamp value"));
4470 err = IE_ERROR;
4471 goto leave;
4474 leave:
4475 if (err) isds_envelope_free(envelope);
4476 free(string);
4477 xmlXPathFreeObject(result);
4478 return err;
4482 /* Convert XSD tReturnedMessage XML tree into message structure.
4483 * It does not store serialized XML tree into message->raw.
4484 * It does store (pointer to) parsed XML tree into message->xml if needed.
4485 * @context is ISDS context
4486 * @include_documents Use true if documents must be extracted
4487 * (tReturnedMessage XSD type), use false if documents shall be omitted
4488 * (tReturnedMessageEnvelope).
4489 * @message is automatically reallocated message structure
4490 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4491 * type
4492 * In case of error @message will be freed. */
4493 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4494 const _Bool include_documents, struct isds_message **message,
4495 xmlXPathContextPtr xpath_ctx) {
4496 isds_error err = IE_SUCCESS;
4497 xmlNodePtr message_node;
4499 if (!context) return IE_INVALID_CONTEXT;
4500 if (!message) return IE_INVAL;
4501 isds_message_free(message);
4502 if (!xpath_ctx) return IE_INVAL;
4505 *message = calloc(1, sizeof(**message));
4506 if (!*message) {
4507 err = IE_NOMEM;
4508 goto leave;
4511 /* Save message XPATH context node */
4512 message_node = xpath_ctx->node;
4515 /* Extract dmDM */
4516 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4517 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4518 if (err) { err = IE_ERROR; goto leave; }
4519 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4520 if (err) goto leave;
4522 if (include_documents) {
4523 struct isds_list *item;
4525 /* Extract dmFiles */
4526 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4527 xpath_ctx);
4528 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4529 err = IE_ISDS; goto leave;
4531 if (err) { err = IE_ERROR; goto leave; }
4532 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4533 if (err) goto leave;
4535 /* Store xmlDoc of this message if needed */
4536 /* Only if we got a XML document in all the documents. */
4537 for (item = (*message)->documents; item; item = item->next) {
4538 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4539 (*message)->xml = xpath_ctx->doc;
4540 break;
4546 /* Restore context to message */
4547 xpath_ctx->node = message_node;
4549 /* Extract dmHash */
4550 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4551 xpath_ctx);
4552 if (err) goto leave;
4554 /* Extract dmQTimestamp, */
4555 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4556 xpath_ctx);
4557 if (err) goto leave;
4559 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4560 * dmAcceptanceTime. */
4561 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4562 if (err) goto leave;
4564 /* Get message type */
4565 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4566 if (err) goto leave;
4568 leave:
4569 if (err) isds_message_free(message);
4570 return err;
4574 /* Extract message event into reallocated isds_event structure
4575 * @context is ISDS context
4576 * @event is automatically reallocated message event structure
4577 * @xpath_ctx is XPath context with current node as isds:dmEvent
4578 * In case of error @event will be freed. */
4579 static isds_error extract_event(struct isds_ctx *context,
4580 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4581 isds_error err = IE_SUCCESS;
4582 xmlXPathObjectPtr result = NULL;
4583 xmlNodePtr event_node;
4584 char *string = NULL;
4586 if (!context) return IE_INVALID_CONTEXT;
4587 if (!event) return IE_INVAL;
4588 isds_event_free(event);
4589 if (!xpath_ctx) return IE_INVAL;
4590 event_node = xpath_ctx->node;
4592 *event = calloc(1, sizeof(**event));
4593 if (!*event) {
4594 err = IE_NOMEM;
4595 goto leave;
4598 /* Extract event data.
4599 * All elements are optional according XSD. That's funny. */
4600 EXTRACT_STRING("sisds:dmEventTime", string);
4601 if (string) {
4602 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4603 if (err) {
4604 char *string_locale = _isds_utf82locale(string);
4605 if (err == IE_DATE) err = IE_ISDS;
4606 isds_printf_message(context,
4607 _("Could not convert dmEventTime as ISO time: %s"),
4608 string_locale);
4609 free(string_locale);
4610 goto leave;
4612 zfree(string);
4615 /* dmEventDescr element has prefix and the rest */
4616 EXTRACT_STRING("sisds:dmEventDescr", string);
4617 if (string) {
4618 err = eventstring2event((xmlChar *) string, *event);
4619 if (err) goto leave;
4620 zfree(string);
4623 leave:
4624 if (err) isds_event_free(event);
4625 free(string);
4626 xmlXPathFreeObject(result);
4627 xpath_ctx->node = event_node;
4628 return err;
4632 /* Convert element of XSD tEventsArray type from XML tree into
4633 * isds_list of isds_event's structure. The list is automatically reallocated.
4634 * @context is ISDS context
4635 * @events is automatically reallocated list of event structures
4636 * @xpath_ctx is XPath context with current node as tEventsArray
4637 * In case of error @events will be freed. */
4638 static isds_error extract_events(struct isds_ctx *context,
4639 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4640 isds_error err = IE_SUCCESS;
4641 xmlXPathObjectPtr result = NULL;
4642 xmlNodePtr events_node;
4643 struct isds_list *event, *prev_event = NULL;
4645 if (!context) return IE_INVALID_CONTEXT;
4646 if (!events) return IE_INVAL;
4647 if (!xpath_ctx) return IE_INVAL;
4648 events_node = xpath_ctx->node;
4650 /* Free old list */
4651 isds_list_free(events);
4653 /* Find events */
4654 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4655 if (!result) {
4656 err = IE_XML;
4657 goto leave;
4660 /* No match */
4661 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4662 isds_printf_message(context,
4663 _("Delivery info does not contain any event"));
4664 err = IE_ISDS;
4665 goto leave;
4669 /* Iterate over events */
4670 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4672 /* Allocate and append list item */
4673 event = calloc(1, sizeof(*event));
4674 if (!event) {
4675 err = IE_NOMEM;
4676 goto leave;
4678 event->destructor = (void (*)(void **))isds_event_free;
4679 if (i == 0) *events = event;
4680 else prev_event->next = event;
4681 prev_event = event;
4683 /* Extract event */
4684 xpath_ctx->node = result->nodesetval->nodeTab[i];
4685 err = extract_event(context,
4686 (struct isds_event **) &(event->data), xpath_ctx);
4687 if (err) goto leave;
4691 leave:
4692 if (err) isds_list_free(events);
4693 xmlXPathFreeObject(result);
4694 xpath_ctx->node = events_node;
4695 return err;
4699 #if HAVE_LIBCURL
4700 /* Insert Base64 encoded data as element with text child.
4701 * @context is session context
4702 * @parent is XML node to append @element with @data as child
4703 * @ns is XML namespace of @element, use NULL to inherit from @parent
4704 * @element is UTF-8 encoded name of new element
4705 * @data is bit stream to encode into @element
4706 * @length is size of @data in bytes
4707 * @return standard error code and fill long error message if needed */
4708 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4709 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4710 const void *data, size_t length) {
4711 isds_error err = IE_SUCCESS;
4712 xmlNodePtr node;
4714 if (!context) return IE_INVALID_CONTEXT;
4715 if (!data && length > 0) return IE_INVAL;
4716 if (!parent || !element) return IE_INVAL;
4718 xmlChar *base64data = NULL;
4719 base64data = (xmlChar *) _isds_b64encode(data, length);
4720 if (!base64data) {
4721 isds_printf_message(context,
4722 ngettext("Not enough memory to encode %zd byte into Base64",
4723 "Not enough memory to encode %zd bytes into Base64",
4724 length),
4725 length);
4726 err = IE_NOMEM;
4727 goto leave;
4729 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4731 leave:
4732 free(base64data);
4733 return err;
4737 /* Convert isds_document structure into XML tree and append to dmFiles node.
4738 * @context is session context
4739 * @document is ISDS document
4740 * @dm_files is XML element the resulting tree will be appended to as a child.
4741 * @return error code, in case of error context' message is filled. */
4742 static isds_error insert_document(struct isds_ctx *context,
4743 struct isds_document *document, xmlNodePtr dm_files) {
4744 isds_error err = IE_SUCCESS;
4745 xmlNodePtr new_file = NULL, file = NULL, node;
4746 xmlAttrPtr attribute_node;
4748 if (!context) return IE_INVALID_CONTEXT;
4749 if (!document || !dm_files) return IE_INVAL;
4751 /* Allocate new dmFile */
4752 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4753 if (!new_file) {
4754 isds_printf_message(context, _("Could not allocate main dmFile"));
4755 err = IE_ERROR;
4756 goto leave;
4758 /* Append the new dmFile.
4759 * XXX: Main document must go first */
4760 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4761 file = xmlAddPrevSibling(dm_files->children, new_file);
4762 else
4763 file = xmlAddChild(dm_files, new_file);
4765 if (!file) {
4766 xmlFreeNode(new_file); new_file = NULL;
4767 isds_printf_message(context, _("Could not add dmFile child to "
4768 "%s element"), dm_files->name);
4769 err = IE_ERROR;
4770 goto leave;
4773 /* @dmMimeType is required */
4774 if (!document->dmMimeType) {
4775 isds_log_message(context,
4776 _("Document is missing mandatory MIME type definition"));
4777 err = IE_INVAL;
4778 goto leave;
4780 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4782 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4783 if (!string) {
4784 isds_printf_message(context,
4785 _("Document has unknown dmFileMetaType: %ld"),
4786 document->dmFileMetaType);
4787 err = IE_ENUM;
4788 goto leave;
4790 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4792 if (document->dmFileGuid) {
4793 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4795 if (document->dmUpFileGuid) {
4796 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4799 /* @dmFileDescr is required */
4800 if (!document->dmFileDescr) {
4801 isds_log_message(context,
4802 _("Document is missing mandatory description (title)"));
4803 err = IE_INVAL;
4804 goto leave;
4806 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4808 if (document->dmFormat) {
4809 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4813 /* Insert content (body) of the document. */
4814 if (document->is_xml) {
4815 /* XML document requested */
4817 /* Allocate new dmXMLContent */
4818 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4819 if (!xmlcontent) {
4820 isds_printf_message(context,
4821 _("Could not allocate dmXMLContent element"));
4822 err = IE_ERROR;
4823 goto leave;
4825 /* Append it */
4826 node = xmlAddChild(file, xmlcontent);
4827 if (!node) {
4828 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4829 isds_printf_message(context,
4830 _("Could not add dmXMLContent child to %s element"),
4831 file->name);
4832 err = IE_ERROR;
4833 goto leave;
4836 /* Copy non-empty node list */
4837 if (document->xml_node_list) {
4838 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4839 document->xml_node_list);
4840 if (!content) {
4841 isds_printf_message(context,
4842 _("Not enough memory to copy XML document"));
4843 err = IE_NOMEM;
4844 goto leave;
4847 if (!xmlAddChildList(node, content)) {
4848 xmlFreeNodeList(content);
4849 isds_printf_message(context,
4850 _("Error while adding XML document into dmXMLContent"));
4851 err = IE_XML;
4852 goto leave;
4854 /* XXX: We cannot free the content here because it's part of node's
4855 * document since now. It will be freed with it automatically. */
4857 } else {
4858 /* Binary document requested */
4859 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4860 document->data, document->data_length);
4861 if (err) goto leave;
4864 leave:
4865 return err;
4869 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4870 * The copy must be preallocated, the date are just appended into structure.
4871 * @context is ISDS context
4872 * @copy is message copy structure
4873 * @xpath_ctx is XPath context with current node as tMStatus */
4874 static isds_error append_TMStatus(struct isds_ctx *context,
4875 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4876 isds_error err = IE_SUCCESS;
4877 xmlXPathObjectPtr result = NULL;
4878 char *code = NULL, *message = NULL;
4880 if (!context) return IE_INVALID_CONTEXT;
4881 if (!copy || !xpath_ctx) return IE_INVAL;
4883 /* Free old values */
4884 zfree(copy->dmStatus);
4885 zfree(copy->dmID);
4887 /* Get error specific to this copy */
4888 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4889 if (!code) {
4890 isds_log_message(context,
4891 _("Missing isds:dmStatusCode under "
4892 "XSD:tMStatus type element"));
4893 err = IE_ISDS;
4894 goto leave;
4897 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4898 /* This copy failed */
4899 copy->error = IE_ISDS;
4900 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4901 if (message) {
4902 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4903 if (!copy->dmStatus) {
4904 copy->dmStatus = code;
4905 code = NULL;
4907 } else {
4908 copy->dmStatus = code;
4909 code = NULL;
4911 } else {
4912 /* This copy succeeded. In this case only, message ID is valid */
4913 copy->error = IE_SUCCESS;
4915 EXTRACT_STRING("isds:dmID", copy->dmID);
4916 if (!copy->dmID) {
4917 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4918 "but did not returned assigned message ID\n"));
4919 err = IE_ISDS;
4923 leave:
4924 free(code);
4925 free(message);
4926 xmlXPathFreeObject(result);
4927 return err;
4931 /* Insert struct isds_approval data (box approval) into XML tree
4932 * @context is session context
4933 * @approval is libisds structure with approval description. NULL is
4934 * acceptable.
4935 * @parent is XML element to append @approval to */
4936 static isds_error insert_GExtApproval(struct isds_ctx *context,
4937 const struct isds_approval *approval, xmlNodePtr parent) {
4939 isds_error err = IE_SUCCESS;
4940 xmlNodePtr node;
4942 if (!context) return IE_INVALID_CONTEXT;
4943 if (!parent) return IE_INVAL;
4945 if (!approval) return IE_SUCCESS;
4947 /* Build XSD:gExtApproval */
4948 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4949 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4951 leave:
4952 return err;
4956 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4957 * code
4958 * @context is session context
4959 * @service_name is name of SERVICE_DB_ACCESS
4960 * @response is reallocated server SOAP body response as XML document
4961 * @raw_response is reallocated bit stream with response body. Use
4962 * NULL if you don't care
4963 * @raw_response_length is size of @raw_response in bytes
4964 * @code is reallocated ISDS status code
4965 * @status_message is reallocated ISDS status message
4966 * @return error coded from lower layer, context message will be set up
4967 * appropriately. */
4968 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4969 const xmlChar *service_name,
4970 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4971 xmlChar **code, xmlChar **status_message) {
4973 isds_error err = IE_SUCCESS;
4974 char *service_name_locale = NULL;
4975 xmlNodePtr request = NULL, node;
4976 xmlNsPtr isds_ns = NULL;
4978 if (!context) return IE_INVALID_CONTEXT;
4979 if (!service_name) return IE_INVAL;
4980 if (!response || !code || !status_message) return IE_INVAL;
4981 if (!raw_response_length && raw_response) return IE_INVAL;
4983 /* Free output argument */
4984 xmlFreeDoc(*response); *response = NULL;
4985 if (raw_response) zfree(*raw_response);
4986 zfree(*code);
4987 zfree(*status_message);
4990 /* Check if connection is established
4991 * TODO: This check should be done downstairs. */
4992 if (!context->curl) return IE_CONNECTION_CLOSED;
4994 service_name_locale = _isds_utf82locale((char*)service_name);
4995 if (!service_name_locale) {
4996 err = IE_NOMEM;
4997 goto leave;
5000 /* Build request */
5001 request = xmlNewNode(NULL, service_name);
5002 if (!request) {
5003 isds_printf_message(context,
5004 _("Could not build %s request"), service_name_locale);
5005 err = IE_ERROR;
5006 goto leave;
5008 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5009 if(!isds_ns) {
5010 isds_log_message(context, _("Could not create ISDS name space"));
5011 err = IE_ERROR;
5012 goto leave;
5014 xmlSetNs(request, isds_ns);
5017 /* Add XSD:tDummyInput child */
5018 INSERT_STRING(request, "dbDummy", NULL);
5021 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5022 service_name_locale);
5024 /* Send request */
5025 err = _isds(context, SERVICE_DB_ACCESS, request, response,
5026 raw_response, raw_response_length);
5027 xmlFreeNode(request); request = NULL;
5029 if (err) {
5030 isds_log(ILF_ISDS, ILL_DEBUG,
5031 _("Processing ISDS response on %s request failed\n"),
5032 service_name_locale);
5033 goto leave;
5036 /* Check for response status */
5037 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
5038 code, status_message, NULL);
5039 if (err) {
5040 isds_log(ILF_ISDS, ILL_DEBUG,
5041 _("ISDS response on %s request is missing status\n"),
5042 service_name_locale);
5043 goto leave;
5046 /* Request processed, but nothing found */
5047 if (xmlStrcmp(*code, BAD_CAST "0000")) {
5048 char *code_locale = _isds_utf82locale((char*) *code);
5049 char *status_message_locale =
5050 _isds_utf82locale((char*) *status_message);
5051 isds_log(ILF_ISDS, ILL_DEBUG,
5052 _("Server refused %s request (code=%s, message=%s)\n"),
5053 service_name_locale, code_locale, status_message_locale);
5054 isds_log_message(context, status_message_locale);
5055 free(code_locale);
5056 free(status_message_locale);
5057 err = IE_ISDS;
5058 goto leave;
5061 leave:
5062 free(service_name_locale);
5063 xmlFreeNode(request);
5064 return err;
5066 #endif
5069 /* Get data about logged in user and his box.
5070 * @context is session context
5071 * @db_owner_info is reallocated box owner description. It will be freed on
5072 * error.
5073 * @return error code from lower layer, context message will be set up
5074 * appropriately. */
5075 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
5076 struct isds_DbOwnerInfo **db_owner_info) {
5077 isds_error err = IE_SUCCESS;
5078 #if HAVE_LIBCURL
5079 xmlDocPtr response = NULL;
5080 xmlChar *code = NULL, *message = NULL;
5081 xmlXPathContextPtr xpath_ctx = NULL;
5082 xmlXPathObjectPtr result = NULL;
5083 char *string = NULL;
5084 #endif
5086 if (!context) return IE_INVALID_CONTEXT;
5087 zfree(context->long_message);
5088 if (!db_owner_info) return IE_INVAL;
5089 isds_DbOwnerInfo_free(db_owner_info);
5091 #if HAVE_LIBCURL
5092 /* Check if connection is established */
5093 if (!context->curl) return IE_CONNECTION_CLOSED;
5096 /* Do request and check for success */
5097 err = build_send_check_dbdummy_request(context,
5098 BAD_CAST "GetOwnerInfoFromLogin",
5099 &response, NULL, NULL, &code, &message);
5100 if (err) goto leave;
5103 /* Extract data */
5104 /* Prepare structure */
5105 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5106 if (!*db_owner_info) {
5107 err = IE_NOMEM;
5108 goto leave;
5110 xpath_ctx = xmlXPathNewContext(response);
5111 if (!xpath_ctx) {
5112 err = IE_ERROR;
5113 goto leave;
5115 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5116 err = IE_ERROR;
5117 goto leave;
5120 /* Set context node */
5121 result = xmlXPathEvalExpression(BAD_CAST
5122 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5123 if (!result) {
5124 err = IE_ERROR;
5125 goto leave;
5127 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5128 isds_log_message(context, _("Missing dbOwnerInfo element"));
5129 err = IE_ISDS;
5130 goto leave;
5132 if (result->nodesetval->nodeNr > 1) {
5133 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5134 err = IE_ISDS;
5135 goto leave;
5137 xpath_ctx->node = result->nodesetval->nodeTab[0];
5138 xmlXPathFreeObject(result); result = NULL;
5140 /* Extract it */
5141 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5144 leave:
5145 if (err) {
5146 isds_DbOwnerInfo_free(db_owner_info);
5149 free(string);
5150 xmlXPathFreeObject(result);
5151 xmlXPathFreeContext(xpath_ctx);
5153 free(code);
5154 free(message);
5155 xmlFreeDoc(response);
5157 if (!err)
5158 isds_log(ILF_ISDS, ILL_DEBUG,
5159 _("GetOwnerInfoFromLogin request processed by server "
5160 "successfully.\n"));
5161 #else /* not HAVE_LIBCURL */
5162 err = IE_NOTSUP;
5163 #endif
5165 return err;
5169 /* Get data about logged in user. */
5170 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5171 struct isds_DbUserInfo **db_user_info) {
5172 isds_error err = IE_SUCCESS;
5173 #if HAVE_LIBCURL
5174 xmlDocPtr response = NULL;
5175 xmlChar *code = NULL, *message = NULL;
5176 xmlXPathContextPtr xpath_ctx = NULL;
5177 xmlXPathObjectPtr result = NULL;
5178 #endif
5180 if (!context) return IE_INVALID_CONTEXT;
5181 zfree(context->long_message);
5182 if (!db_user_info) return IE_INVAL;
5183 isds_DbUserInfo_free(db_user_info);
5185 #if HAVE_LIBCURL
5186 /* Check if connection is established */
5187 if (!context->curl) return IE_CONNECTION_CLOSED;
5190 /* Do request and check for success */
5191 err = build_send_check_dbdummy_request(context,
5192 BAD_CAST "GetUserInfoFromLogin",
5193 &response, NULL, NULL, &code, &message);
5194 if (err) goto leave;
5197 /* Extract data */
5198 /* Prepare structure */
5199 *db_user_info = calloc(1, sizeof(**db_user_info));
5200 if (!*db_user_info) {
5201 err = IE_NOMEM;
5202 goto leave;
5204 xpath_ctx = xmlXPathNewContext(response);
5205 if (!xpath_ctx) {
5206 err = IE_ERROR;
5207 goto leave;
5209 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5210 err = IE_ERROR;
5211 goto leave;
5214 /* Set context node */
5215 result = xmlXPathEvalExpression(BAD_CAST
5216 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5217 if (!result) {
5218 err = IE_ERROR;
5219 goto leave;
5221 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5222 isds_log_message(context, _("Missing dbUserInfo element"));
5223 err = IE_ISDS;
5224 goto leave;
5226 if (result->nodesetval->nodeNr > 1) {
5227 isds_log_message(context, _("Multiple dbUserInfo element"));
5228 err = IE_ISDS;
5229 goto leave;
5231 xpath_ctx->node = result->nodesetval->nodeTab[0];
5232 xmlXPathFreeObject(result); result = NULL;
5234 /* Extract it */
5235 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5237 leave:
5238 if (err) {
5239 isds_DbUserInfo_free(db_user_info);
5242 xmlXPathFreeObject(result);
5243 xmlXPathFreeContext(xpath_ctx);
5245 free(code);
5246 free(message);
5247 xmlFreeDoc(response);
5249 if (!err)
5250 isds_log(ILF_ISDS, ILL_DEBUG,
5251 _("GetUserInfoFromLogin request processed by server "
5252 "successfully.\n"));
5253 #else /* not HAVE_LIBCURL */
5254 err = IE_NOTSUP;
5255 #endif
5257 return err;
5261 /* Get expiration time of current password
5262 * @context is session context
5263 * @expiration is automatically reallocated time when password expires. If
5264 * password expiration is disabled, NULL will be returned. In case of error
5265 * it will be nulled too. */
5266 isds_error isds_get_password_expiration(struct isds_ctx *context,
5267 struct timeval **expiration) {
5268 isds_error err = IE_SUCCESS;
5269 #if HAVE_LIBCURL
5270 xmlDocPtr response = NULL;
5271 xmlChar *code = NULL, *message = NULL;
5272 xmlXPathContextPtr xpath_ctx = NULL;
5273 xmlXPathObjectPtr result = NULL;
5274 char *string = NULL;
5275 #endif
5277 if (!context) return IE_INVALID_CONTEXT;
5278 zfree(context->long_message);
5279 if (!expiration) return IE_INVAL;
5280 zfree(*expiration);
5282 #if HAVE_LIBCURL
5283 /* Check if connection is established */
5284 if (!context->curl) return IE_CONNECTION_CLOSED;
5287 /* Do request and check for success */
5288 err = build_send_check_dbdummy_request(context,
5289 BAD_CAST "GetPasswordInfo",
5290 &response, NULL, NULL, &code, &message);
5291 if (err) goto leave;
5294 /* Extract data */
5295 xpath_ctx = xmlXPathNewContext(response);
5296 if (!xpath_ctx) {
5297 err = IE_ERROR;
5298 goto leave;
5300 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5301 err = IE_ERROR;
5302 goto leave;
5305 /* Set context node */
5306 result = xmlXPathEvalExpression(BAD_CAST
5307 "/isds:GetPasswordInfoResponse", xpath_ctx);
5308 if (!result) {
5309 err = IE_ERROR;
5310 goto leave;
5312 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5313 isds_log_message(context,
5314 _("Missing GetPasswordInfoResponse element"));
5315 err = IE_ISDS;
5316 goto leave;
5318 if (result->nodesetval->nodeNr > 1) {
5319 isds_log_message(context,
5320 _("Multiple GetPasswordInfoResponse element"));
5321 err = IE_ISDS;
5322 goto leave;
5324 xpath_ctx->node = result->nodesetval->nodeTab[0];
5325 xmlXPathFreeObject(result); result = NULL;
5327 /* Extract expiration date */
5328 EXTRACT_STRING("isds:pswExpDate", string);
5329 if (string) {
5330 /* And convert it if any returned. Otherwise expiration is disabled. */
5331 err = timestring2timeval((xmlChar *) string, expiration);
5332 if (err) {
5333 char *string_locale = _isds_utf82locale(string);
5334 if (err == IE_DATE) err = IE_ISDS;
5335 isds_printf_message(context,
5336 _("Could not convert pswExpDate as ISO time: %s"),
5337 string_locale);
5338 free(string_locale);
5339 goto leave;
5343 leave:
5344 if (err) {
5345 if (*expiration) {
5346 zfree(*expiration);
5350 free(string);
5351 xmlXPathFreeObject(result);
5352 xmlXPathFreeContext(xpath_ctx);
5354 free(code);
5355 free(message);
5356 xmlFreeDoc(response);
5358 if (!err)
5359 isds_log(ILF_ISDS, ILL_DEBUG,
5360 _("GetPasswordInfo request processed by server "
5361 "successfully.\n"));
5362 #else /* not HAVE_LIBCURL */
5363 err = IE_NOTSUP;
5364 #endif
5366 return err;
5370 #if HAVE_LIBCURL
5371 /* Request delivering new TOTP code from ISDS through side channel before
5372 * changing password.
5373 * @context is session context
5374 * @password is current password.
5375 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5376 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5377 * function for more details.
5378 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5379 * NULL, if you don't care.
5380 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5381 * error code. */
5382 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5383 const char *password, struct isds_otp *otp, char **refnumber) {
5384 isds_error err = IE_SUCCESS;
5385 char *saved_url = NULL; /* No copy */
5386 #if HAVE_CURL_REAUTHORIZATION_BUG
5387 CURL *saved_curl = NULL; /* No copy */
5388 #endif
5389 xmlNsPtr isds_ns = NULL;
5390 xmlNodePtr request = NULL;
5391 xmlDocPtr response = NULL;
5392 xmlChar *code = NULL, *message = NULL;
5393 const xmlChar *codes[] = {
5394 BAD_CAST "2300",
5395 BAD_CAST "2301",
5396 BAD_CAST "2302"
5398 const char *meanings[] = {
5399 N_("Unexpected error"),
5400 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5401 N_("One-time code could not been sent. Try later again.")
5403 const isds_otp_resolution resolutions[] = {
5404 OTP_RESOLUTION_UNKNOWN,
5405 OTP_RESOLUTION_TO_FAST,
5406 OTP_RESOLUTION_TOTP_NOT_SENT
5409 if (NULL == context) return IE_INVALID_CONTEXT;
5410 zfree(context->long_message);
5411 if (NULL == password) {
5412 isds_log_message(context,
5413 _("Second argument (password) of isds_change_password() "
5414 "is NULL"));
5415 return IE_INVAL;
5418 /* Check if connection is established
5419 * TODO: This check should be done downstairs. */
5420 if (!context->curl) return IE_CONNECTION_CLOSED;
5422 if (!context->otp) {
5423 isds_log_message(context, _("This function requires OTP-authenticated "
5424 "context"));
5425 return IE_INVALID_CONTEXT;
5427 if (NULL == otp) {
5428 isds_log_message(context, _("If one-time password authentication "
5429 "method is in use, requesting new OTP code requires "
5430 "one-time credentials argument either"));
5431 return IE_INVAL;
5433 if (otp->method != OTP_TIME) {
5434 isds_log_message(context, _("Requesting new time-based OTP code from "
5435 "server requires one-time password authentication "
5436 "method"));
5437 return IE_INVAL;
5439 if (otp->otp_code != NULL) {
5440 isds_log_message(context, _("Requesting new time-based OTP code from "
5441 "server requires undefined OTP code member in "
5442 "one-time credentials argument"));
5443 return IE_INVAL;
5447 /* Build request */
5448 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5449 if (!request) {
5450 isds_log_message(context, _("Could not build SendSMSCode request"));
5451 return IE_ERROR;
5453 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5454 if(!isds_ns) {
5455 isds_log_message(context, _("Could not create ISDS name space"));
5456 xmlFreeNode(request);
5457 return IE_ERROR;
5459 xmlSetNs(request, isds_ns);
5461 /* Change URL temporarily for sending this request only */
5463 char *new_url = NULL;
5464 if ((err = _isds_build_url_from_context(context,
5465 "%.*sasws/changePassword", &new_url))) {
5466 goto leave;
5468 saved_url = context->url;
5469 context->url = new_url;
5472 /* Store credentials for sending this request only */
5473 context->otp_credentials = otp;
5474 _isds_discard_credentials(context, 0);
5475 if ((err = _isds_store_credentials(context, context->saved_username,
5476 password, NULL))) {
5477 _isds_discard_credentials(context, 0);
5478 goto leave;
5480 #if HAVE_CURL_REAUTHORIZATION_BUG
5481 saved_curl = context->curl;
5482 context->curl = curl_easy_init();
5483 if (NULL == context->curl) {
5484 err = IE_ERROR;
5485 goto leave;
5487 if (context->timeout) {
5488 err = isds_set_timeout(context, context->timeout);
5489 if (err) goto leave;
5491 #endif
5493 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5495 /* Sent request */
5496 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5498 /* Remove temporal credentials */
5499 _isds_discard_credentials(context, 0);
5500 /* Detach pointer to OTP credentials from context */
5501 context->otp_credentials = NULL;
5502 /* Keep context->otp true to keep signaling this is OTP session */
5504 /* Destroy request */
5505 xmlFreeNode(request); request = NULL;
5507 if (err) {
5508 isds_log(ILF_ISDS, ILL_DEBUG,
5509 _("Processing ISDS response on SendSMSCode request failed\n"));
5510 goto leave;
5513 /* Check for response status */
5514 err = isds_response_status(context, SERVICE_ASWS, response,
5515 &code, &message, (xmlChar **)refnumber);
5516 if (err) {
5517 isds_log(ILF_ISDS, ILL_DEBUG,
5518 _("ISDS response on SendSMSCode request is missing "
5519 "status\n"));
5520 goto leave;
5523 /* Check for error */
5524 if (xmlStrcmp(code, BAD_CAST "0000")) {
5525 char *code_locale = _isds_utf82locale((char*)code);
5526 char *message_locale = _isds_utf82locale((char*)message);
5527 size_t i;
5528 isds_log(ILF_ISDS, ILL_DEBUG,
5529 _("Server refused to send new code on SendSMSCode "
5530 "request (code=%s, message=%s)\n"),
5531 code_locale, message_locale);
5533 /* Check for known error codes */
5534 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5535 if (!xmlStrcmp(code, codes[i])) break;
5537 if (i < sizeof(codes)/sizeof(*codes)) {
5538 isds_log_message(context, _(meanings[i]));
5539 /* Mimic otp->resolution according to the code, specification does
5540 * prescribe OTP header to be available. */
5541 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5542 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5543 otp->resolution = resolutions[i];
5544 } else
5545 isds_log_message(context, message_locale);
5547 free(code_locale);
5548 free(message_locale);
5550 err = IE_ISDS;
5551 goto leave;
5554 /* Otherwise new code sent successfully */
5555 /* Mimic otp->resolution according to the code, specification does
5556 * prescribe OTP header to be available. */
5557 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5558 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5560 leave:
5561 if (NULL != saved_url) {
5562 /* Revert URL to original one */
5563 zfree(context->url);
5564 context->url = saved_url;
5566 #if HAVE_CURL_REAUTHORIZATION_BUG
5567 if (NULL != saved_curl) {
5568 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5569 context->curl = saved_curl;
5571 #endif
5573 free(code);
5574 free(message);
5575 xmlFreeDoc(response);
5576 xmlFreeNode(request);
5578 if (!err)
5579 isds_log(ILF_ISDS, ILL_DEBUG,
5580 _("New OTP code has been sent successfully on SendSMSCode "
5581 "request.\n"));
5582 return err;
5586 /* Convert response status code to isds_error code and set long message
5587 * @context is context to save long message to
5588 * @map is mapping from codes to errors and messages. Pass NULL for generic
5589 * handling.
5590 * @code is status code to translate
5591 * @message is non-localized status message to put into long message in case
5592 * of uknown error. It can be NULL if server did not provide any.
5593 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5594 * invalid invocation. */
5595 static isds_error statuscode2isds_error(struct isds_ctx *context,
5596 const struct code_map_isds_error *map,
5597 const xmlChar *code, const xmlChar *message) {
5598 if (NULL == code) {
5599 isds_log_message(context,
5600 _("NULL status code passed to statuscode2isds_error()"));
5601 return IE_INVAL;
5604 if (NULL != map) {
5605 /* Check for known error codes */
5606 for (int i=0; map->codes[i] != NULL; i++) {
5607 if (!xmlStrcmp(code, map->codes[i])) {
5608 isds_log_message(context, _(map->meanings[i]));
5609 return map->errors[i];
5614 /* Other error */
5615 if (xmlStrcmp(code, BAD_CAST "0000")) {
5616 char *message_locale = _isds_utf82locale((char*)message);
5617 if (NULL == message_locale)
5618 isds_log_message(context, _("ISDS server returned unknown error"));
5619 else
5620 isds_log_message(context, message_locale);
5621 free(message_locale);
5622 return IE_ISDS;
5625 return IE_SUCCESS;
5627 #endif
5630 /* Change user password in ISDS.
5631 * User must supply old password, new password will takes effect after some
5632 * time, current session can continue. Password must fulfill some constraints.
5633 * @context is session context
5634 * @old_password is current password.
5635 * @new_password is requested new password
5636 * @otp auxiliary data required if one-time password authentication is in use,
5637 * defines OTP code (if known) and returns fine grade resolution of OTP
5638 * procedure. Pass NULL, if one-time password authentication is not needed.
5639 * Please note the @otp argument must match OTP method used at log-in time. See
5640 * isds_login() function for more details.
5641 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5642 * NULL, if you don't care.
5643 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5644 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5645 * awaiting OTP code that has been delivered by side channel to the user. */
5646 isds_error isds_change_password(struct isds_ctx *context,
5647 const char *old_password, const char *new_password,
5648 struct isds_otp *otp, char **refnumber) {
5649 isds_error err = IE_SUCCESS;
5650 #if HAVE_LIBCURL
5651 char *saved_url = NULL; /* No copy */
5652 #if HAVE_CURL_REAUTHORIZATION_BUG
5653 CURL *saved_curl = NULL; /* No copy */
5654 #endif
5655 xmlNsPtr isds_ns = NULL;
5656 xmlNodePtr request = NULL, node;
5657 xmlDocPtr response = NULL;
5658 xmlChar *code = NULL, *message = NULL;
5659 const xmlChar *codes[] = {
5660 BAD_CAST "1066",
5661 BAD_CAST "1067",
5662 BAD_CAST "1079",
5663 BAD_CAST "1080",
5664 BAD_CAST "1081",
5665 BAD_CAST "1082",
5666 BAD_CAST "1083",
5667 BAD_CAST "1090",
5668 BAD_CAST "1091",
5669 BAD_CAST "2300",
5670 BAD_CAST "9204"
5672 const char *meanings[] = {
5673 N_("Password length must be between 8 and 32 characters"),
5674 N_("Password cannot be reused"), /* Server does not distinguish 1067
5675 and 1091 on ChangePasswordOTP */
5676 N_("Password contains forbidden character"),
5677 N_("Password must contain at least one upper-case letter, "
5678 "one lower-case, and one digit"),
5679 N_("Password cannot contain sequence of three identical characters"),
5680 N_("Password cannot contain user identifier"),
5681 N_("Password is too simmple"),
5682 N_("Old password is not valid"),
5683 N_("Password cannot be reused"),
5684 N_("Unexpected error"),
5685 N_("LDAP update error")
5687 #endif
5689 if (!context) return IE_INVALID_CONTEXT;
5690 zfree(context->long_message);
5691 if (NULL != refnumber)
5692 zfree(*refnumber);
5693 if (NULL == old_password) {
5694 isds_log_message(context,
5695 _("Second argument (old password) of isds_change_password() "
5696 "is NULL"));
5697 return IE_INVAL;
5699 if (NULL == otp && NULL == new_password) {
5700 isds_log_message(context,
5701 _("Third argument (new password) of isds_change_password() "
5702 "is NULL"));
5703 return IE_INVAL;
5706 #if HAVE_LIBCURL
5707 /* Check if connection is established
5708 * TODO: This check should be done downstairs. */
5709 if (!context->curl) return IE_CONNECTION_CLOSED;
5711 if (context->otp && NULL == otp) {
5712 isds_log_message(context, _("If one-time password authentication "
5713 "method is in use, changing password requires one-time "
5714 "credentials either"));
5715 return IE_INVAL;
5718 /* Build ChangeISDSPassword request */
5719 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5720 BAD_CAST "ChangePasswordOTP");
5721 if (!request) {
5722 isds_log_message(context, (NULL == otp) ?
5723 _("Could not build ChangeISDSPassword request") :
5724 _("Could not build ChangePasswordOTP request"));
5725 return IE_ERROR;
5727 isds_ns = xmlNewNs(request,
5728 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5729 NULL);
5730 if(!isds_ns) {
5731 isds_log_message(context, _("Could not create ISDS name space"));
5732 xmlFreeNode(request);
5733 return IE_ERROR;
5735 xmlSetNs(request, isds_ns);
5737 INSERT_STRING(request, "dbOldPassword", old_password);
5738 INSERT_STRING(request, "dbNewPassword", new_password);
5740 if (NULL != otp) {
5741 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5742 switch (otp->method) {
5743 case OTP_HMAC:
5744 isds_log(ILF_SEC, ILL_INFO,
5745 _("Selected authentication method: "
5746 "HMAC-based one-time password\n"));
5747 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5748 break;
5749 case OTP_TIME:
5750 isds_log(ILF_SEC, ILL_INFO,
5751 _("Selected authentication method: "
5752 "Time-based one-time password\n"));
5753 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5754 if (otp->otp_code == NULL) {
5755 isds_log(ILF_SEC, ILL_INFO,
5756 _("OTP code has not been provided by "
5757 "application, requesting server for "
5758 "new one.\n"));
5759 err = _isds_request_totp_code(context, old_password, otp,
5760 refnumber);
5761 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5762 goto leave;
5764 } else {
5765 isds_log(ILF_SEC, ILL_INFO,
5766 _("OTP code has been provided by "
5767 "application, not requesting server "
5768 "for new one.\n"));
5770 break;
5771 default:
5772 isds_log_message(context,
5773 _("Unknown one-time password authentication "
5774 "method requested by application"));
5775 err = IE_ENUM;
5776 goto leave;
5779 /* Change URL temporarily for sending this request only */
5781 char *new_url = NULL;
5782 if ((err = _isds_build_url_from_context(context,
5783 "%.*sasws/changePassword", &new_url))) {
5784 goto leave;
5786 saved_url = context->url;
5787 context->url = new_url;
5790 /* Store credentials for sending this request only */
5791 context->otp_credentials = otp;
5792 _isds_discard_credentials(context, 0);
5793 if ((err = _isds_store_credentials(context, context->saved_username,
5794 old_password, NULL))) {
5795 _isds_discard_credentials(context, 0);
5796 goto leave;
5798 #if HAVE_CURL_REAUTHORIZATION_BUG
5799 saved_curl = context->curl;
5800 context->curl = curl_easy_init();
5801 if (NULL == context->curl) {
5802 err = IE_ERROR;
5803 goto leave;
5805 if (context->timeout) {
5806 err = isds_set_timeout(context, context->timeout);
5807 if (err) goto leave;
5809 #endif
5812 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5813 _("Sending ChangeISDSPassword request to ISDS\n") :
5814 _("Sending ChangePasswordOTP request to ISDS\n"));
5816 /* Sent request */
5817 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5818 request, &response, NULL, NULL);
5820 if (otp) {
5821 /* Remove temporal credentials */
5822 _isds_discard_credentials(context, 0);
5823 /* Detach pointer to OTP credentials from context */
5824 context->otp_credentials = NULL;
5825 /* Keep context->otp true to keep signaling this is OTP session */
5828 /* Destroy request */
5829 xmlFreeNode(request); request = NULL;
5831 if (err) {
5832 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5833 _("Processing ISDS response on ChangeISDSPassword "
5834 "request failed\n") :
5835 _("Processing ISDS response on ChangePasswordOTP "
5836 "request failed\n"));
5837 goto leave;
5840 /* Check for response status */
5841 err = isds_response_status(context,
5842 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5843 &code, &message, (xmlChar **)refnumber);
5844 if (err) {
5845 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5846 _("ISDS response on ChangeISDSPassword request is missing "
5847 "status\n") :
5848 _("ISDS response on ChangePasswordOTP request is missing "
5849 "status\n"));
5850 goto leave;
5853 /* Check for known error codes */
5854 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5855 if (!xmlStrcmp(code, codes[i])) {
5856 char *code_locale = _isds_utf82locale((char*)code);
5857 char *message_locale = _isds_utf82locale((char*)message);
5858 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5859 _("Server refused to change password on ChangeISDSPassword "
5860 "request (code=%s, message=%s)\n") :
5861 _("Server refused to change password on ChangePasswordOTP "
5862 "request (code=%s, message=%s)\n"),
5863 code_locale, message_locale);
5864 free(code_locale);
5865 free(message_locale);
5866 isds_log_message(context, _(meanings[i]));
5867 err = IE_INVAL;
5868 goto leave;
5872 /* Other error */
5873 if (xmlStrcmp(code, BAD_CAST "0000")) {
5874 char *code_locale = _isds_utf82locale((char*)code);
5875 char *message_locale = _isds_utf82locale((char*)message);
5876 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5877 _("Server refused to change password on ChangeISDSPassword "
5878 "request (code=%s, message=%s)\n") :
5879 _("Server refused to change password on ChangePasswordOTP "
5880 "request (code=%s, message=%s)\n"),
5881 code_locale, message_locale);
5882 isds_log_message(context, message_locale);
5883 free(code_locale);
5884 free(message_locale);
5885 err = IE_ISDS;
5886 goto leave;
5889 /* Otherwise password changed successfully */
5891 leave:
5892 if (NULL != saved_url) {
5893 /* Revert URL to original one */
5894 zfree(context->url);
5895 context->url = saved_url;
5897 #if HAVE_CURL_REAUTHORIZATION_BUG
5898 if (NULL != saved_curl) {
5899 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5900 context->curl = saved_curl;
5902 #endif
5904 free(code);
5905 free(message);
5906 xmlFreeDoc(response);
5907 xmlFreeNode(request);
5909 if (!err)
5910 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5911 _("Password changed successfully on ChangeISDSPassword "
5912 "request.\n") :
5913 _("Password changed successfully on ChangePasswordOTP "
5914 "request.\n"));
5915 #else /* not HAVE_LIBCURL */
5916 err = IE_NOTSUP;
5917 #endif
5919 return err;
5923 #if HAVE_LIBCURL
5924 /* Generic middle part with request sending and response check.
5925 * It sends prepared request and checks for error code.
5926 * @context is ISDS session context.
5927 * @service is ISDS service handler
5928 * @service_name is name in scope of given @service
5929 * @request is XML tree with request. Will be freed to save memory.
5930 * @response is XML document outputting ISDS response.
5931 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5932 * @map is mapping from status code to library error. Pass NULL if no special
5933 * handling is requested.
5934 * NULL, if you don't care. */
5935 static isds_error send_destroy_request_check_response(
5936 struct isds_ctx *context,
5937 const isds_service service, const xmlChar *service_name,
5938 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5939 const struct code_map_isds_error *map) {
5940 isds_error err = IE_SUCCESS;
5941 char *service_name_locale = NULL;
5942 xmlChar *code = NULL, *message = NULL;
5945 if (!context) return IE_INVALID_CONTEXT;
5946 if (!service_name || *service_name == '\0' || !request || !*request ||
5947 !response)
5948 return IE_INVAL;
5950 /* Check if connection is established
5951 * TODO: This check should be done downstairs. */
5952 if (!context->curl) return IE_CONNECTION_CLOSED;
5954 service_name_locale = _isds_utf82locale((char*) service_name);
5955 if (!service_name_locale) {
5956 err = IE_NOMEM;
5957 goto leave;
5960 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5961 service_name_locale);
5963 /* Send request */
5964 err = _isds(context, service, *request, response, NULL, NULL);
5965 xmlFreeNode(*request); *request = NULL;
5967 if (err) {
5968 isds_log(ILF_ISDS, ILL_DEBUG,
5969 _("Processing ISDS response on %s request failed\n"),
5970 service_name_locale);
5971 goto leave;
5974 /* Check for response status */
5975 err = isds_response_status(context, service, *response,
5976 &code, &message, refnumber);
5977 if (err) {
5978 isds_log(ILF_ISDS, ILL_DEBUG,
5979 _("ISDS response on %s request is missing status\n"),
5980 service_name_locale);
5981 goto leave;
5984 err = statuscode2isds_error(context, map, code, message);
5986 /* Request processed, but server failed */
5987 if (xmlStrcmp(code, BAD_CAST "0000")) {
5988 char *code_locale = _isds_utf82locale((char*) code);
5989 char *message_locale = _isds_utf82locale((char*) message);
5990 isds_log(ILF_ISDS, ILL_DEBUG,
5991 _("Server refused %s request (code=%s, message=%s)\n"),
5992 service_name_locale, code_locale, message_locale);
5993 free(code_locale);
5994 free(message_locale);
5995 goto leave;
5999 leave:
6000 free(code);
6001 free(message);
6002 if (err && *response) {
6003 xmlFreeDoc(*response);
6004 *response = NULL;
6006 if (*request) {
6007 xmlFreeNode(*request);
6008 *request = NULL;
6010 free(service_name_locale);
6012 return err;
6016 /* Generic bottom half with request sending.
6017 * It sends prepared request, checks for error code, destroys response and
6018 * request and log success or failure.
6019 * @context is ISDS session context.
6020 * @service is ISDS service handler
6021 * @service_name is name in scope of given @service
6022 * @request is XML tree with request. Will be freed to save memory.
6023 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6024 * NULL, if you don't care. */
6025 static isds_error send_request_check_drop_response(
6026 struct isds_ctx *context,
6027 const isds_service service, const xmlChar *service_name,
6028 xmlNodePtr *request, xmlChar **refnumber) {
6029 isds_error err = IE_SUCCESS;
6030 xmlDocPtr response = NULL;
6033 if (!context) return IE_INVALID_CONTEXT;
6034 if (!service_name || *service_name == '\0' || !request || !*request)
6035 return IE_INVAL;
6037 /* Send request and check response*/
6038 err = send_destroy_request_check_response(context,
6039 service, service_name, request, &response, refnumber, NULL);
6041 xmlFreeDoc(response);
6043 if (*request) {
6044 xmlFreeNode(*request);
6045 *request = NULL;
6048 if (!err) {
6049 char *service_name_locale = _isds_utf82locale((char *) service_name);
6050 isds_log(ILF_ISDS, ILL_DEBUG,
6051 _("%s request processed by server successfully.\n"),
6052 service_name_locale);
6053 free(service_name_locale);
6056 return err;
6060 /* Insert isds_credentials_delivery structure into XML request if not NULL
6061 * @context is session context
6062 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6063 * credentials delivery. The email field is passed.
6064 * @parent is XML element where to insert */
6065 static isds_error insert_credentials_delivery(struct isds_ctx *context,
6066 const struct isds_credentials_delivery *credentials_delivery,
6067 xmlNodePtr parent) {
6068 isds_error err = IE_SUCCESS;
6069 xmlNodePtr node;
6071 if (!context) return IE_INVALID_CONTEXT;
6072 if (!parent) return IE_INVAL;
6074 if (credentials_delivery) {
6075 /* Following elements are valid only for services:
6076 * NewAccessData, AddDataBoxUser, CreateDataBox */
6077 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
6078 INSERT_STRING(parent, "email", credentials_delivery->email);
6081 leave:
6082 return err;
6086 /* Extract credentials delivery from ISDS response.
6087 * @context is session context
6088 * @credentials_delivery is pointer to valid structure to fill in returned
6089 * user's password (and new log-in name). If NULL, do not extract the data.
6090 * @response is pointer to XML document with ISDS response
6091 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6092 * @return IE_SUCCESS even if new user name has not been found because it's not
6093 * clear whether it's returned always. */
6094 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6095 struct isds_credentials_delivery *credentials_delivery,
6096 xmlDocPtr response, const char *request_name) {
6097 isds_error err = IE_SUCCESS;
6098 xmlXPathContextPtr xpath_ctx = NULL;
6099 xmlXPathObjectPtr result = NULL;
6100 char *xpath_query = NULL;
6102 if (!context) return IE_INVALID_CONTEXT;
6103 if (credentials_delivery) {
6104 zfree(credentials_delivery->token);
6105 zfree(credentials_delivery->new_user_name);
6107 if (!response || !request_name || !*request_name) return IE_INVAL;
6110 /* Extract optional token */
6111 if (credentials_delivery) {
6112 xpath_ctx = xmlXPathNewContext(response);
6113 if (!xpath_ctx) {
6114 err = IE_ERROR;
6115 goto leave;
6117 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6118 err = IE_ERROR;
6119 goto leave;
6122 /* Verify root element */
6123 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6124 request_name)) {
6125 err = IE_NOMEM;
6126 goto leave;
6128 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6129 if (!result) {
6130 err = IE_ERROR;
6131 goto leave;
6133 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6134 char *request_name_locale = _isds_utf82locale(request_name);
6135 isds_log(ILF_ISDS, ILL_WARNING,
6136 _("Wrong element in ISDS response for %s request "
6137 "while extracting credentials delivery details\n"),
6138 request_name_locale);
6139 free(request_name_locale);
6140 err = IE_ERROR;
6141 goto leave;
6143 xpath_ctx->node = result->nodesetval->nodeTab[0];
6146 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6147 * optional. */
6148 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6150 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6151 if (!credentials_delivery->token) {
6152 char *request_name_locale = _isds_utf82locale(request_name);
6153 isds_log(ILF_ISDS, ILL_ERR,
6154 _("ISDS did not return token on %s request "
6155 "even if requested\n"), request_name_locale);
6156 free(request_name_locale);
6157 err = IE_ERROR;
6161 leave:
6162 free(xpath_query);
6163 xmlXPathFreeObject(result);
6164 xmlXPathFreeContext(xpath_ctx);
6166 return err;
6170 /* Build XSD:tCreateDBInput request type for box creating.
6171 * @context is session context
6172 * @request outputs built XML tree
6173 * @service_name is request name of SERVICE_DB_MANIPULATION service
6174 * @box is box description to create including single primary user (in case of
6175 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6176 * ignored.
6177 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6178 * box, or contact address of PFO box owner)
6179 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6180 * @upper_box_id is optional ID of supper box if currently created box is
6181 * subordinated.
6182 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6183 * don't care.
6184 * @credentials_delivery is valid pointer if ISDS should return token that box
6185 * owner can use to obtain his new credentials in on-line way. Then valid email
6186 * member value should be supplied.
6187 * @approval is optional external approval of box manipulation */
6188 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6189 xmlNodePtr *request, const xmlChar *service_name,
6190 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6191 const xmlChar *former_names, const xmlChar *upper_box_id,
6192 const xmlChar *ceo_label,
6193 const struct isds_credentials_delivery *credentials_delivery,
6194 const struct isds_approval *approval) {
6195 isds_error err = IE_SUCCESS;
6196 xmlNsPtr isds_ns = NULL;
6197 xmlNodePtr node, dbPrimaryUsers;
6198 xmlChar *string = NULL;
6199 const struct isds_list *item;
6202 if (!context) return IE_INVALID_CONTEXT;
6203 if (!request || !service_name || service_name[0] == '\0' || !box)
6204 return IE_INVAL;
6207 /* Build CreateDataBox-similar request */
6208 *request = xmlNewNode(NULL, service_name);
6209 if (!*request) {
6210 char *service_name_locale = _isds_utf82locale((char*) service_name);
6211 isds_printf_message(context, _("Could build %s request"),
6212 service_name_locale);
6213 free(service_name_locale);
6214 return IE_ERROR;
6216 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6217 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6218 if (!isds_ns) {
6219 isds_log_message(context, _("Could not create ISDS1 name space"));
6220 xmlFreeNode(*request);
6221 return IE_ERROR;
6223 } else {
6224 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6225 if (!isds_ns) {
6226 isds_log_message(context, _("Could not create ISDS name space"));
6227 xmlFreeNode(*request);
6228 return IE_ERROR;
6231 xmlSetNs(*request, isds_ns);
6233 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6234 err = insert_DbOwnerInfo(context, box, 0, node);
6235 if (err) goto leave;
6237 /* Insert users */
6238 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6239 * verbose documentation allows none dbUserInfo */
6240 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6241 for (item = users; item; item = item->next) {
6242 if (item->data) {
6243 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6244 err = insert_DbUserInfo(context,
6245 (struct isds_DbUserInfo *) item->data, 1, node);
6246 if (err) goto leave;
6250 INSERT_STRING(*request, "dbFormerNames", former_names);
6251 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6252 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6254 err = insert_credentials_delivery(context, credentials_delivery, *request);
6255 if (err) goto leave;
6257 err = insert_GExtApproval(context, approval, *request);
6258 if (err) goto leave;
6260 leave:
6261 if (err) {
6262 xmlFreeNode(*request);
6263 *request = NULL;
6265 free(string);
6266 return err;
6268 #endif /* HAVE_LIBCURL */
6271 /* Create new box.
6272 * @context is session context
6273 * @box is box description to create including single primary user (in case of
6274 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6275 * ignored. It outputs box ID assigned by ISDS in dbID element.
6276 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6277 * box, or contact address of PFO box owner)
6278 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6279 * @upper_box_id is optional ID of supper box if currently created box is
6280 * subordinated.
6281 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6282 * @credentials_delivery is NULL if new password should be delivered off-line
6283 * to box owner. It is valid pointer if owner should obtain new password on-line
6284 * on dedicated web server. Then input @credentials_delivery.email value is
6285 * his e-mail address he must provide to dedicated web server together
6286 * with output reallocated @credentials_delivery.token member. Output
6287 * member @credentials_delivery.new_user_name is unused up on this call.
6288 * @approval is optional external approval of box manipulation
6289 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6290 * NULL, if you don't care.*/
6291 isds_error isds_add_box(struct isds_ctx *context,
6292 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6293 const char *former_names, const char *upper_box_id,
6294 const char *ceo_label,
6295 struct isds_credentials_delivery *credentials_delivery,
6296 const struct isds_approval *approval, char **refnumber) {
6297 isds_error err = IE_SUCCESS;
6298 #if HAVE_LIBCURL
6299 xmlNodePtr request = NULL;
6300 xmlDocPtr response = NULL;
6301 xmlXPathContextPtr xpath_ctx = NULL;
6302 xmlXPathObjectPtr result = NULL;
6303 #endif
6306 if (!context) return IE_INVALID_CONTEXT;
6307 zfree(context->long_message);
6308 if (credentials_delivery) {
6309 zfree(credentials_delivery->token);
6310 zfree(credentials_delivery->new_user_name);
6312 if (!box) return IE_INVAL;
6314 #if HAVE_LIBCURL
6315 /* Scratch box ID */
6316 zfree(box->dbID);
6318 /* Build CreateDataBox request */
6319 err = build_CreateDBInput_request(context,
6320 &request, BAD_CAST "CreateDataBox",
6321 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6322 (xmlChar *) ceo_label, credentials_delivery, approval);
6323 if (err) goto leave;
6325 /* Send it to server and process response */
6326 err = send_destroy_request_check_response(context,
6327 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6328 &response, (xmlChar **) refnumber, NULL);
6330 /* Extract box ID */
6331 xpath_ctx = xmlXPathNewContext(response);
6332 if (!xpath_ctx) {
6333 err = IE_ERROR;
6334 goto leave;
6336 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6337 err = IE_ERROR;
6338 goto leave;
6340 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6342 /* Extract optional token */
6343 err = extract_credentials_delivery(context, credentials_delivery, response,
6344 "CreateDataBox");
6346 leave:
6347 xmlXPathFreeObject(result);
6348 xmlXPathFreeContext(xpath_ctx);
6349 xmlFreeDoc(response);
6350 xmlFreeNode(request);
6352 if (!err) {
6353 isds_log(ILF_ISDS, ILL_DEBUG,
6354 _("CreateDataBox request processed by server successfully.\n"));
6356 #else /* not HAVE_LIBCURL */
6357 err = IE_NOTSUP;
6358 #endif
6360 return err;
6364 /* Notify ISDS about new PFO entity.
6365 * This function has no real effect.
6366 * @context is session context
6367 * @box is PFO description including single primary user. aifoIsds,
6368 * address->adCode, address->adDistrict members are ignored.
6369 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6370 * @former_names is optional undocumented string. Pass NULL if you don't care.
6371 * @upper_box_id is optional ID of supper box if currently created box is
6372 * subordinated.
6373 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6374 * @approval is optional external approval of box manipulation
6375 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6376 * NULL, if you don't care.*/
6377 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6378 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6379 const char *former_names, const char *upper_box_id,
6380 const char *ceo_label, const struct isds_approval *approval,
6381 char **refnumber) {
6382 isds_error err = IE_SUCCESS;
6383 #if HAVE_LIBCURL
6384 xmlNodePtr request = NULL;
6385 #endif
6387 if (!context) return IE_INVALID_CONTEXT;
6388 zfree(context->long_message);
6389 if (!box) return IE_INVAL;
6391 #if HAVE_LIBCURL
6392 /* Build CreateDataBoxPFOInfo request */
6393 err = build_CreateDBInput_request(context,
6394 &request, BAD_CAST "CreateDataBoxPFOInfo",
6395 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6396 (xmlChar *) ceo_label, NULL, approval);
6397 if (err) goto leave;
6399 /* Send it to server and process response */
6400 err = send_request_check_drop_response(context,
6401 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6402 (xmlChar **) refnumber);
6403 /* XXX: XML Schema names output dbID element but textual documentation
6404 * states no box identifier is returned. */
6405 leave:
6406 xmlFreeNode(request);
6407 #else /* not HAVE_LIBCURL */
6408 err = IE_NOTSUP;
6409 #endif
6410 return err;
6414 /* Common implementation for removing given box.
6415 * @context is session context
6416 * @service_name is UTF-8 encoded name fo ISDS service
6417 * @box is box description to delete. aifoIsds, address->adCode,
6418 * address->adDistrict members are ignored.
6419 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6420 * carry sane value. If NULL, do not inject this information into request.
6421 * @approval is optional external approval of box manipulation
6422 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6423 * NULL, if you don't care.*/
6424 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6425 const xmlChar *service_name,
6426 const struct isds_DbOwnerInfo *box, const struct tm *since,
6427 const struct isds_approval *approval, char **refnumber) {
6428 isds_error err = IE_SUCCESS;
6429 #if HAVE_LIBCURL
6430 xmlNsPtr isds_ns = NULL;
6431 xmlNodePtr request = NULL;
6432 xmlNodePtr node;
6433 xmlChar *string = NULL;
6434 #endif
6437 if (!context) return IE_INVALID_CONTEXT;
6438 zfree(context->long_message);
6439 if (!service_name || !*service_name || !box) return IE_INVAL;
6442 #if HAVE_LIBCURL
6443 /* Build DeleteDataBox(Promptly) request */
6444 request = xmlNewNode(NULL, service_name);
6445 if (!request) {
6446 char *service_name_locale = _isds_utf82locale((char*)service_name);
6447 isds_printf_message(context,
6448 _("Could build %s request"), service_name_locale);
6449 free(service_name_locale);
6450 return IE_ERROR;
6452 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6453 if(!isds_ns) {
6454 isds_log_message(context, _("Could not create ISDS name space"));
6455 xmlFreeNode(request);
6456 return IE_ERROR;
6458 xmlSetNs(request, isds_ns);
6460 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6461 err = insert_DbOwnerInfo(context, box, 0, node);
6462 if (err) goto leave;
6464 if (since) {
6465 err = tm2datestring(since, &string);
6466 if (err) {
6467 isds_log_message(context,
6468 _("Could not convert `since' argument to ISO date string"));
6469 goto leave;
6471 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6472 zfree(string);
6475 err = insert_GExtApproval(context, approval, request);
6476 if (err) goto leave;
6479 /* Send it to server and process response */
6480 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6481 service_name, &request, (xmlChar **) refnumber);
6483 leave:
6484 xmlFreeNode(request);
6485 free(string);
6486 #else /* not HAVE_LIBCURL */
6487 err = IE_NOTSUP;
6488 #endif
6489 return err;
6493 /* Remove given box permanently.
6494 * @context is session context
6495 * @box is box description to delete. aifoIsds, address->adCode,
6496 * address->adDistrict members are ignored.
6497 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6498 * carry sane value.
6499 * @approval is optional external approval of box manipulation
6500 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6501 * NULL, if you don't care.*/
6502 isds_error isds_delete_box(struct isds_ctx *context,
6503 const struct isds_DbOwnerInfo *box, const struct tm *since,
6504 const struct isds_approval *approval, char **refnumber) {
6505 if (!context) return IE_INVALID_CONTEXT;
6506 zfree(context->long_message);
6507 if (!box || !since) return IE_INVAL;
6509 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6510 box, since, approval, refnumber);
6514 /* Undocumented function.
6515 * @context is session context
6516 * @box is box description to delete. aifoIsds, address->adCode,
6517 * address->adDistrict members are ignored.
6518 * @approval is optional external approval of box manipulation
6519 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6520 * NULL, if you don't care.*/
6521 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6522 const struct isds_DbOwnerInfo *box,
6523 const struct isds_approval *approval, char **refnumber) {
6524 if (!context) return IE_INVALID_CONTEXT;
6525 zfree(context->long_message);
6526 if (!box) return IE_INVAL;
6528 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6529 box, NULL, approval, refnumber);
6533 /* Update data about given box.
6534 * @context is session context
6535 * @old_box current box description. aifoIsds, address->adCode,
6536 * address->adDistrict members are ignored.
6537 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6538 * address->adDistrict members are ignored.
6539 * @approval is optional external approval of box manipulation
6540 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6541 * NULL, if you don't care.*/
6542 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6543 const struct isds_DbOwnerInfo *old_box,
6544 const struct isds_DbOwnerInfo *new_box,
6545 const struct isds_approval *approval, char **refnumber) {
6546 isds_error err = IE_SUCCESS;
6547 #if HAVE_LIBCURL
6548 xmlNsPtr isds_ns = NULL;
6549 xmlNodePtr request = NULL;
6550 xmlNodePtr node;
6551 #endif
6554 if (!context) return IE_INVALID_CONTEXT;
6555 zfree(context->long_message);
6556 if (!old_box || !new_box) return IE_INVAL;
6559 #if HAVE_LIBCURL
6560 /* Build UpdateDataBoxDescr request */
6561 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6562 if (!request) {
6563 isds_log_message(context,
6564 _("Could build UpdateDataBoxDescr request"));
6565 return IE_ERROR;
6567 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6568 if(!isds_ns) {
6569 isds_log_message(context, _("Could not create ISDS name space"));
6570 xmlFreeNode(request);
6571 return IE_ERROR;
6573 xmlSetNs(request, isds_ns);
6575 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6576 err = insert_DbOwnerInfo(context, old_box, 0, node);
6577 if (err) goto leave;
6579 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6580 err = insert_DbOwnerInfo(context, new_box, 0, node);
6581 if (err) goto leave;
6583 err = insert_GExtApproval(context, approval, request);
6584 if (err) goto leave;
6587 /* Send it to server and process response */
6588 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6589 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6591 leave:
6592 xmlFreeNode(request);
6593 #else /* not HAVE_LIBCURL */
6594 err = IE_NOTSUP;
6595 #endif
6597 return err;
6601 #if HAVE_LIBCURL
6602 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6603 * code
6604 * @context is session context
6605 * @service is SOAP service
6606 * @service_name is name of request in @service
6607 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6608 * @box_id is box ID of interest
6609 * @approval is optional external approval of box manipulation
6610 * @response is server SOAP body response as XML document
6611 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6612 * NULL, if you don't care.
6613 * @return error coded from lower layer, context message will be set up
6614 * appropriately. */
6615 static isds_error build_send_dbid_request_check_response(
6616 struct isds_ctx *context, const isds_service service,
6617 const xmlChar *service_name, const xmlChar *box_id_element,
6618 const xmlChar *box_id, const struct isds_approval *approval,
6619 xmlDocPtr *response, xmlChar **refnumber) {
6621 isds_error err = IE_SUCCESS;
6622 char *service_name_locale = NULL, *box_id_locale = NULL;
6623 xmlNodePtr request = NULL, node;
6624 xmlNsPtr isds_ns = NULL;
6626 if (!context) return IE_INVALID_CONTEXT;
6627 if (!service_name || !box_id) return IE_INVAL;
6628 if (!response) return IE_INVAL;
6630 /* Free output argument */
6631 xmlFreeDoc(*response); *response = NULL;
6633 /* Prepare strings */
6634 service_name_locale = _isds_utf82locale((char*)service_name);
6635 if (!service_name_locale) {
6636 err = IE_NOMEM;
6637 goto leave;
6639 box_id_locale = _isds_utf82locale((char*)box_id);
6640 if (!box_id_locale) {
6641 err = IE_NOMEM;
6642 goto leave;
6645 /* Build request */
6646 request = xmlNewNode(NULL, service_name);
6647 if (!request) {
6648 isds_printf_message(context,
6649 _("Could not build %s request for %s box"), service_name_locale,
6650 box_id_locale);
6651 err = IE_ERROR;
6652 goto leave;
6654 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6655 if(!isds_ns) {
6656 isds_log_message(context, _("Could not create ISDS name space"));
6657 err = IE_ERROR;
6658 goto leave;
6660 xmlSetNs(request, isds_ns);
6662 /* Add XSD:tIdDbInput children */
6663 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6664 INSERT_STRING(request, box_id_element, box_id);
6665 err = insert_GExtApproval(context, approval, request);
6666 if (err) goto leave;
6668 /* Send request and check response*/
6669 err = send_destroy_request_check_response(context,
6670 service, service_name, &request, response, refnumber, NULL);
6672 leave:
6673 free(service_name_locale);
6674 free(box_id_locale);
6675 xmlFreeNode(request);
6676 return err;
6678 #endif /* HAVE_LIBCURL */
6681 /* Get data about all users assigned to given box.
6682 * @context is session context
6683 * @box_id is box ID
6684 * @users is automatically reallocated list of struct isds_DbUserInfo */
6685 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6686 struct isds_list **users) {
6687 isds_error err = IE_SUCCESS;
6688 #if HAVE_LIBCURL
6689 xmlDocPtr response = NULL;
6690 xmlXPathContextPtr xpath_ctx = NULL;
6691 xmlXPathObjectPtr result = NULL;
6692 int i;
6693 struct isds_list *item, *prev_item = NULL;
6694 #endif
6696 if (!context) return IE_INVALID_CONTEXT;
6697 zfree(context->long_message);
6698 if (!users || !box_id) return IE_INVAL;
6699 isds_list_free(users);
6702 #if HAVE_LIBCURL
6703 /* Do request and check for success */
6704 err = build_send_dbid_request_check_response(context,
6705 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6706 BAD_CAST box_id, NULL, &response, NULL);
6707 if (err) goto leave;
6710 /* Extract data */
6711 /* Prepare structure */
6712 xpath_ctx = xmlXPathNewContext(response);
6713 if (!xpath_ctx) {
6714 err = IE_ERROR;
6715 goto leave;
6717 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6718 err = IE_ERROR;
6719 goto leave;
6722 /* Set context node */
6723 result = xmlXPathEvalExpression(BAD_CAST
6724 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6725 xpath_ctx);
6726 if (!result) {
6727 err = IE_ERROR;
6728 goto leave;
6730 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6731 /* Iterate over all users */
6732 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6734 /* Prepare structure */
6735 item = calloc(1, sizeof(*item));
6736 if (!item) {
6737 err = IE_NOMEM;
6738 goto leave;
6740 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6741 if (i == 0) *users = item;
6742 else prev_item->next = item;
6743 prev_item = item;
6745 /* Extract it */
6746 xpath_ctx->node = result->nodesetval->nodeTab[i];
6747 err = extract_DbUserInfo(context,
6748 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6749 if (err) goto leave;
6753 leave:
6754 if (err) {
6755 isds_list_free(users);
6758 xmlXPathFreeObject(result);
6759 xmlXPathFreeContext(xpath_ctx);
6760 xmlFreeDoc(response);
6762 if (!err)
6763 isds_log(ILF_ISDS, ILL_DEBUG,
6764 _("GetDataBoxUsers request processed by server "
6765 "successfully.\n"));
6766 #else /* not HAVE_LIBCURL */
6767 err = IE_NOTSUP;
6768 #endif
6770 return err;
6774 /* Update data about user assigned to given box.
6775 * @context is session context
6776 * @box is box identification. aifoIsds, address->adCode,
6777 * address->adDistrict members are ignored.
6778 * @old_user identifies user to update, aifo_ticket member is ignored
6779 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6780 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6781 * NULL, if you don't care.*/
6782 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6783 const struct isds_DbOwnerInfo *box,
6784 const struct isds_DbUserInfo *old_user,
6785 const struct isds_DbUserInfo *new_user,
6786 char **refnumber) {
6787 isds_error err = IE_SUCCESS;
6788 #if HAVE_LIBCURL
6789 xmlNsPtr isds_ns = NULL;
6790 xmlNodePtr request = NULL;
6791 xmlNodePtr node;
6792 #endif
6795 if (!context) return IE_INVALID_CONTEXT;
6796 zfree(context->long_message);
6797 if (!box || !old_user || !new_user) return IE_INVAL;
6800 #if HAVE_LIBCURL
6801 /* Build UpdateDataBoxUser request */
6802 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6803 if (!request) {
6804 isds_log_message(context,
6805 _("Could build UpdateDataBoxUser request"));
6806 return IE_ERROR;
6808 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6809 if(!isds_ns) {
6810 isds_log_message(context, _("Could not create ISDS name space"));
6811 xmlFreeNode(request);
6812 return IE_ERROR;
6814 xmlSetNs(request, isds_ns);
6816 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6817 err = insert_DbOwnerInfo(context, box, 0, node);
6818 if (err) goto leave;
6820 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6821 err = insert_DbUserInfo(context, old_user, 0, node);
6822 if (err) goto leave;
6824 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6825 err = insert_DbUserInfo(context, new_user, 0, node);
6826 if (err) goto leave;
6828 /* Send it to server and process response */
6829 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6830 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6832 leave:
6833 xmlFreeNode(request);
6834 #else /* not HAVE_LIBCURL */
6835 err = IE_NOTSUP;
6836 #endif
6838 return err;
6842 /* Undocumented function.
6843 * @context is session context
6844 * @box_id is UTF-8 encoded box identifier
6845 * @token is UTF-8 encoded temporary password
6846 * @user_id outputs UTF-8 encoded reallocated user identifier
6847 * @password outpus UTF-8 encoded reallocated user password
6848 * Output arguments will be nulled in case of error */
6849 isds_error isds_activate(struct isds_ctx *context,
6850 const char *box_id, const char *token,
6851 char **user_id, char **password) {
6852 isds_error err = IE_SUCCESS;
6853 #if HAVE_LIBCURL
6854 xmlNsPtr isds_ns = NULL;
6855 xmlNodePtr request = NULL, node;
6856 xmlDocPtr response = NULL;
6857 xmlXPathContextPtr xpath_ctx = NULL;
6858 xmlXPathObjectPtr result = NULL;
6859 #endif
6862 if (!context) return IE_INVALID_CONTEXT;
6863 zfree(context->long_message);
6865 if (user_id) zfree(*user_id);
6866 if (password) zfree(*password);
6868 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6871 #if HAVE_LIBCURL
6872 /* Build Activate request */
6873 request = xmlNewNode(NULL, BAD_CAST "Activate");
6874 if (!request) {
6875 isds_log_message(context, _("Could build Activate request"));
6876 return IE_ERROR;
6878 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6879 if(!isds_ns) {
6880 isds_log_message(context, _("Could not create ISDS name space"));
6881 xmlFreeNode(request);
6882 return IE_ERROR;
6884 xmlSetNs(request, isds_ns);
6886 INSERT_STRING(request, "dbAccessDataId", token);
6887 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6888 INSERT_STRING(request, "dbID", box_id);
6891 /* Send request and check response*/
6892 err = send_destroy_request_check_response(context,
6893 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6894 &response, NULL, NULL);
6895 if (err) goto leave;
6898 /* Extract data */
6899 xpath_ctx = xmlXPathNewContext(response);
6900 if (!xpath_ctx) {
6901 err = IE_ERROR;
6902 goto leave;
6904 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6905 err = IE_ERROR;
6906 goto leave;
6908 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6909 xpath_ctx);
6910 if (!result) {
6911 err = IE_ERROR;
6912 goto leave;
6914 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6915 isds_log_message(context, _("Missing ActivateResponse element"));
6916 err = IE_ISDS;
6917 goto leave;
6919 if (result->nodesetval->nodeNr > 1) {
6920 isds_log_message(context, _("Multiple ActivateResponse element"));
6921 err = IE_ISDS;
6922 goto leave;
6924 xpath_ctx->node = result->nodesetval->nodeTab[0];
6925 xmlXPathFreeObject(result); result = NULL;
6927 EXTRACT_STRING("isds:userId", *user_id);
6928 if (!*user_id)
6929 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6930 "but did not return `userId' element.\n"));
6932 EXTRACT_STRING("isds:password", *password);
6933 if (!*password)
6934 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6935 "but did not return `password' element.\n"));
6937 leave:
6938 xmlXPathFreeObject(result);
6939 xmlXPathFreeContext(xpath_ctx);
6940 xmlFreeDoc(response);
6941 xmlFreeNode(request);
6943 if (!err)
6944 isds_log(ILF_ISDS, ILL_DEBUG,
6945 _("Activate request processed by server successfully.\n"));
6946 #else /* not HAVE_LIBCURL */
6947 err = IE_NOTSUP;
6948 #endif
6950 return err;
6954 /* Reset credentials of user assigned to given box.
6955 * @context is session context
6956 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6957 * members are ignored.
6958 * @user identifies user to reset password, aifo_ticket member is ignored
6959 * @fee_paid is true if fee has been paid, false otherwise
6960 * @approval is optional external approval of box manipulation
6961 * @credentials_delivery is NULL if new password should be delivered off-line
6962 * to the user. It is valid pointer if user should obtain new password on-line
6963 * on dedicated web server. Then input @credentials_delivery.email value is
6964 * user's e-mail address user must provide to dedicated web server together
6965 * with @credentials_delivery.token. The output reallocated token user needs
6966 * to use to authorize on the web server to view his new password. Output
6967 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6968 * ISDS changed up on this call. (No reason why server could change the name
6969 * is known now.)
6970 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6971 * NULL, if you don't care.*/
6972 isds_error isds_reset_password(struct isds_ctx *context,
6973 const struct isds_DbOwnerInfo *box,
6974 const struct isds_DbUserInfo *user,
6975 const _Bool fee_paid, const struct isds_approval *approval,
6976 struct isds_credentials_delivery *credentials_delivery,
6977 char **refnumber) {
6978 isds_error err = IE_SUCCESS;
6979 #if HAVE_LIBCURL
6980 xmlNsPtr isds_ns = NULL;
6981 xmlNodePtr request = NULL, node;
6982 xmlDocPtr response = NULL;
6983 #endif
6986 if (!context) return IE_INVALID_CONTEXT;
6987 zfree(context->long_message);
6989 if (credentials_delivery) {
6990 zfree(credentials_delivery->token);
6991 zfree(credentials_delivery->new_user_name);
6993 if (!box || !user) return IE_INVAL;
6996 #if HAVE_LIBCURL
6997 /* Build NewAccessData request */
6998 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6999 if (!request) {
7000 isds_log_message(context,
7001 _("Could build NewAccessData request"));
7002 return IE_ERROR;
7004 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7005 if(!isds_ns) {
7006 isds_log_message(context, _("Could not create ISDS name space"));
7007 xmlFreeNode(request);
7008 return IE_ERROR;
7010 xmlSetNs(request, isds_ns);
7012 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7013 err = insert_DbOwnerInfo(context, box, 0, node);
7014 if (err) goto leave;
7016 INSERT_ELEMENT(node, request, "dbUserInfo");
7017 err = insert_DbUserInfo(context, user, 0, node);
7018 if (err) goto leave;
7020 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
7022 err = insert_credentials_delivery(context, credentials_delivery, request);
7023 if (err) goto leave;
7025 err = insert_GExtApproval(context, approval, request);
7026 if (err) goto leave;
7028 /* Send request and check response*/
7029 err = send_destroy_request_check_response(context,
7030 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
7031 &response, (xmlChar **) refnumber, NULL);
7032 if (err) goto leave;
7035 /* Extract optional token */
7036 err = extract_credentials_delivery(context, credentials_delivery,
7037 response, "NewAccessData");
7039 leave:
7040 xmlFreeDoc(response);
7041 xmlFreeNode(request);
7043 if (!err)
7044 isds_log(ILF_ISDS, ILL_DEBUG,
7045 _("NewAccessData request processed by server "
7046 "successfully.\n"));
7047 #else /* not HAVE_LIBCURL */
7048 err = IE_NOTSUP;
7049 #endif
7051 return err;
7055 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7056 * code, destroy response and log success.
7057 * @context is ISDS session context.
7058 * @service_name is name of SERVICE_DB_MANIPULATION service
7059 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7060 * members are ignored.
7061 * @user identifies user to remove
7062 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7063 * @credentials_delivery is NULL if new user's password should be delivered
7064 * off-line to the user. It is valid pointer if user should obtain new
7065 * password on-line on dedicated web server. Then input
7066 * @credentials_delivery.email value is user's e-mail address user must
7067 * provide to dedicated web server together with @credentials_delivery.token.
7068 * The output reallocated token user needs to use to authorize on the web
7069 * server to view his new password. Output reallocated
7070 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7071 * assingned or changed up on this call.
7072 * @approval is optional external approval of box manipulation
7073 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7074 * NULL, if you don't care. */
7075 static isds_error build_send_manipulationboxuser_request_check_drop_response(
7076 struct isds_ctx *context, const xmlChar *service_name,
7077 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7078 _Bool honor_aifo_ticket,
7079 struct isds_credentials_delivery *credentials_delivery,
7080 const struct isds_approval *approval, xmlChar **refnumber) {
7081 isds_error err = IE_SUCCESS;
7082 #if HAVE_LIBCURL
7083 xmlNsPtr isds_ns = NULL;
7084 xmlNodePtr request = NULL, node;
7085 xmlDocPtr response = NULL;
7086 #endif
7089 if (!context) return IE_INVALID_CONTEXT;
7090 zfree(context->long_message);
7091 if (credentials_delivery) {
7092 zfree(credentials_delivery->token);
7093 zfree(credentials_delivery->new_user_name);
7095 if (!service_name || service_name[0] == '\0' || !box || !user)
7096 return IE_INVAL;
7099 #if HAVE_LIBCURL
7100 /* Build NewAccessData or similar request */
7101 request = xmlNewNode(NULL, service_name);
7102 if (!request) {
7103 char *service_name_locale = _isds_utf82locale((char *) service_name);
7104 isds_printf_message(context, _("Could not build %s request"),
7105 service_name_locale);
7106 free(service_name_locale);
7107 return IE_ERROR;
7109 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7110 if(!isds_ns) {
7111 isds_log_message(context, _("Could not create ISDS name space"));
7112 xmlFreeNode(request);
7113 return IE_ERROR;
7115 xmlSetNs(request, isds_ns);
7117 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7118 err = insert_DbOwnerInfo(context, box, 0, node);
7119 if (err) goto leave;
7121 INSERT_ELEMENT(node, request, "dbUserInfo");
7122 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7123 if (err) goto leave;
7125 err = insert_credentials_delivery(context, credentials_delivery, request);
7126 if (err) goto leave;
7128 err = insert_GExtApproval(context, approval, request);
7129 if (err) goto leave;
7132 /* Send request and check response*/
7133 err = send_destroy_request_check_response(context,
7134 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7135 refnumber, NULL);
7137 xmlFreeNode(request);
7138 request = NULL;
7140 /* Pick up credentials_delivery if requested */
7141 err = extract_credentials_delivery(context, credentials_delivery, response,
7142 (char *)service_name);
7144 leave:
7145 xmlFreeDoc(response);
7146 if (request) xmlFreeNode(request);
7148 if (!err) {
7149 char *service_name_locale = _isds_utf82locale((char *) service_name);
7150 isds_log(ILF_ISDS, ILL_DEBUG,
7151 _("%s request processed by server successfully.\n"),
7152 service_name_locale);
7153 free(service_name_locale);
7155 #else /* not HAVE_LIBCURL */
7156 err = IE_NOTSUP;
7157 #endif
7159 return err;
7163 /* Assign new user to given box.
7164 * @context is session context
7165 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7166 * members are ignored.
7167 * @user defines new user to add
7168 * @credentials_delivery is NULL if new user's password should be delivered
7169 * off-line to the user. It is valid pointer if user should obtain new
7170 * password on-line on dedicated web server. Then input
7171 * @credentials_delivery.email value is user's e-mail address user must
7172 * provide to dedicated web server together with @credentials_delivery.token.
7173 * The output reallocated token user needs to use to authorize on the web
7174 * server to view his new password. Output reallocated
7175 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7176 * assingned up on this call.
7177 * @approval is optional external approval of box manipulation
7178 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7179 * NULL, if you don't care.*/
7180 isds_error isds_add_user(struct isds_ctx *context,
7181 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7182 struct isds_credentials_delivery *credentials_delivery,
7183 const struct isds_approval *approval, char **refnumber) {
7184 return build_send_manipulationboxuser_request_check_drop_response(context,
7185 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7186 approval, (xmlChar **) refnumber);
7190 /* Remove user assigned to given box.
7191 * @context is session context
7192 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7193 * members are ignored.
7194 * @user identifies user to remove, aifo_ticket member is ignored
7195 * @approval is optional external approval of box manipulation
7196 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7197 * NULL, if you don't care.*/
7198 isds_error isds_delete_user(struct isds_ctx *context,
7199 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7200 const struct isds_approval *approval, char **refnumber) {
7201 return build_send_manipulationboxuser_request_check_drop_response(context,
7202 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7203 (xmlChar **) refnumber);
7207 /* Get list of boxes in ZIP archive.
7208 * @context is session context
7209 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7210 * System recognizes following values currently: ALL (all boxes), UPG
7211 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7212 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7213 * commercial messages). This argument is a string because specification
7214 * states new values can appear in the future. Not all list types are
7215 * available to all users.
7216 * @buffer is automatically reallocated memory to store the list of boxes. The
7217 * list is zipped CSV file.
7218 * @buffer_length is size of @buffer data in bytes.
7219 * In case of error @buffer will be freed and @buffer_length will be
7220 * undefined.*/
7221 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7222 const char *list_identifier, void **buffer, size_t *buffer_length) {
7223 isds_error err = IE_SUCCESS;
7224 #if HAVE_LIBCURL
7225 xmlNsPtr isds_ns = NULL;
7226 xmlNodePtr request = NULL, node;
7227 xmlDocPtr response = NULL;
7228 xmlXPathContextPtr xpath_ctx = NULL;
7229 xmlXPathObjectPtr result = NULL;
7230 char *string = NULL;
7231 #endif
7234 if (!context) return IE_INVALID_CONTEXT;
7235 zfree(context->long_message);
7236 if (buffer) zfree(*buffer);
7237 if (!buffer || !buffer_length) return IE_INVAL;
7240 #if HAVE_LIBCURL
7241 /* Check if connection is established
7242 * TODO: This check should be done downstairs. */
7243 if (!context->curl) return IE_CONNECTION_CLOSED;
7246 /* Build AuthenticateMessage request */
7247 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7248 if (!request) {
7249 isds_log_message(context,
7250 _("Could not build GetDataBoxList request"));
7251 return IE_ERROR;
7253 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7254 if(!isds_ns) {
7255 isds_log_message(context, _("Could not create ISDS name space"));
7256 xmlFreeNode(request);
7257 return IE_ERROR;
7259 xmlSetNs(request, isds_ns);
7260 INSERT_STRING(request, "dblType", list_identifier);
7262 /* Send request to server and process response */
7263 err = send_destroy_request_check_response(context,
7264 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7265 &response, NULL, NULL);
7266 if (err) goto leave;
7269 /* Extract Base-64 encoded ZIP file */
7270 xpath_ctx = xmlXPathNewContext(response);
7271 if (!xpath_ctx) {
7272 err = IE_ERROR;
7273 goto leave;
7275 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7276 err = IE_ERROR;
7277 goto leave;
7279 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7281 /* Decode non-empty archive */
7282 if (string && string[0] != '\0') {
7283 *buffer_length = _isds_b64decode(string, buffer);
7284 if (*buffer_length == (size_t) -1) {
7285 isds_printf_message(context,
7286 _("Error while Base64-decoding box list archive"));
7287 err = IE_ERROR;
7288 goto leave;
7293 leave:
7294 free(string);
7295 xmlXPathFreeObject(result);
7296 xmlXPathFreeContext(xpath_ctx);
7297 xmlFreeDoc(response);
7298 xmlFreeNode(request);
7300 if (!err) {
7301 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7302 "processed by server successfully.\n"));
7304 #else /* not HAVE_LIBCURL */
7305 err = IE_NOTSUP;
7306 #endif
7308 return err;
7312 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7313 * send it, check for error code, extract list of results, destroy response
7314 * and log success.
7315 * @context is ISDS session context.
7316 * @service_name is name of SERVICE_DB_SEARCH service
7317 * @pfo_service is false if tDbOwnerInfo request should be built from
7318 * @criteria and corresponding result extracted. It is true if
7319 * tDbPersonalOwnerInfoRequest request should be built. The request and
7320 * response differ subset of significant isds_DbOwnerInfo structure members.
7321 * @criteria is filter. You should fill in at least some members.
7322 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7323 * members will be ignored. If @pfo_service is true, dbType, ic,
7324 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7325 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7326 * ignored.
7327 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7328 * possibly empty. Input NULL or valid old structure. The same memebers as
7329 * in described for @criteria argument will be NULL according to @pfo_service
7330 * switch.
7331 * @return:
7332 * IE_SUCCESS if search succeeded, @boxes contains useful data
7333 * IE_NOEXIST if no such box exists, @boxes will be NULL
7334 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7335 * contains still valid data
7336 * other code if something bad happens. @boxes will be NULL. */
7337 static isds_error build_send_findbox_request_check_parse_drop_response(
7338 struct isds_ctx *context, const xmlChar *service_name,
7339 _Bool pfo_service, const struct isds_DbOwnerInfo *criteria,
7340 struct isds_list **boxes) {
7341 isds_error err = IE_SUCCESS;
7342 #if HAVE_LIBCURL
7343 char *service_name_locale = NULL;
7344 _Bool truncated = 0;
7345 xmlNsPtr isds_ns = NULL;
7346 xmlNodePtr request = NULL;
7347 xmlDocPtr response = NULL;
7348 xmlChar *code = NULL, *message = NULL;
7349 xmlNodePtr db_owner_info;
7350 xmlXPathContextPtr xpath_ctx = NULL;
7351 xmlXPathObjectPtr result = NULL;
7352 xmlChar *string = NULL;
7353 #endif
7356 if (!context) return IE_INVALID_CONTEXT;
7357 zfree(context->long_message);
7358 if (!boxes) return IE_INVAL;
7359 isds_list_free(boxes);
7361 if (!criteria) {
7362 return IE_INVAL;
7365 #if HAVE_LIBCURL
7366 /* Check if connection is established
7367 * TODO: This check should be done downstairs. */
7368 if (!context->curl) return IE_CONNECTION_CLOSED;
7369 service_name_locale = _isds_utf82locale((char *) service_name);
7371 /* Build request */
7372 request = xmlNewNode(NULL, service_name);
7373 if (!request) {
7374 isds_printf_message(context, _("Could not build %s request"),
7375 service_name_locale);
7376 free(service_name_locale);
7377 return IE_ERROR;
7379 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7380 if(!isds_ns) {
7381 isds_log_message(context, _("Could not create ISDS name space"));
7382 free(service_name_locale);
7383 xmlFreeNode(request);
7384 return IE_ERROR;
7386 xmlSetNs(request, isds_ns);
7387 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7388 if (!db_owner_info) {
7389 isds_printf_message(context,
7390 _("Could not add dbOwnerInfo child to %s element"),
7391 service_name_locale);
7392 free(service_name_locale);
7393 xmlFreeNode(request);
7394 return IE_ERROR;
7397 err = insert_DbOwnerInfo(context, criteria, pfo_service, db_owner_info);
7398 if (err) goto leave;
7401 /* Send request */
7402 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
7403 service_name_locale);
7404 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7406 /* Destroy request */
7407 xmlFreeNode(request); request = NULL;
7409 if (err) {
7410 isds_log(ILF_ISDS, ILL_DEBUG,
7411 _("Processing ISDS response on %s request failed\n"),
7412 service_name_locale);
7413 goto leave;
7416 /* Check for response status */
7417 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7418 &code, &message, NULL);
7419 if (err) {
7420 isds_log(ILF_ISDS, ILL_DEBUG,
7421 _("ISDS response on %s request is missing status\n"),
7422 service_name_locale);
7423 goto leave;
7426 /* Request processed, but nothing found */
7427 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7428 !xmlStrcmp(code, BAD_CAST "5001")) {
7429 char *code_locale = _isds_utf82locale((char*)code);
7430 char *message_locale = _isds_utf82locale((char*)message);
7431 isds_log(ILF_ISDS, ILL_DEBUG,
7432 _("Server did not find any box on %s request "
7433 "(code=%s, message=%s)\n"), service_name_locale,
7434 code_locale, message_locale);
7435 isds_log_message(context, message_locale);
7436 free(code_locale);
7437 free(message_locale);
7438 err = IE_NOEXIST;
7439 goto leave;
7442 /* Warning, not an error */
7443 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7444 char *code_locale = _isds_utf82locale((char*)code);
7445 char *message_locale = _isds_utf82locale((char*)message);
7446 isds_log(ILF_ISDS, ILL_DEBUG,
7447 _("Server truncated response on %s request "
7448 "(code=%s, message=%s)\n"), service_name_locale,
7449 code_locale, message_locale);
7450 isds_log_message(context, message_locale);
7451 free(code_locale);
7452 free(message_locale);
7453 truncated = 1;
7456 /* Other error */
7457 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7458 char *code_locale = _isds_utf82locale((char*)code);
7459 char *message_locale = _isds_utf82locale((char*)message);
7460 isds_log(ILF_ISDS, ILL_DEBUG,
7461 _("Server refused %s request (code=%s, message=%s)\n"),
7462 service_name_locale, code_locale, message_locale);
7463 isds_log_message(context, message_locale);
7464 free(code_locale);
7465 free(message_locale);
7466 err = IE_ISDS;
7467 goto leave;
7470 xpath_ctx = xmlXPathNewContext(response);
7471 if (!xpath_ctx) {
7472 err = IE_ERROR;
7473 goto leave;
7475 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7476 err = IE_ERROR;
7477 goto leave;
7480 /* Extract boxes if they present */
7481 if (-1 == isds_asprintf((char **)&string,
7482 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7483 service_name)) {
7484 err = IE_NOMEM;
7485 goto leave;
7487 result = xmlXPathEvalExpression(string, xpath_ctx);
7488 zfree(string);
7489 if (!result) {
7490 err = IE_ERROR;
7491 goto leave;
7493 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7494 struct isds_list *item, *prev_item = NULL;
7495 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7496 item = calloc(1, sizeof(*item));
7497 if (!item) {
7498 err = IE_NOMEM;
7499 goto leave;
7502 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7503 if (i == 0) *boxes = item;
7504 else prev_item->next = item;
7505 prev_item = item;
7507 xpath_ctx->node = result->nodesetval->nodeTab[i];
7508 err = extract_DbOwnerInfo(context,
7509 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7510 if (err) goto leave;
7514 leave:
7515 if (err) {
7516 isds_list_free(boxes);
7517 } else {
7518 if (truncated) err = IE_2BIG;
7521 free(string);
7522 xmlFreeNode(request);
7523 xmlXPathFreeObject(result);
7524 xmlXPathFreeContext(xpath_ctx);
7526 free(code);
7527 free(message);
7528 xmlFreeDoc(response);
7530 if (!err)
7531 isds_log(ILF_ISDS, ILL_DEBUG,
7532 _("%s request processed by server successfully.\n"),
7533 service_name_locale);
7534 free(service_name_locale);
7535 #else /* not HAVE_LIBCURL */
7536 err = IE_NOTSUP;
7537 #endif
7539 return err;
7543 /* Find boxes suiting given criteria.
7544 * @criteria is filter. You should fill in at least some members. aifoIsds,
7545 * address->adCode, address->adDistrict members are ignored.
7546 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7547 * possibly empty. Input NULL or valid old structure.
7548 * @return:
7549 * IE_SUCCESS if search succeeded, @boxes contains useful data
7550 * IE_NOEXIST if no such box exists, @boxes will be NULL
7551 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7552 * contains still valid data
7553 * other code if something bad happens. @boxes will be NULL. */
7554 isds_error isds_FindDataBox(struct isds_ctx *context,
7555 const struct isds_DbOwnerInfo *criteria,
7556 struct isds_list **boxes) {
7557 return build_send_findbox_request_check_parse_drop_response(context,
7558 BAD_CAST "FindDataBox", 0, criteria, boxes);
7562 /* Find accessible FO-type boxes suiting given criteria.
7563 * @criteria is filter. You should fill in at least some members. dbType, ic,
7564 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7565 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7566 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7567 * possibly empty. Input NULL or valid old structure.
7568 * @return:
7569 * IE_SUCCESS if search succeeded, @boxes contains useful data
7570 * IE_NOEXIST if no such box exists, @boxes will be NULL
7571 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7572 * contains still valid data
7573 * other code if something bad happens. @boxes will be NULL. */
7574 isds_error isds_FindPersonalDataBox(struct isds_ctx *context,
7575 const struct isds_DbOwnerInfo *criteria,
7576 struct isds_list **boxes) {
7577 return build_send_findbox_request_check_parse_drop_response(context,
7578 BAD_CAST "FindPersonalDataBox", 1, criteria, boxes);
7582 #if HAVE_LIBCURL
7583 /* Convert a string with match markers into a plain string with list of
7584 * pointers to the matches
7585 * @string is an UTF-8 encoded non-constant string with match markers
7586 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7587 * The markers will be removed from the string.
7588 * @starts is a reallocated list of static pointers into the @string pointing
7589 * to places where match start markers occured.
7590 * @ends is a reallocated list of static pointers into the @string pointing
7591 * to places where match end markers occured.
7592 * @return IE_SUCCESS in case of no failure. */
7593 static isds_error interpret_matches(xmlChar *string,
7594 struct isds_list **starts, struct isds_list **ends) {
7595 isds_error err = IE_SUCCESS;
7596 xmlChar *pointer, *destination, *source;
7597 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7599 isds_list_free(starts);
7600 isds_list_free(ends);
7601 if (NULL == starts || NULL == ends) return IE_INVAL;
7602 if (NULL == string) return IE_SUCCESS;
7604 for (pointer = string; *pointer != '\0';) {
7605 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7606 /* Remove the start marker */
7607 for (source = pointer + 14, destination = pointer;
7608 *source != '\0'; source++, destination++) {
7609 *destination = *source;
7611 *destination = '\0';
7612 /* Append the pointer into the list */
7613 item = calloc(1, sizeof(*item));
7614 if (!item) {
7615 err = IE_NOMEM;
7616 goto leave;
7618 item->destructor = (void (*)(void **))NULL;
7619 item->data = pointer;
7620 if (NULL == prev_start) *starts = item;
7621 else prev_start->next = item;
7622 prev_start = item;
7623 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7624 /* Remove the end marker */
7625 for (source = pointer + 12, destination = pointer;
7626 *source != '\0'; source++, destination++) {
7627 *destination = *source;
7629 *destination = '\0';
7630 /* Append the pointer into the list */
7631 item = calloc(1, sizeof(*item));
7632 if (!item) {
7633 err = IE_NOMEM;
7634 goto leave;
7636 item->destructor = (void (*)(void **))NULL;
7637 item->data = pointer;
7638 if (NULL == prev_end) *ends = item;
7639 else prev_end->next = item;
7640 prev_end = item;
7641 } else {
7642 pointer++;
7646 leave:
7647 if (err) {
7648 isds_list_free(starts);
7649 isds_list_free(ends);
7651 return err;
7655 /* Convert isds:dbResult XML tree into structure
7656 * @context is ISDS context.
7657 * @fulltext_result is automatically reallocated found box structure.
7658 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7659 * @collect_matches is true to interpret match markers.
7660 * In case of error @result will be freed. */
7661 static isds_error extract_dbResult(struct isds_ctx *context,
7662 struct isds_fulltext_result **fulltext_result,
7663 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7664 isds_error err = IE_SUCCESS;
7665 xmlXPathObjectPtr result = NULL;
7666 char *string = NULL;
7668 if (NULL == context) return IE_INVALID_CONTEXT;
7669 if (NULL == fulltext_result) return IE_INVAL;
7670 isds_fulltext_result_free(fulltext_result);
7671 if (!xpath_ctx) return IE_INVAL;
7674 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7675 if (NULL == *fulltext_result) {
7676 err = IE_NOMEM;
7677 goto leave;
7680 /* Extract data */
7681 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7683 EXTRACT_STRING("isds:dbType", string);
7684 if (NULL == string) {
7685 err = IE_ISDS;
7686 isds_log_message(context, _("Empty isds:dbType element"));
7687 goto leave;
7689 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7690 if (err) {
7691 if (err == IE_ENUM) {
7692 err = IE_ISDS;
7693 char *string_locale = _isds_utf82locale(string);
7694 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7695 string_locale);
7696 free(string_locale);
7698 goto leave;
7700 zfree(string);
7702 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7703 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7705 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7706 if (err) goto leave;
7708 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7709 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7710 (*fulltext_result)->dbEffectiveOVM);
7712 EXTRACT_STRING("isds:dbSendOptions", string);
7713 if (NULL == string) {
7714 err = IE_ISDS;
7715 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7716 goto leave;
7718 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7719 (*fulltext_result)->active = 1;
7720 (*fulltext_result)->public_sending = 1;
7721 (*fulltext_result)->commercial_sending = 0;
7722 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7723 (*fulltext_result)->active = 1;
7724 (*fulltext_result)->public_sending = 1;
7725 (*fulltext_result)->commercial_sending = 1;
7726 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7727 (*fulltext_result)->active = 1;
7728 (*fulltext_result)->public_sending = 0;
7729 (*fulltext_result)->commercial_sending = 1;
7730 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7731 (*fulltext_result)->active = 1;
7732 (*fulltext_result)->public_sending = 0;
7733 (*fulltext_result)->commercial_sending = 0;
7734 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7735 (*fulltext_result)->active = 0;
7736 (*fulltext_result)->public_sending = 0;
7737 (*fulltext_result)->commercial_sending = 0;
7738 } else {
7739 err = IE_ISDS;
7740 char *string_locale = _isds_utf82locale(string);
7741 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7742 string_locale);
7743 free(string_locale);
7744 goto leave;
7746 zfree(string);
7748 /* Interpret match marks */
7749 if (collect_matches) {
7750 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7751 &((*fulltext_result)->name_match_start),
7752 &((*fulltext_result)->name_match_end));
7753 if (err) goto leave;
7754 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7755 &((*fulltext_result)->address_match_start),
7756 &((*fulltext_result)->address_match_end));
7757 if (err) goto leave;
7760 leave:
7761 if (err) isds_fulltext_result_free(fulltext_result);
7762 free(string);
7763 xmlXPathFreeObject(result);
7764 return err;
7766 #endif /* HAVE_LIBCURL */
7769 /* Find boxes matching a given full-text criteria.
7770 * @context is a session context
7771 * @query is a non-empty string which consists of words to search
7772 * @target selects box attributes to search for @query words. Pass NULL if you
7773 * don't care.
7774 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7775 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7776 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7777 * which is DBTYPE_SYSTEM.
7778 * @page_size defines count of boxes to constitute a response page. It counts
7779 * from zero. Pass NULL to let server to use a default value (50 now).
7780 * @page_number defines ordinar number of the response page to return. It
7781 * counts from zero. Pass NULL to let server to use a default value (0 now).
7782 * @track_matches points to true for marking @query words found in the box
7783 * attributes. It points to false for not marking. Pass NULL to let the server
7784 * to use default value (false now).
7785 * @total_matching_boxes outputs reallocated number of all boxes matching the
7786 * query. Will be pointer to NULL if server did not provide the value.
7787 * Pass NULL if you don't care.
7788 * @current_page_beginning outputs reallocated ordinar number of the first box
7789 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7790 * server did not provide the value. Pass NULL if you don't care.
7791 * @current_page_size outputs reallocated count of boxes in the this @boxes
7792 * page. It will be pointer to NULL if the server did not provide the value.
7793 * Pass NULL if you don't care.
7794 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7795 * is the last one, false if more boxes match, NULL if the server did not
7796 * provude the value. Pass NULL if you don't care.
7797 * @boxes outputs reallocated list of isds_fulltext_result structures,
7798 * possibly empty.
7799 * @return:
7800 * IE_SUCCESS if search succeeded
7801 * IE_2BIG if @page_size is too large
7802 * other code if something bad happens; output arguments will be NULL. */
7803 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7804 const char *query,
7805 const isds_fulltext_target *target,
7806 const isds_DbType *box_type,
7807 const unsigned long int *page_size,
7808 const unsigned long int *page_number,
7809 const _Bool *track_matches,
7810 unsigned long int **total_matching_boxes,
7811 unsigned long int **current_page_beginning,
7812 unsigned long int **current_page_size,
7813 _Bool **last_page,
7814 struct isds_list **boxes) {
7815 isds_error err = IE_SUCCESS;
7816 #if HAVE_LIBCURL
7817 xmlNsPtr isds_ns = NULL;
7818 xmlNodePtr request = NULL;
7819 xmlDocPtr response = NULL;
7820 xmlNodePtr node;
7821 xmlXPathContextPtr xpath_ctx = NULL;
7822 xmlXPathObjectPtr result = NULL;
7823 const xmlChar *static_string = NULL;
7824 xmlChar *string = NULL;
7826 const xmlChar *codes[] = {
7827 BAD_CAST "1004",
7828 BAD_CAST "1152",
7829 BAD_CAST "1153",
7830 BAD_CAST "1154",
7831 BAD_CAST "1155",
7832 BAD_CAST "1156",
7833 BAD_CAST "9002",
7834 NULL
7836 const char *meanings[] = {
7837 N_("You are not allowed to perform the search"),
7838 N_("The query string is empty"),
7839 N_("Searched box ID is malformed"),
7840 N_("Searched organization ID is malformed"),
7841 N_("Invalid input"),
7842 N_("Requested page size is too large"),
7843 N_("Search engine internal error")
7845 const isds_error errors[] = {
7846 IE_ISDS,
7847 IE_INVAL,
7848 IE_INVAL,
7849 IE_INVAL,
7850 IE_INVAL,
7851 IE_2BIG,
7852 IE_ISDS
7854 struct code_map_isds_error map = {
7855 .codes = codes,
7856 .meanings = meanings,
7857 .errors = errors
7859 #endif
7862 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7863 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7864 if (NULL != current_page_size) zfree(*current_page_size);
7865 if (NULL != last_page) zfree(*last_page);
7866 isds_list_free(boxes);
7868 if (NULL == context) return IE_INVALID_CONTEXT;
7869 zfree(context->long_message);
7871 if (NULL == boxes) return IE_INVAL;
7873 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7874 isds_log_message(context, _("Query string must be non-empty"));
7875 return IE_INVAL;
7878 #if HAVE_LIBCURL
7879 /* Check if connection is established
7880 * TODO: This check should be done downstairs. */
7881 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7883 /* Build FindDataBox request */
7884 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7885 if (NULL == request) {
7886 isds_log_message(context,
7887 _("Could not build ISDSSearch2 request"));
7888 return IE_ERROR;
7890 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7891 if(NULL == isds_ns) {
7892 isds_log_message(context, _("Could not create ISDS name space"));
7893 xmlFreeNode(request);
7894 return IE_ERROR;
7896 xmlSetNs(request, isds_ns);
7898 INSERT_STRING(request, "searchText", query);
7900 if (NULL != target) {
7901 static_string = isds_fulltext_target2string(*(target));
7902 if (NULL == static_string) {
7903 isds_printf_message(context, _("Invalid target value: %d"),
7904 *(target));
7905 err = IE_ENUM;
7906 goto leave;
7909 INSERT_STRING(request, "searchType", static_string);
7910 static_string = NULL;
7912 if (NULL != box_type) {
7913 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7914 if (DBTYPE_SYSTEM == *box_type) {
7915 static_string = BAD_CAST "ALL";
7916 } else if (DBTYPE_OVM_MAIN == *box_type) {
7917 static_string = BAD_CAST "OVM_MAIN";
7918 } else {
7919 static_string = isds_DbType2string(*(box_type));
7920 if (NULL == static_string) {
7921 isds_printf_message(context, _("Invalid box type value: %d"),
7922 *(box_type));
7923 err = IE_ENUM;
7924 goto leave;
7928 INSERT_STRING(request, "searchScope", static_string);
7929 static_string = NULL;
7931 INSERT_ULONGINT(request, "page", page_number, string);
7932 INSERT_ULONGINT(request, "pageSize", page_size, string);
7933 INSERT_BOOLEAN(request, "highlighting", track_matches);
7935 /* Send request and check response */
7936 err = send_destroy_request_check_response(context,
7937 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7938 &request, &response, NULL, &map);
7939 if (err) goto leave;
7941 /* Parse response */
7942 xpath_ctx = xmlXPathNewContext(response);
7943 if (NULL == xpath_ctx) {
7944 err = IE_ERROR;
7945 goto leave;
7947 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7948 err = IE_ERROR;
7949 goto leave;
7951 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7952 xpath_ctx);
7953 if (!result) {
7954 err = IE_ERROR;
7955 goto leave;
7957 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7958 isds_log_message(context, _("Missing ISDSSearch2 element"));
7959 err = IE_ISDS;
7960 goto leave;
7962 if (result->nodesetval->nodeNr > 1) {
7963 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7964 err = IE_ISDS;
7965 goto leave;
7967 xpath_ctx->node = result->nodesetval->nodeTab[0];
7968 xmlXPathFreeObject(result); result = NULL;
7971 /* Extract counters */
7972 if (NULL != total_matching_boxes) {
7973 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7975 if (NULL != current_page_size) {
7976 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7978 if (NULL != current_page_beginning) {
7979 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7981 if (NULL != last_page) {
7982 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7984 xmlXPathFreeObject(result); result = NULL;
7986 /* Extract boxes if they present */
7987 result = xmlXPathEvalExpression(BAD_CAST
7988 "isds:dbResults/isds:dbResult", xpath_ctx);
7989 if (NULL == result) {
7990 err = IE_ERROR;
7991 goto leave;
7993 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7994 struct isds_list *item, *prev_item = NULL;
7995 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7996 item = calloc(1, sizeof(*item));
7997 if (!item) {
7998 err = IE_NOMEM;
7999 goto leave;
8002 item->destructor = (void (*)(void **))isds_fulltext_result_free;
8003 if (i == 0) *boxes = item;
8004 else prev_item->next = item;
8005 prev_item = item;
8007 xpath_ctx->node = result->nodesetval->nodeTab[i];
8008 err = extract_dbResult(context,
8009 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
8010 (NULL == track_matches) ? 0 : *track_matches);
8011 if (err) goto leave;
8015 leave:
8016 if (err) {
8017 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
8018 if (NULL != current_page_beginning) zfree(*current_page_beginning);
8019 if (NULL != current_page_size) zfree(*current_page_size);
8020 if (NULL != last_page) zfree(*last_page);
8021 isds_list_free(boxes);
8024 free(string);
8025 xmlFreeNode(request);
8026 xmlXPathFreeObject(result);
8027 xmlXPathFreeContext(xpath_ctx);
8028 xmlFreeDoc(response);
8030 if (!err)
8031 isds_log(ILF_ISDS, ILL_DEBUG,
8032 _("ISDSSearch2 request processed by server successfully.\n"));
8033 #else /* not HAVE_LIBCURL */
8034 err = IE_NOTSUP;
8035 #endif
8037 return err;
8041 /* Get status of a box.
8042 * @context is ISDS session context.
8043 * @box_id is UTF-8 encoded box identifier as zero terminated string
8044 * @box_status is return value of box status.
8045 * @return:
8046 * IE_SUCCESS if box has been found and its status retrieved
8047 * IE_NOEXIST if box is not known to ISDS server
8048 * or other appropriate error.
8049 * You can use isds_DbState to enumerate box status. However out of enum
8050 * range value can be returned too. This is feature because ISDS
8051 * specification leaves the set of values open.
8052 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8053 * the box has been deleted, but ISDS still lists its former existence. */
8054 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
8055 long int *box_status) {
8056 isds_error err = IE_SUCCESS;
8057 #if HAVE_LIBCURL
8058 xmlNsPtr isds_ns = NULL;
8059 xmlNodePtr request = NULL, db_id;
8060 xmlDocPtr response = NULL;
8061 xmlXPathContextPtr xpath_ctx = NULL;
8062 xmlXPathObjectPtr result = NULL;
8063 xmlChar *string = NULL;
8065 const xmlChar *codes[] = {
8066 BAD_CAST "5001",
8067 BAD_CAST "1007",
8068 BAD_CAST "2011",
8069 NULL
8071 const char *meanings[] = {
8072 "The box does not exist",
8073 "Box ID is malformed",
8074 "Box ID malformed",
8076 const isds_error errors[] = {
8077 IE_NOEXIST,
8078 IE_INVAL,
8079 IE_INVAL,
8081 struct code_map_isds_error map = {
8082 .codes = codes,
8083 .meanings = meanings,
8084 .errors = errors
8086 #endif
8088 if (!context) return IE_INVALID_CONTEXT;
8089 zfree(context->long_message);
8090 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
8092 #if HAVE_LIBCURL
8093 /* Check if connection is established
8094 * TODO: This check should be done downstairs. */
8095 if (!context->curl) return IE_CONNECTION_CLOSED;
8098 /* Build CheckDataBox request */
8099 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
8100 if (!request) {
8101 isds_log_message(context,
8102 _("Could build CheckDataBox request"));
8103 return IE_ERROR;
8105 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8106 if(!isds_ns) {
8107 isds_log_message(context, _("Could not create ISDS name space"));
8108 xmlFreeNode(request);
8109 return IE_ERROR;
8111 xmlSetNs(request, isds_ns);
8112 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
8113 if (!db_id) {
8114 isds_log_message(context, _("Could not add dbID child to "
8115 "CheckDataBox element"));
8116 xmlFreeNode(request);
8117 return IE_ERROR;
8121 /* Send request and check response*/
8122 err = send_destroy_request_check_response(context,
8123 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
8124 &request, &response, NULL, &map);
8125 if (err) goto leave;
8128 /* Extract data */
8129 xpath_ctx = xmlXPathNewContext(response);
8130 if (!xpath_ctx) {
8131 err = IE_ERROR;
8132 goto leave;
8134 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8135 err = IE_ERROR;
8136 goto leave;
8138 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
8139 xpath_ctx);
8140 if (!result) {
8141 err = IE_ERROR;
8142 goto leave;
8144 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8145 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
8146 err = IE_ISDS;
8147 goto leave;
8149 if (result->nodesetval->nodeNr > 1) {
8150 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
8151 err = IE_ISDS;
8152 goto leave;
8154 xpath_ctx->node = result->nodesetval->nodeTab[0];
8155 xmlXPathFreeObject(result); result = NULL;
8157 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8160 leave:
8161 free(string);
8162 xmlXPathFreeObject(result);
8163 xmlXPathFreeContext(xpath_ctx);
8165 xmlFreeDoc(response);
8167 if (!err)
8168 isds_log(ILF_ISDS, ILL_DEBUG,
8169 _("CheckDataBox request processed by server successfully.\n"));
8170 #else /* not HAVE_LIBCURL */
8171 err = IE_NOTSUP;
8172 #endif
8174 return err;
8178 #if HAVE_LIBCURL
8179 /* Convert XSD:tdbPeriod XML tree into structure
8180 * @context is ISDS context.
8181 * @period is automatically reallocated found box status period structure.
8182 * @xpath_ctx is XPath context with current node as element of
8183 * XSD:tDbPeriod type.
8184 * In case of error @period will be freed. */
8185 static isds_error extract_Period(struct isds_ctx *context,
8186 struct isds_box_state_period **period, xmlXPathContextPtr xpath_ctx) {
8187 isds_error err = IE_SUCCESS;
8188 xmlXPathObjectPtr result = NULL;
8189 char *string = NULL;
8190 long int *dbState_ptr;
8192 if (NULL == context) return IE_INVALID_CONTEXT;
8193 if (NULL == period) return IE_INVAL;
8194 isds_box_state_period_free(period);
8195 if (!xpath_ctx) return IE_INVAL;
8198 *period = calloc(1, sizeof(**period));
8199 if (NULL == *period) {
8200 err = IE_NOMEM;
8201 goto leave;
8204 /* Extract data */
8205 EXTRACT_STRING("isds:PeriodFrom", string);
8206 if (NULL == string) {
8207 err = IE_XML;
8208 isds_log_message(context,
8209 _("Could not find PeriodFrom element value"));
8210 goto leave;
8212 err = timestring2static_timeval((xmlChar *) string,
8213 &((*period)->from));
8214 if (err) {
8215 char *string_locale = _isds_utf82locale(string);
8216 if (err == IE_DATE) err = IE_ISDS;
8217 isds_printf_message(context,
8218 _("Could not convert PeriodFrom as ISO time: %s"),
8219 string_locale);
8220 free(string_locale);
8221 goto leave;
8223 zfree(string);
8225 EXTRACT_STRING("isds:PeriodTo", string);
8226 if (NULL == string) {
8227 err = IE_XML;
8228 isds_log_message(context,
8229 _("Could not find PeriodTo element value"));
8230 goto leave;
8232 err = timestring2static_timeval((xmlChar *) string,
8233 &((*period)->to));
8234 if (err) {
8235 char *string_locale = _isds_utf82locale(string);
8236 if (err == IE_DATE) err = IE_ISDS;
8237 isds_printf_message(context,
8238 _("Could not convert PeriodTo as ISO time: %s"),
8239 string_locale);
8240 free(string_locale);
8241 goto leave;
8243 zfree(string);
8245 dbState_ptr = &((*period)->dbState);
8246 EXTRACT_LONGINT("isds:DbState", dbState_ptr, 1);
8248 leave:
8249 if (err) isds_box_state_period_free(period);
8250 free(string);
8251 xmlXPathFreeObject(result);
8252 return err;
8254 #endif /* HAVE_LIBCURL */
8257 /* Get history of box state changes.
8258 * @context is ISDS session context.
8259 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8260 * @from_time is first second of history to return in @history. Server ignores
8261 * subseconds. NULL means time of creating the box.
8262 * @to_time is last second of history to return in @history. Server ignores
8263 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8264 * interval is closed from both ends. NULL means now.
8265 * @history outputs auto-reallocated list of pointers to struct
8266 * isds_box_state_period. Each item describes a continues time when the box
8267 * was in one state. The state is 1 for accessible box. Otherwise the box
8268 * is inaccessible (priviledged users will get exact box state as enumerated
8269 * in isds_DbState, other users 0).
8270 * @return:
8271 * IE_SUCCESS if the history has been obtained correctly,
8272 * or other appropriate error. Please note that server allows to retrieve
8273 * the history only to some users. */
8274 isds_error isds_get_box_state_history(struct isds_ctx *context,
8275 const char *box_id,
8276 const struct timeval *from_time, const struct timeval *to_time,
8277 struct isds_list **history) {
8278 isds_error err = IE_SUCCESS;
8279 #if HAVE_LIBCURL
8280 char *box_id_locale = NULL;
8281 xmlNodePtr request = NULL, node;
8282 xmlNsPtr isds_ns = NULL;
8283 xmlChar *string = NULL;
8285 xmlDocPtr response = NULL;
8286 xmlXPathContextPtr xpath_ctx = NULL;
8287 xmlXPathObjectPtr result = NULL;
8288 #endif
8290 if (!context) return IE_INVALID_CONTEXT;
8291 zfree(context->long_message);
8293 /* Free output argument */
8294 isds_list_free(history);
8296 #if HAVE_LIBCURL
8297 /* Check if connection is established */
8298 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8300 /* ??? XML schema allows empty box ID, textual documentation
8301 * requries the value. */
8302 /* Allow undefined box_id */
8303 if (NULL != box_id) {
8304 box_id_locale = _isds_utf82locale((char*)box_id);
8305 if (NULL == box_id_locale) {
8306 err = IE_NOMEM;
8307 goto leave;
8311 /* Build request */
8312 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxActivityStatus");
8313 if (NULL == request) {
8314 isds_printf_message(context,
8315 _("Could not build GetDataBoxActivityStatus request "
8316 "for %s box"),
8317 box_id_locale);
8318 err = IE_ERROR;
8319 goto leave;
8321 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8322 if(!isds_ns) {
8323 isds_log_message(context, _("Could not create ISDS name space"));
8324 err = IE_ERROR;
8325 goto leave;
8327 xmlSetNs(request, isds_ns);
8329 /* Add mandatory XSD:tIdDbInput child */
8330 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8331 /* Add times elements only when defined */
8332 /* ???: XML schema requires the values, textual documentation does not. */
8333 if (from_time) {
8334 err = timeval2timestring(from_time, &string);
8335 if (err) {
8336 isds_log_message(context,
8337 _("Could not convert `from_time' argument to ISO time "
8338 "string"));
8339 goto leave;
8341 INSERT_STRING(request, "baFrom", string);
8342 zfree(string);
8344 if (to_time) {
8345 err = timeval2timestring(to_time, &string);
8346 if (err) {
8347 isds_log_message(context,
8348 _("Could not convert `to_time' argument to ISO time "
8349 "string"));
8350 goto leave;
8352 INSERT_STRING(request, "baTo", string);
8353 zfree(string);
8356 /* Send request and check response*/
8357 err = send_destroy_request_check_response(context,
8358 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxActivityStatus",
8359 &request, &response, NULL, NULL);
8360 if (err) goto leave;
8363 /* Extract data */
8364 /* Set context to the root */
8365 xpath_ctx = xmlXPathNewContext(response);
8366 if (!xpath_ctx) {
8367 err = IE_ERROR;
8368 goto leave;
8370 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8371 err = IE_ERROR;
8372 goto leave;
8374 result = xmlXPathEvalExpression(BAD_CAST "/isds:GetDataBoxActivityStatusResponse",
8375 xpath_ctx);
8376 if (!result) {
8377 err = IE_ERROR;
8378 goto leave;
8380 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8381 isds_log_message(context, _("Missing GetDataBoxActivityStatusResponse element"));
8382 err = IE_ISDS;
8383 goto leave;
8385 if (result->nodesetval->nodeNr > 1) {
8386 isds_log_message(context, _("Multiple GetDataBoxActivityStatusResponse element"));
8387 err = IE_ISDS;
8388 goto leave;
8390 xpath_ctx->node = result->nodesetval->nodeTab[0];
8391 xmlXPathFreeObject(result); result = NULL;
8393 /* Ignore dbID, it's the same as the input argument. */
8395 /* Extract records */
8396 if (NULL == history) goto leave;
8397 result = xmlXPathEvalExpression(BAD_CAST "isds:Periods/isds:Period",
8398 xpath_ctx);
8399 if (!result) {
8400 err = IE_ERROR;
8401 goto leave;
8403 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8404 struct isds_list *prev_item = NULL;
8406 /* Iterate over all records */
8407 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8408 struct isds_list *item;
8410 /* Prepare structure */
8411 item = calloc(1, sizeof(*item));
8412 if (!item) {
8413 err = IE_NOMEM;
8414 goto leave;
8416 item->destructor = (void(*)(void**))isds_box_state_period_free;
8417 if (i == 0) *history = item;
8418 else prev_item->next = item;
8419 prev_item = item;
8421 /* Extract it */
8422 xpath_ctx->node = result->nodesetval->nodeTab[i];
8423 err = extract_Period(context,
8424 (struct isds_box_state_period **) (&item->data),
8425 xpath_ctx);
8426 if (err) goto leave;
8430 leave:
8431 if (!err) {
8432 isds_log(ILF_ISDS, ILL_DEBUG,
8433 _("GetDataBoxActivityStatus request for %s box "
8434 "processed by server successfully.\n"), box_id_locale);
8436 if (err) {
8437 isds_list_free(history);
8440 free(box_id_locale);
8441 xmlXPathFreeObject(result);
8442 xmlXPathFreeContext(xpath_ctx);
8443 xmlFreeDoc(response);
8445 #else /* not HAVE_LIBCURL */
8446 err = IE_NOTSUP;
8447 #endif
8449 return err;
8453 /* Get list of permissions to send commercial messages.
8454 * @context is ISDS session context.
8455 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8456 * @permissions is a reallocated list of permissions (struct
8457 * isds_commercial_permission*) to send commercial messages from @box_id. The
8458 * order of permissions is significant as the server applies the permissions
8459 * and associated pre-paid credits in the order. Empty list means no
8460 * permission.
8461 * @return:
8462 * IE_SUCCESS if the list has been obtained correctly,
8463 * or other appropriate error. */
8464 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8465 const char *box_id, struct isds_list **permissions) {
8466 isds_error err = IE_SUCCESS;
8467 #if HAVE_LIBCURL
8468 xmlDocPtr response = NULL;
8469 xmlXPathContextPtr xpath_ctx = NULL;
8470 xmlXPathObjectPtr result = NULL;
8471 #endif
8473 if (!context) return IE_INVALID_CONTEXT;
8474 zfree(context->long_message);
8475 if (NULL == permissions) return IE_INVAL;
8476 isds_list_free(permissions);
8477 if (NULL == box_id) return IE_INVAL;
8479 #if HAVE_LIBCURL
8480 /* Check if connection is established */
8481 if (!context->curl) return IE_CONNECTION_CLOSED;
8483 /* Do request and check for success */
8484 err = build_send_dbid_request_check_response(context,
8485 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8486 BAD_CAST box_id, NULL, &response, NULL);
8487 if (!err) {
8488 isds_log(ILF_ISDS, ILL_DEBUG,
8489 _("PDZInfo request processed by server successfully.\n"));
8492 /* Extract data */
8493 /* Prepare structure */
8494 xpath_ctx = xmlXPathNewContext(response);
8495 if (!xpath_ctx) {
8496 err = IE_ERROR;
8497 goto leave;
8499 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8500 err = IE_ERROR;
8501 goto leave;
8504 /* Set context node */
8505 result = xmlXPathEvalExpression(BAD_CAST
8506 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8507 xpath_ctx);
8508 if (!result) {
8509 err = IE_ERROR;
8510 goto leave;
8512 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8513 struct isds_list *prev_item = NULL;
8515 /* Iterate over all permission records */
8516 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8517 struct isds_list *item;
8519 /* Prepare structure */
8520 item = calloc(1, sizeof(*item));
8521 if (!item) {
8522 err = IE_NOMEM;
8523 goto leave;
8525 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8526 if (i == 0) *permissions = item;
8527 else prev_item->next = item;
8528 prev_item = item;
8530 /* Extract it */
8531 xpath_ctx->node = result->nodesetval->nodeTab[i];
8532 err = extract_DbPDZRecord(context,
8533 (struct isds_commercial_permission **) (&item->data),
8534 xpath_ctx);
8535 if (err) goto leave;
8539 leave:
8540 if (err) {
8541 isds_list_free(permissions);
8544 xmlXPathFreeObject(result);
8545 xmlXPathFreeContext(xpath_ctx);
8546 xmlFreeDoc(response);
8548 #else /* not HAVE_LIBCURL */
8549 err = IE_NOTSUP;
8550 #endif
8552 return err;
8556 /* Get details about credit for sending pre-paid commercial messages.
8557 * @context is ISDS session context.
8558 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8559 * @from_date is first day of credit history to return in @history. Only
8560 * tm_year, tm_mon and tm_mday carry sane value.
8561 * @to_date is last day of credit history to return in @history. Only
8562 * tm_year, tm_mon and tm_mday carry sane value.
8563 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8564 * if you don't care. This and all other credit values are integers in
8565 * hundredths of Czech Crowns.
8566 * @email outputs notification e-mail address where notifications about credit
8567 * are sent. This is automatically reallocated string. Pass NULL if you don't
8568 * care. It can return NULL if no address is defined.
8569 * @history outputs auto-reallocated list of pointers to struct
8570 * isds_credit_event. Events in closed interval @from_time to @to_time are
8571 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8572 * are sorted by time.
8573 * @return:
8574 * IE_SUCCESS if the credit details have been obtained correctly,
8575 * or other appropriate error. Please note that server allows to retrieve
8576 * only limited history of events. */
8577 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8578 const char *box_id,
8579 const struct tm *from_date, const struct tm *to_date,
8580 long int *credit, char **email, struct isds_list **history) {
8581 isds_error err = IE_SUCCESS;
8582 #if HAVE_LIBCURL
8583 char *box_id_locale = NULL;
8584 xmlNodePtr request = NULL, node;
8585 xmlNsPtr isds_ns = NULL;
8586 xmlChar *string = NULL;
8588 xmlDocPtr response = NULL;
8589 xmlXPathContextPtr xpath_ctx = NULL;
8590 xmlXPathObjectPtr result = NULL;
8592 const xmlChar *codes[] = {
8593 BAD_CAST "1004",
8594 BAD_CAST "2011",
8595 BAD_CAST "1093",
8596 BAD_CAST "1137",
8597 BAD_CAST "1058",
8598 NULL
8600 const char *meanings[] = {
8601 "Insufficient priviledges for the box",
8602 "The box does not exist",
8603 "Date is too long (history is not available after 15 months)",
8604 "Interval is too long (limit is 3 months)",
8605 "Invalid date"
8607 const isds_error errors[] = {
8608 IE_ISDS,
8609 IE_NOEXIST,
8610 IE_DATE,
8611 IE_DATE,
8612 IE_DATE,
8614 struct code_map_isds_error map = {
8615 .codes = codes,
8616 .meanings = meanings,
8617 .errors = errors
8619 #endif
8621 if (!context) return IE_INVALID_CONTEXT;
8622 zfree(context->long_message);
8624 /* Free output argument */
8625 if (NULL != credit) *credit = 0;
8626 if (NULL != email) zfree(*email);
8627 isds_list_free(history);
8629 if (NULL == box_id) return IE_INVAL;
8631 #if HAVE_LIBCURL
8632 /* Check if connection is established */
8633 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8635 box_id_locale = _isds_utf82locale((char*)box_id);
8636 if (NULL == box_id_locale) {
8637 err = IE_NOMEM;
8638 goto leave;
8641 /* Build request */
8642 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8643 if (NULL == request) {
8644 isds_printf_message(context,
8645 _("Could not build DataBoxCreditInfo request for %s box"),
8646 box_id_locale);
8647 err = IE_ERROR;
8648 goto leave;
8650 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8651 if(!isds_ns) {
8652 isds_log_message(context, _("Could not create ISDS name space"));
8653 err = IE_ERROR;
8654 goto leave;
8656 xmlSetNs(request, isds_ns);
8658 /* Add mandatory XSD:tIdDbInput child */
8659 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8660 /* Add mandatory dates elements with optional values */
8661 if (from_date) {
8662 err = tm2datestring(from_date, &string);
8663 if (err) {
8664 isds_log_message(context,
8665 _("Could not convert `from_date' argument to ISO date "
8666 "string"));
8667 goto leave;
8669 INSERT_STRING(request, "ciFromDate", string);
8670 zfree(string);
8671 } else {
8672 INSERT_STRING(request, "ciFromDate", NULL);
8674 if (to_date) {
8675 err = tm2datestring(to_date, &string);
8676 if (err) {
8677 isds_log_message(context,
8678 _("Could not convert `to_date' argument to ISO date "
8679 "string"));
8680 goto leave;
8682 INSERT_STRING(request, "ciTodate", string);
8683 zfree(string);
8684 } else {
8685 INSERT_STRING(request, "ciTodate", NULL);
8688 /* Send request and check response*/
8689 err = send_destroy_request_check_response(context,
8690 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8691 &request, &response, NULL, &map);
8692 if (err) goto leave;
8695 /* Extract data */
8696 /* Set context to the root */
8697 xpath_ctx = xmlXPathNewContext(response);
8698 if (!xpath_ctx) {
8699 err = IE_ERROR;
8700 goto leave;
8702 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8703 err = IE_ERROR;
8704 goto leave;
8706 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8707 xpath_ctx);
8708 if (!result) {
8709 err = IE_ERROR;
8710 goto leave;
8712 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8713 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8714 err = IE_ISDS;
8715 goto leave;
8717 if (result->nodesetval->nodeNr > 1) {
8718 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8719 err = IE_ISDS;
8720 goto leave;
8722 xpath_ctx->node = result->nodesetval->nodeTab[0];
8723 xmlXPathFreeObject(result); result = NULL;
8725 /* Extract common data */
8726 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8727 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8729 /* Extract records */
8730 if (NULL == history) goto leave;
8731 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8732 xpath_ctx);
8733 if (!result) {
8734 err = IE_ERROR;
8735 goto leave;
8737 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8738 struct isds_list *prev_item = NULL;
8740 /* Iterate over all records */
8741 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8742 struct isds_list *item;
8744 /* Prepare structure */
8745 item = calloc(1, sizeof(*item));
8746 if (!item) {
8747 err = IE_NOMEM;
8748 goto leave;
8750 item->destructor = (void(*)(void**))isds_credit_event_free;
8751 if (i == 0) *history = item;
8752 else prev_item->next = item;
8753 prev_item = item;
8755 /* Extract it */
8756 xpath_ctx->node = result->nodesetval->nodeTab[i];
8757 err = extract_CiRecord(context,
8758 (struct isds_credit_event **) (&item->data),
8759 xpath_ctx);
8760 if (err) goto leave;
8764 leave:
8765 if (!err) {
8766 isds_log(ILF_ISDS, ILL_DEBUG,
8767 _("DataBoxCreditInfo request processed by server successfully.\n"));
8769 if (err) {
8770 isds_list_free(history);
8771 if (NULL != email) zfree(*email)
8774 free(box_id_locale);
8775 xmlXPathFreeObject(result);
8776 xmlXPathFreeContext(xpath_ctx);
8777 xmlFreeDoc(response);
8779 #else /* not HAVE_LIBCURL */
8780 err = IE_NOTSUP;
8781 #endif
8783 return err;
8787 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8788 * code, destroy response and log success.
8789 * @context is ISDS session context.
8790 * @service_name is name of SERVICE_DB_MANIPULATION service
8791 * @box_id is UTF-8 encoded box identifier as zero terminated string
8792 * @approval is optional external approval of box manipulation
8793 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8794 * NULL, if you don't care. */
8795 static isds_error build_send_manipulationdbid_request_check_drop_response(
8796 struct isds_ctx *context, const xmlChar *service_name,
8797 const xmlChar *box_id, const struct isds_approval *approval,
8798 xmlChar **refnumber) {
8799 isds_error err = IE_SUCCESS;
8800 #if HAVE_LIBCURL
8801 xmlDocPtr response = NULL;
8802 #endif
8804 if (!context) return IE_INVALID_CONTEXT;
8805 zfree(context->long_message);
8806 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8808 #if HAVE_LIBCURL
8809 /* Check if connection is established */
8810 if (!context->curl) return IE_CONNECTION_CLOSED;
8812 /* Do request and check for success */
8813 err = build_send_dbid_request_check_response(context,
8814 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8815 &response, refnumber);
8816 xmlFreeDoc(response);
8818 if (!err) {
8819 char *service_name_locale = _isds_utf82locale((char *) service_name);
8820 isds_log(ILF_ISDS, ILL_DEBUG,
8821 _("%s request processed by server successfully.\n"),
8822 service_name_locale);
8823 free(service_name_locale);
8825 #else /* not HAVE_LIBCURL */
8826 err = IE_NOTSUP;
8827 #endif
8829 return err;
8833 /* Switch box into state where box can receive commercial messages (off by
8834 * default)
8835 * @context is ISDS session context.
8836 * @box_id is UTF-8 encoded box identifier as zero terminated string
8837 * @allow is true for enable, false for disable commercial messages income
8838 * @approval is optional external approval of box manipulation
8839 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8840 * NULL, if you don't care. */
8841 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8842 const char *box_id, const _Bool allow,
8843 const struct isds_approval *approval, char **refnumber) {
8844 return build_send_manipulationdbid_request_check_drop_response(context,
8845 (allow) ? BAD_CAST "SetOpenAddressing" :
8846 BAD_CAST "ClearOpenAddressing",
8847 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8851 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8852 * message acceptance). This is just a box permission. Sender must apply
8853 * such role by sending each message.
8854 * @context is ISDS session context.
8855 * @box_id is UTF-8 encoded box identifier as zero terminated string
8856 * @allow is true for enable, false for disable OVM role permission
8857 * @approval is optional external approval of box manipulation
8858 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8859 * NULL, if you don't care. */
8860 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8861 const char *box_id, const _Bool allow,
8862 const struct isds_approval *approval, char **refnumber) {
8863 return build_send_manipulationdbid_request_check_drop_response(context,
8864 (allow) ? BAD_CAST "SetEffectiveOVM" :
8865 BAD_CAST "ClearEffectiveOVM",
8866 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8870 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8871 * code, destroy response and log success.
8872 * @context is ISDS session context.
8873 * @service_name is name of SERVICE_DB_MANIPULATION service
8874 * @owner is structure describing box. aifoIsds, address->adCode,
8875 * address->adDistrict members are ignored.
8876 * @approval is optional external approval of box manipulation
8877 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8878 * NULL, if you don't care. */
8879 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8880 struct isds_ctx *context, const xmlChar *service_name,
8881 const struct isds_DbOwnerInfo *owner,
8882 const struct isds_approval *approval, xmlChar **refnumber) {
8883 isds_error err = IE_SUCCESS;
8884 #if HAVE_LIBCURL
8885 char *service_name_locale = NULL;
8886 xmlNodePtr request = NULL, db_owner_info;
8887 xmlNsPtr isds_ns = NULL;
8888 #endif
8891 if (!context) return IE_INVALID_CONTEXT;
8892 zfree(context->long_message);
8893 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8895 #if HAVE_LIBCURL
8896 service_name_locale = _isds_utf82locale((char*)service_name);
8897 if (!service_name_locale) {
8898 err = IE_NOMEM;
8899 goto leave;
8902 /* Build request */
8903 request = xmlNewNode(NULL, service_name);
8904 if (!request) {
8905 isds_printf_message(context,
8906 _("Could not build %s request"), service_name_locale);
8907 err = IE_ERROR;
8908 goto leave;
8910 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8911 if(!isds_ns) {
8912 isds_log_message(context, _("Could not create ISDS name space"));
8913 err = IE_ERROR;
8914 goto leave;
8916 xmlSetNs(request, isds_ns);
8919 /* Add XSD:tOwnerInfoInput child*/
8920 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8921 err = insert_DbOwnerInfo(context, owner, 0, db_owner_info);
8922 if (err) goto leave;
8924 /* Add XSD:gExtApproval*/
8925 err = insert_GExtApproval(context, approval, request);
8926 if (err) goto leave;
8928 /* Send it to server and process response */
8929 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8930 service_name, &request, refnumber);
8932 leave:
8933 xmlFreeNode(request);
8934 free(service_name_locale);
8935 #else /* not HAVE_LIBCURL */
8936 err = IE_NOTSUP;
8937 #endif
8939 return err;
8943 /* Switch box accessibility state on request of box owner.
8944 * Despite the name, owner must do the request off-line. This function is
8945 * designed for such off-line meeting points (e.g. Czech POINT).
8946 * @context is ISDS session context.
8947 * @box identifies box to switch accessibility state. aifoIsds,
8948 * address->adCode, address->adDistrict members are ignored.
8949 * @allow is true for making accessible, false to disallow access.
8950 * @approval is optional external approval of box manipulation
8951 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8952 * NULL, if you don't care. */
8953 isds_error isds_switch_box_accessibility_on_owner_request(
8954 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8955 const _Bool allow, const struct isds_approval *approval,
8956 char **refnumber) {
8957 return build_send_manipulationdbowner_request_check_drop_response(context,
8958 (allow) ? BAD_CAST "EnableOwnDataBox" :
8959 BAD_CAST "DisableOwnDataBox",
8960 box, approval, (xmlChar **) refnumber);
8964 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8965 * date.
8966 * @context is ISDS session context.
8967 * @box identifies box to switch accessibility state. aifoIsds,
8968 * address->adCode, address->adDistrict members are ignored.
8969 * @since is date since accessibility has been denied. This can be past too.
8970 * Only tm_year, tm_mon and tm_mday carry sane value.
8971 * @approval is optional external approval of box manipulation
8972 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8973 * NULL, if you don't care. */
8974 isds_error isds_disable_box_accessibility_externaly(
8975 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8976 const struct tm *since, const struct isds_approval *approval,
8977 char **refnumber) {
8978 isds_error err = IE_SUCCESS;
8979 #if HAVE_LIBCURL
8980 char *service_name_locale = NULL;
8981 xmlNodePtr request = NULL, node;
8982 xmlNsPtr isds_ns = NULL;
8983 xmlChar *string = NULL;
8984 #endif
8987 if (!context) return IE_INVALID_CONTEXT;
8988 zfree(context->long_message);
8989 if (!box || !since) return IE_INVAL;
8991 #if HAVE_LIBCURL
8992 /* Build request */
8993 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8994 if (!request) {
8995 isds_printf_message(context,
8996 _("Could not build %s request"), "DisableDataBoxExternally");
8997 err = IE_ERROR;
8998 goto leave;
9000 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9001 if(!isds_ns) {
9002 isds_log_message(context, _("Could not create ISDS name space"));
9003 err = IE_ERROR;
9004 goto leave;
9006 xmlSetNs(request, isds_ns);
9009 /* Add @box identification */
9010 INSERT_ELEMENT(node, request, "dbOwnerInfo");
9011 err = insert_DbOwnerInfo(context, box, 0, node);
9012 if (err) goto leave;
9014 /* Add @since date */
9015 err = tm2datestring(since, &string);
9016 if(err) {
9017 isds_log_message(context,
9018 _("Could not convert `since' argument to ISO date string"));
9019 goto leave;
9021 INSERT_STRING(request, "dbOwnerDisableDate", string);
9022 zfree(string);
9024 /* Add @approval */
9025 err = insert_GExtApproval(context, approval, request);
9026 if (err) goto leave;
9028 /* Send it to server and process response */
9029 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
9030 BAD_CAST "DisableDataBoxExternally", &request,
9031 (xmlChar **) refnumber);
9033 leave:
9034 free(string);
9035 xmlFreeNode(request);
9036 free(service_name_locale);
9037 #else /* not HAVE_LIBCURL */
9038 err = IE_NOTSUP;
9039 #endif
9041 return err;
9045 #if HAVE_LIBCURL
9046 /* Insert struct isds_message data (envelope (recipient data optional) and
9047 * documents into XML tree
9048 * @context is session context
9049 * @outgoing_message is libisds structure with message data
9050 * @create_message is XML CreateMessage or CreateMultipleMessage element
9051 * @process_recipient true for recipient data serialization, false for no
9052 * serialization */
9053 static isds_error insert_envelope_files(struct isds_ctx *context,
9054 const struct isds_message *outgoing_message, xmlNodePtr create_message,
9055 const _Bool process_recipient) {
9057 isds_error err = IE_SUCCESS;
9058 xmlNodePtr envelope, dm_files, node;
9059 xmlChar *string = NULL;
9061 if (!context) return IE_INVALID_CONTEXT;
9062 if (!outgoing_message || !create_message) return IE_INVAL;
9065 /* Build envelope */
9066 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
9067 if (!envelope) {
9068 isds_printf_message(context, _("Could not add dmEnvelope child to "
9069 "%s element"), create_message->name);
9070 return IE_ERROR;
9073 if (!outgoing_message->envelope) {
9074 isds_log_message(context, _("Outgoing message is missing envelope"));
9075 err = IE_INVAL;
9076 goto leave;
9079 /* Insert optional message type */
9080 err = insert_message_type(context, outgoing_message->envelope->dmType,
9081 envelope);
9082 if (err) goto leave;
9084 INSERT_STRING(envelope, "dmSenderOrgUnit",
9085 outgoing_message->envelope->dmSenderOrgUnit);
9086 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
9087 outgoing_message->envelope->dmSenderOrgUnitNum, string);
9089 if (process_recipient) {
9090 if (!outgoing_message->envelope->dbIDRecipient) {
9091 isds_log_message(context,
9092 _("Outgoing message is missing recipient box identifier"));
9093 err = IE_INVAL;
9094 goto leave;
9096 INSERT_STRING(envelope, "dbIDRecipient",
9097 outgoing_message->envelope->dbIDRecipient);
9099 INSERT_STRING(envelope, "dmRecipientOrgUnit",
9100 outgoing_message->envelope->dmRecipientOrgUnit);
9101 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
9102 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
9103 INSERT_STRING(envelope, "dmToHands",
9104 outgoing_message->envelope->dmToHands);
9107 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
9108 "dmAnnotation");
9109 INSERT_STRING(envelope, "dmAnnotation",
9110 outgoing_message->envelope->dmAnnotation);
9112 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
9113 0, 50, "dmRecipientRefNumber");
9114 INSERT_STRING(envelope, "dmRecipientRefNumber",
9115 outgoing_message->envelope->dmRecipientRefNumber);
9117 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
9118 0, 50, "dmSenderRefNumber");
9119 INSERT_STRING(envelope, "dmSenderRefNumber",
9120 outgoing_message->envelope->dmSenderRefNumber);
9122 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
9123 0, 50, "dmRecipientIdent");
9124 INSERT_STRING(envelope, "dmRecipientIdent",
9125 outgoing_message->envelope->dmRecipientIdent);
9127 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
9128 0, 50, "dmSenderIdent");
9129 INSERT_STRING(envelope, "dmSenderIdent",
9130 outgoing_message->envelope->dmSenderIdent);
9132 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
9133 outgoing_message->envelope->dmLegalTitleLaw, string);
9134 INSERT_LONGINT(envelope, "dmLegalTitleYear",
9135 outgoing_message->envelope->dmLegalTitleYear, string);
9136 INSERT_STRING(envelope, "dmLegalTitleSect",
9137 outgoing_message->envelope->dmLegalTitleSect);
9138 INSERT_STRING(envelope, "dmLegalTitlePar",
9139 outgoing_message->envelope->dmLegalTitlePar);
9140 INSERT_STRING(envelope, "dmLegalTitlePoint",
9141 outgoing_message->envelope->dmLegalTitlePoint);
9143 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
9144 outgoing_message->envelope->dmPersonalDelivery);
9145 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
9146 outgoing_message->envelope->dmAllowSubstDelivery);
9148 /* ???: Should we require value for dbEffectiveOVM sender?
9149 * ISDS has default as true */
9150 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
9151 INSERT_BOOLEAN(envelope, "dmPublishOwnID",
9152 outgoing_message->envelope->dmPublishOwnID);
9155 /* Append dmFiles */
9156 if (!outgoing_message->documents) {
9157 isds_log_message(context,
9158 _("Outgoing message is missing list of documents"));
9159 err = IE_INVAL;
9160 goto leave;
9162 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
9163 if (!dm_files) {
9164 isds_printf_message(context, _("Could not add dmFiles child to "
9165 "%s element"), create_message->name);
9166 err = IE_ERROR;
9167 goto leave;
9170 /* Check for document hierarchy */
9171 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
9172 if (err) goto leave;
9174 /* Process each document */
9175 for (struct isds_list *item =
9176 (struct isds_list *) outgoing_message->documents;
9177 item; item = item->next) {
9178 if (!item->data) {
9179 isds_log_message(context,
9180 _("List of documents contains empty item"));
9181 err = IE_INVAL;
9182 goto leave;
9184 /* FIXME: Check for dmFileMetaType and for document references.
9185 * Only first document can be of MAIN type */
9186 err = insert_document(context, (struct isds_document*) item->data,
9187 dm_files);
9189 if (err) goto leave;
9192 leave:
9193 free(string);
9194 return err;
9196 #endif /* HAVE_LIBCURL */
9199 /* Send a message via ISDS to a recipient
9200 * @context is session context
9201 * @outgoing_message is message to send; Some members are mandatory (like
9202 * dbIDRecipient), some are optional and some are irrelevant (especially data
9203 * about sender). Included pointer to isds_list documents must contain at
9204 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9205 * members will be filled with valid data from ISDS. Exact list of write
9206 * members is subject to change. Currently dmID is changed.
9207 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9208 isds_error isds_send_message(struct isds_ctx *context,
9209 struct isds_message *outgoing_message) {
9211 isds_error err = IE_SUCCESS;
9212 #if HAVE_LIBCURL
9213 xmlNsPtr isds_ns = NULL;
9214 xmlNodePtr request = NULL;
9215 xmlDocPtr response = NULL;
9216 xmlChar *code = NULL, *message = NULL;
9217 xmlXPathContextPtr xpath_ctx = NULL;
9218 xmlXPathObjectPtr result = NULL;
9219 /*_Bool message_is_complete = 0;*/
9220 #endif
9222 if (!context) return IE_INVALID_CONTEXT;
9223 zfree(context->long_message);
9224 if (!outgoing_message) return IE_INVAL;
9226 #if HAVE_LIBCURL
9227 /* Check if connection is established
9228 * TODO: This check should be done downstairs. */
9229 if (!context->curl) return IE_CONNECTION_CLOSED;
9232 /* Build CreateMessage request */
9233 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
9234 if (!request) {
9235 isds_log_message(context,
9236 _("Could not build CreateMessage request"));
9237 return IE_ERROR;
9239 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9240 if(!isds_ns) {
9241 isds_log_message(context, _("Could not create ISDS name space"));
9242 xmlFreeNode(request);
9243 return IE_ERROR;
9245 xmlSetNs(request, isds_ns);
9247 /* Append envelope and files */
9248 err = insert_envelope_files(context, outgoing_message, request, 1);
9249 if (err) goto leave;
9252 /* Signal we can serialize message since now */
9253 /*message_is_complete = 1;*/
9256 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
9258 /* Sent request */
9259 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9261 /* Don't' destroy request, we want to provide it to application later */
9263 if (err) {
9264 isds_log(ILF_ISDS, ILL_DEBUG,
9265 _("Processing ISDS response on CreateMessage "
9266 "request failed\n"));
9267 goto leave;
9270 /* Check for response status */
9271 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9272 &code, &message, NULL);
9273 if (err) {
9274 isds_log(ILF_ISDS, ILL_DEBUG,
9275 _("ISDS response on CreateMessage request "
9276 "is missing status\n"));
9277 goto leave;
9280 /* Request processed, but refused by server or server failed */
9281 if (xmlStrcmp(code, BAD_CAST "0000")) {
9282 char *box_id_locale =
9283 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9284 char *code_locale = _isds_utf82locale((char*)code);
9285 char *message_locale = _isds_utf82locale((char*)message);
9286 isds_log(ILF_ISDS, ILL_DEBUG,
9287 _("Server did not accept message for %s on CreateMessage "
9288 "request (code=%s, message=%s)\n"),
9289 box_id_locale, code_locale, message_locale);
9290 isds_log_message(context, message_locale);
9291 free(box_id_locale);
9292 free(code_locale);
9293 free(message_locale);
9294 err = IE_ISDS;
9295 goto leave;
9299 /* Extract data */
9300 xpath_ctx = xmlXPathNewContext(response);
9301 if (!xpath_ctx) {
9302 err = IE_ERROR;
9303 goto leave;
9305 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9306 err = IE_ERROR;
9307 goto leave;
9309 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
9310 xpath_ctx);
9311 if (!result) {
9312 err = IE_ERROR;
9313 goto leave;
9315 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9316 isds_log_message(context, _("Missing CreateMessageResponse element"));
9317 err = IE_ISDS;
9318 goto leave;
9320 if (result->nodesetval->nodeNr > 1) {
9321 isds_log_message(context, _("Multiple CreateMessageResponse element"));
9322 err = IE_ISDS;
9323 goto leave;
9325 xpath_ctx->node = result->nodesetval->nodeTab[0];
9326 xmlXPathFreeObject(result); result = NULL;
9328 if (outgoing_message->envelope->dmID) {
9329 free(outgoing_message->envelope->dmID);
9330 outgoing_message->envelope->dmID = NULL;
9332 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
9333 if (!outgoing_message->envelope->dmID) {
9334 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
9335 "but did not return assigned message ID\n"));
9338 leave:
9339 /* TODO: Serialize message into structure member raw */
9340 /* XXX: Each web service transport message in different format.
9341 * Therefore it's not possible to save them directly.
9342 * To save them, one must figure out common format.
9343 * We can leave it on application, or we can implement the ESS format. */
9344 /*if (message_is_complete) {
9345 if (outgoing_message->envelope->dmID) {
9347 /* Add assigned message ID as first child*/
9348 /*xmlNodePtr dmid_text = xmlNewText(
9349 (xmlChar *) outgoing_message->envelope->dmID);
9350 if (!dmid_text) goto serialization_failed;
9352 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9353 BAD_CAST "dmID");
9354 if (!dmid_element) {
9355 xmlFreeNode(dmid_text);
9356 goto serialization_failed;
9359 xmlNodePtr dmid_element_with_text =
9360 xmlAddChild(dmid_element, dmid_text);
9361 if (!dmid_element_with_text) {
9362 xmlFreeNode(dmid_element);
9363 xmlFreeNode(dmid_text);
9364 goto serialization_failed;
9367 node = xmlAddPrevSibling(envelope->childern,
9368 dmid_element_with_text);
9369 if (!node) {
9370 xmlFreeNodeList(dmid_element_with_text);
9371 goto serialization_failed;
9375 /* Serialize message with ID into raw */
9376 /*buffer = serialize_element(envelope)*/
9377 /* }
9379 serialization_failed:
9383 /* Clean up */
9384 xmlXPathFreeObject(result);
9385 xmlXPathFreeContext(xpath_ctx);
9387 free(code);
9388 free(message);
9389 xmlFreeDoc(response);
9390 xmlFreeNode(request);
9392 if (!err)
9393 isds_log(ILF_ISDS, ILL_DEBUG,
9394 _("CreateMessage request processed by server "
9395 "successfully.\n"));
9396 #else /* not HAVE_LIBCURL */
9397 err = IE_NOTSUP;
9398 #endif
9400 return err;
9404 /* Send a message via ISDS to a multiple recipients
9405 * @context is session context
9406 * @outgoing_message is message to send; Some members are mandatory,
9407 * some are optional and some are irrelevant (especially data
9408 * about sender). Data about recipient will be substituted by ISDS from
9409 * @copies. Included pointer to isds_list documents must
9410 * contain at least one document of FILEMETATYPE_MAIN.
9411 * @copies is list of isds_message_copy structures addressing all desired
9412 * recipients. This is read-write structure, some members will be filled with
9413 * valid data from ISDS (message IDs, error codes, error descriptions).
9414 * @return
9415 * ISDS_SUCCESS if all messages have been sent
9416 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9417 * succeeded messages can be identified by copies->data->error),
9418 * or other error code if something other goes wrong. */
9419 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
9420 const struct isds_message *outgoing_message,
9421 struct isds_list *copies) {
9423 isds_error err = IE_SUCCESS;
9424 #if HAVE_LIBCURL
9425 isds_error append_err;
9426 xmlNsPtr isds_ns = NULL;
9427 xmlNodePtr request = NULL, recipients, recipient, node;
9428 struct isds_list *item;
9429 struct isds_message_copy *copy;
9430 xmlDocPtr response = NULL;
9431 xmlChar *code = NULL, *message = NULL;
9432 xmlXPathContextPtr xpath_ctx = NULL;
9433 xmlXPathObjectPtr result = NULL;
9434 xmlChar *string = NULL;
9435 int i;
9436 #endif
9438 if (!context) return IE_INVALID_CONTEXT;
9439 zfree(context->long_message);
9440 if (!outgoing_message || !copies) return IE_INVAL;
9442 #if HAVE_LIBCURL
9443 /* Check if connection is established
9444 * TODO: This check should be done downstairs. */
9445 if (!context->curl) return IE_CONNECTION_CLOSED;
9448 /* Build CreateMultipleMessage request */
9449 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9450 if (!request) {
9451 isds_log_message(context,
9452 _("Could not build CreateMultipleMessage request"));
9453 return IE_ERROR;
9455 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9456 if(!isds_ns) {
9457 isds_log_message(context, _("Could not create ISDS name space"));
9458 xmlFreeNode(request);
9459 return IE_ERROR;
9461 xmlSetNs(request, isds_ns);
9464 /* Build recipients */
9465 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9466 if (!recipients) {
9467 isds_log_message(context, _("Could not add dmRecipients child to "
9468 "CreateMultipleMessage element"));
9469 xmlFreeNode(request);
9470 return IE_ERROR;
9473 /* Insert each recipient */
9474 for (item = copies; item; item = item->next) {
9475 copy = (struct isds_message_copy *) item->data;
9476 if (!copy) {
9477 isds_log_message(context,
9478 _("`copies' list item contains empty data"));
9479 err = IE_INVAL;
9480 goto leave;
9483 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9484 if (!recipient) {
9485 isds_log_message(context, _("Could not add dmRecipient child to "
9486 "dmRecipients element"));
9487 err = IE_ERROR;
9488 goto leave;
9491 if (!copy->dbIDRecipient) {
9492 isds_log_message(context,
9493 _("Message copy is missing recipient box identifier"));
9494 err = IE_INVAL;
9495 goto leave;
9497 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9498 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9499 copy->dmRecipientOrgUnit);
9500 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9501 copy->dmRecipientOrgUnitNum, string);
9502 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9505 /* Append envelope and files */
9506 err = insert_envelope_files(context, outgoing_message, request, 0);
9507 if (err) goto leave;
9510 isds_log(ILF_ISDS, ILL_DEBUG,
9511 _("Sending CreateMultipleMessage request to ISDS\n"));
9513 /* Sent request */
9514 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9515 if (err) {
9516 isds_log(ILF_ISDS, ILL_DEBUG,
9517 _("Processing ISDS response on CreateMultipleMessage "
9518 "request failed\n"));
9519 goto leave;
9522 /* Check for response status */
9523 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9524 &code, &message, NULL);
9525 if (err) {
9526 isds_log(ILF_ISDS, ILL_DEBUG,
9527 _("ISDS response on CreateMultipleMessage request "
9528 "is missing status\n"));
9529 goto leave;
9532 /* Request processed, but some copies failed */
9533 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9534 char *box_id_locale =
9535 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9536 char *code_locale = _isds_utf82locale((char*)code);
9537 char *message_locale = _isds_utf82locale((char*)message);
9538 isds_log(ILF_ISDS, ILL_DEBUG,
9539 _("Server did accept message for multiple recipients "
9540 "on CreateMultipleMessage request but delivery to "
9541 "some of them failed (code=%s, message=%s)\n"),
9542 box_id_locale, code_locale, message_locale);
9543 isds_log_message(context, message_locale);
9544 free(box_id_locale);
9545 free(code_locale);
9546 free(message_locale);
9547 err = IE_PARTIAL_SUCCESS;
9550 /* Request refused by server as whole */
9551 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9552 char *box_id_locale =
9553 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9554 char *code_locale = _isds_utf82locale((char*)code);
9555 char *message_locale = _isds_utf82locale((char*)message);
9556 isds_log(ILF_ISDS, ILL_DEBUG,
9557 _("Server did not accept message for multiple recipients "
9558 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9559 box_id_locale, code_locale, message_locale);
9560 isds_log_message(context, message_locale);
9561 free(box_id_locale);
9562 free(code_locale);
9563 free(message_locale);
9564 err = IE_ISDS;
9565 goto leave;
9569 /* Extract data */
9570 xpath_ctx = xmlXPathNewContext(response);
9571 if (!xpath_ctx) {
9572 err = IE_ERROR;
9573 goto leave;
9575 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9576 err = IE_ERROR;
9577 goto leave;
9579 result = xmlXPathEvalExpression(
9580 BAD_CAST "/isds:CreateMultipleMessageResponse"
9581 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9582 xpath_ctx);
9583 if (!result) {
9584 err = IE_ERROR;
9585 goto leave;
9587 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9588 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9589 err = IE_ISDS;
9590 goto leave;
9593 /* Extract message ID and delivery status for each copy */
9594 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9595 item = item->next, i++) {
9596 copy = (struct isds_message_copy *) item->data;
9597 xpath_ctx->node = result->nodesetval->nodeTab[i];
9599 append_err = append_TMStatus(context, copy, xpath_ctx);
9600 if (append_err) {
9601 err = append_err;
9602 goto leave;
9605 if (item || i < result->nodesetval->nodeNr) {
9606 isds_printf_message(context, _("ISDS returned unexpected number of "
9607 "message copy delivery states: %d"),
9608 result->nodesetval->nodeNr);
9609 err = IE_ISDS;
9610 goto leave;
9614 leave:
9615 /* Clean up */
9616 free(string);
9617 xmlXPathFreeObject(result);
9618 xmlXPathFreeContext(xpath_ctx);
9620 free(code);
9621 free(message);
9622 xmlFreeDoc(response);
9623 xmlFreeNode(request);
9625 if (!err)
9626 isds_log(ILF_ISDS, ILL_DEBUG,
9627 _("CreateMultipleMessageResponse request processed by server "
9628 "successfully.\n"));
9629 #else /* not HAVE_LIBCURL */
9630 err = IE_NOTSUP;
9631 #endif
9633 return err;
9637 /* Get list of messages. This is common core for getting sent or received
9638 * messages.
9639 * Any criterion argument can be NULL, if you don't care about it.
9640 * @context is session context. Must not be NULL.
9641 * @outgoing_direction is true if you want list of outgoing messages,
9642 * it's false if you want incoming messages.
9643 * @from_time is minimal time and date of message sending inclusive.
9644 * @to_time is maximal time and date of message sending inclusive
9645 * @organization_unit_number is number of sender/recipient respectively.
9646 * @status_filter is bit field of isds_message_status values. Use special
9647 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9648 * all values, you can use bit-wise arithmetic if you want.)
9649 * @offset is index of first message we are interested in. First message is 1.
9650 * Set to 0 (or 1) if you don't care.
9651 * @number is maximal length of list you want to get as input value, outputs
9652 * number of messages matching these criteria. Can be NULL if you don't care
9653 * (applies to output value either).
9654 * @messages is automatically reallocated list of isds_message's. Be ware that
9655 * it returns only brief overview (envelope and some other fields) about each
9656 * message, not the complete message. FIXME: Specify exact fields.
9657 * The list is sorted by delivery time in ascending order.
9658 * Use NULL if you don't care about don't need the data (useful if you want to
9659 * know only the @number). If you provide &NULL, list will be allocated on
9660 * heap, if you provide pointer to non-NULL, list will be freed automatically
9661 * at first. Also in case of error the list will be NULLed.
9662 * @return IE_SUCCESS or appropriate error code. */
9663 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9664 _Bool outgoing_direction,
9665 const struct timeval *from_time, const struct timeval *to_time,
9666 const long int *organization_unit_number,
9667 const unsigned int status_filter,
9668 const unsigned long int offset, unsigned long int *number,
9669 struct isds_list **messages) {
9671 isds_error err = IE_SUCCESS;
9672 #if HAVE_LIBCURL
9673 xmlNsPtr isds_ns = NULL;
9674 xmlNodePtr request = NULL, node;
9675 xmlDocPtr response = NULL;
9676 xmlChar *code = NULL, *message = NULL;
9677 xmlXPathContextPtr xpath_ctx = NULL;
9678 xmlXPathObjectPtr result = NULL;
9679 xmlChar *string = NULL;
9680 int count = 0;
9681 #endif
9683 if (!context) return IE_INVALID_CONTEXT;
9684 zfree(context->long_message);
9686 /* Free former message list if any */
9687 if (messages) isds_list_free(messages);
9689 #if HAVE_LIBCURL
9690 /* Check if connection is established
9691 * TODO: This check should be done downstairs. */
9692 if (!context->curl) return IE_CONNECTION_CLOSED;
9694 /* Build GetListOf*Messages request */
9695 request = xmlNewNode(NULL,
9696 (outgoing_direction) ?
9697 BAD_CAST "GetListOfSentMessages" :
9698 BAD_CAST "GetListOfReceivedMessages"
9700 if (!request) {
9701 isds_log_message(context,
9702 (outgoing_direction) ?
9703 _("Could not build GetListOfSentMessages request") :
9704 _("Could not build GetListOfReceivedMessages request")
9706 return IE_ERROR;
9708 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9709 if(!isds_ns) {
9710 isds_log_message(context, _("Could not create ISDS name space"));
9711 xmlFreeNode(request);
9712 return IE_ERROR;
9714 xmlSetNs(request, isds_ns);
9717 if (from_time) {
9718 err = timeval2timestring(from_time, &string);
9719 if (err) goto leave;
9721 INSERT_STRING(request, "dmFromTime", string);
9722 free(string); string = NULL;
9724 if (to_time) {
9725 err = timeval2timestring(to_time, &string);
9726 if (err) goto leave;
9728 INSERT_STRING(request, "dmToTime", string);
9729 free(string); string = NULL;
9731 if (outgoing_direction) {
9732 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9733 organization_unit_number, string);
9734 } else {
9735 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9736 organization_unit_number, string);
9739 if (status_filter > MESSAGESTATE_ANY) {
9740 isds_printf_message(context,
9741 _("Invalid message state filter value: %ld"), status_filter);
9742 err = IE_INVAL;
9743 goto leave;
9745 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9747 if (offset > 0 ) {
9748 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9749 } else {
9750 INSERT_STRING(request, "dmOffset", "1");
9753 /* number 0 means no limit */
9754 if (number && *number == 0) {
9755 INSERT_STRING(request, "dmLimit", NULL);
9756 } else {
9757 INSERT_ULONGINT(request, "dmLimit", number, string);
9761 isds_log(ILF_ISDS, ILL_DEBUG,
9762 (outgoing_direction) ?
9763 _("Sending GetListOfSentMessages request to ISDS\n") :
9764 _("Sending GetListOfReceivedMessages request to ISDS\n")
9767 /* Sent request */
9768 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9769 xmlFreeNode(request); request = NULL;
9771 if (err) {
9772 isds_log(ILF_ISDS, ILL_DEBUG,
9773 (outgoing_direction) ?
9774 _("Processing ISDS response on GetListOfSentMessages "
9775 "request failed\n") :
9776 _("Processing ISDS response on GetListOfReceivedMessages "
9777 "request failed\n")
9779 goto leave;
9782 /* Check for response status */
9783 err = isds_response_status(context, SERVICE_DM_INFO, response,
9784 &code, &message, NULL);
9785 if (err) {
9786 isds_log(ILF_ISDS, ILL_DEBUG,
9787 (outgoing_direction) ?
9788 _("ISDS response on GetListOfSentMessages request "
9789 "is missing status\n") :
9790 _("ISDS response on GetListOfReceivedMessages request "
9791 "is missing status\n")
9793 goto leave;
9796 /* Request processed, but nothing found */
9797 if (xmlStrcmp(code, BAD_CAST "0000")) {
9798 char *code_locale = _isds_utf82locale((char*)code);
9799 char *message_locale = _isds_utf82locale((char*)message);
9800 isds_log(ILF_ISDS, ILL_DEBUG,
9801 (outgoing_direction) ?
9802 _("Server refused GetListOfSentMessages request "
9803 "(code=%s, message=%s)\n") :
9804 _("Server refused GetListOfReceivedMessages request "
9805 "(code=%s, message=%s)\n"),
9806 code_locale, message_locale);
9807 isds_log_message(context, message_locale);
9808 free(code_locale);
9809 free(message_locale);
9810 err = IE_ISDS;
9811 goto leave;
9815 /* Extract data */
9816 xpath_ctx = xmlXPathNewContext(response);
9817 if (!xpath_ctx) {
9818 err = IE_ERROR;
9819 goto leave;
9821 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9822 err = IE_ERROR;
9823 goto leave;
9825 result = xmlXPathEvalExpression(
9826 (outgoing_direction) ?
9827 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9828 "isds:dmRecords/isds:dmRecord" :
9829 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9830 "isds:dmRecords/isds:dmRecord",
9831 xpath_ctx);
9832 if (!result) {
9833 err = IE_ERROR;
9834 goto leave;
9837 /* Fill output arguments in */
9838 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9839 struct isds_envelope *envelope;
9840 struct isds_list *item = NULL, *last_item = NULL;
9842 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9843 /* Create new message */
9844 item = calloc(1, sizeof(*item));
9845 if (!item) {
9846 err = IE_NOMEM;
9847 goto leave;
9849 item->destructor = (void(*)(void**)) &isds_message_free;
9850 item->data = calloc(1, sizeof(struct isds_message));
9851 if (!item->data) {
9852 isds_list_free(&item);
9853 err = IE_NOMEM;
9854 goto leave;
9857 /* Extract envelope data */
9858 xpath_ctx->node = result->nodesetval->nodeTab[count];
9859 envelope = NULL;
9860 err = extract_DmRecord(context, &envelope, xpath_ctx);
9861 if (err) {
9862 isds_list_free(&item);
9863 goto leave;
9866 /* Attach extracted envelope */
9867 ((struct isds_message *) item->data)->envelope = envelope;
9869 /* Append new message into the list */
9870 if (!*messages) {
9871 *messages = last_item = item;
9872 } else {
9873 last_item->next = item;
9874 last_item = item;
9878 if (number) *number = count;
9880 leave:
9881 if (err) {
9882 isds_list_free(messages);
9885 free(string);
9886 xmlXPathFreeObject(result);
9887 xmlXPathFreeContext(xpath_ctx);
9889 free(code);
9890 free(message);
9891 xmlFreeDoc(response);
9892 xmlFreeNode(request);
9894 if (!err)
9895 isds_log(ILF_ISDS, ILL_DEBUG,
9896 (outgoing_direction) ?
9897 _("GetListOfSentMessages request processed by server "
9898 "successfully.\n") :
9899 _("GetListOfReceivedMessages request processed by server "
9900 "successfully.\n")
9902 #else /* not HAVE_LIBCURL */
9903 err = IE_NOTSUP;
9904 #endif
9905 return err;
9909 /* Get list of outgoing (already sent) messages.
9910 * Any criterion argument can be NULL, if you don't care about it.
9911 * @context is session context. Must not be NULL.
9912 * @from_time is minimal time and date of message sending inclusive.
9913 * @to_time is maximal time and date of message sending inclusive
9914 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9915 * @status_filter is bit field of isds_message_status values. Use special
9916 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9917 * all values, you can use bit-wise arithmetic if you want.)
9918 * @offset is index of first message we are interested in. First message is 1.
9919 * Set to 0 (or 1) if you don't care.
9920 * @number is maximal length of list you want to get as input value, outputs
9921 * number of messages matching these criteria. Can be NULL if you don't care
9922 * (applies to output value either).
9923 * @messages is automatically reallocated list of isds_message's. Be ware that
9924 * it returns only brief overview (envelope and some other fields) about each
9925 * message, not the complete message. FIXME: Specify exact fields.
9926 * The list is sorted by delivery time in ascending order.
9927 * Use NULL if you don't care about the meta data (useful if you want to know
9928 * only the @number). If you provide &NULL, list will be allocated on heap,
9929 * if you provide pointer to non-NULL, list will be freed automatically at
9930 * first. Also in case of error the list will be NULLed.
9931 * @return IE_SUCCESS or appropriate error code. */
9932 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9933 const struct timeval *from_time, const struct timeval *to_time,
9934 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9935 const unsigned long int offset, unsigned long int *number,
9936 struct isds_list **messages) {
9938 return isds_get_list_of_messages(
9939 context, 1,
9940 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9941 offset, number,
9942 messages);
9946 /* Get list of incoming (addressed to you) messages.
9947 * Any criterion argument can be NULL, if you don't care about it.
9948 * @context is session context. Must not be NULL.
9949 * @from_time is minimal time and date of message sending inclusive.
9950 * @to_time is maximal time and date of message sending inclusive
9951 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9952 * @status_filter is bit field of isds_message_status values. Use special
9953 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9954 * all values, you can use bit-wise arithmetic if you want.)
9955 * @offset is index of first message we are interested in. First message is 1.
9956 * Set to 0 (or 1) if you don't care.
9957 * @number is maximal length of list you want to get as input value, outputs
9958 * number of messages matching these criteria. Can be NULL if you don't care
9959 * (applies to output value either).
9960 * @messages is automatically reallocated list of isds_message's. Be ware that
9961 * it returns only brief overview (envelope and some other fields) about each
9962 * message, not the complete message. FIXME: Specify exact fields.
9963 * Use NULL if you don't care about the meta data (useful if you want to know
9964 * only the @number). If you provide &NULL, list will be allocated on heap,
9965 * if you provide pointer to non-NULL, list will be freed automatically at
9966 * first. Also in case of error the list will be NULLed.
9967 * @return IE_SUCCESS or appropriate error code. */
9968 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9969 const struct timeval *from_time, const struct timeval *to_time,
9970 const long int *dmRecipientOrgUnitNum,
9971 const unsigned int status_filter,
9972 const unsigned long int offset, unsigned long int *number,
9973 struct isds_list **messages) {
9975 return isds_get_list_of_messages(
9976 context, 0,
9977 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9978 offset, number,
9979 messages);
9983 /* Get list of sent message state changes.
9984 * Any criterion argument can be NULL, if you don't care about it.
9985 * @context is session context. Must not be NULL.
9986 * @from_time is minimal time and date of status changes inclusive
9987 * @to_time is maximal time and date of status changes inclusive
9988 * @changed_states is automatically reallocated list of
9989 * isds_message_status_change's. If you provide &NULL, list will be allocated
9990 * on heap, if you provide pointer to non-NULL, list will be freed
9991 * automatically at first. Also in case of error the list will be NULLed.
9992 * XXX: The list item ordering is not specified.
9993 * XXX: Server provides only `recent' changes.
9994 * @return IE_SUCCESS or appropriate error code. */
9995 isds_error isds_get_list_of_sent_message_state_changes(
9996 struct isds_ctx *context,
9997 const struct timeval *from_time, const struct timeval *to_time,
9998 struct isds_list **changed_states) {
10000 isds_error err = IE_SUCCESS;
10001 #if HAVE_LIBCURL
10002 xmlNsPtr isds_ns = NULL;
10003 xmlNodePtr request = NULL, node;
10004 xmlDocPtr response = NULL;
10005 xmlXPathContextPtr xpath_ctx = NULL;
10006 xmlXPathObjectPtr result = NULL;
10007 xmlChar *string = NULL;
10008 int count = 0;
10009 #endif
10011 if (!context) return IE_INVALID_CONTEXT;
10012 zfree(context->long_message);
10014 /* Free former message list if any */
10015 isds_list_free(changed_states);
10017 #if HAVE_LIBCURL
10018 /* Check if connection is established
10019 * TODO: This check should be done downstairs. */
10020 if (!context->curl) return IE_CONNECTION_CLOSED;
10022 /* Build GetMessageStateChanges request */
10023 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
10024 if (!request) {
10025 isds_log_message(context,
10026 _("Could not build GetMessageStateChanges request"));
10027 return IE_ERROR;
10029 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10030 if(!isds_ns) {
10031 isds_log_message(context, _("Could not create ISDS name space"));
10032 xmlFreeNode(request);
10033 return IE_ERROR;
10035 xmlSetNs(request, isds_ns);
10038 if (from_time) {
10039 err = timeval2timestring(from_time, &string);
10040 if (err) goto leave;
10042 INSERT_STRING(request, "dmFromTime", string);
10043 zfree(string);
10045 if (to_time) {
10046 err = timeval2timestring(to_time, &string);
10047 if (err) goto leave;
10049 INSERT_STRING(request, "dmToTime", string);
10050 zfree(string);
10053 /* Sent request */
10054 err = send_destroy_request_check_response(context,
10055 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
10056 &response, NULL, NULL);
10057 if (err) goto leave;
10060 /* Extract data */
10061 xpath_ctx = xmlXPathNewContext(response);
10062 if (!xpath_ctx) {
10063 err = IE_ERROR;
10064 goto leave;
10066 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10067 err = IE_ERROR;
10068 goto leave;
10070 result = xmlXPathEvalExpression(
10071 BAD_CAST "/isds:GetMessageStateChangesResponse/"
10072 "isds:dmRecords/isds:dmRecord", xpath_ctx);
10073 if (!result) {
10074 err = IE_ERROR;
10075 goto leave;
10078 /* Fill output arguments in */
10079 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10080 struct isds_list *item = NULL, *last_item = NULL;
10082 for (count = 0; count < result->nodesetval->nodeNr; count++) {
10083 /* Create new status change */
10084 item = calloc(1, sizeof(*item));
10085 if (!item) {
10086 err = IE_NOMEM;
10087 goto leave;
10089 item->destructor =
10090 (void(*)(void**)) &isds_message_status_change_free;
10092 /* Extract message status change */
10093 xpath_ctx->node = result->nodesetval->nodeTab[count];
10094 err = extract_StateChangesRecord(context,
10095 (struct isds_message_status_change **) &item->data,
10096 xpath_ctx);
10097 if (err) {
10098 isds_list_free(&item);
10099 goto leave;
10102 /* Append new message status change into the list */
10103 if (!*changed_states) {
10104 *changed_states = last_item = item;
10105 } else {
10106 last_item->next = item;
10107 last_item = item;
10112 leave:
10113 if (err) {
10114 isds_list_free(changed_states);
10117 free(string);
10118 xmlXPathFreeObject(result);
10119 xmlXPathFreeContext(xpath_ctx);
10120 xmlFreeDoc(response);
10121 xmlFreeNode(request);
10123 if (!err)
10124 isds_log(ILF_ISDS, ILL_DEBUG,
10125 _("GetMessageStateChanges request processed by server "
10126 "successfully.\n"));
10127 #else /* not HAVE_LIBCURL */
10128 err = IE_NOTSUP;
10129 #endif
10130 return err;
10134 #if HAVE_LIBCURL
10135 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10136 * code
10137 * @context is session context
10138 * @service is ISDS WS service handler
10139 * @service_name is name of SERVICE_DM_OPERATIONS
10140 * @message_id is message ID to send as service argument to ISDS
10141 * @response is reallocated server SOAP body response as XML document
10142 * @raw_response is reallocated bit stream with response body. Use
10143 * NULL if you don't care
10144 * @raw_response_length is size of @raw_response in bytes
10145 * @code is reallocated ISDS status code
10146 * @status_message is reallocated ISDS status message
10147 * @return error coded from lower layer, context message will be set up
10148 * appropriately. */
10149 static isds_error build_send_check_message_request(struct isds_ctx *context,
10150 const isds_service service, const xmlChar *service_name,
10151 const char *message_id,
10152 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
10153 xmlChar **code, xmlChar **status_message) {
10155 isds_error err = IE_SUCCESS;
10156 char *service_name_locale = NULL, *message_id_locale = NULL;
10157 xmlNodePtr request = NULL, node;
10158 xmlNsPtr isds_ns = NULL;
10160 if (!context) return IE_INVALID_CONTEXT;
10161 if (!service_name || !message_id) return IE_INVAL;
10162 if (!response || !code || !status_message) return IE_INVAL;
10163 if (!raw_response_length && raw_response) return IE_INVAL;
10165 /* Free output argument */
10166 xmlFreeDoc(*response); *response = NULL;
10167 if (raw_response) zfree(*raw_response);
10168 zfree(*code);
10169 zfree(*status_message);
10172 /* Check if connection is established
10173 * TODO: This check should be done downstairs. */
10174 if (!context->curl) return IE_CONNECTION_CLOSED;
10176 service_name_locale = _isds_utf82locale((char*)service_name);
10177 message_id_locale = _isds_utf82locale(message_id);
10178 if (!service_name_locale || !message_id_locale) {
10179 err = IE_NOMEM;
10180 goto leave;
10183 /* Build request */
10184 request = xmlNewNode(NULL, service_name);
10185 if (!request) {
10186 isds_printf_message(context,
10187 _("Could not build %s request for %s message ID"),
10188 service_name_locale, message_id_locale);
10189 err = IE_ERROR;
10190 goto leave;
10192 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10193 if(!isds_ns) {
10194 isds_log_message(context, _("Could not create ISDS name space"));
10195 err = IE_ERROR;
10196 goto leave;
10198 xmlSetNs(request, isds_ns);
10201 /* Add requested ID */
10202 err = validate_message_id_length(context, (xmlChar *) message_id);
10203 if (err) goto leave;
10204 INSERT_STRING(request, "dmID", message_id);
10207 isds_log(ILF_ISDS, ILL_DEBUG,
10208 _("Sending %s request for %s message ID to ISDS\n"),
10209 service_name_locale, message_id_locale);
10211 /* Send request */
10212 err = _isds(context, service, request, response,
10213 raw_response, raw_response_length);
10214 xmlFreeNode(request); request = NULL;
10216 if (err) {
10217 isds_log(ILF_ISDS, ILL_DEBUG,
10218 _("Processing ISDS response on %s request failed\n"),
10219 service_name_locale);
10220 goto leave;
10223 /* Check for response status */
10224 err = isds_response_status(context, service, *response,
10225 code, status_message, NULL);
10226 if (err) {
10227 isds_log(ILF_ISDS, ILL_DEBUG,
10228 _("ISDS response on %s request is missing status\n"),
10229 service_name_locale);
10230 goto leave;
10233 /* Request processed, but nothing found */
10234 if (xmlStrcmp(*code, BAD_CAST "0000")) {
10235 char *code_locale = _isds_utf82locale((char*) *code);
10236 char *status_message_locale = _isds_utf82locale((char*) *status_message);
10237 isds_log(ILF_ISDS, ILL_DEBUG,
10238 _("Server refused %s request for %s message ID "
10239 "(code=%s, message=%s)\n"),
10240 service_name_locale, message_id_locale,
10241 code_locale, status_message_locale);
10242 isds_log_message(context, status_message_locale);
10243 free(code_locale);
10244 free(status_message_locale);
10245 err = IE_ISDS;
10246 goto leave;
10249 leave:
10250 free(message_id_locale);
10251 free(service_name_locale);
10252 xmlFreeNode(request);
10253 return err;
10257 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10258 * signed data and free ISDS response.
10259 * @context is session context
10260 * @message_id is UTF-8 encoded message ID for logging purpose
10261 * @response is parsed XML document. It will be freed and NULLed in the middle
10262 * of function run to save memory. This is not guaranteed in case of error.
10263 * @request_name is name of ISDS request used to construct response root
10264 * element name and for logging purpose.
10265 * @raw is reallocated output buffer with DER encoded CMS data
10266 * @raw_length is size of @raw buffer in bytes
10267 * @returns standard error codes, in case of error, @raw will be freed and
10268 * NULLed, @response sometimes. */
10269 static isds_error find_extract_signed_data_free_response(
10270 struct isds_ctx *context, const xmlChar *message_id,
10271 xmlDocPtr *response, const xmlChar *request_name,
10272 void **raw, size_t *raw_length) {
10274 isds_error err = IE_SUCCESS;
10275 char *xpath_expression = NULL;
10276 xmlXPathContextPtr xpath_ctx = NULL;
10277 xmlXPathObjectPtr result = NULL;
10278 char *encoded_structure = NULL;
10280 if (!context) return IE_INVALID_CONTEXT;
10281 if (!raw) return IE_INVAL;
10282 zfree(*raw);
10283 if (!message_id || !response || !*response || !request_name || !raw_length)
10284 return IE_INVAL;
10286 /* Build XPath expression */
10287 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
10288 "Response/isds:dmSignature");
10289 if (!xpath_expression) return IE_NOMEM;
10291 /* Extract data */
10292 xpath_ctx = xmlXPathNewContext(*response);
10293 if (!xpath_ctx) {
10294 err = IE_ERROR;
10295 goto leave;
10297 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10298 err = IE_ERROR;
10299 goto leave;
10301 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
10302 if (!result) {
10303 err = IE_ERROR;
10304 goto leave;
10306 /* Empty response */
10307 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10308 char *message_id_locale = _isds_utf82locale((char*) message_id);
10309 isds_printf_message(context,
10310 _("Server did not return any signed data for message ID `%s' "
10311 "on %s request"),
10312 message_id_locale, request_name);
10313 free(message_id_locale);
10314 err = IE_ISDS;
10315 goto leave;
10317 /* More responses */
10318 if (result->nodesetval->nodeNr > 1) {
10319 char *message_id_locale = _isds_utf82locale((char*) message_id);
10320 isds_printf_message(context,
10321 _("Server did return more signed data for message ID `%s' "
10322 "on %s request"),
10323 message_id_locale, request_name);
10324 free(message_id_locale);
10325 err = IE_ISDS;
10326 goto leave;
10328 /* One response */
10329 xpath_ctx->node = result->nodesetval->nodeTab[0];
10331 /* Extract PKCS#7 structure */
10332 EXTRACT_STRING(".", encoded_structure);
10333 if (!encoded_structure) {
10334 isds_log_message(context, _("dmSignature element is empty"));
10337 /* Here we have delivery info as standalone CMS in encoded_structure.
10338 * We don't need any other data, free them: */
10339 xmlXPathFreeObject(result); result = NULL;
10340 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
10341 xmlFreeDoc(*response); *response = NULL;
10344 /* Decode PKCS#7 to DER format */
10345 *raw_length = _isds_b64decode(encoded_structure, raw);
10346 if (*raw_length == (size_t) -1) {
10347 isds_log_message(context,
10348 _("Error while Base64-decoding PKCS#7 structure"));
10349 err = IE_ERROR;
10350 goto leave;
10353 leave:
10354 if (err) {
10355 zfree(*raw);
10356 raw_length = 0;
10359 free(encoded_structure);
10360 xmlXPathFreeObject(result);
10361 xmlXPathFreeContext(xpath_ctx);
10362 free(xpath_expression);
10364 return err;
10366 #endif /* HAVE_LIBCURL */
10369 /* Download incoming message envelope identified by ID.
10370 * @context is session context
10371 * @message_id is message identifier (you can get them from
10372 * isds_get_list_of_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) too.
10376 * Returned hash and timestamp require documents to be verifiable. */
10377 isds_error isds_get_received_envelope(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 xmlXPathContextPtr xpath_ctx = NULL;
10385 xmlXPathObjectPtr result = NULL;
10386 #endif
10388 if (!context) return IE_INVALID_CONTEXT;
10389 zfree(context->long_message);
10391 /* Free former message if any */
10392 if (!message) return IE_INVAL;
10393 isds_message_free(message);
10395 #if HAVE_LIBCURL
10396 /* Do request and check for success */
10397 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10398 BAD_CAST "MessageEnvelopeDownload", message_id,
10399 &response, NULL, NULL, &code, &status_message);
10400 if (err) goto leave;
10402 /* Extract data */
10403 xpath_ctx = xmlXPathNewContext(response);
10404 if (!xpath_ctx) {
10405 err = IE_ERROR;
10406 goto leave;
10408 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10409 err = IE_ERROR;
10410 goto leave;
10412 result = xmlXPathEvalExpression(
10413 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
10414 "isds:dmReturnedMessageEnvelope",
10415 xpath_ctx);
10416 if (!result) {
10417 err = IE_ERROR;
10418 goto leave;
10420 /* Empty response */
10421 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10422 char *message_id_locale = _isds_utf82locale((char*) message_id);
10423 isds_printf_message(context,
10424 _("Server did not return any envelope for ID `%s' "
10425 "on MessageEnvelopeDownload request"), message_id_locale);
10426 free(message_id_locale);
10427 err = IE_ISDS;
10428 goto leave;
10430 /* More envelops */
10431 if (result->nodesetval->nodeNr > 1) {
10432 char *message_id_locale = _isds_utf82locale((char*) message_id);
10433 isds_printf_message(context,
10434 _("Server did return more envelopes for ID `%s' "
10435 "on MessageEnvelopeDownload request"), message_id_locale);
10436 free(message_id_locale);
10437 err = IE_ISDS;
10438 goto leave;
10440 /* One message */
10441 xpath_ctx->node = result->nodesetval->nodeTab[0];
10443 /* Extract the envelope (= message without documents, hence 0) */
10444 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10445 if (err) goto leave;
10447 /* Save XML blob */
10448 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10449 &(*message)->raw_length);
10451 leave:
10452 if (err) {
10453 isds_message_free(message);
10456 xmlXPathFreeObject(result);
10457 xmlXPathFreeContext(xpath_ctx);
10459 free(code);
10460 free(status_message);
10461 if (!*message || !(*message)->xml) {
10462 xmlFreeDoc(response);
10465 if (!err)
10466 isds_log(ILF_ISDS, ILL_DEBUG,
10467 _("MessageEnvelopeDownload request processed by server "
10468 "successfully.\n")
10470 #else /* not HAVE_LIBCURL */
10471 err = IE_NOTSUP;
10472 #endif
10473 return err;
10477 /* Load delivery info of any format from buffer.
10478 * @context is session context
10479 * @raw_type advertises format of @buffer content. Only delivery info types
10480 * are accepted.
10481 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10482 * retrieve such data from message->raw after calling
10483 * isds_get_signed_delivery_info().
10484 * @length is length of buffer in bytes.
10485 * @message is automatically reallocated message parsed from @buffer.
10486 * @strategy selects how buffer will be attached into raw isds_message member.
10487 * */
10488 isds_error isds_load_delivery_info(struct isds_ctx *context,
10489 const isds_raw_type raw_type,
10490 const void *buffer, const size_t length,
10491 struct isds_message **message, const isds_buffer_strategy strategy) {
10493 isds_error err = IE_SUCCESS;
10494 message_ns_type message_ns;
10495 xmlDocPtr message_doc = NULL;
10496 xmlXPathContextPtr xpath_ctx = NULL;
10497 xmlXPathObjectPtr result = NULL;
10498 void *xml_stream = NULL;
10499 size_t xml_stream_length = 0;
10501 if (!context) return IE_INVALID_CONTEXT;
10502 zfree(context->long_message);
10503 if (!message) return IE_INVAL;
10504 isds_message_free(message);
10505 if (!buffer) return IE_INVAL;
10508 /* Select buffer format and extract XML from CMS*/
10509 switch (raw_type) {
10510 case RAWTYPE_DELIVERYINFO:
10511 message_ns = MESSAGE_NS_UNSIGNED;
10512 xml_stream = (void *) buffer;
10513 xml_stream_length = length;
10514 break;
10516 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10517 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10518 xml_stream = (void *) buffer;
10519 xml_stream_length = length;
10520 break;
10522 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10523 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10524 err = _isds_extract_cms_data(context, buffer, length,
10525 &xml_stream, &xml_stream_length);
10526 if (err) goto leave;
10527 break;
10529 default:
10530 isds_log_message(context, _("Bad raw delivery representation type"));
10531 return IE_INVAL;
10532 break;
10535 if (_isds_sizet2int(xml_stream_length) >= 0) {
10536 isds_log(ILF_ISDS, ILL_DEBUG,
10537 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10538 _isds_sizet2int(xml_stream_length), xml_stream);
10541 /* Convert delivery info XML stream into XPath context */
10542 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10543 if (!message_doc) {
10544 err = IE_XML;
10545 goto leave;
10547 xpath_ctx = xmlXPathNewContext(message_doc);
10548 if (!xpath_ctx) {
10549 err = IE_ERROR;
10550 goto leave;
10552 /* XXX: Name spaces mangled for signed delivery info:
10553 * http://isds.czechpoint.cz/v20/delivery:
10555 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10556 * <q:dmDelivery>
10557 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10558 * <p:dmID>170272</p:dmID>
10559 * ...
10560 * </p:dmDm>
10561 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10562 * ...
10563 * </q:dmEvents>...</q:dmEvents>
10564 * </q:dmDelivery>
10565 * </q:GetDeliveryInfoResponse>
10566 * */
10567 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10568 err = IE_ERROR;
10569 goto leave;
10571 result = xmlXPathEvalExpression(
10572 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10573 xpath_ctx);
10574 if (!result) {
10575 err = IE_ERROR;
10576 goto leave;
10578 /* Empty delivery info */
10579 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10580 isds_printf_message(context,
10581 _("XML document is not sisds:dmDelivery document"));
10582 err = IE_ISDS;
10583 goto leave;
10585 /* More delivery info's */
10586 if (result->nodesetval->nodeNr > 1) {
10587 isds_printf_message(context,
10588 _("XML document has more sisds:dmDelivery elements"));
10589 err = IE_ISDS;
10590 goto leave;
10592 /* One delivery info */
10593 xpath_ctx->node = result->nodesetval->nodeTab[0];
10595 /* Extract the envelope (= message without documents, hence 0).
10596 * XXX: extract_TReturnedMessage() can obtain attachments size,
10597 * but delivery info carries none. It's coded as option elements,
10598 * so it should work. */
10599 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10600 if (err) goto leave;
10602 /* Extract events */
10603 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10604 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10605 if (err) { err = IE_ERROR; goto leave; }
10606 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10607 if (err) goto leave;
10609 /* Append raw CMS structure into message */
10610 (*message)->raw_type = raw_type;
10611 switch (strategy) {
10612 case BUFFER_DONT_STORE:
10613 break;
10614 case BUFFER_COPY:
10615 (*message)->raw = malloc(length);
10616 if (!(*message)->raw) {
10617 err = IE_NOMEM;
10618 goto leave;
10620 memcpy((*message)->raw, buffer, length);
10621 (*message)->raw_length = length;
10622 break;
10623 case BUFFER_MOVE:
10624 (*message)->raw = (void *) buffer;
10625 (*message)->raw_length = length;
10626 break;
10627 default:
10628 err = IE_ENUM;
10629 goto leave;
10632 leave:
10633 if (err) {
10634 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10635 isds_message_free(message);
10638 xmlXPathFreeObject(result);
10639 xmlXPathFreeContext(xpath_ctx);
10640 if (!*message || !(*message)->xml) {
10641 xmlFreeDoc(message_doc);
10643 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10645 if (!err)
10646 isds_log(ILF_ISDS, ILL_DEBUG,
10647 _("Delivery info loaded successfully.\n"));
10648 return err;
10652 /* Download signed delivery info-sheet of given message identified by ID.
10653 * @context is session context
10654 * @message_id is message identifier (you can get them from
10655 * isds_get_list_of_{sent,received}_messages())
10656 * @message is automatically reallocated message retrieved from ISDS.
10657 * It will miss documents per se. Use isds_get_signed_received_message(),
10658 * if you are interested in documents (content). OTOH, only this function
10659 * can get list events message has gone through. */
10660 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10661 const char *message_id, struct isds_message **message) {
10663 isds_error err = IE_SUCCESS;
10664 #if HAVE_LIBCURL
10665 xmlDocPtr response = NULL;
10666 xmlChar *code = NULL, *status_message = NULL;
10667 void *raw = NULL;
10668 size_t raw_length = 0;
10669 #endif
10671 if (!context) return IE_INVALID_CONTEXT;
10672 zfree(context->long_message);
10674 /* Free former message if any */
10675 if (!message) return IE_INVAL;
10676 isds_message_free(message);
10678 #if HAVE_LIBCURL
10679 /* Do request and check for success */
10680 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10681 BAD_CAST "GetSignedDeliveryInfo", message_id,
10682 &response, NULL, NULL, &code, &status_message);
10683 if (err) goto leave;
10685 /* Find signed delivery info, extract it into raw and maybe free
10686 * response */
10687 err = find_extract_signed_data_free_response(context,
10688 (xmlChar *)message_id, &response,
10689 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10690 if (err) goto leave;
10692 /* Parse delivery info */
10693 err = isds_load_delivery_info(context,
10694 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10695 message, BUFFER_MOVE);
10696 if (err) goto leave;
10698 raw = NULL;
10700 leave:
10701 if (err) {
10702 isds_message_free(message);
10705 free(raw);
10706 free(code);
10707 free(status_message);
10708 xmlFreeDoc(response);
10710 if (!err)
10711 isds_log(ILF_ISDS, ILL_DEBUG,
10712 _("GetSignedDeliveryInfo request processed by server "
10713 "successfully.\n")
10715 #else /* not HAVE_LIBCURL */
10716 err = IE_NOTSUP;
10717 #endif
10718 return err;
10722 /* Download delivery info-sheet of given message identified by ID.
10723 * @context is session context
10724 * @message_id is message identifier (you can get them from
10725 * isds_get_list_of_{sent,received}_messages())
10726 * @message is automatically reallocated message retrieved from ISDS.
10727 * It will miss documents per se. Use isds_get_received_message(), if you are
10728 * interested in documents (content). OTOH, only this function can get list
10729 * of events message has gone through. */
10730 isds_error isds_get_delivery_info(struct isds_ctx *context,
10731 const char *message_id, struct isds_message **message) {
10733 isds_error err = IE_SUCCESS;
10734 #if HAVE_LIBCURL
10735 xmlDocPtr response = NULL;
10736 xmlChar *code = NULL, *status_message = NULL;
10737 xmlNodePtr delivery_node = NULL;
10738 void *raw = NULL;
10739 size_t raw_length = 0;
10740 #endif
10742 if (!context) return IE_INVALID_CONTEXT;
10743 zfree(context->long_message);
10745 /* Free former message if any */
10746 if (!message) return IE_INVAL;
10747 isds_message_free(message);
10749 #if HAVE_LIBCURL
10750 /* Do request and check for success */
10751 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10752 BAD_CAST "GetDeliveryInfo", message_id,
10753 &response, NULL, NULL, &code, &status_message);
10754 if (err) goto leave;
10757 /* Serialize delivery info */
10758 delivery_node = xmlDocGetRootElement(response);
10759 if (!delivery_node) {
10760 char *message_id_locale = _isds_utf82locale((char*) message_id);
10761 isds_printf_message(context,
10762 _("Server did not return any delivery info for ID `%s' "
10763 "on GetDeliveryInfo request"), message_id_locale);
10764 free(message_id_locale);
10765 err = IE_ISDS;
10766 goto leave;
10768 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10769 if (err) goto leave;
10771 /* Parse delivery info */
10772 /* TODO: Here we parse the response second time. We could single delivery
10773 * parser from isds_load_delivery_info() to make things faster. */
10774 err = isds_load_delivery_info(context,
10775 RAWTYPE_DELIVERYINFO, raw, raw_length,
10776 message, BUFFER_MOVE);
10777 if (err) goto leave;
10779 raw = NULL;
10782 leave:
10783 if (err) {
10784 isds_message_free(message);
10787 free(raw);
10788 free(code);
10789 free(status_message);
10790 xmlFreeDoc(response);
10792 if (!err)
10793 isds_log(ILF_ISDS, ILL_DEBUG,
10794 _("GetDeliveryInfo request processed by server "
10795 "successfully.\n")
10797 #else /* not HAVE_LIBCURL */
10798 err = IE_NOTSUP;
10799 #endif
10800 return err;
10804 /* Download incoming message identified by ID.
10805 * @context is session context
10806 * @message_id is message identifier (you can get them from
10807 * isds_get_list_of_received_messages())
10808 * @message is automatically reallocated message retrieved from ISDS */
10809 isds_error isds_get_received_message(struct isds_ctx *context,
10810 const char *message_id, struct isds_message **message) {
10812 isds_error err = IE_SUCCESS;
10813 #if HAVE_LIBCURL
10814 xmlDocPtr response = NULL;
10815 void *xml_stream = NULL;
10816 size_t xml_stream_length;
10817 xmlChar *code = NULL, *status_message = NULL;
10818 xmlXPathContextPtr xpath_ctx = NULL;
10819 xmlXPathObjectPtr result = NULL;
10820 char *phys_path = NULL;
10821 size_t phys_start, phys_end;
10822 #endif
10824 if (!context) return IE_INVALID_CONTEXT;
10825 zfree(context->long_message);
10827 /* Free former message if any */
10828 if (NULL == message) return IE_INVAL;
10829 if (message) isds_message_free(message);
10831 #if HAVE_LIBCURL
10832 /* Do request and check for success */
10833 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10834 BAD_CAST "MessageDownload", message_id,
10835 &response, &xml_stream, &xml_stream_length,
10836 &code, &status_message);
10837 if (err) goto leave;
10839 /* Extract data */
10840 xpath_ctx = xmlXPathNewContext(response);
10841 if (!xpath_ctx) {
10842 err = IE_ERROR;
10843 goto leave;
10845 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10846 err = IE_ERROR;
10847 goto leave;
10849 result = xmlXPathEvalExpression(
10850 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10851 xpath_ctx);
10852 if (!result) {
10853 err = IE_ERROR;
10854 goto leave;
10856 /* Empty response */
10857 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10858 char *message_id_locale = _isds_utf82locale((char*) message_id);
10859 isds_printf_message(context,
10860 _("Server did not return any message for ID `%s' "
10861 "on MessageDownload request"), message_id_locale);
10862 free(message_id_locale);
10863 err = IE_ISDS;
10864 goto leave;
10866 /* More messages */
10867 if (result->nodesetval->nodeNr > 1) {
10868 char *message_id_locale = _isds_utf82locale((char*) message_id);
10869 isds_printf_message(context,
10870 _("Server did return more messages for ID `%s' "
10871 "on MessageDownload request"), message_id_locale);
10872 free(message_id_locale);
10873 err = IE_ISDS;
10874 goto leave;
10876 /* One message */
10877 xpath_ctx->node = result->nodesetval->nodeTab[0];
10879 /* Extract the message */
10880 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10881 if (err) goto leave;
10883 /* Locate raw XML blob */
10884 phys_path = strdup(
10885 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10886 PHYSXML_ELEMENT_SEPARATOR
10887 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10888 PHYSXML_ELEMENT_SEPARATOR
10889 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10891 if (!phys_path) {
10892 err = IE_NOMEM;
10893 goto leave;
10895 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10896 phys_path, &phys_start, &phys_end);
10897 zfree(phys_path);
10898 if (err) {
10899 isds_log_message(context,
10900 _("Substring with isds:MessageDownloadResponse element "
10901 "could not be located in raw SOAP message"));
10902 goto leave;
10904 /* Save XML blob */
10905 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10906 &(*message)->raw_length);*/
10907 /* TODO: Store name space declarations from ancestors */
10908 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10909 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10910 (*message)->raw_length = phys_end - phys_start + 1;
10911 (*message)->raw = malloc((*message)->raw_length);
10912 if (!(*message)->raw) {
10913 err = IE_NOMEM;
10914 goto leave;
10916 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10919 leave:
10920 if (err) {
10921 isds_message_free(message);
10924 free(phys_path);
10926 xmlXPathFreeObject(result);
10927 xmlXPathFreeContext(xpath_ctx);
10929 free(code);
10930 free(status_message);
10931 free(xml_stream);
10932 if (!*message || !(*message)->xml) {
10933 xmlFreeDoc(response);
10936 if (!err)
10937 isds_log(ILF_ISDS, ILL_DEBUG,
10938 _("MessageDownload request processed by server "
10939 "successfully.\n")
10941 #else /* not HAVE_LIBCURL */
10942 err = IE_NOTSUP;
10943 #endif
10944 return err;
10948 /* Load message of any type from buffer.
10949 * @context is session context
10950 * @raw_type defines content type of @buffer. Only message types are allowed.
10951 * @buffer is message raw representation. Format (CMS, plain signed,
10952 * message direction) is defined in @raw_type. You can retrieve such data
10953 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10954 * @length is length of buffer in bytes.
10955 * @message is automatically reallocated message parsed from @buffer.
10956 * @strategy selects how buffer will be attached into raw isds_message member.
10957 * */
10958 isds_error isds_load_message(struct isds_ctx *context,
10959 const isds_raw_type raw_type, const void *buffer, const size_t length,
10960 struct isds_message **message, const isds_buffer_strategy strategy) {
10962 isds_error err = IE_SUCCESS;
10963 void *xml_stream = NULL;
10964 size_t xml_stream_length = 0;
10965 message_ns_type message_ns;
10966 xmlDocPtr message_doc = NULL;
10967 xmlXPathContextPtr xpath_ctx = NULL;
10968 xmlXPathObjectPtr result = NULL;
10970 if (!context) return IE_INVALID_CONTEXT;
10971 zfree(context->long_message);
10972 if (!message) return IE_INVAL;
10973 isds_message_free(message);
10974 if (!buffer) return IE_INVAL;
10977 /* Select buffer format and extract XML from CMS*/
10978 switch (raw_type) {
10979 case RAWTYPE_INCOMING_MESSAGE:
10980 message_ns = MESSAGE_NS_UNSIGNED;
10981 xml_stream = (void *) buffer;
10982 xml_stream_length = length;
10983 break;
10985 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10986 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10987 xml_stream = (void *) buffer;
10988 xml_stream_length = length;
10989 break;
10991 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10992 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10993 err = _isds_extract_cms_data(context, buffer, length,
10994 &xml_stream, &xml_stream_length);
10995 if (err) goto leave;
10996 break;
10998 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10999 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11000 xml_stream = (void *) buffer;
11001 xml_stream_length = length;
11002 break;
11004 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11005 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11006 err = _isds_extract_cms_data(context, buffer, length,
11007 &xml_stream, &xml_stream_length);
11008 if (err) goto leave;
11009 break;
11011 default:
11012 isds_log_message(context, _("Bad raw message representation type"));
11013 return IE_INVAL;
11014 break;
11017 if (_isds_sizet2int(xml_stream_length) >= 0) {
11018 isds_log(ILF_ISDS, ILL_DEBUG,
11019 _("Loading message:\n%.*s\nEnd of message\n"),
11020 _isds_sizet2int(xml_stream_length), xml_stream);
11023 /* Convert messages XML stream into XPath context */
11024 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
11025 if (!message_doc) {
11026 err = IE_XML;
11027 goto leave;
11029 xpath_ctx = xmlXPathNewContext(message_doc);
11030 if (!xpath_ctx) {
11031 err = IE_ERROR;
11032 goto leave;
11034 /* XXX: Standard name space for unsigned incoming direction:
11035 * http://isds.czechpoint.cz/v20/
11037 * XXX: Name spaces mangled for signed outgoing direction:
11038 * http://isds.czechpoint.cz/v20/SentMessage:
11040 * <q:MessageDownloadResponse
11041 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11042 * <q:dmReturnedMessage>
11043 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11044 * <p:dmID>151916</p:dmID>
11045 * ...
11046 * </p:dmDm>
11047 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11048 * ...
11049 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11050 * </q:dmReturnedMessage>
11051 * </q:MessageDownloadResponse>
11053 * XXX: Name spaces mangled for signed incoming direction:
11054 * http://isds.czechpoint.cz/v20/message:
11056 * <q:MessageDownloadResponse
11057 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11058 * <q:dmReturnedMessage>
11059 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11060 * <p:dmID>151916</p:dmID>
11061 * ...
11062 * </p:dmDm>
11063 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11064 * ...
11065 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11066 * </q:dmReturnedMessage>
11067 * </q:MessageDownloadResponse>
11069 * Stupidity of ISDS developers is unlimited */
11070 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
11071 err = IE_ERROR;
11072 goto leave;
11074 result = xmlXPathEvalExpression(
11075 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11076 xpath_ctx);
11077 if (!result) {
11078 err = IE_ERROR;
11079 goto leave;
11081 /* Empty message */
11082 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11083 isds_printf_message(context,
11084 _("XML document does not contain "
11085 "sisds:dmReturnedMessage element"));
11086 err = IE_ISDS;
11087 goto leave;
11089 /* More messages */
11090 if (result->nodesetval->nodeNr > 1) {
11091 isds_printf_message(context,
11092 _("XML document has more sisds:dmReturnedMessage elements"));
11093 err = IE_ISDS;
11094 goto leave;
11096 /* One message */
11097 xpath_ctx->node = result->nodesetval->nodeTab[0];
11099 /* Extract the message */
11100 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
11101 if (err) goto leave;
11103 /* Append raw buffer into message */
11104 (*message)->raw_type = raw_type;
11105 switch (strategy) {
11106 case BUFFER_DONT_STORE:
11107 break;
11108 case BUFFER_COPY:
11109 (*message)->raw = malloc(length);
11110 if (!(*message)->raw) {
11111 err = IE_NOMEM;
11112 goto leave;
11114 memcpy((*message)->raw, buffer, length);
11115 (*message)->raw_length = length;
11116 break;
11117 case BUFFER_MOVE:
11118 (*message)->raw = (void *) buffer;
11119 (*message)->raw_length = length;
11120 break;
11121 default:
11122 err = IE_ENUM;
11123 goto leave;
11127 leave:
11128 if (err) {
11129 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
11130 isds_message_free(message);
11133 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11134 xmlXPathFreeObject(result);
11135 xmlXPathFreeContext(xpath_ctx);
11136 if (!*message || !(*message)->xml) {
11137 xmlFreeDoc(message_doc);
11140 if (!err)
11141 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
11142 return err;
11146 /* Determine type of raw message or delivery info according some heuristics.
11147 * It does not validate the raw blob.
11148 * @context is session context
11149 * @raw_type returns content type of @buffer. Valid only if exit code of this
11150 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11151 * reallocated memory.
11152 * @buffer is message raw representation.
11153 * @length is length of buffer in bytes. */
11154 isds_error isds_guess_raw_type(struct isds_ctx *context,
11155 isds_raw_type *raw_type, const void *buffer, const size_t length) {
11156 isds_error err;
11157 void *xml_stream = NULL;
11158 size_t xml_stream_length = 0;
11159 xmlDocPtr document = NULL;
11160 xmlNodePtr root = NULL;
11162 if (!context) return IE_INVALID_CONTEXT;
11163 zfree(context->long_message);
11164 if (length == 0 || !buffer) return IE_INVAL;
11165 if (!raw_type) return IE_INVAL;
11167 /* Try CMS */
11168 err = _isds_extract_cms_data(context, buffer, length,
11169 &xml_stream, &xml_stream_length);
11170 if (err) {
11171 xml_stream = (void *) buffer;
11172 xml_stream_length = (size_t) length;
11173 err = IE_SUCCESS;
11176 /* Try XML */
11177 document = xmlParseMemory(xml_stream, xml_stream_length);
11178 if (!document) {
11179 isds_printf_message(context,
11180 _("Could not parse data as XML document"));
11181 err = IE_NOTSUP;
11182 goto leave;
11185 /* Get root element */
11186 root = xmlDocGetRootElement(document);
11187 if (!root) {
11188 isds_printf_message(context,
11189 _("XML document is missing root element"));
11190 err = IE_XML;
11191 goto leave;
11194 if (!root->ns || !root->ns->href) {
11195 isds_printf_message(context,
11196 _("Root element does not belong to any name space"));
11197 err = IE_NOTSUP;
11198 goto leave;
11201 /* Test name space */
11202 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
11203 if (xml_stream == buffer)
11204 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
11205 else
11206 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
11207 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
11208 if (xml_stream == buffer)
11209 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
11210 else
11211 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
11212 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
11213 if (xml_stream == buffer)
11214 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
11215 else
11216 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
11217 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
11218 if (xml_stream != buffer) {
11219 isds_printf_message(context,
11220 _("Document in ISDS name space is encapsulated into CMS" ));
11221 err = IE_NOTSUP;
11222 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
11223 *raw_type = RAWTYPE_INCOMING_MESSAGE;
11224 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
11225 *raw_type = RAWTYPE_DELIVERYINFO;
11226 else {
11227 isds_printf_message(context,
11228 _("Unknown root element in ISDS name space"));
11229 err = IE_NOTSUP;
11231 } else {
11232 isds_printf_message(context,
11233 _("Unknown name space"));
11234 err = IE_NOTSUP;
11237 leave:
11238 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11239 xmlFreeDoc(document);
11240 return err;
11244 /* Download signed incoming/outgoing message identified by ID.
11245 * @context is session context
11246 * @output is true for outgoing message, false for incoming message
11247 * @message_id is message identifier (you can get them from
11248 * isds_get_list_of_{sent,received}_messages())
11249 * @message is automatically reallocated message retrieved from ISDS. The raw
11250 * member will be filled with PKCS#7 structure in DER format. */
11251 static isds_error isds_get_signed_message(struct isds_ctx *context,
11252 const _Bool outgoing, const char *message_id,
11253 struct isds_message **message) {
11255 isds_error err = IE_SUCCESS;
11256 #if HAVE_LIBCURL
11257 xmlDocPtr response = NULL;
11258 xmlChar *code = NULL, *status_message = NULL;
11259 xmlXPathContextPtr xpath_ctx = NULL;
11260 xmlXPathObjectPtr result = NULL;
11261 char *encoded_structure = NULL;
11262 void *raw = NULL;
11263 size_t raw_length = 0;
11264 #endif
11266 if (!context) return IE_INVALID_CONTEXT;
11267 zfree(context->long_message);
11268 if (!message) return IE_INVAL;
11269 isds_message_free(message);
11271 #if HAVE_LIBCURL
11272 /* Do request and check for success */
11273 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
11274 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11275 BAD_CAST "SignedMessageDownload",
11276 message_id, &response, NULL, NULL, &code, &status_message);
11277 if (err) goto leave;
11279 /* Find signed message, extract it into raw and maybe free
11280 * response */
11281 err = find_extract_signed_data_free_response(context,
11282 (xmlChar *)message_id, &response,
11283 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11284 BAD_CAST "SignedMessageDownload",
11285 &raw, &raw_length);
11286 if (err) goto leave;
11288 /* Parse message */
11289 err = isds_load_message(context,
11290 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
11291 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
11292 raw, raw_length, message, BUFFER_MOVE);
11293 if (err) goto leave;
11295 raw = NULL;
11297 leave:
11298 if (err) {
11299 isds_message_free(message);
11302 free(encoded_structure);
11303 xmlXPathFreeObject(result);
11304 xmlXPathFreeContext(xpath_ctx);
11305 free(raw);
11307 free(code);
11308 free(status_message);
11309 xmlFreeDoc(response);
11311 if (!err)
11312 isds_log(ILF_ISDS, ILL_DEBUG,
11313 (outgoing) ?
11314 _("SignedSentMessageDownload request processed by server "
11315 "successfully.\n") :
11316 _("SignedMessageDownload request processed by server "
11317 "successfully.\n")
11319 #else /* not HAVE_LIBCURL */
11320 err = IE_NOTSUP;
11321 #endif
11322 return err;
11326 /* Download signed incoming message identified by ID.
11327 * @context is session context
11328 * @message_id is message identifier (you can get them from
11329 * isds_get_list_of_received_messages())
11330 * @message is automatically reallocated message retrieved from ISDS. The raw
11331 * member will be filled with PKCS#7 structure in DER format. */
11332 isds_error isds_get_signed_received_message(struct isds_ctx *context,
11333 const char *message_id, struct isds_message **message) {
11334 return isds_get_signed_message(context, 0, message_id, message);
11338 /* Download signed outgoing message identified by ID.
11339 * @context is session context
11340 * @message_id is message identifier (you can get them from
11341 * isds_get_list_of_sent_messages())
11342 * @message is automatically reallocated message retrieved from ISDS. The raw
11343 * member will be filled with PKCS#7 structure in DER format. */
11344 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
11345 const char *message_id, struct isds_message **message) {
11346 return isds_get_signed_message(context, 1, message_id, message);
11350 /* Get type and name of user who sent a message identified by ID.
11351 * @context is session context
11352 * @message_id is message identifier
11353 * @sender_type is pointer to automatically allocated type of sender detected
11354 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11355 * library or to the server, NULL will be returned. Pass NULL if you don't
11356 * care about it.
11357 * @raw_sender_type is automatically reallocated UTF-8 string describing
11358 * sender type or NULL if not known to server. Pass NULL if you don't care.
11359 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11360 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11361 isds_error isds_get_message_sender(struct isds_ctx *context,
11362 const char *message_id, isds_sender_type **sender_type,
11363 char **raw_sender_type, char **sender_name) {
11364 isds_error err = IE_SUCCESS;
11365 #if HAVE_LIBCURL
11366 xmlDocPtr response = NULL;
11367 xmlChar *code = NULL, *status_message = NULL;
11368 xmlXPathContextPtr xpath_ctx = NULL;
11369 xmlXPathObjectPtr result = NULL;
11370 char *type_string = NULL;
11371 #endif
11373 if (!context) return IE_INVALID_CONTEXT;
11374 zfree(context->long_message);
11375 if (sender_type) zfree(*sender_type);
11376 if (raw_sender_type) zfree(*raw_sender_type);
11377 if (sender_name) zfree(*sender_name);
11378 if (!message_id) return IE_INVAL;
11380 #if HAVE_LIBCURL
11381 /* Do request and check for success */
11382 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11383 BAD_CAST "GetMessageAuthor",
11384 message_id, &response, NULL, NULL, &code, &status_message);
11385 if (err) goto leave;
11387 /* Extract data */
11388 xpath_ctx = xmlXPathNewContext(response);
11389 if (!xpath_ctx) {
11390 err = IE_ERROR;
11391 goto leave;
11393 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11394 err = IE_ERROR;
11395 goto leave;
11397 result = xmlXPathEvalExpression(
11398 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
11399 if (!result) {
11400 err = IE_ERROR;
11401 goto leave;
11403 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11404 isds_log_message(context,
11405 _("Missing GetMessageAuthorResponse element"));
11406 err = IE_ISDS;
11407 goto leave;
11409 if (result->nodesetval->nodeNr > 1) {
11410 isds_log_message(context,
11411 _("Multiple GetMessageAuthorResponse element"));
11412 err = IE_ISDS;
11413 goto leave;
11415 xpath_ctx->node = result->nodesetval->nodeTab[0];
11416 xmlXPathFreeObject(result); result = NULL;
11418 /* Fill output arguments in */
11419 EXTRACT_STRING("isds:userType", type_string);
11420 if (NULL != type_string) {
11421 if (NULL != sender_type) {
11422 *sender_type = calloc(1, sizeof(**sender_type));
11423 if (NULL == *sender_type) {
11424 err = IE_NOMEM;
11425 goto leave;
11428 err = string2isds_sender_type((xmlChar *)type_string,
11429 *sender_type);
11430 if (err) {
11431 zfree(*sender_type);
11432 if (err == IE_ENUM) {
11433 err = IE_SUCCESS;
11434 char *type_string_locale = _isds_utf82locale(type_string);
11435 isds_log(ILF_ISDS, ILL_WARNING,
11436 _("Unknown isds:userType value: %s"),
11437 type_string_locale);
11438 free(type_string_locale);
11443 if (NULL != sender_name)
11444 EXTRACT_STRING("isds:authorName", *sender_name);
11446 leave:
11447 if (err) {
11448 if (NULL != sender_type) zfree(*sender_type);
11449 zfree(type_string);
11450 if (NULL != sender_name) zfree(*sender_name);
11452 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11454 xmlXPathFreeObject(result);
11455 xmlXPathFreeContext(xpath_ctx);
11457 free(code);
11458 free(status_message);
11459 xmlFreeDoc(response);
11461 if (!err)
11462 isds_log(ILF_ISDS, ILL_DEBUG,
11463 _("GetMessageAuthor request processed by server "
11464 "successfully.\n"));
11465 #else /* not HAVE_LIBCURL */
11466 err = IE_NOTSUP;
11467 #endif
11468 return err;
11472 /* Retrieve hash of message identified by ID stored in ISDS.
11473 * @context is session context
11474 * @message_id is message identifier
11475 * @hash is automatically reallocated message hash downloaded from ISDS.
11476 * Message must exist in system and must not be deleted. */
11477 isds_error isds_download_message_hash(struct isds_ctx *context,
11478 const char *message_id, struct isds_hash **hash) {
11480 isds_error err = IE_SUCCESS;
11481 #if HAVE_LIBCURL
11482 xmlDocPtr response = NULL;
11483 xmlChar *code = NULL, *status_message = NULL;
11484 xmlXPathContextPtr xpath_ctx = NULL;
11485 xmlXPathObjectPtr result = NULL;
11486 #endif
11488 if (!context) return IE_INVALID_CONTEXT;
11489 zfree(context->long_message);
11491 isds_hash_free(hash);
11493 #if HAVE_LIBCURL
11494 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11495 BAD_CAST "VerifyMessage", message_id,
11496 &response, NULL, NULL, &code, &status_message);
11497 if (err) goto leave;
11500 /* Extract data */
11501 xpath_ctx = xmlXPathNewContext(response);
11502 if (!xpath_ctx) {
11503 err = IE_ERROR;
11504 goto leave;
11506 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11507 err = IE_ERROR;
11508 goto leave;
11510 result = xmlXPathEvalExpression(
11511 BAD_CAST "/isds:VerifyMessageResponse",
11512 xpath_ctx);
11513 if (!result) {
11514 err = IE_ERROR;
11515 goto leave;
11517 /* Empty response */
11518 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11519 char *message_id_locale = _isds_utf82locale((char*) message_id);
11520 isds_printf_message(context,
11521 _("Server did not return any response for ID `%s' "
11522 "on VerifyMessage request"), message_id_locale);
11523 free(message_id_locale);
11524 err = IE_ISDS;
11525 goto leave;
11527 /* More responses */
11528 if (result->nodesetval->nodeNr > 1) {
11529 char *message_id_locale = _isds_utf82locale((char*) message_id);
11530 isds_printf_message(context,
11531 _("Server did return more responses for ID `%s' "
11532 "on VerifyMessage request"), message_id_locale);
11533 free(message_id_locale);
11534 err = IE_ISDS;
11535 goto leave;
11537 /* One response */
11538 xpath_ctx->node = result->nodesetval->nodeTab[0];
11540 /* Extract the hash */
11541 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11543 leave:
11544 if (err) {
11545 isds_hash_free(hash);
11548 xmlXPathFreeObject(result);
11549 xmlXPathFreeContext(xpath_ctx);
11551 free(code);
11552 free(status_message);
11553 xmlFreeDoc(response);
11555 if (!err)
11556 isds_log(ILF_ISDS, ILL_DEBUG,
11557 _("VerifyMessage request processed by server "
11558 "successfully.\n")
11560 #else /* not HAVE_LIBCURL */
11561 err = IE_NOTSUP;
11562 #endif
11563 return err;
11567 /* Erase message specified by @message_id from long term storage. Other
11568 * message cannot be erased on user request.
11569 * @context is session context
11570 * @message_id is message identifier.
11571 * @incoming is true for incoming message, false for outgoing message.
11572 * @return
11573 * IE_SUCCESS if message has ben removed
11574 * IE_INVAL if message does not exist in long term storage or message
11575 * belongs to different box
11576 * TODO: IE_NOEPRM if user has no permission to erase a message */
11577 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11578 const char *message_id, _Bool incoming) {
11579 isds_error err = IE_SUCCESS;
11580 #if HAVE_LIBCURL
11581 xmlNodePtr request = NULL, node;
11582 xmlNsPtr isds_ns = NULL;
11583 xmlDocPtr response = NULL;
11584 xmlChar *code = NULL, *status_message = NULL;
11585 #endif
11587 if (!context) return IE_INVALID_CONTEXT;
11588 zfree(context->long_message);
11589 if (NULL == message_id) return IE_INVAL;
11591 #if HAVE_LIBCURL
11592 /* Check if connection is established
11593 * TODO: This check should be done downstairs. */
11594 if (!context->curl) return IE_CONNECTION_CLOSED;
11596 /* Build request */
11597 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11598 if (!request) {
11599 isds_log_message(context,
11600 _("Could build EraseMessage request"));
11601 return IE_ERROR;
11603 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11604 if(!isds_ns) {
11605 isds_log_message(context, _("Could not create ISDS name space"));
11606 xmlFreeNode(request);
11607 return IE_ERROR;
11609 xmlSetNs(request, isds_ns);
11611 err = validate_message_id_length(context, (xmlChar *) message_id);
11612 if (err) goto leave;
11613 INSERT_STRING(request, "dmID", message_id);
11615 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11618 /* Send request */
11619 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11620 "message ID %s to ISDS\n"), message_id);
11621 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11622 xmlFreeNode(request); request = NULL;
11624 if (err) {
11625 isds_log(ILF_ISDS, ILL_DEBUG,
11626 _("Processing ISDS response on EraseMessage request "
11627 "failed\n"));
11628 goto leave;
11631 /* Check for response status */
11632 err = isds_response_status(context, SERVICE_DM_INFO, response,
11633 &code, &status_message, NULL);
11634 if (err) {
11635 isds_log(ILF_ISDS, ILL_DEBUG,
11636 _("ISDS response on EraseMessage request is missing "
11637 "status\n"));
11638 goto leave;
11641 /* Check server status code */
11642 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11643 isds_log_message(context, _("Message to erase belongs to other box"));
11644 err = IE_INVAL;
11645 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11646 isds_log_message(context, _("Message to erase is not saved in "
11647 "long term storage or the direction does not match"));
11648 err = IE_INVAL;
11649 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11650 char *code_locale = _isds_utf82locale((char*) code);
11651 char *message_locale = _isds_utf82locale((char*) status_message);
11652 isds_log(ILF_ISDS, ILL_DEBUG,
11653 _("Server refused EraseMessage request "
11654 "(code=%s, message=%s)\n"),
11655 code_locale, message_locale);
11656 isds_log_message(context, message_locale);
11657 free(code_locale);
11658 free(message_locale);
11659 err = IE_ISDS;
11660 goto leave;
11663 leave:
11664 free(code);
11665 free(status_message);
11666 xmlFreeDoc(response);
11667 xmlFreeNode(request);
11669 if (!err)
11670 isds_log(ILF_ISDS, ILL_DEBUG,
11671 _("EraseMessage request processed by server "
11672 "successfully.\n")
11674 #else /* not HAVE_LIBCURL */
11675 err = IE_NOTSUP;
11676 #endif
11677 return err;
11681 /* Mark message as read. This is a transactional commit function to acknowledge
11682 * to ISDS the message has been downloaded and processed by client properly.
11683 * @context is session context
11684 * @message_id is message identifier. */
11685 isds_error isds_mark_message_read(struct isds_ctx *context,
11686 const char *message_id) {
11688 isds_error err = IE_SUCCESS;
11689 #if HAVE_LIBCURL
11690 xmlDocPtr response = NULL;
11691 xmlChar *code = NULL, *status_message = NULL;
11692 #endif
11694 if (!context) return IE_INVALID_CONTEXT;
11695 zfree(context->long_message);
11697 #if HAVE_LIBCURL
11698 /* Do request and check for success */
11699 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11700 BAD_CAST "MarkMessageAsDownloaded", message_id,
11701 &response, NULL, NULL, &code, &status_message);
11703 free(code);
11704 free(status_message);
11705 xmlFreeDoc(response);
11707 if (!err)
11708 isds_log(ILF_ISDS, ILL_DEBUG,
11709 _("MarkMessageAsDownloaded request processed by server "
11710 "successfully.\n")
11712 #else /* not HAVE_LIBCURL */
11713 err = IE_NOTSUP;
11714 #endif
11715 return err;
11719 /* Mark message as received by recipient. This is applicable only to
11720 * commercial message. Use envelope->dmType message member to distinguish
11721 * commercial message from government message. Government message is
11722 * received automatically (by law), commercial message on recipient request.
11723 * @context is session context
11724 * @message_id is message identifier. */
11725 isds_error isds_mark_message_received(struct isds_ctx *context,
11726 const char *message_id) {
11728 isds_error err = IE_SUCCESS;
11729 #if HAVE_LIBCURL
11730 xmlDocPtr response = NULL;
11731 xmlChar *code = NULL, *status_message = NULL;
11732 #endif
11734 if (!context) return IE_INVALID_CONTEXT;
11735 zfree(context->long_message);
11737 #if HAVE_LIBCURL
11738 /* Do request and check for success */
11739 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11740 BAD_CAST "ConfirmDelivery", message_id,
11741 &response, NULL, NULL, &code, &status_message);
11743 free(code);
11744 free(status_message);
11745 xmlFreeDoc(response);
11747 if (!err)
11748 isds_log(ILF_ISDS, ILL_DEBUG,
11749 _("ConfirmDelivery request processed by server "
11750 "successfully.\n")
11752 #else /* not HAVE_LIBCURL */
11753 err = IE_NOTSUP;
11754 #endif
11755 return err;
11759 /* Send document for authorized conversion into Czech POINT system.
11760 * This is public anonymous service, no log-in necessary. Special context is
11761 * used to reuse keep-a-live HTTPS connection.
11762 * @context is Czech POINT session context. DO NOT use context connected to
11763 * ISDS server. Use new context or context used by this function previously.
11764 * @document is document to convert. Only data, data_length, dmFileDescr and
11765 * is_xml members are significant. Be ware that not all document formats can be
11766 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11767 * @id is reallocated identifier assigned by Czech POINT system to
11768 * your document on submit. Use is to tell it to Czech POINT officer.
11769 * @date is reallocated document submit date (submitted documents
11770 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11771 * value. */
11772 isds_error czp_convert_document(struct isds_ctx *context,
11773 const struct isds_document *document,
11774 char **id, struct tm **date) {
11775 isds_error err = IE_SUCCESS;
11776 #if HAVE_LIBCURL
11777 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11778 xmlNodePtr request = NULL, node;
11779 xmlDocPtr response = NULL;
11781 xmlXPathContextPtr xpath_ctx = NULL;
11782 xmlXPathObjectPtr result = NULL;
11783 long int status = -1;
11784 long int *status_ptr = &status;
11785 char *string = NULL;
11786 #endif
11789 if (!context) return IE_INVALID_CONTEXT;
11790 zfree(context->long_message);
11791 if (!document || !id || !date) return IE_INVAL;
11793 if (document->is_xml) {
11794 isds_log_message(context,
11795 _("XML documents cannot be submitted to conversion"));
11796 return IE_NOTSUP;
11799 /* Free output arguments */
11800 zfree(*id);
11801 zfree(*date);
11803 #if HAVE_LIBCURL
11804 /* Store configuration */
11805 context->type = CTX_TYPE_CZP;
11806 free(context->url);
11807 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11808 if (!(context->url))
11809 return IE_NOMEM;
11811 /* Prepare CURL handle if not yet connected */
11812 if (!context->curl) {
11813 context->curl = curl_easy_init();
11814 if (!(context->curl))
11815 return IE_ERROR;
11818 /* Build conversion request */
11819 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11820 if (!request) {
11821 isds_log_message(context,
11822 _("Could not build Czech POINT conversion request"));
11823 return IE_ERROR;
11825 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11826 if(!deposit_ns) {
11827 isds_log_message(context,
11828 _("Could not create Czech POINT deposit name space"));
11829 xmlFreeNode(request);
11830 return IE_ERROR;
11832 xmlSetNs(request, deposit_ns);
11834 /* Insert children. They are in empty namespace! */
11835 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11836 if(!empty_ns) {
11837 isds_log_message(context, _("Could not create empty name space"));
11838 err = IE_ERROR;
11839 goto leave;
11841 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11842 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11843 document->dmFileDescr);
11845 /* Document encoded in Base64 */
11846 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11847 document->data, document->data_length);
11848 if (err) goto leave;
11850 isds_log(ILF_ISDS, ILL_DEBUG,
11851 _("Submitting document for conversion into Czech POINT deposit"));
11853 /* Send conversion request */
11854 err = _czp_czpdeposit(context, request, &response);
11855 xmlFreeNode(request); request = NULL;
11857 if (err) {
11858 czp_do_close_connection(context);
11859 goto leave;
11863 /* Extract response */
11864 xpath_ctx = xmlXPathNewContext(response);
11865 if (!xpath_ctx) {
11866 err = IE_ERROR;
11867 goto leave;
11869 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11870 err = IE_ERROR;
11871 goto leave;
11873 result = xmlXPathEvalExpression(
11874 BAD_CAST "/deposit:saveDocumentResponse/return",
11875 xpath_ctx);
11876 if (!result) {
11877 err = IE_ERROR;
11878 goto leave;
11880 /* Empty response */
11881 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11882 isds_printf_message(context,
11883 _("Missing `return' element in Czech POINT deposit response"));
11884 err = IE_ISDS;
11885 goto leave;
11887 /* More responses */
11888 if (result->nodesetval->nodeNr > 1) {
11889 isds_printf_message(context,
11890 _("Multiple `return' element in Czech POINT deposit response"));
11891 err = IE_ISDS;
11892 goto leave;
11894 /* One response */
11895 xpath_ctx->node = result->nodesetval->nodeTab[0];
11897 /* Get status */
11898 EXTRACT_LONGINT("status", status_ptr, 1);
11899 if (status) {
11900 EXTRACT_STRING("statusMsg", string);
11901 char *string_locale = _isds_utf82locale(string);
11902 isds_printf_message(context,
11903 _("Czech POINT deposit refused document for conversion "
11904 "(code=%ld, message=%s)"),
11905 status, string_locale);
11906 free(string_locale);
11907 err = IE_ISDS;
11908 goto leave;
11911 /* Get document ID */
11912 EXTRACT_STRING("documentID", *id);
11914 /* Get submit date */
11915 EXTRACT_STRING("dateInserted", string);
11916 if (string) {
11917 *date = calloc(1, sizeof(**date));
11918 if (!*date) {
11919 err = IE_NOMEM;
11920 goto leave;
11922 err = _isds_datestring2tm((xmlChar *)string, *date);
11923 if (err) {
11924 if (err == IE_NOTSUP) {
11925 err = IE_ISDS;
11926 char *string_locale = _isds_utf82locale(string);
11927 isds_printf_message(context,
11928 _("Invalid dateInserted value: %s"), string_locale);
11929 free(string_locale);
11931 goto leave;
11935 leave:
11936 free(string);
11937 xmlXPathFreeObject(result);
11938 xmlXPathFreeContext(xpath_ctx);
11940 xmlFreeDoc(response);
11941 xmlFreeNode(request);
11943 if (!err) {
11944 char *id_locale = _isds_utf82locale((char *) *id);
11945 isds_log(ILF_ISDS, ILL_DEBUG,
11946 _("Document %s has been submitted for conversion "
11947 "to server successfully\n"), id_locale);
11948 free(id_locale);
11950 #else /* not HAVE_LIBCURL */
11951 err = IE_NOTSUP;
11952 #endif
11953 return err;
11957 /* Close possibly opened connection to Czech POINT document deposit.
11958 * @context is Czech POINT session context. */
11959 isds_error czp_close_connection(struct isds_ctx *context) {
11960 if (!context) return IE_INVALID_CONTEXT;
11961 zfree(context->long_message);
11962 #if HAVE_LIBCURL
11963 return czp_do_close_connection(context);
11964 #else
11965 return IE_NOTSUP;
11966 #endif
11970 /* Send request for new box creation in testing ISDS instance.
11971 * It's not possible to request for a production box currently, as it
11972 * communicates via e-mail.
11973 * XXX: This function does not work either. Server complains about invalid
11974 * e-mail address.
11975 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11976 * this function
11977 * @context is special session context for box creation request. DO NOT use
11978 * standard context as it could reveal your password. Use fresh new context or
11979 * context previously used by this function.
11980 * @box is box description to create including single primary user (in case of
11981 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11982 * ignored. It outputs box ID assigned by ISDS in dbID element.
11983 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11984 * box, or contact address of PFO box owner). The email member is mandatory as
11985 * it will be used to deliver credentials.
11986 * @former_names is former name of box owner. Pass NULL if you don't care.
11987 * @approval is optional external approval of box manipulation
11988 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11989 * NULL, if you don't care.*/
11990 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11991 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11992 const char *former_names, const struct isds_approval *approval,
11993 char **refnumber) {
11994 isds_error err = IE_SUCCESS;
11995 #if HAVE_LIBCURL
11996 xmlNodePtr request = NULL;
11997 xmlDocPtr response = NULL;
11998 xmlXPathContextPtr xpath_ctx = NULL;
11999 xmlXPathObjectPtr result = NULL;
12000 #endif
12003 if (!context) return IE_INVALID_CONTEXT;
12004 zfree(context->long_message);
12005 if (!box) return IE_INVAL;
12007 #if HAVE_LIBCURL
12008 if (!box->email || box->email[0] == '\0') {
12009 isds_log_message(context, _("E-mail field is mandatory"));
12010 return IE_INVAL;
12013 /* Scratch box ID */
12014 zfree(box->dbID);
12016 /* Store configuration */
12017 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
12018 free(context->url);
12019 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
12020 if (!(context->url))
12021 return IE_NOMEM;
12023 /* Prepare CURL handle if not yet connected */
12024 if (!context->curl) {
12025 context->curl = curl_easy_init();
12026 if (!(context->curl))
12027 return IE_ERROR;
12030 /* Build CreateDataBox request */
12031 err = build_CreateDBInput_request(context,
12032 &request, BAD_CAST "CreateDataBox",
12033 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
12034 if (err) goto leave;
12036 /* Send it to server and process response */
12037 err = send_destroy_request_check_response(context,
12038 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
12039 &response, (xmlChar **) refnumber, NULL);
12040 if (err) goto leave;
12042 /* Extract box ID */
12043 xpath_ctx = xmlXPathNewContext(response);
12044 if (!xpath_ctx) {
12045 err = IE_ERROR;
12046 goto leave;
12048 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12049 err = IE_ERROR;
12050 goto leave;
12052 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
12054 leave:
12055 xmlXPathFreeObject(result);
12056 xmlXPathFreeContext(xpath_ctx);
12057 xmlFreeDoc(response);
12058 xmlFreeNode(request);
12060 if (!err) {
12061 isds_log(ILF_ISDS, ILL_DEBUG,
12062 _("CreateDataBox request processed by server successfully.\n"));
12064 #else /* not HAVE_LIBCURL */
12065 err = IE_NOTSUP;
12066 #endif
12068 return err;
12072 /* Submit CMS signed message to ISDS to verify its originality. This is
12073 * stronger form of isds_verify_message_hash() because ISDS does more checks
12074 * than simple one (potentialy old weak) hash comparison.
12075 * @context is session context
12076 * @message is memory with raw CMS signed message bit stream
12077 * @length is @message size in bytes
12078 * @return
12079 * IE_SUCCESS if message originates in ISDS
12080 * IE_NOTEQUAL if message is unknown to ISDS
12081 * other code for other errors */
12082 isds_error isds_authenticate_message(struct isds_ctx *context,
12083 const void *message, size_t length) {
12084 isds_error err = IE_SUCCESS;
12085 #if HAVE_LIBCURL
12086 xmlNsPtr isds_ns = NULL;
12087 xmlNodePtr request = NULL;
12088 xmlDocPtr response = NULL;
12089 xmlXPathContextPtr xpath_ctx = NULL;
12090 xmlXPathObjectPtr result = NULL;
12091 _Bool *authentic = NULL;
12092 #endif
12094 if (!context) return IE_INVALID_CONTEXT;
12095 zfree(context->long_message);
12096 if (!message || length == 0) return IE_INVAL;
12098 #if HAVE_LIBCURL
12099 /* Check if connection is established
12100 * TODO: This check should be done downstairs. */
12101 if (!context->curl) return IE_CONNECTION_CLOSED;
12104 /* Build AuthenticateMessage request */
12105 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
12106 if (!request) {
12107 isds_log_message(context,
12108 _("Could not build AuthenticateMessage request"));
12109 return IE_ERROR;
12111 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12112 if(!isds_ns) {
12113 isds_log_message(context, _("Could not create ISDS name space"));
12114 xmlFreeNode(request);
12115 return IE_ERROR;
12117 xmlSetNs(request, isds_ns);
12119 /* Insert Base64 encoded message */
12120 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
12121 message, length);
12122 if (err) goto leave;
12124 /* Send request to server and process response */
12125 err = send_destroy_request_check_response(context,
12126 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
12127 &response, NULL, NULL);
12128 if (err) goto leave;
12131 /* ISDS has decided */
12132 xpath_ctx = xmlXPathNewContext(response);
12133 if (!xpath_ctx) {
12134 err = IE_ERROR;
12135 goto leave;
12137 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12138 err = IE_ERROR;
12139 goto leave;
12142 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
12144 if (!authentic) {
12145 isds_log_message(context,
12146 _("Server did not return any response on "
12147 "AuthenticateMessage request"));
12148 err = IE_ISDS;
12149 goto leave;
12151 if (*authentic) {
12152 isds_log(ILF_ISDS, ILL_DEBUG,
12153 _("ISDS authenticated the message successfully\n"));
12154 } else {
12155 isds_log_message(context, _("ISDS does not know the message"));
12156 err = IE_NOTEQUAL;
12160 leave:
12161 free(authentic);
12162 xmlXPathFreeObject(result);
12163 xmlXPathFreeContext(xpath_ctx);
12165 xmlFreeDoc(response);
12166 xmlFreeNode(request);
12167 #else /* not HAVE_LIBCURL */
12168 err = IE_NOTSUP;
12169 #endif
12171 return err;
12175 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12176 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12177 * be re-signed.
12178 * @context is session context
12179 * @input_data is memory with raw CMS signed message or delivery info bit
12180 * stream to re-sign
12181 * @input_length is @input_data size in bytes
12182 * @output_data is pointer to auto-allocated memory where to store re-signed
12183 * input data blob. Caller must free it.
12184 * @output_data is pointer where to store @output_data size in bytes
12185 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12186 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12187 * @return
12188 * IE_SUCCESS if CMS blob has been re-signed successfully
12189 * other code for other errors */
12190 isds_error isds_resign_message(struct isds_ctx *context,
12191 const void *input_data, size_t input_length,
12192 void **output_data, size_t *output_length, struct tm **valid_to) {
12193 isds_error err = IE_SUCCESS;
12194 #if HAVE_LIBCURL
12195 xmlNsPtr isds_ns = NULL;
12196 xmlNodePtr request = NULL;
12197 xmlDocPtr response = NULL;
12198 xmlXPathContextPtr xpath_ctx = NULL;
12199 xmlXPathObjectPtr result = NULL;
12200 char *string = NULL;
12201 const xmlChar *codes[] = {
12202 BAD_CAST "2200",
12203 BAD_CAST "2201",
12204 BAD_CAST "2204",
12205 BAD_CAST "2207",
12206 NULL
12208 const char *meanings[] = {
12209 "Message is bad",
12210 "Message is not original",
12211 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12212 "Time stamp could not been generated in time"
12214 const isds_error errors[] = {
12215 IE_INVAL,
12216 IE_NOTUNIQ,
12217 IE_INVAL,
12218 IE_ISDS,
12220 struct code_map_isds_error map = {
12221 .codes = codes,
12222 .meanings = meanings,
12223 .errors = errors
12225 #endif
12227 if (NULL != output_data) *output_data = NULL;
12228 if (NULL != output_length) *output_length = 0;
12229 if (NULL != valid_to) *valid_to = NULL;
12231 if (NULL == context) return IE_INVALID_CONTEXT;
12232 zfree(context->long_message);
12233 if (NULL == input_data || 0 == input_length) {
12234 isds_log_message(context, _("Empty CMS blob on input"));
12235 return IE_INVAL;
12237 if (NULL == output_data || NULL == output_length) {
12238 isds_log_message(context,
12239 _("NULL pointer provided for output CMS blob"));
12240 return IE_INVAL;
12243 #if HAVE_LIBCURL
12244 /* Check if connection is established
12245 * TODO: This check should be done downstairs. */
12246 if (!context->curl) return IE_CONNECTION_CLOSED;
12249 /* Build Re-signISDSDocument request */
12250 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
12251 if (!request) {
12252 isds_log_message(context,
12253 _("Could not build Re-signISDSDocument request"));
12254 return IE_ERROR;
12256 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12257 if(!isds_ns) {
12258 isds_log_message(context, _("Could not create ISDS name space"));
12259 xmlFreeNode(request);
12260 return IE_ERROR;
12262 xmlSetNs(request, isds_ns);
12264 /* Insert Base64 encoded CMS blob */
12265 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
12266 input_data, input_length);
12267 if (err) goto leave;
12269 /* Send request to server and process response */
12270 err = send_destroy_request_check_response(context,
12271 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
12272 &response, NULL, &map);
12273 if (err) goto leave;
12276 /* Extract re-signed data */
12277 xpath_ctx = xmlXPathNewContext(response);
12278 if (!xpath_ctx) {
12279 err = IE_ERROR;
12280 goto leave;
12282 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12283 err = IE_ERROR;
12284 goto leave;
12286 result = xmlXPathEvalExpression(
12287 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
12288 if (!result) {
12289 err = IE_ERROR;
12290 goto leave;
12292 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
12293 isds_log_message(context,
12294 _("Missing Re-signISDSDocumentResponse element"));
12295 err = IE_ISDS;
12296 goto leave;
12298 if (result->nodesetval->nodeNr > 1) {
12299 isds_log_message(context,
12300 _("Multiple Re-signISDSDocumentResponse element"));
12301 err = IE_ISDS;
12302 goto leave;
12304 xpath_ctx->node = result->nodesetval->nodeTab[0];
12305 xmlXPathFreeObject(result); result = NULL;
12307 EXTRACT_STRING("isds:dmResultDoc", string);
12308 /* Decode non-empty data */
12309 if (NULL != string && string[0] != '\0') {
12310 *output_length = _isds_b64decode(string, output_data);
12311 if (*output_length == (size_t) -1) {
12312 isds_log_message(context,
12313 _("Error while Base64-decoding re-signed data"));
12314 err = IE_ERROR;
12315 goto leave;
12317 } else {
12318 isds_log_message(context, _("Server did not send re-signed data"));
12319 err = IE_ISDS;
12320 goto leave;
12322 zfree(string);
12324 if (NULL != valid_to) {
12325 /* Get time stamp expiration date */
12326 EXTRACT_STRING("isds:dmValidTo", string);
12327 if (NULL != string) {
12328 *valid_to = calloc(1, sizeof(**valid_to));
12329 if (!*valid_to) {
12330 err = IE_NOMEM;
12331 goto leave;
12333 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
12334 if (err) {
12335 if (err == IE_NOTSUP) {
12336 err = IE_ISDS;
12337 char *string_locale = _isds_utf82locale(string);
12338 isds_printf_message(context,
12339 _("Invalid dmValidTo value: %s"), string_locale);
12340 free(string_locale);
12342 goto leave;
12347 leave:
12348 free(string);
12350 xmlXPathFreeObject(result);
12351 xmlXPathFreeContext(xpath_ctx);
12353 xmlFreeDoc(response);
12354 xmlFreeNode(request);
12355 #else /* not HAVE_LIBCURL */
12356 err = IE_NOTSUP;
12357 #endif
12359 return err;
12362 #undef INSERT_ELEMENT
12363 #undef CHECK_FOR_STRING_LENGTH
12364 #undef INSERT_STRING_ATTRIBUTE
12365 #undef INSERT_ULONGINTNOPTR
12366 #undef INSERT_ULONGINT
12367 #undef INSERT_LONGINT
12368 #undef INSERT_BOOLEAN
12369 #undef INSERT_SCALAR_BOOLEAN
12370 #undef INSERT_STRING
12371 #undef INSERT_STRING_WITH_NS
12372 #undef EXTRACT_STRING_ATTRIBUTE
12373 #undef EXTRACT_ULONGINT
12374 #undef EXTRACT_LONGINT
12375 #undef EXTRACT_BOOLEAN
12376 #undef EXTRACT_STRING
12379 /* Compute hash of message from raw representation and store it into envelope.
12380 * Original hash structure will be destroyed in envelope.
12381 * @context is session context
12382 * @message is message carrying raw XML message blob
12383 * @algorithm is desired hash algorithm to use */
12384 isds_error isds_compute_message_hash(struct isds_ctx *context,
12385 struct isds_message *message, const isds_hash_algorithm algorithm) {
12386 isds_error err = IE_SUCCESS;
12387 const char *nsuri;
12388 void *xml_stream = NULL;
12389 size_t xml_stream_length;
12390 size_t phys_start, phys_end;
12391 char *phys_path = NULL;
12392 struct isds_hash *new_hash = NULL;
12395 if (!context) return IE_INVALID_CONTEXT;
12396 zfree(context->long_message);
12397 if (!message) return IE_INVAL;
12399 if (!message->raw) {
12400 isds_log_message(context,
12401 _("Message does not carry raw representation"));
12402 return IE_INVAL;
12405 switch (message->raw_type) {
12406 case RAWTYPE_INCOMING_MESSAGE:
12407 nsuri = ISDS_NS;
12408 xml_stream = message->raw;
12409 xml_stream_length = message->raw_length;
12410 break;
12412 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
12413 nsuri = SISDS_INCOMING_NS;
12414 xml_stream = message->raw;
12415 xml_stream_length = message->raw_length;
12416 break;
12418 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
12419 nsuri = SISDS_INCOMING_NS;
12420 err = _isds_extract_cms_data(context,
12421 message->raw, message->raw_length,
12422 &xml_stream, &xml_stream_length);
12423 if (err) goto leave;
12424 break;
12426 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
12427 nsuri = SISDS_OUTGOING_NS;
12428 xml_stream = message->raw;
12429 xml_stream_length = message->raw_length;
12430 break;
12432 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12433 nsuri = SISDS_OUTGOING_NS;
12434 err = _isds_extract_cms_data(context,
12435 message->raw, message->raw_length,
12436 &xml_stream, &xml_stream_length);
12437 if (err) goto leave;
12438 break;
12440 default:
12441 isds_log_message(context, _("Bad raw representation type"));
12442 return IE_INVAL;
12443 break;
12447 /* XXX: Hash is computed from original string representing isds:dmDm
12448 * subtree. That means no encoding, white space, xmlns attributes changes.
12449 * In other words, input for hash can be invalid XML stream. */
12450 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12451 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12452 PHYSXML_ELEMENT_SEPARATOR,
12453 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12454 PHYSXML_ELEMENT_SEPARATOR
12455 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12456 err = IE_NOMEM;
12457 goto leave;
12459 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12460 phys_path, &phys_start, &phys_end);
12461 zfree(phys_path);
12462 if (err) {
12463 isds_log_message(context,
12464 _("Substring with isds:dmDM element could not be located "
12465 "in raw message"));
12466 goto leave;
12470 /* Compute hash */
12471 new_hash = calloc(1, sizeof(*new_hash));
12472 if (!new_hash) {
12473 err = IE_NOMEM;
12474 goto leave;
12476 new_hash->algorithm = algorithm;
12477 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12478 new_hash);
12479 if (err) {
12480 isds_log_message(context, _("Could not compute message hash"));
12481 goto leave;
12484 /* Save computed hash */
12485 if (!message->envelope) {
12486 message->envelope = calloc(1, sizeof(*message->envelope));
12487 if (!message->envelope) {
12488 err = IE_NOMEM;
12489 goto leave;
12492 isds_hash_free(&message->envelope->hash);
12493 message->envelope->hash = new_hash;
12495 leave:
12496 if (err) {
12497 isds_hash_free(&new_hash);
12500 free(phys_path);
12501 if (xml_stream != message->raw) free(xml_stream);
12502 return err;
12506 /* Compare two hashes.
12507 * @h1 is first hash
12508 * @h2 is another hash
12509 * @return
12510 * IE_SUCCESS if hashes equal
12511 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12512 * IE_ENUM if not comparable, but both structures defined
12513 * IE_INVAL if some of the structures are undefined (NULL)
12514 * IE_ERROR if internal error occurs */
12515 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12516 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12517 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12518 if (h1->length != h2->length) return IE_ERROR;
12519 if (h1->length > 0 && !h1->value) return IE_ERROR;
12520 if (h2->length > 0 && !h2->value) return IE_ERROR;
12522 for (size_t i = 0; i < h1->length; i++) {
12523 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12524 return IE_NOTEQUAL;
12526 return IE_SUCCESS;
12530 /* Check message has gone through ISDS by comparing message hash stored in
12531 * ISDS and locally computed hash. You must provide message with valid raw
12532 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12533 * This is convenient wrapper for isds_download_message_hash(),
12534 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12535 * @context is session context
12536 * @message is message with valid raw and envelope member; envelope->hash
12537 * member will be changed during function run. Use envelope on heap only.
12538 * @return
12539 * IE_SUCCESS if message originates in ISDS
12540 * IE_NOTEQUAL if message is unknown to ISDS
12541 * other code for other errors */
12542 isds_error isds_verify_message_hash(struct isds_ctx *context,
12543 struct isds_message *message) {
12544 isds_error err = IE_SUCCESS;
12545 struct isds_hash *downloaded_hash = NULL;
12547 if (!context) return IE_INVALID_CONTEXT;
12548 zfree(context->long_message);
12549 if (!message) return IE_INVAL;
12551 if (!message->envelope) {
12552 isds_log_message(context,
12553 _("Given message structure is missing envelope"));
12554 return IE_INVAL;
12556 if (!message->raw) {
12557 isds_log_message(context,
12558 _("Given message structure is missing raw representation"));
12559 return IE_INVAL;
12562 err = isds_download_message_hash(context, message->envelope->dmID,
12563 &downloaded_hash);
12564 if (err) goto leave;
12566 err = isds_compute_message_hash(context, message,
12567 downloaded_hash->algorithm);
12568 if (err) goto leave;
12570 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12572 leave:
12573 isds_hash_free(&downloaded_hash);
12574 return err;
12578 /* Search for document by document ID in list of documents. IDs are compared
12579 * as UTF-8 string.
12580 * @documents is list of isds_documents
12581 * @id is document identifier
12582 * @return first matching document or NULL. */
12583 const struct isds_document *isds_find_document_by_id(
12584 const struct isds_list *documents, const char *id) {
12585 const struct isds_list *item;
12586 const struct isds_document *document;
12588 for (item = documents; item; item = item->next) {
12589 document = (struct isds_document *) item->data;
12590 if (!document) continue;
12592 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12593 return document;
12596 return NULL;
12600 /* Normalize @mime_type to be proper MIME type.
12601 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12602 * guess regular MIME type (e.g. "application/pdf").
12603 * @mime_type is UTF-8 encoded MIME type to fix
12604 * @return original @mime_type if no better interpretation exists, or
12605 * constant static UTF-8 encoded string with proper MIME type. */
12606 const char *isds_normalize_mime_type(const char *mime_type) {
12607 if (!mime_type) return NULL;
12609 for (size_t offset = 0;
12610 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12611 offset += 2) {
12612 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12613 extension_map_mime[offset]))
12614 return (const char *) extension_map_mime[offset + 1];
12617 return mime_type;
12621 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12622 struct isds_message **message);
12623 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12624 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12625 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12626 struct isds_address **address);
12628 int isds_message_free(struct isds_message **message);
12629 int isds_address_free(struct isds_address **address);
12633 /* Makes known all relevant namespaces to given XPath context
12634 * @xpath_ctx is XPath context
12635 * @message_ns selects proper message name space. Unsigned and signed
12636 * messages and delivery info's differ in prefix and URI. */
12637 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12638 const message_ns_type message_ns) {
12639 const xmlChar *message_namespace = NULL;
12641 if (!xpath_ctx) return IE_ERROR;
12643 switch(message_ns) {
12644 case MESSAGE_NS_1:
12645 message_namespace = BAD_CAST ISDS1_NS; break;
12646 case MESSAGE_NS_UNSIGNED:
12647 message_namespace = BAD_CAST ISDS_NS; break;
12648 case MESSAGE_NS_SIGNED_INCOMING:
12649 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12650 case MESSAGE_NS_SIGNED_OUTGOING:
12651 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12652 case MESSAGE_NS_SIGNED_DELIVERY:
12653 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12654 default:
12655 return IE_ENUM;
12658 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12659 return IE_ERROR;
12660 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12661 return IE_ERROR;
12662 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12663 return IE_ERROR;
12664 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12665 return IE_ERROR;
12666 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12667 return IE_ERROR;
12668 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12669 return IE_ERROR;
12670 return IE_SUCCESS;