Added OVM_FO, OVM_PFO and OVM_PO box types.
[libisds.git] / src / isds.c
blobe88f75cdb257483c10b8ad62d56ee29e2c45e168
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 "PFO_AUDITOR"))
2018 *type = DBTYPE_PFO_AUDITOR;
2019 else if (!xmlStrcmp(string, BAD_CAST "PO"))
2020 *type = DBTYPE_PO;
2021 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
2022 *type = DBTYPE_PO_ZAK;
2023 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
2024 *type = DBTYPE_PO_REQ;
2025 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
2026 *type = DBTYPE_OVM;
2027 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
2028 *type = DBTYPE_OVM_NOTAR;
2029 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
2030 *type = DBTYPE_OVM_EXEKUT;
2031 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2032 *type = DBTYPE_OVM_REQ;
2033 else if (!xmlStrcmp(string, BAD_CAST "OVM_FO"))
2034 *type = DBTYPE_OVM_FO;
2035 else if (!xmlStrcmp(string, BAD_CAST "OVM_PFO"))
2036 *type = DBTYPE_OVM_PFO;
2037 else if (!xmlStrcmp(string, BAD_CAST "OVM_PO"))
2038 *type = DBTYPE_OVM_PO;
2039 else
2040 return IE_ENUM;
2041 return IE_SUCCESS;
2045 /* Convert ISDS dbType enum @type to UTF-8 string.
2046 * @Return pointer to static string, or NULL if unknown enum value */
2047 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2048 switch(type) {
2049 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2050 * of view of generic public SOAP interface. */
2051 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2052 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2053 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2054 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2055 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2056 case DBTYPE_PFO_AUDITOR: return(BAD_CAST "PFO_AUDITOR"); break;
2057 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2058 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2059 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2060 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2061 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2062 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2063 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2064 case DBTYPE_OVM_FO: return(BAD_CAST "OVM_FO"); break;
2065 case DBTYPE_OVM_PFO: return(BAD_CAST "OVM_PFO"); break;
2066 case DBTYPE_OVM_PO: return(BAD_CAST "OVM_PO"); break;
2067 default: return NULL; break;
2072 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2073 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2074 if (!string || !type) return IE_INVAL;
2076 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2077 *type = USERTYPE_PRIMARY;
2078 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2079 *type = USERTYPE_ENTRUSTED;
2080 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2081 *type = USERTYPE_ADMINISTRATOR;
2082 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2083 *type = USERTYPE_OFFICIAL;
2084 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2085 *type = USERTYPE_OFFICIAL_CERT;
2086 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2087 *type = USERTYPE_LIQUIDATOR;
2088 else if (!xmlStrcmp(string, BAD_CAST "RECEIVER"))
2089 *type = USERTYPE_RECEIVER;
2090 else if (!xmlStrcmp(string, BAD_CAST "GUARDIAN"))
2091 *type = USERTYPE_GUARDIAN;
2092 else
2093 return IE_ENUM;
2094 return IE_SUCCESS;
2098 /* Convert ISDS userType enum @type to UTF-8 string.
2099 * @Return pointer to static string, or NULL if unknown enum value */
2100 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2101 switch(type) {
2102 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2103 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2104 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2105 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2106 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2107 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2108 case USERTYPE_RECEIVER: return(BAD_CAST "RECEIVER"); break;
2109 case USERTYPE_GUARDIAN: return(BAD_CAST "GUARDIAN"); break;
2110 default: return NULL; break;
2115 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2116 static isds_error string2isds_sender_type(const xmlChar *string,
2117 isds_sender_type *type) {
2118 if (!string || !type) return IE_INVAL;
2120 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2121 *type = SENDERTYPE_PRIMARY;
2122 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2123 *type = SENDERTYPE_ENTRUSTED;
2124 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2125 *type = SENDERTYPE_ADMINISTRATOR;
2126 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2127 *type = SENDERTYPE_OFFICIAL;
2128 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2129 *type = SENDERTYPE_VIRTUAL;
2130 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2131 *type = SENDERTYPE_OFFICIAL_CERT;
2132 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2133 *type = SENDERTYPE_LIQUIDATOR;
2134 else if (!xmlStrcmp(string, BAD_CAST "RECEIVER"))
2135 *type = SENDERTYPE_RECEIVER;
2136 else if (!xmlStrcmp(string, BAD_CAST "GUARDIAN"))
2137 *type = SENDERTYPE_GUARDIAN;
2138 else
2139 return IE_ENUM;
2140 return IE_SUCCESS;
2144 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2145 static isds_error string2isds_payment_type(const xmlChar *string,
2146 isds_payment_type *type) {
2147 if (!string || !type) return IE_INVAL;
2149 if (!xmlStrcmp(string, BAD_CAST "K"))
2150 *type = PAYMENT_SENDER;
2151 else if (!xmlStrcmp(string, BAD_CAST "O"))
2152 *type = PAYMENT_RESPONSE;
2153 else if (!xmlStrcmp(string, BAD_CAST "G"))
2154 *type = PAYMENT_SPONSOR;
2155 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2156 *type = PAYMENT_SPONSOR_LIMITED;
2157 else if (!xmlStrcmp(string, BAD_CAST "D"))
2158 *type = PAYMENT_SPONSOR_EXTERNAL;
2159 else if (!xmlStrcmp(string, BAD_CAST "E"))
2160 *type = PAYMENT_STAMP;
2161 else
2162 return IE_ENUM;
2163 return IE_SUCCESS;
2167 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2168 * ciEventType is integer but we convert it from string representation
2169 * directly. */
2170 static isds_error string2isds_credit_event_type(const xmlChar *string,
2171 isds_credit_event_type *type) {
2172 if (!string || !type) return IE_INVAL;
2174 if (!xmlStrcmp(string, BAD_CAST "1"))
2175 *type = ISDS_CREDIT_CHARGED;
2176 else if (!xmlStrcmp(string, BAD_CAST "2"))
2177 *type = ISDS_CREDIT_DISCHARGED;
2178 else if (!xmlStrcmp(string, BAD_CAST "3"))
2179 *type = ISDS_CREDIT_MESSAGE_SENT;
2180 else if (!xmlStrcmp(string, BAD_CAST "4"))
2181 *type = ISDS_CREDIT_STORAGE_SET;
2182 else if (!xmlStrcmp(string, BAD_CAST "5"))
2183 *type = ISDS_CREDIT_EXPIRED;
2184 else
2185 return IE_ENUM;
2186 return IE_SUCCESS;
2190 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2191 * @Return pointer to static string, or NULL if unknown enum value */
2192 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2193 switch(type) {
2194 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2195 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2196 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2197 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2198 default: return NULL; break;
2203 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2204 * ISDSSearch2/searchType value.
2205 * @Return pointer to static string, or NULL if unknown enum value */
2206 static const xmlChar *isds_fulltext_target2string(
2207 const isds_fulltext_target type) {
2208 switch(type) {
2209 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2210 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2211 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2212 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2213 default: return NULL; break;
2216 #endif /* HAVE_LIBCURL */
2219 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2220 * @Return IE_ENUM if @string is not valid enum member */
2221 static isds_error string2isds_FileMetaType(const xmlChar *string,
2222 isds_FileMetaType *type) {
2223 if (!string || !type) return IE_INVAL;
2225 if (!xmlStrcmp(string, BAD_CAST "main"))
2226 *type = FILEMETATYPE_MAIN;
2227 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2228 *type = FILEMETATYPE_ENCLOSURE;
2229 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2230 *type = FILEMETATYPE_SIGNATURE;
2231 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2232 *type = FILEMETATYPE_META;
2233 else
2234 return IE_ENUM;
2235 return IE_SUCCESS;
2239 /* Convert UTF-8 @string to ISDS hash @algorithm.
2240 * @Return IE_ENUM if @string is not valid enum member */
2241 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2242 isds_hash_algorithm *algorithm) {
2243 if (!string || !algorithm) return IE_INVAL;
2245 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2246 *algorithm = HASH_ALGORITHM_MD5;
2247 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2248 *algorithm = HASH_ALGORITHM_SHA_1;
2249 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2250 *algorithm = HASH_ALGORITHM_SHA_224;
2251 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2252 *algorithm = HASH_ALGORITHM_SHA_256;
2253 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2254 *algorithm = HASH_ALGORITHM_SHA_384;
2255 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2256 *algorithm = HASH_ALGORITHM_SHA_512;
2257 else
2258 return IE_ENUM;
2259 return IE_SUCCESS;
2263 #if HAVE_LIBCURL
2264 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2265 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2266 if (!time || !string) return IE_INVAL;
2268 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2269 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2270 return IE_ERROR;
2272 return IE_SUCCESS;
2276 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2277 * respects the @time microseconds too. */
2278 static isds_error timeval2timestring(const struct timeval *time,
2279 xmlChar **string) {
2280 struct tm broken;
2281 time_t seconds_as_time_t;
2283 if (!time || !string) return IE_INVAL;
2285 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2286 * 32-bit long in Microsoft API. Convert value to the type expected by
2287 * gmtime_r(). */
2288 seconds_as_time_t = time->tv_sec;
2289 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2290 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2292 /* TODO: small negative year should be formatted as "-0012". This is not
2293 * true for glibc "%04d". We should implement it.
2294 * time->tv_usec type is su_seconds_t which is required to be signed
2295 * integer to accomodate values from range [-1, 1000000].
2296 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2297 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2298 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2299 * of the range check above. */
2300 if (-1 == isds_asprintf((char **) string,
2301 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2302 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2303 broken.tm_hour, broken.tm_min, broken.tm_sec,
2304 (int32_t)time->tv_usec))
2305 return IE_ERROR;
2307 return IE_SUCCESS;
2309 #endif /* HAVE_LIBCURL */
2312 /* Convert UTF-8 ISO 8601 date-time @string to static struct timeval.
2313 * It respects microseconds too. Microseconds are rounded half up.
2314 * In case of error, @time will be undefined. */
2315 static isds_error timestring2static_timeval(const xmlChar *string,
2316 struct timeval *time) {
2317 struct tm broken;
2318 char *offset, *delim, *endptr;
2319 const int subsecond_resolution = 6;
2320 char subseconds[subsecond_resolution + 1];
2321 _Bool round_up = 0;
2322 int offset_hours, offset_minutes;
2323 int i;
2324 long int long_number;
2325 #ifdef _WIN32
2326 int tmp;
2327 #endif
2329 if (!time) return IE_INVAL;
2330 if (!string) {
2331 return IE_INVAL;
2334 memset(&broken, 0, sizeof(broken));
2335 memset(time, 0, sizeof(*time));
2338 /* xsd:date is ISO 8601 string, thus ASCII */
2339 /*TODO: negative year */
2341 #ifdef _WIN32
2342 i = 0;
2343 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2344 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2345 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2346 &i)) < 6) {
2347 return IE_DATE;
2350 broken.tm_year -= 1900;
2351 broken.tm_mon--;
2352 broken.tm_isdst = -1;
2353 offset = (char*)string + i;
2354 #else
2355 /* Parse date and time without subseconds and offset */
2356 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2357 if (!offset) {
2358 return IE_DATE;
2360 #endif
2362 /* Get subseconds */
2363 if (*offset == '.' ) {
2364 offset++;
2366 /* Copy first 6 digits, pad it with zeros.
2367 * Current server implementation uses only millisecond resolution. */
2368 /* TODO: isdigit() is locale sensitive */
2369 for (i = 0;
2370 i < subsecond_resolution && isdigit(*offset);
2371 i++, offset++) {
2372 subseconds[i] = *offset;
2374 if (subsecond_resolution == i && isdigit(*offset)) {
2375 /* Check 7th digit for rounding */
2376 if (*offset >= '5') round_up = 1;
2377 offset++;
2379 for (; i < subsecond_resolution; i++) {
2380 subseconds[i] = '0';
2382 subseconds[subsecond_resolution] = '\0';
2384 /* Convert it into integer */
2385 long_number = strtol(subseconds, &endptr, 10);
2386 if (*endptr != '\0' || long_number == LONG_MIN ||
2387 long_number == LONG_MAX) {
2388 return IE_DATE;
2390 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2391 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2392 * microseconds" and "the type shall be a signed integer capable of
2393 * storing values at least in the range [-1, 1000000]. */
2394 if (long_number < -1 || long_number >= 1000000) {
2395 return IE_DATE;
2397 time->tv_usec = long_number;
2399 /* Round the subseconds */
2400 if (round_up) {
2401 if (999999 == time->tv_usec) {
2402 time->tv_usec = 0;
2403 broken.tm_sec++;
2404 } else {
2405 time->tv_usec++;
2409 /* move to the zone offset delimiter or signal NULL*/
2410 delim = strchr(offset, '-');
2411 if (!delim)
2412 delim = strchr(offset, '+');
2413 if (!delim)
2414 delim = strchr(offset, 'Z');
2415 offset = delim;
2418 /* Get zone offset */
2419 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2420 * "" equals to "Z" and it means UTC zone. */
2421 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2422 * colon separator */
2423 if (offset && (*offset == '-' || *offset == '+')) {
2424 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2425 return IE_DATE;
2427 if (*offset == '+') {
2428 broken.tm_hour -= offset_hours;
2429 broken.tm_min -= offset_minutes;
2430 } else {
2431 broken.tm_hour += offset_hours;
2432 broken.tm_min += offset_minutes;
2436 /* Convert to time_t */
2437 time->tv_sec = _isds_timegm(&broken);
2438 if (time->tv_sec == (time_t) -1) {
2439 return IE_DATE;
2442 return IE_SUCCESS;
2446 /* Convert UTF-8 ISO 8601 date-time @string to reallocated struct timeval.
2447 * It respects microseconds too. Microseconds are rounded half up.
2448 * In case of error, @time will be freed. */
2449 static isds_error timestring2timeval(const xmlChar *string,
2450 struct timeval **time) {
2451 isds_error error;
2453 if (!time) return IE_INVAL;
2454 if (!string) {
2455 zfree(*time);
2456 return IE_INVAL;
2459 if (!*time) {
2460 *time = calloc(1, sizeof(**time));
2461 if (!*time) return IE_NOMEM;
2462 } else {
2463 memset(*time, 0, sizeof(**time));
2466 error = timestring2static_timeval(string, *time);
2467 if (error) {
2468 zfree(*time);
2471 return error;
2475 /* Convert unsigned int into isds_message_status.
2476 * @context is session context
2477 * @number is pointer to number value. NULL will be treated as invalid value.
2478 * @status is automatically reallocated status
2479 * @return IE_SUCCESS, or error code and free status */
2480 static isds_error uint2isds_message_status(struct isds_ctx *context,
2481 const unsigned long int *number, isds_message_status **status) {
2482 if (!context) return IE_INVALID_CONTEXT;
2483 if (!status) return IE_INVAL;
2485 free(*status); *status = NULL;
2486 if (!number) return IE_INVAL;
2488 if (*number < 1 || *number > 10) {
2489 isds_printf_message(context, _("Invalid message status value: %lu"),
2490 *number);
2491 return IE_ENUM;
2494 *status = malloc(sizeof(**status));
2495 if (!*status) return IE_NOMEM;
2497 **status = 1 << *number;
2498 return IE_SUCCESS;
2502 /* Convert event description string into isds_event members type and
2503 * description
2504 * @string is raw event description starting with event prefix
2505 * @event is structure where to store type and stripped description to
2506 * @return standard error code, unknown prefix is not classified as an error.
2507 * */
2508 static isds_error eventstring2event(const xmlChar *string,
2509 struct isds_event* event) {
2510 const xmlChar *known_prefixes[] = {
2511 BAD_CAST "EV0:",
2512 BAD_CAST "EV1:",
2513 BAD_CAST "EV2:",
2514 BAD_CAST "EV3:",
2515 BAD_CAST "EV4:",
2516 BAD_CAST "EV5:",
2517 BAD_CAST "EV11:",
2518 BAD_CAST "EV12:",
2519 BAD_CAST "EV13:"
2521 const isds_event_type types[] = {
2522 EVENT_ENTERED_SYSTEM,
2523 EVENT_ACCEPTED_BY_RECIPIENT,
2524 EVENT_ACCEPTED_BY_FICTION,
2525 EVENT_UNDELIVERABLE,
2526 EVENT_COMMERCIAL_ACCEPTED,
2527 EVENT_DELIVERED,
2528 EVENT_PRIMARY_LOGIN,
2529 EVENT_ENTRUSTED_LOGIN,
2530 EVENT_SYSCERT_LOGIN
2532 unsigned int index;
2533 size_t length;
2535 if (!string || !event) return IE_INVAL;
2537 if (!event->type) {
2538 event->type = malloc(sizeof(*event->type));
2539 if (!(event->type)) return IE_NOMEM;
2541 zfree(event->description);
2543 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2544 index++) {
2545 length = xmlUTF8Strlen(known_prefixes[index]);
2547 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2548 /* Prefix is known */
2549 *event->type = types[index];
2551 /* Strip prefix from description and spaces */
2552 /* TODO: Recognize all white spaces from UCS blank class and
2553 * operate on UTF-8 chars. */
2554 for (; string[length] != '\0' && string[length] == ' '; length++);
2555 event->description = strdup((char *) (string + length));
2556 if (!(event->description)) return IE_NOMEM;
2558 return IE_SUCCESS;
2562 /* Unknown event prefix.
2563 * XSD allows any string */
2564 char *string_locale = _isds_utf82locale((char *) string);
2565 isds_log(ILF_ISDS, ILL_WARNING,
2566 _("Unknown delivery info event prefix: %s\n"), string_locale);
2567 free(string_locale);
2569 *event->type = EVENT_UKNOWN;
2570 event->description = strdup((char *) string);
2571 if (!(event->description)) return IE_NOMEM;
2573 return IE_SUCCESS;
2577 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2578 * and leave label */
2579 #define EXTRACT_STRING(element, string) { \
2580 xmlXPathFreeObject(result); \
2581 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2582 if (NULL == (result)) { \
2583 err = IE_ERROR; \
2584 goto leave; \
2586 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2587 if (result->nodesetval->nodeNr > 1) { \
2588 isds_printf_message(context, _("Multiple %s element"), element); \
2589 err = IE_ERROR; \
2590 goto leave; \
2592 (string) = (char *) \
2593 xmlXPathCastNodeSetToString(result->nodesetval); \
2594 if (NULL == (string)) { \
2595 err = IE_ERROR; \
2596 goto leave; \
2601 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2603 char *string = NULL; \
2604 EXTRACT_STRING(element, string); \
2606 if (string) { \
2607 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2608 if (!(booleanPtr)) { \
2609 free(string); \
2610 err = IE_NOMEM; \
2611 goto leave; \
2614 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2615 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2616 *(booleanPtr) = 1; \
2617 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2618 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2619 *(booleanPtr) = 0; \
2620 else { \
2621 char *string_locale = _isds_utf82locale((char*)string); \
2622 isds_printf_message(context, \
2623 _("%s value is not valid boolean: %s"), \
2624 element, string_locale); \
2625 free(string_locale); \
2626 free(string); \
2627 err = IE_ERROR; \
2628 goto leave; \
2631 free(string); \
2635 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2637 char *string = NULL; \
2638 EXTRACT_STRING(element, string); \
2640 if (NULL == string) { \
2641 isds_printf_message(context, _("%s element is empty"), element); \
2642 err = IE_ERROR; \
2643 goto leave; \
2645 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2646 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2647 (boolean) = 1; \
2648 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2649 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2650 (boolean) = 0; \
2651 else { \
2652 char *string_locale = _isds_utf82locale((char*)string); \
2653 isds_printf_message(context, \
2654 _("%s value is not valid boolean: %s"), \
2655 element, string_locale); \
2656 free(string_locale); \
2657 free(string); \
2658 err = IE_ERROR; \
2659 goto leave; \
2662 free(string); \
2665 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2667 char *string = NULL; \
2668 EXTRACT_STRING(element, string); \
2669 if (string) { \
2670 long int number; \
2671 char *endptr; \
2673 number = strtol((char*)string, &endptr, 10); \
2675 if (*endptr != '\0') { \
2676 char *string_locale = _isds_utf82locale((char *)string); \
2677 isds_printf_message(context, \
2678 _("%s is not valid integer: %s"), \
2679 element, string_locale); \
2680 free(string_locale); \
2681 free(string); \
2682 err = IE_ISDS; \
2683 goto leave; \
2686 if (number == LONG_MIN || number == LONG_MAX) { \
2687 char *string_locale = _isds_utf82locale((char *)string); \
2688 isds_printf_message(context, \
2689 _("%s value out of range of long int: %s"), \
2690 element, string_locale); \
2691 free(string_locale); \
2692 free(string); \
2693 err = IE_ERROR; \
2694 goto leave; \
2697 free(string); string = NULL; \
2699 if (!(preallocated)) { \
2700 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2701 if (!(longintPtr)) { \
2702 err = IE_NOMEM; \
2703 goto leave; \
2706 *(longintPtr) = number; \
2710 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2712 char *string = NULL; \
2713 EXTRACT_STRING(element, string); \
2714 if (string) { \
2715 long int number; \
2716 char *endptr; \
2718 number = strtol((char*)string, &endptr, 10); \
2720 if (*endptr != '\0') { \
2721 char *string_locale = _isds_utf82locale((char *)string); \
2722 isds_printf_message(context, \
2723 _("%s is not valid integer: %s"), \
2724 element, string_locale); \
2725 free(string_locale); \
2726 free(string); \
2727 err = IE_ISDS; \
2728 goto leave; \
2731 if (number == LONG_MIN || number == LONG_MAX) { \
2732 char *string_locale = _isds_utf82locale((char *)string); \
2733 isds_printf_message(context, \
2734 _("%s value out of range of long int: %s"), \
2735 element, string_locale); \
2736 free(string_locale); \
2737 free(string); \
2738 err = IE_ERROR; \
2739 goto leave; \
2742 free(string); string = NULL; \
2743 if (number < 0) { \
2744 isds_printf_message(context, \
2745 _("%s value is negative: %ld"), element, number); \
2746 err = IE_ERROR; \
2747 goto leave; \
2750 if (!(preallocated)) { \
2751 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2752 if (!(ulongintPtr)) { \
2753 err = IE_NOMEM; \
2754 goto leave; \
2757 *(ulongintPtr) = number; \
2761 #define EXTRACT_DATE(element, tmPtr) { \
2762 char *string = NULL; \
2763 EXTRACT_STRING(element, string); \
2764 if (NULL != string) { \
2765 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2766 if (NULL == (tmPtr)) { \
2767 free(string); \
2768 err = IE_NOMEM; \
2769 goto leave; \
2771 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2772 if (err) { \
2773 if (err == IE_NOTSUP) { \
2774 err = IE_ISDS; \
2775 char *string_locale = _isds_utf82locale(string); \
2776 char *element_locale = _isds_utf82locale(element); \
2777 isds_printf_message(context, _("Invalid %s value: %s"), \
2778 element_locale, string_locale); \
2779 free(string_locale); \
2780 free(element_locale); \
2782 free(string); \
2783 goto leave; \
2785 free(string); \
2789 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2790 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2791 NULL); \
2792 if ((required) && (!string)) { \
2793 char *attribute_locale = _isds_utf82locale(attribute); \
2794 char *element_locale = \
2795 _isds_utf82locale((char *)xpath_ctx->node->name); \
2796 isds_printf_message(context, \
2797 _("Could not extract required %s attribute value from " \
2798 "%s element"), attribute_locale, element_locale); \
2799 free(element_locale); \
2800 free(attribute_locale); \
2801 err = IE_ERROR; \
2802 goto leave; \
2807 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2809 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2810 (xmlChar *) (string)); \
2811 if (!node) { \
2812 isds_printf_message(context, \
2813 _("Could not add %s child to %s element"), \
2814 element, (parent)->name); \
2815 err = IE_ERROR; \
2816 goto leave; \
2820 #define INSERT_STRING(parent, element, string) \
2821 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2823 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2825 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2826 else { INSERT_STRING(parent, element, "false"); } \
2829 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2831 if (booleanPtr) { \
2832 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2833 } else { \
2834 INSERT_STRING(parent, element, NULL); \
2838 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2839 if ((longintPtr)) { \
2840 /* FIXME: locale sensitive */ \
2841 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
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_ULONGINT(parent, element, ulongintPtr, buffer) { \
2851 if ((ulongintPtr)) { \
2852 /* FIXME: locale sensitive */ \
2853 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2854 err = IE_NOMEM; \
2855 goto leave; \
2857 INSERT_STRING(parent, element, buffer) \
2858 free(buffer); (buffer) = NULL; \
2859 } else { INSERT_STRING(parent, element, NULL) } \
2862 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2864 /* FIXME: locale sensitive */ \
2865 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2866 err = IE_NOMEM; \
2867 goto leave; \
2869 INSERT_STRING(parent, element, buffer) \
2870 free(buffer); (buffer) = NULL; \
2873 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2874 * new attribute. */
2875 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2877 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2878 (xmlChar *) (string)); \
2879 if (!attribute_node) { \
2880 isds_printf_message(context, _("Could not add %s " \
2881 "attribute to %s element"), \
2882 (attribute), (parent)->name); \
2883 err = IE_ERROR; \
2884 goto leave; \
2888 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2889 if (string) { \
2890 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2891 if (length > (maximum)) { \
2892 isds_printf_message(context, \
2893 ngettext("%s has more than %d characters", \
2894 "%s has more than %d characters", (maximum)), \
2895 (name), (maximum)); \
2896 err = IE_2BIG; \
2897 goto leave; \
2899 if (length < (minimum)) { \
2900 isds_printf_message(context, \
2901 ngettext("%s has less than %d characters", \
2902 "%s has less than %d characters", (minimum)), \
2903 (name), (minimum)); \
2904 err = IE_2SMALL; \
2905 goto leave; \
2910 #define INSERT_ELEMENT(child, parent, element) \
2912 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2913 if (!(child)) { \
2914 isds_printf_message(context, \
2915 _("Could not add %s child to %s element"), \
2916 (element), (parent)->name); \
2917 err = IE_ERROR; \
2918 goto leave; \
2923 /* Find child element by name in given XPath context and switch context onto
2924 * it. The child must be uniq and must exist. Otherwise fails.
2925 * @context is ISDS context
2926 * @child is child element name
2927 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2928 * into it child. In error case, the @xpath_ctx keeps original value. */
2929 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2930 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2931 isds_error err = IE_SUCCESS;
2932 xmlXPathObjectPtr result = NULL;
2934 if (!context) return IE_INVALID_CONTEXT;
2935 if (!child || !xpath_ctx) return IE_INVAL;
2937 /* Find child */
2938 result = xmlXPathEvalExpression(child, xpath_ctx);
2939 if (!result) {
2940 err = IE_XML;
2941 goto leave;
2944 /* No match */
2945 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2946 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2947 char *child_locale = _isds_utf82locale((char*) child);
2948 isds_printf_message(context,
2949 _("%s element does not contain %s child"),
2950 parent_locale, child_locale);
2951 free(child_locale);
2952 free(parent_locale);
2953 err = IE_NOEXIST;
2954 goto leave;
2957 /* More matches */
2958 if (result->nodesetval->nodeNr > 1) {
2959 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2960 char *child_locale = _isds_utf82locale((char*) child);
2961 isds_printf_message(context,
2962 _("%s element contains multiple %s children"),
2963 parent_locale, child_locale);
2964 free(child_locale);
2965 free(parent_locale);
2966 err = IE_NOTUNIQ;
2967 goto leave;
2970 /* Switch context */
2971 xpath_ctx->node = result->nodesetval->nodeTab[0];
2973 leave:
2974 xmlXPathFreeObject(result);
2975 return err;
2980 #if HAVE_LIBCURL
2981 /* Find and convert XSD:gPersonName group in current node into structure
2982 * @context is ISDS context
2983 * @personName is automatically reallocated person name structure. If no member
2984 * value is found, will be freed.
2985 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2986 * elements
2987 * In case of error @personName will be freed. */
2988 static isds_error extract_gPersonName(struct isds_ctx *context,
2989 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2990 isds_error err = IE_SUCCESS;
2991 xmlXPathObjectPtr result = NULL;
2993 if (!context) return IE_INVALID_CONTEXT;
2994 if (!personName) return IE_INVAL;
2995 isds_PersonName_free(personName);
2996 if (!xpath_ctx) return IE_INVAL;
2999 *personName = calloc(1, sizeof(**personName));
3000 if (!*personName) {
3001 err = IE_NOMEM;
3002 goto leave;
3005 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
3006 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
3007 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
3008 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
3010 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
3011 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
3012 isds_PersonName_free(personName);
3014 leave:
3015 if (err) isds_PersonName_free(personName);
3016 xmlXPathFreeObject(result);
3017 return err;
3021 /* Find and convert XSD:gAddress group extended with relevant
3022 * tdbPersonalOwnerinfo members in current node into structure
3023 * @context is ISDS context
3024 * @address is automatically reallocated address structure. If no member
3025 * value is found, will be freed.
3026 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
3027 * elements
3028 * In case of error @address will be freed. */
3029 static isds_error extract_gAddress(struct isds_ctx *context,
3030 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
3031 isds_error err = IE_SUCCESS;
3032 xmlXPathObjectPtr result = NULL;
3034 if (!context) return IE_INVALID_CONTEXT;
3035 if (!address) return IE_INVAL;
3036 isds_Address_free(address);
3037 if (!xpath_ctx) return IE_INVAL;
3040 *address = calloc(1, sizeof(**address));
3041 if (!*address) {
3042 err = IE_NOMEM;
3043 goto leave;
3046 EXTRACT_LONGINT("isds:adCode", (*address)->adCode, 0);
3047 EXTRACT_STRING("isds:adCity", (*address)->adCity);
3048 EXTRACT_STRING("isds:adDistrict", (*address)->adDistrict);
3049 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
3050 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
3051 EXTRACT_STRING("isds:adNumberInMunicipality",
3052 (*address)->adNumberInMunicipality);
3053 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
3054 EXTRACT_STRING("isds:adState", (*address)->adState);
3056 if (!(*address)->adCity && !(*address)->adStreet &&
3057 !(*address)->adNumberInStreet &&
3058 !(*address)->adNumberInMunicipality &&
3059 !(*address)->adZipCode && !(*address)->adState)
3060 isds_Address_free(address);
3062 leave:
3063 if (err) isds_Address_free(address);
3064 xmlXPathFreeObject(result);
3065 return err;
3069 /* Find and convert isds:biDate element in current node into structure
3070 * @context is ISDS context
3071 * @biDate is automatically reallocated birth date structure. If no member
3072 * value is found, will be freed.
3073 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3074 * element
3075 * In case of error @biDate will be freed. */
3076 static isds_error extract_BiDate(struct isds_ctx *context,
3077 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3078 isds_error err = IE_SUCCESS;
3079 xmlXPathObjectPtr result = NULL;
3080 char *string = NULL;
3082 if (!context) return IE_INVALID_CONTEXT;
3083 if (!biDate) return IE_INVAL;
3084 zfree(*biDate);
3085 if (!xpath_ctx) return IE_INVAL;
3087 EXTRACT_STRING("isds:biDate", string);
3088 if (string) {
3089 *biDate = calloc(1, sizeof(**biDate));
3090 if (!*biDate) {
3091 err = IE_NOMEM;
3092 goto leave;
3094 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3095 if (err) {
3096 if (err == IE_NOTSUP) {
3097 err = IE_ISDS;
3098 char *string_locale = _isds_utf82locale(string);
3099 isds_printf_message(context,
3100 _("Invalid isds:biDate value: %s"), string_locale);
3101 free(string_locale);
3103 goto leave;
3107 leave:
3108 if (err) zfree(*biDate);
3109 free(string);
3110 xmlXPathFreeObject(result);
3111 return err;
3115 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3116 * @context is ISDS context
3117 * @db_owner_info is automatically reallocated box owner info structure
3118 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3119 * XSD:tdbPersonalOwenerInfo element
3120 * In case of error @db_owner_info will be freed. */
3121 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3122 struct isds_DbOwnerInfo **db_owner_info,
3123 xmlXPathContextPtr xpath_ctx) {
3124 isds_error err = IE_SUCCESS;
3125 xmlXPathObjectPtr result = NULL;
3126 char *string = NULL;
3128 if (!context) return IE_INVALID_CONTEXT;
3129 if (!db_owner_info) return IE_INVAL;
3130 isds_DbOwnerInfo_free(db_owner_info);
3131 if (!xpath_ctx) return IE_INVAL;
3134 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3135 if (!*db_owner_info) {
3136 err = IE_NOMEM;
3137 goto leave;
3140 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3142 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info)->aifoIsds);
3144 EXTRACT_STRING("isds:dbType", string);
3145 if (string) {
3146 (*db_owner_info)->dbType =
3147 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3148 if (!(*db_owner_info)->dbType) {
3149 err = IE_NOMEM;
3150 goto leave;
3152 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3153 if (err) {
3154 zfree((*db_owner_info)->dbType);
3155 if (err == IE_ENUM) {
3156 err = IE_ISDS;
3157 char *string_locale = _isds_utf82locale(string);
3158 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3159 string_locale);
3160 free(string_locale);
3162 goto leave;
3164 zfree(string);
3167 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3169 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3170 xpath_ctx);
3171 if (err) goto leave;
3173 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3175 (*db_owner_info)->birthInfo =
3176 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3177 if (!(*db_owner_info)->birthInfo) {
3178 err = IE_NOMEM;
3179 goto leave;
3181 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3182 xpath_ctx);
3183 if (err) goto leave;
3184 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3185 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3186 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3187 if (!(*db_owner_info)->birthInfo->biDate &&
3188 !(*db_owner_info)->birthInfo->biCity &&
3189 !(*db_owner_info)->birthInfo->biCounty &&
3190 !(*db_owner_info)->birthInfo->biState)
3191 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3193 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3194 if (err) goto leave;
3196 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3197 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3198 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3199 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3200 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3202 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3204 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3205 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3206 (*db_owner_info)->dbOpenAddressing);
3208 leave:
3209 if (err) isds_DbOwnerInfo_free(db_owner_info);
3210 free(string);
3211 xmlXPathFreeObject(result);
3212 return err;
3216 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3217 * @context is session context
3218 * @owner is libisds structure with box description.
3219 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3220 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3221 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3222 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3223 * ignored.
3224 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3225 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3226 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3227 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3228 * type. */
3229 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3230 const struct isds_DbOwnerInfo *owner, _Bool pfo_subtype,
3231 xmlNodePtr db_owner_info) {
3233 isds_error err = IE_SUCCESS;
3234 xmlNodePtr node;
3235 xmlChar *string = NULL;
3236 const xmlChar *type_string = NULL;
3238 if (!context) return IE_INVALID_CONTEXT;
3239 if (!owner || !db_owner_info) return IE_INVAL;
3242 /* XXX: All the elements except email and telNumber are mandatory. */
3243 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3244 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3246 if (pfo_subtype) {
3247 INSERT_BOOLEAN(db_owner_info, "aifoIsds", owner->aifoIsds);
3250 if (!pfo_subtype) {
3251 /* dbType */
3252 if (owner->dbType) {
3253 type_string = isds_DbType2string(*(owner->dbType));
3254 if (!type_string) {
3255 isds_printf_message(context, _("Invalid dbType value: %d"),
3256 *(owner->dbType));
3257 err = IE_ENUM;
3258 goto leave;
3261 INSERT_STRING(db_owner_info, "dbType", type_string);
3263 INSERT_STRING(db_owner_info, "ic", owner->ic);
3266 INSERT_STRING(db_owner_info, "pnFirstName",
3267 (NULL == owner->personName) ? NULL: owner->personName->pnFirstName);
3268 INSERT_STRING(db_owner_info, "pnMiddleName",
3269 (NULL == owner->personName) ? NULL: owner->personName->pnMiddleName);
3270 INSERT_STRING(db_owner_info, "pnLastName",
3271 (NULL == owner->personName) ? NULL: owner->personName->pnLastName);
3272 if (!pfo_subtype) {
3273 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3274 (NULL == owner->personName) ? NULL:
3275 owner->personName->pnLastNameAtBirth);
3277 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3280 if (NULL != owner->birthInfo && NULL != owner->birthInfo->biDate) {
3281 err = tm2datestring(owner->birthInfo->biDate, &string);
3282 if (err) goto leave;
3284 INSERT_STRING(db_owner_info, "biDate", string);
3285 zfree(string);
3287 INSERT_STRING(db_owner_info, "biCity",
3288 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCity);
3289 INSERT_STRING(db_owner_info, "biCounty",
3290 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCounty);
3291 INSERT_STRING(db_owner_info, "biState",
3292 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biState);
3294 if (pfo_subtype) {
3295 INSERT_LONGINT(db_owner_info, "adCode",
3296 (NULL == owner->address) ? NULL : owner->address->adCode,
3297 string);
3299 INSERT_STRING(db_owner_info, "adCity",
3300 (NULL == owner->address) ? NULL: owner->address->adCity);
3301 if (pfo_subtype) {
3302 INSERT_STRING(db_owner_info, "adDistrict",
3303 (NULL == owner->address) ? NULL: owner->address->adDistrict);
3305 INSERT_STRING(db_owner_info, "adStreet",
3306 (NULL == owner->address) ? NULL: owner->address->adStreet);
3307 INSERT_STRING(db_owner_info, "adNumberInStreet",
3308 (NULL == owner->address) ? NULL: owner->address->adNumberInStreet);
3309 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3310 (NULL == owner->address) ? NULL: owner->address->adNumberInMunicipality);
3311 INSERT_STRING(db_owner_info, "adZipCode",
3312 (NULL == owner->address) ? NULL: owner->address->adZipCode);
3313 INSERT_STRING(db_owner_info, "adState",
3314 (NULL == owner->address) ? NULL: owner->address->adState);
3316 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3318 if (!pfo_subtype) {
3319 INSERT_STRING(db_owner_info, "email", owner->email);
3320 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3322 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3323 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3325 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3326 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3328 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3330 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3331 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3332 owner->dbOpenAddressing);
3335 leave:
3336 free(string);
3337 return err;
3341 /* Convert XSD:tDbUserInfo XML tree into structure
3342 * @context is ISDS context
3343 * @db_user_info is automatically reallocated user info structure
3344 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3345 * In case of error @db_user_info will be freed. */
3346 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3347 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3348 isds_error err = IE_SUCCESS;
3349 xmlXPathObjectPtr result = NULL;
3350 char *string = NULL;
3352 if (!context) return IE_INVALID_CONTEXT;
3353 if (!db_user_info) return IE_INVAL;
3354 isds_DbUserInfo_free(db_user_info);
3355 if (!xpath_ctx) return IE_INVAL;
3358 *db_user_info = calloc(1, sizeof(**db_user_info));
3359 if (!*db_user_info) {
3360 err = IE_NOMEM;
3361 goto leave;
3364 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3366 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3368 EXTRACT_STRING("isds:userType", string);
3369 if (string) {
3370 (*db_user_info)->userType =
3371 calloc(1, sizeof(*((*db_user_info)->userType)));
3372 if (!(*db_user_info)->userType) {
3373 err = IE_NOMEM;
3374 goto leave;
3376 err = string2isds_UserType((xmlChar *)string,
3377 (*db_user_info)->userType);
3378 if (err) {
3379 zfree((*db_user_info)->userType);
3380 if (err == IE_ENUM) {
3381 err = IE_ISDS;
3382 char *string_locale = _isds_utf82locale(string);
3383 isds_printf_message(context,
3384 _("Unknown isds:userType value: %s"), string_locale);
3385 free(string_locale);
3387 goto leave;
3389 zfree(string);
3392 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3394 (*db_user_info)->personName =
3395 calloc(1, sizeof(*((*db_user_info)->personName)));
3396 if (!(*db_user_info)->personName) {
3397 err = IE_NOMEM;
3398 goto leave;
3401 err = extract_gPersonName(context, &(*db_user_info)->personName,
3402 xpath_ctx);
3403 if (err) goto leave;
3405 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3406 if (err) goto leave;
3408 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3409 if (err) goto leave;
3411 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3412 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3414 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3415 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3416 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3418 /* ???: Default value is "CZ" according specification. Should we provide
3419 * it? */
3420 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3422 leave:
3423 if (err) isds_DbUserInfo_free(db_user_info);
3424 free(string);
3425 xmlXPathFreeObject(result);
3426 return err;
3430 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3431 * @context is session context
3432 * @user is libisds structure with user description
3433 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3434 * @db_user_info is XML element of XSD:tDbUserInfo */
3435 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3436 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3437 xmlNodePtr db_user_info) {
3439 isds_error err = IE_SUCCESS;
3440 xmlNodePtr node;
3441 xmlAttrPtr attribute_node;
3442 xmlChar *string = NULL;
3444 if (!context) return IE_INVALID_CONTEXT;
3445 if (!user || !db_user_info) return IE_INVAL;
3447 /* Build XSD:tDbUserInfo */
3449 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3450 * allow it everywhere. */
3451 if (honor_aifo_ticket && user->aifo_ticket) {
3452 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3455 if (user->personName) {
3456 INSERT_STRING(db_user_info, "pnFirstName",
3457 user->personName->pnFirstName);
3458 INSERT_STRING(db_user_info, "pnMiddleName",
3459 user->personName->pnMiddleName);
3460 INSERT_STRING(db_user_info, "pnLastName",
3461 user->personName->pnLastName);
3462 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3463 user->personName->pnLastNameAtBirth);
3465 if (user->address) {
3466 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3467 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3468 INSERT_STRING(db_user_info, "adNumberInStreet",
3469 user->address->adNumberInStreet);
3470 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3471 user->address->adNumberInMunicipality);
3472 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3473 INSERT_STRING(db_user_info, "adState", user->address->adState);
3475 if (user->biDate) {
3476 if (!tm2datestring(user->biDate, &string))
3477 INSERT_STRING(db_user_info, "biDate", string);
3478 zfree(string);
3480 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3481 INSERT_STRING(db_user_info, "userID", user->userID);
3483 /* userType */
3484 if (user->userType) {
3485 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3486 if (!type_string) {
3487 isds_printf_message(context, _("Invalid userType value: %d"),
3488 *(user->userType));
3489 err = IE_ENUM;
3490 goto leave;
3492 INSERT_STRING(db_user_info, "userType", type_string);
3495 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3496 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3497 INSERT_STRING(db_user_info, "ic", user->ic);
3498 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3499 INSERT_STRING(db_user_info, "firmName", user->firmName);
3500 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3501 INSERT_STRING(db_user_info, "caCity", user->caCity);
3502 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3503 INSERT_STRING(db_user_info, "caState", user->caState);
3505 leave:
3506 free(string);
3507 return err;
3511 /* Convert XSD:tPDZRec XML tree into structure
3512 * @context is ISDS context
3513 * @permission is automatically reallocated commercial permission structure
3514 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3515 * In case of error @permission will be freed. */
3516 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3517 struct isds_commercial_permission **permission,
3518 xmlXPathContextPtr xpath_ctx) {
3519 isds_error err = IE_SUCCESS;
3520 xmlXPathObjectPtr result = NULL;
3521 char *string = NULL;
3523 if (!context) return IE_INVALID_CONTEXT;
3524 if (!permission) return IE_INVAL;
3525 isds_commercial_permission_free(permission);
3526 if (!xpath_ctx) return IE_INVAL;
3529 *permission = calloc(1, sizeof(**permission));
3530 if (!*permission) {
3531 err = IE_NOMEM;
3532 goto leave;
3535 EXTRACT_STRING("isds:PDZType", string);
3536 if (string) {
3537 err = string2isds_payment_type((xmlChar *)string,
3538 &(*permission)->type);
3539 if (err) {
3540 if (err == IE_ENUM) {
3541 err = IE_ISDS;
3542 char *string_locale = _isds_utf82locale(string);
3543 isds_printf_message(context,
3544 _("Unknown isds:PDZType value: %s"), string_locale);
3545 free(string_locale);
3547 goto leave;
3549 zfree(string);
3552 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3553 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3555 EXTRACT_STRING("isds:PDZExpire", string);
3556 if (string) {
3557 err = timestring2timeval((xmlChar *) string,
3558 &((*permission)->expiration));
3559 if (err) {
3560 char *string_locale = _isds_utf82locale(string);
3561 if (err == IE_DATE) err = IE_ISDS;
3562 isds_printf_message(context,
3563 _("Could not convert PDZExpire as ISO time: %s"),
3564 string_locale);
3565 free(string_locale);
3566 goto leave;
3568 zfree(string);
3571 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3572 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3574 leave:
3575 if (err) isds_commercial_permission_free(permission);
3576 free(string);
3577 xmlXPathFreeObject(result);
3578 return err;
3582 /* Convert XSD:tCiRecord XML tree into structure
3583 * @context is ISDS context
3584 * @event is automatically reallocated commercial credit event structure
3585 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3586 * In case of error @event will be freed. */
3587 static isds_error extract_CiRecord(struct isds_ctx *context,
3588 struct isds_credit_event **event,
3589 xmlXPathContextPtr xpath_ctx) {
3590 isds_error err = IE_SUCCESS;
3591 xmlXPathObjectPtr result = NULL;
3592 char *string = NULL;
3593 long int *number_ptr;
3595 if (!context) return IE_INVALID_CONTEXT;
3596 if (!event) return IE_INVAL;
3597 isds_credit_event_free(event);
3598 if (!xpath_ctx) return IE_INVAL;
3601 *event = calloc(1, sizeof(**event));
3602 if (!*event) {
3603 err = IE_NOMEM;
3604 goto leave;
3607 EXTRACT_STRING("isds:ciEventTime", string);
3608 if (string) {
3609 err = timestring2timeval((xmlChar *) string,
3610 &(*event)->time);
3611 if (err) {
3612 char *string_locale = _isds_utf82locale(string);
3613 if (err == IE_DATE) err = IE_ISDS;
3614 isds_printf_message(context,
3615 _("Could not convert ciEventTime as ISO time: %s"),
3616 string_locale);
3617 free(string_locale);
3618 goto leave;
3620 zfree(string);
3623 EXTRACT_STRING("isds:ciEventType", string);
3624 if (string) {
3625 err = string2isds_credit_event_type((xmlChar *)string,
3626 &(*event)->type);
3627 if (err) {
3628 if (err == IE_ENUM) {
3629 err = IE_ISDS;
3630 char *string_locale = _isds_utf82locale(string);
3631 isds_printf_message(context,
3632 _("Unknown isds:ciEventType value: %s"), string_locale);
3633 free(string_locale);
3635 goto leave;
3637 zfree(string);
3640 number_ptr = &((*event)->credit_change);
3641 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3642 number_ptr = &(*event)->new_credit;
3643 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3645 switch((*event)->type) {
3646 case ISDS_CREDIT_CHARGED:
3647 EXTRACT_STRING("isds:ciTransID",
3648 (*event)->details.charged.transaction);
3649 break;
3650 case ISDS_CREDIT_DISCHARGED:
3651 EXTRACT_STRING("isds:ciTransID",
3652 (*event)->details.discharged.transaction);
3653 break;
3654 case ISDS_CREDIT_MESSAGE_SENT:
3655 EXTRACT_STRING("isds:ciRecipientID",
3656 (*event)->details.message_sent.recipient);
3657 EXTRACT_STRING("isds:ciPDZID",
3658 (*event)->details.message_sent.message_id);
3659 break;
3660 case ISDS_CREDIT_STORAGE_SET:
3661 number_ptr = &((*event)->details.storage_set.new_capacity);
3662 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3663 EXTRACT_DATE("isds:ciNewFrom",
3664 (*event)->details.storage_set.new_valid_from);
3665 EXTRACT_DATE("isds:ciNewTo",
3666 (*event)->details.storage_set.new_valid_to);
3667 EXTRACT_LONGINT("isds:ciOldCapacity",
3668 (*event)->details.storage_set.old_capacity, 0);
3669 EXTRACT_DATE("isds:ciOldFrom",
3670 (*event)->details.storage_set.old_valid_from);
3671 EXTRACT_DATE("isds:ciOldTo",
3672 (*event)->details.storage_set.old_valid_to);
3673 EXTRACT_STRING("isds:ciDoneBy",
3674 (*event)->details.storage_set.initiator);
3675 break;
3676 case ISDS_CREDIT_EXPIRED:
3677 break;
3680 leave:
3681 if (err) isds_credit_event_free(event);
3682 free(string);
3683 xmlXPathFreeObject(result);
3684 return err;
3688 #endif /* HAVE_LIBCURL */
3691 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3692 * isds_envelope structure. The envelope is automatically allocated but not
3693 * reallocated. The date are just appended into envelope structure.
3694 * @context is ISDS context
3695 * @envelope is automatically allocated message envelope structure
3696 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3697 * In case of error @envelope will be freed. */
3698 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3699 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3700 isds_error err = IE_SUCCESS;
3701 xmlXPathObjectPtr result = NULL;
3703 if (!context) return IE_INVALID_CONTEXT;
3704 if (!envelope) return IE_INVAL;
3705 if (!xpath_ctx) return IE_INVAL;
3708 if (!*envelope) {
3709 /* Allocate envelope */
3710 *envelope = calloc(1, sizeof(**envelope));
3711 if (!*envelope) {
3712 err = IE_NOMEM;
3713 goto leave;
3715 } else {
3716 /* Else free former data */
3717 zfree((*envelope)->dmSenderOrgUnit);
3718 zfree((*envelope)->dmSenderOrgUnitNum);
3719 zfree((*envelope)->dbIDRecipient);
3720 zfree((*envelope)->dmRecipientOrgUnit);
3721 zfree((*envelope)->dmRecipientOrgUnitNum);
3722 zfree((*envelope)->dmToHands);
3723 zfree((*envelope)->dmAnnotation);
3724 zfree((*envelope)->dmRecipientRefNumber);
3725 zfree((*envelope)->dmSenderRefNumber);
3726 zfree((*envelope)->dmRecipientIdent);
3727 zfree((*envelope)->dmSenderIdent);
3728 zfree((*envelope)->dmLegalTitleLaw);
3729 zfree((*envelope)->dmLegalTitleYear);
3730 zfree((*envelope)->dmLegalTitleSect);
3731 zfree((*envelope)->dmLegalTitlePar);
3732 zfree((*envelope)->dmLegalTitlePoint);
3733 zfree((*envelope)->dmPersonalDelivery);
3734 zfree((*envelope)->dmAllowSubstDelivery);
3737 /* Extract envelope elements added by sender or ISDS
3738 * (XSD: gMessageEnvelopeSub type) */
3739 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3740 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3741 (*envelope)->dmSenderOrgUnitNum, 0);
3742 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3743 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3744 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3745 (*envelope)->dmRecipientOrgUnitNum, 0);
3746 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3747 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3748 EXTRACT_STRING("isds:dmRecipientRefNumber",
3749 (*envelope)->dmRecipientRefNumber);
3750 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3751 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3752 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3754 /* Extract envelope elements regarding law reference */
3755 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3756 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3757 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3758 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3759 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3761 /* Extract envelope other elements */
3762 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3763 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3764 (*envelope)->dmAllowSubstDelivery);
3766 leave:
3767 if (err) isds_envelope_free(envelope);
3768 xmlXPathFreeObject(result);
3769 return err;
3774 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3775 * isds_envelope structure. The envelope is automatically allocated but not
3776 * reallocated. The date are just appended into envelope structure.
3777 * @context is ISDS context
3778 * @envelope is automatically allocated message envelope structure
3779 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3780 * In case of error @envelope will be freed. */
3781 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3782 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3783 isds_error err = IE_SUCCESS;
3784 xmlXPathObjectPtr result = NULL;
3786 if (!context) return IE_INVALID_CONTEXT;
3787 if (!envelope) return IE_INVAL;
3788 if (!xpath_ctx) return IE_INVAL;
3791 if (!*envelope) {
3792 /* Allocate envelope */
3793 *envelope = calloc(1, sizeof(**envelope));
3794 if (!*envelope) {
3795 err = IE_NOMEM;
3796 goto leave;
3798 } else {
3799 /* Else free former data */
3800 zfree((*envelope)->dmID);
3801 zfree((*envelope)->dbIDSender);
3802 zfree((*envelope)->dmSender);
3803 zfree((*envelope)->dmSenderAddress);
3804 zfree((*envelope)->dmSenderType);
3805 zfree((*envelope)->dmRecipient);
3806 zfree((*envelope)->dmRecipientAddress);
3807 zfree((*envelope)->dmAmbiguousRecipient);
3810 /* Extract envelope elements added by ISDS
3811 * (XSD: gMessageEnvelope type) */
3812 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3813 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3814 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3815 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3816 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3817 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3818 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3819 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3820 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3821 (*envelope)->dmAmbiguousRecipient);
3823 /* Extract envelope elements added by sender and ISDS
3824 * (XSD: gMessageEnvelope type) */
3825 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3826 if (err) goto leave;
3828 leave:
3829 if (err) isds_envelope_free(envelope);
3830 xmlXPathFreeObject(result);
3831 return err;
3835 /* Convert other envelope elements from XML tree into isds_envelope structure:
3836 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3837 * The envelope is automatically allocated but not reallocated.
3838 * The data are just appended into envelope structure.
3839 * @context is ISDS context
3840 * @envelope is automatically allocated message envelope structure
3841 * @xpath_ctx is XPath context with current node as parent desired elements
3842 * In case of error @envelope will be freed. */
3843 static isds_error append_status_size_times(struct isds_ctx *context,
3844 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3845 isds_error err = IE_SUCCESS;
3846 xmlXPathObjectPtr result = NULL;
3847 char *string = NULL;
3848 unsigned long int *unumber = NULL;
3850 if (!context) return IE_INVALID_CONTEXT;
3851 if (!envelope) return IE_INVAL;
3852 if (!xpath_ctx) return IE_INVAL;
3855 if (!*envelope) {
3856 /* Allocate new */
3857 *envelope = calloc(1, sizeof(**envelope));
3858 if (!*envelope) {
3859 err = IE_NOMEM;
3860 goto leave;
3862 } else {
3863 /* Free old data */
3864 zfree((*envelope)->dmMessageStatus);
3865 zfree((*envelope)->dmAttachmentSize);
3866 zfree((*envelope)->dmDeliveryTime);
3867 zfree((*envelope)->dmAcceptanceTime);
3871 /* dmMessageStatus element is mandatory */
3872 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3873 if (!unumber) {
3874 isds_log_message(context,
3875 _("Missing mandatory sisds:dmMessageStatus integer"));
3876 err = IE_ISDS;
3877 goto leave;
3879 err = uint2isds_message_status(context, unumber,
3880 &((*envelope)->dmMessageStatus));
3881 if (err) {
3882 if (err == IE_ENUM) err = IE_ISDS;
3883 goto leave;
3885 free(unumber); unumber = NULL;
3887 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3890 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3891 if (string) {
3892 err = timestring2timeval((xmlChar *) string,
3893 &((*envelope)->dmDeliveryTime));
3894 if (err) {
3895 char *string_locale = _isds_utf82locale(string);
3896 if (err == IE_DATE) err = IE_ISDS;
3897 isds_printf_message(context,
3898 _("Could not convert dmDeliveryTime as ISO time: %s"),
3899 string_locale);
3900 free(string_locale);
3901 goto leave;
3903 zfree(string);
3906 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3907 if (string) {
3908 err = timestring2timeval((xmlChar *) string,
3909 &((*envelope)->dmAcceptanceTime));
3910 if (err) {
3911 char *string_locale = _isds_utf82locale(string);
3912 if (err == IE_DATE) err = IE_ISDS;
3913 isds_printf_message(context,
3914 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3915 string_locale);
3916 free(string_locale);
3917 goto leave;
3919 zfree(string);
3922 leave:
3923 if (err) isds_envelope_free(envelope);
3924 free(unumber);
3925 free(string);
3926 xmlXPathFreeObject(result);
3927 return err;
3931 /* Convert message type attribute of current element into isds_envelope
3932 * structure.
3933 * TODO: This function can be incorporated into append_status_size_times() as
3934 * they are called always together.
3935 * The envelope is automatically allocated but not reallocated.
3936 * The data are just appended into envelope structure.
3937 * @context is ISDS context
3938 * @envelope is automatically allocated message envelope structure
3939 * @xpath_ctx is XPath context with current node as parent of attribute
3940 * carrying message type
3941 * In case of error @envelope will be freed. */
3942 static isds_error append_message_type(struct isds_ctx *context,
3943 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3944 isds_error err = IE_SUCCESS;
3946 if (!context) return IE_INVALID_CONTEXT;
3947 if (!envelope) return IE_INVAL;
3948 if (!xpath_ctx) return IE_INVAL;
3951 if (!*envelope) {
3952 /* Allocate new */
3953 *envelope = calloc(1, sizeof(**envelope));
3954 if (!*envelope) {
3955 err = IE_NOMEM;
3956 goto leave;
3958 } else {
3959 /* Free old data */
3960 zfree((*envelope)->dmType);
3964 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3966 if (!(*envelope)->dmType) {
3967 /* Use default value */
3968 (*envelope)->dmType = strdup("V");
3969 if (!(*envelope)->dmType) {
3970 err = IE_NOMEM;
3971 goto leave;
3973 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3974 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3975 isds_printf_message(context,
3976 _("Message type in dmType attribute is not 1 character long: "
3977 "%s"),
3978 type_locale);
3979 free(type_locale);
3980 err = IE_ISDS;
3981 goto leave;
3984 leave:
3985 if (err) isds_envelope_free(envelope);
3986 return err;
3990 #if HAVE_LIBCURL
3991 /* Convert dmType isds_envelope member into XML attribute and append it to
3992 * current node.
3993 * @context is ISDS context
3994 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3995 * @dm_envelope is XML element the resulting attribute will be appended to.
3996 * @return error code, in case of error context' message is filled. */
3997 static isds_error insert_message_type(struct isds_ctx *context,
3998 const char *type, xmlNodePtr dm_envelope) {
3999 isds_error err = IE_SUCCESS;
4000 xmlAttrPtr attribute_node;
4002 if (!context) return IE_INVALID_CONTEXT;
4003 if (!dm_envelope) return IE_INVAL;
4005 /* Insert optional message type */
4006 if (type) {
4007 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
4008 char *type_locale = _isds_utf82locale(type);
4009 isds_printf_message(context,
4010 _("Message type in envelope is not 1 character long: %s"),
4011 type_locale);
4012 free(type_locale);
4013 err = IE_INVAL;
4014 goto leave;
4016 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
4019 leave:
4020 return err;
4022 #endif /* HAVE_LIBCURL */
4025 /* Extract message document into reallocated document structure
4026 * @context is ISDS context
4027 * @document is automatically reallocated message documents structure
4028 * @xpath_ctx is XPath context with current node as isds:dmFile
4029 * In case of error @document will be freed. */
4030 static isds_error extract_document(struct isds_ctx *context,
4031 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
4032 isds_error err = IE_SUCCESS;
4033 xmlXPathObjectPtr result = NULL;
4034 xmlNodePtr file_node;
4035 char *string = NULL;
4037 if (!context) return IE_INVALID_CONTEXT;
4038 if (!document) return IE_INVAL;
4039 isds_document_free(document);
4040 if (!xpath_ctx) return IE_INVAL;
4041 file_node = xpath_ctx->node;
4043 *document = calloc(1, sizeof(**document));
4044 if (!*document) {
4045 err = IE_NOMEM;
4046 goto leave;
4049 /* Extract document meta data */
4050 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
4051 if (context->normalize_mime_type) {
4052 const char *normalized_type =
4053 isds_normalize_mime_type((*document)->dmMimeType);
4054 if (NULL != normalized_type &&
4055 normalized_type != (*document)->dmMimeType) {
4056 char *new_type = strdup(normalized_type);
4057 if (NULL == new_type) {
4058 isds_printf_message(context,
4059 _("Not enough memory to normalize document MIME type"));
4060 err = IE_NOMEM;
4061 goto leave;
4063 free((*document)->dmMimeType);
4064 (*document)->dmMimeType = new_type;
4068 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
4069 err = string2isds_FileMetaType((xmlChar*)string,
4070 &((*document)->dmFileMetaType));
4071 if (err) {
4072 char *meta_type_locale = _isds_utf82locale(string);
4073 isds_printf_message(context,
4074 _("Document has invalid dmFileMetaType attribute value: %s"),
4075 meta_type_locale);
4076 free(meta_type_locale);
4077 err = IE_ISDS;
4078 goto leave;
4080 zfree(string);
4082 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
4083 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
4084 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
4085 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
4088 /* Extract document data.
4089 * Base64 encoded blob or XML subtree must be presented. */
4091 /* Check for dmEncodedContent */
4092 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
4093 xpath_ctx);
4094 if (!result) {
4095 err = IE_XML;
4096 goto leave;
4099 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4100 /* Here we have Base64 blob */
4101 (*document)->is_xml = 0;
4103 if (result->nodesetval->nodeNr > 1) {
4104 isds_printf_message(context,
4105 _("Document has more dmEncodedContent elements"));
4106 err = IE_ISDS;
4107 goto leave;
4110 xmlXPathFreeObject(result); result = NULL;
4111 EXTRACT_STRING("isds:dmEncodedContent", string);
4113 /* Decode non-empty document */
4114 if (string && string[0] != '\0') {
4115 (*document)->data_length =
4116 _isds_b64decode(string, &((*document)->data));
4117 if ((*document)->data_length == (size_t) -1) {
4118 isds_printf_message(context,
4119 _("Error while Base64-decoding document content"));
4120 err = IE_ERROR;
4121 goto leave;
4124 } else {
4125 /* No Base64 blob, try XML document */
4126 xmlXPathFreeObject(result); result = NULL;
4127 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4128 xpath_ctx);
4129 if (!result) {
4130 err = IE_XML;
4131 goto leave;
4134 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4135 /* Here we have XML document */
4136 (*document)->is_xml = 1;
4138 if (result->nodesetval->nodeNr > 1) {
4139 isds_printf_message(context,
4140 _("Document has more dmXMLContent elements"));
4141 err = IE_ISDS;
4142 goto leave;
4145 /* XXX: We cannot serialize the content simply because:
4146 * - XML document may point out of its scope (e.g. to message
4147 * envelope)
4148 * - isds:dmXMLContent can contain more elements, no element,
4149 * a text node only
4150 * - it's not the XML way
4151 * Thus we provide the only right solution: XML DOM. Let's
4152 * application to cope with this hot potato :) */
4153 (*document)->xml_node_list =
4154 result->nodesetval->nodeTab[0]->children;
4155 } else {
4156 /* No base64 blob, nor XML document */
4157 isds_printf_message(context,
4158 _("Document has no dmEncodedContent, nor dmXMLContent "
4159 "element"));
4160 err = IE_ISDS;
4161 goto leave;
4166 leave:
4167 if (err) isds_document_free(document);
4168 free(string);
4169 xmlXPathFreeObject(result);
4170 xpath_ctx->node = file_node;
4171 return err;
4176 /* Extract message documents into reallocated list of documents
4177 * @context is ISDS context
4178 * @documents is automatically reallocated message documents list structure
4179 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4180 * In case of error @documents will be freed. */
4181 static isds_error extract_documents(struct isds_ctx *context,
4182 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4183 isds_error err = IE_SUCCESS;
4184 xmlXPathObjectPtr result = NULL;
4185 xmlNodePtr files_node;
4186 struct isds_list *document, *prev_document = NULL;
4188 if (!context) return IE_INVALID_CONTEXT;
4189 if (!documents) return IE_INVAL;
4190 isds_list_free(documents);
4191 if (!xpath_ctx) return IE_INVAL;
4192 files_node = xpath_ctx->node;
4194 /* Find documents */
4195 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4196 if (!result) {
4197 err = IE_XML;
4198 goto leave;
4201 /* No match */
4202 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4203 isds_printf_message(context,
4204 _("Message does not contain any document"));
4205 err = IE_ISDS;
4206 goto leave;
4210 /* Iterate over documents */
4211 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4213 /* Allocate and append list item */
4214 document = calloc(1, sizeof(*document));
4215 if (!document) {
4216 err = IE_NOMEM;
4217 goto leave;
4219 document->destructor = (void (*)(void **))isds_document_free;
4220 if (i == 0) *documents = document;
4221 else prev_document->next = document;
4222 prev_document = document;
4224 /* Extract document */
4225 xpath_ctx->node = result->nodesetval->nodeTab[i];
4226 err = extract_document(context,
4227 (struct isds_document **) &(document->data), xpath_ctx);
4228 if (err) goto leave;
4232 leave:
4233 if (err) isds_list_free(documents);
4234 xmlXPathFreeObject(result);
4235 xpath_ctx->node = files_node;
4236 return err;
4240 #if HAVE_LIBCURL
4241 /* Convert isds:dmRecord XML tree into structure
4242 * @context is ISDS context
4243 * @envelope is automatically reallocated message envelope structure
4244 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4245 * In case of error @envelope will be freed. */
4246 static isds_error extract_DmRecord(struct isds_ctx *context,
4247 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4248 isds_error err = IE_SUCCESS;
4249 xmlXPathObjectPtr result = NULL;
4251 if (!context) return IE_INVALID_CONTEXT;
4252 if (!envelope) return IE_INVAL;
4253 isds_envelope_free(envelope);
4254 if (!xpath_ctx) return IE_INVAL;
4257 *envelope = calloc(1, sizeof(**envelope));
4258 if (!*envelope) {
4259 err = IE_NOMEM;
4260 goto leave;
4264 /* Extract tRecord data */
4265 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4267 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4268 * dmAcceptanceTime. */
4269 err = append_status_size_times(context, envelope, xpath_ctx);
4270 if (err) goto leave;
4272 /* Extract envelope elements added by sender and ISDS
4273 * (XSD: gMessageEnvelope type) */
4274 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4275 if (err) goto leave;
4277 /* Get message type */
4278 err = append_message_type(context, envelope, xpath_ctx);
4279 if (err) goto leave;
4282 leave:
4283 if (err) isds_envelope_free(envelope);
4284 xmlXPathFreeObject(result);
4285 return err;
4289 /* Convert XSD:tStateChangesRecord type XML tree into structure
4290 * @context is ISDS context
4291 * @changed_status is automatically reallocated message state change structure
4292 * @xpath_ctx is XPath context with current node as element of
4293 * XSD:tStateChangesRecord type
4294 * In case of error @changed_status will be freed. */
4295 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4296 struct isds_message_status_change **changed_status,
4297 xmlXPathContextPtr xpath_ctx) {
4298 isds_error err = IE_SUCCESS;
4299 xmlXPathObjectPtr result = NULL;
4300 unsigned long int *unumber = NULL;
4301 char *string = NULL;
4303 if (!context) return IE_INVALID_CONTEXT;
4304 if (!changed_status) return IE_INVAL;
4305 isds_message_status_change_free(changed_status);
4306 if (!xpath_ctx) return IE_INVAL;
4309 *changed_status = calloc(1, sizeof(**changed_status));
4310 if (!*changed_status) {
4311 err = IE_NOMEM;
4312 goto leave;
4316 /* Extract tGetStateChangesInput data */
4317 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4319 /* dmEventTime is mandatory */
4320 EXTRACT_STRING("isds:dmEventTime", string);
4321 if (string) {
4322 err = timestring2timeval((xmlChar *) string,
4323 &((*changed_status)->time));
4324 if (err) {
4325 char *string_locale = _isds_utf82locale(string);
4326 if (err == IE_DATE) err = IE_ISDS;
4327 isds_printf_message(context,
4328 _("Could not convert dmEventTime as ISO time: %s"),
4329 string_locale);
4330 free(string_locale);
4331 goto leave;
4333 zfree(string);
4336 /* dmMessageStatus element is mandatory */
4337 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4338 if (!unumber) {
4339 isds_log_message(context,
4340 _("Missing mandatory isds:dmMessageStatus integer"));
4341 err = IE_ISDS;
4342 goto leave;
4344 err = uint2isds_message_status(context, unumber,
4345 &((*changed_status)->dmMessageStatus));
4346 if (err) {
4347 if (err == IE_ENUM) err = IE_ISDS;
4348 goto leave;
4350 zfree(unumber);
4353 leave:
4354 free(unumber);
4355 free(string);
4356 if (err) isds_message_status_change_free(changed_status);
4357 xmlXPathFreeObject(result);
4358 return err;
4360 #endif /* HAVE_LIBCURL */
4363 /* Find and convert isds:dmHash XML tree into structure
4364 * @context is ISDS context
4365 * @envelope is automatically reallocated message hash structure
4366 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4367 * In case of error @hash will be freed. */
4368 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4369 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4370 isds_error err = IE_SUCCESS;
4371 xmlNodePtr old_ctx_node;
4372 xmlXPathObjectPtr result = NULL;
4373 char *string = NULL;
4375 if (!context) return IE_INVALID_CONTEXT;
4376 if (!hash) return IE_INVAL;
4377 isds_hash_free(hash);
4378 if (!xpath_ctx) return IE_INVAL;
4380 old_ctx_node = xpath_ctx->node;
4382 *hash = calloc(1, sizeof(**hash));
4383 if (!*hash) {
4384 err = IE_NOMEM;
4385 goto leave;
4388 /* Locate dmHash */
4389 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4390 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4391 err = IE_ISDS;
4392 goto leave;
4394 if (err) {
4395 err = IE_ERROR;
4396 goto leave;
4399 /* Get hash algorithm */
4400 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4401 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4402 if (err) {
4403 if (err == IE_ENUM) {
4404 char *string_locale = _isds_utf82locale(string);
4405 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4406 string_locale);
4407 free(string_locale);
4409 goto leave;
4411 zfree(string);
4413 /* Get hash value */
4414 EXTRACT_STRING(".", string);
4415 if (!string) {
4416 isds_printf_message(context,
4417 _("sisds:dmHash element is missing hash value"));
4418 err = IE_ISDS;
4419 goto leave;
4421 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4422 if ((*hash)->length == (size_t) -1) {
4423 isds_printf_message(context,
4424 _("Error while Base64-decoding hash value"));
4425 err = IE_ERROR;
4426 goto leave;
4429 leave:
4430 if (err) isds_hash_free(hash);
4431 free(string);
4432 xmlXPathFreeObject(result);
4433 xpath_ctx->node = old_ctx_node;
4434 return err;
4438 /* Find and append isds:dmQTimestamp XML tree into envelope.
4439 * Because one service is allowed to miss time-stamp content, and we think
4440 * other could too (flaw in specification), this function is deliberated and
4441 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4442 * @context is ISDS context
4443 * @envelope is automatically allocated envelope structure
4444 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4445 * child
4446 * In case of error @envelope will be freed. */
4447 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4448 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4449 isds_error err = IE_SUCCESS;
4450 xmlXPathObjectPtr result = NULL;
4451 char *string = NULL;
4453 if (!context) return IE_INVALID_CONTEXT;
4454 if (!envelope) return IE_INVAL;
4455 if (!xpath_ctx) {
4456 isds_envelope_free(envelope);
4457 return IE_INVAL;
4460 if (!*envelope) {
4461 *envelope = calloc(1, sizeof(**envelope));
4462 if (!*envelope) {
4463 err = IE_NOMEM;
4464 goto leave;
4466 } else {
4467 zfree((*envelope)->timestamp);
4468 (*envelope)->timestamp_length = 0;
4471 /* Get dmQTimestamp */
4472 EXTRACT_STRING("sisds:dmQTimestamp", string);
4473 if (!string) {
4474 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4475 goto leave;
4477 (*envelope)->timestamp_length =
4478 _isds_b64decode(string, &((*envelope)->timestamp));
4479 if ((*envelope)->timestamp_length == (size_t) -1) {
4480 isds_printf_message(context,
4481 _("Error while Base64-decoding time stamp value"));
4482 err = IE_ERROR;
4483 goto leave;
4486 leave:
4487 if (err) isds_envelope_free(envelope);
4488 free(string);
4489 xmlXPathFreeObject(result);
4490 return err;
4494 /* Convert XSD tReturnedMessage XML tree into message structure.
4495 * It does not store serialized XML tree into message->raw.
4496 * It does store (pointer to) parsed XML tree into message->xml if needed.
4497 * @context is ISDS context
4498 * @include_documents Use true if documents must be extracted
4499 * (tReturnedMessage XSD type), use false if documents shall be omitted
4500 * (tReturnedMessageEnvelope).
4501 * @message is automatically reallocated message structure
4502 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4503 * type
4504 * In case of error @message will be freed. */
4505 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4506 const _Bool include_documents, struct isds_message **message,
4507 xmlXPathContextPtr xpath_ctx) {
4508 isds_error err = IE_SUCCESS;
4509 xmlNodePtr message_node;
4511 if (!context) return IE_INVALID_CONTEXT;
4512 if (!message) return IE_INVAL;
4513 isds_message_free(message);
4514 if (!xpath_ctx) return IE_INVAL;
4517 *message = calloc(1, sizeof(**message));
4518 if (!*message) {
4519 err = IE_NOMEM;
4520 goto leave;
4523 /* Save message XPATH context node */
4524 message_node = xpath_ctx->node;
4527 /* Extract dmDM */
4528 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4529 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4530 if (err) { err = IE_ERROR; goto leave; }
4531 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4532 if (err) goto leave;
4534 if (include_documents) {
4535 struct isds_list *item;
4537 /* Extract dmFiles */
4538 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4539 xpath_ctx);
4540 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4541 err = IE_ISDS; goto leave;
4543 if (err) { err = IE_ERROR; goto leave; }
4544 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4545 if (err) goto leave;
4547 /* Store xmlDoc of this message if needed */
4548 /* Only if we got a XML document in all the documents. */
4549 for (item = (*message)->documents; item; item = item->next) {
4550 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4551 (*message)->xml = xpath_ctx->doc;
4552 break;
4558 /* Restore context to message */
4559 xpath_ctx->node = message_node;
4561 /* Extract dmHash */
4562 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4563 xpath_ctx);
4564 if (err) goto leave;
4566 /* Extract dmQTimestamp, */
4567 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4568 xpath_ctx);
4569 if (err) goto leave;
4571 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4572 * dmAcceptanceTime. */
4573 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4574 if (err) goto leave;
4576 /* Get message type */
4577 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4578 if (err) goto leave;
4580 leave:
4581 if (err) isds_message_free(message);
4582 return err;
4586 /* Extract message event into reallocated isds_event structure
4587 * @context is ISDS context
4588 * @event is automatically reallocated message event structure
4589 * @xpath_ctx is XPath context with current node as isds:dmEvent
4590 * In case of error @event will be freed. */
4591 static isds_error extract_event(struct isds_ctx *context,
4592 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4593 isds_error err = IE_SUCCESS;
4594 xmlXPathObjectPtr result = NULL;
4595 xmlNodePtr event_node;
4596 char *string = NULL;
4598 if (!context) return IE_INVALID_CONTEXT;
4599 if (!event) return IE_INVAL;
4600 isds_event_free(event);
4601 if (!xpath_ctx) return IE_INVAL;
4602 event_node = xpath_ctx->node;
4604 *event = calloc(1, sizeof(**event));
4605 if (!*event) {
4606 err = IE_NOMEM;
4607 goto leave;
4610 /* Extract event data.
4611 * All elements are optional according XSD. That's funny. */
4612 EXTRACT_STRING("sisds:dmEventTime", string);
4613 if (string) {
4614 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4615 if (err) {
4616 char *string_locale = _isds_utf82locale(string);
4617 if (err == IE_DATE) err = IE_ISDS;
4618 isds_printf_message(context,
4619 _("Could not convert dmEventTime as ISO time: %s"),
4620 string_locale);
4621 free(string_locale);
4622 goto leave;
4624 zfree(string);
4627 /* dmEventDescr element has prefix and the rest */
4628 EXTRACT_STRING("sisds:dmEventDescr", string);
4629 if (string) {
4630 err = eventstring2event((xmlChar *) string, *event);
4631 if (err) goto leave;
4632 zfree(string);
4635 leave:
4636 if (err) isds_event_free(event);
4637 free(string);
4638 xmlXPathFreeObject(result);
4639 xpath_ctx->node = event_node;
4640 return err;
4644 /* Convert element of XSD tEventsArray type from XML tree into
4645 * isds_list of isds_event's structure. The list is automatically reallocated.
4646 * @context is ISDS context
4647 * @events is automatically reallocated list of event structures
4648 * @xpath_ctx is XPath context with current node as tEventsArray
4649 * In case of error @events will be freed. */
4650 static isds_error extract_events(struct isds_ctx *context,
4651 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4652 isds_error err = IE_SUCCESS;
4653 xmlXPathObjectPtr result = NULL;
4654 xmlNodePtr events_node;
4655 struct isds_list *event, *prev_event = NULL;
4657 if (!context) return IE_INVALID_CONTEXT;
4658 if (!events) return IE_INVAL;
4659 if (!xpath_ctx) return IE_INVAL;
4660 events_node = xpath_ctx->node;
4662 /* Free old list */
4663 isds_list_free(events);
4665 /* Find events */
4666 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4667 if (!result) {
4668 err = IE_XML;
4669 goto leave;
4672 /* No match */
4673 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4674 isds_printf_message(context,
4675 _("Delivery info does not contain any event"));
4676 err = IE_ISDS;
4677 goto leave;
4681 /* Iterate over events */
4682 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4684 /* Allocate and append list item */
4685 event = calloc(1, sizeof(*event));
4686 if (!event) {
4687 err = IE_NOMEM;
4688 goto leave;
4690 event->destructor = (void (*)(void **))isds_event_free;
4691 if (i == 0) *events = event;
4692 else prev_event->next = event;
4693 prev_event = event;
4695 /* Extract event */
4696 xpath_ctx->node = result->nodesetval->nodeTab[i];
4697 err = extract_event(context,
4698 (struct isds_event **) &(event->data), xpath_ctx);
4699 if (err) goto leave;
4703 leave:
4704 if (err) isds_list_free(events);
4705 xmlXPathFreeObject(result);
4706 xpath_ctx->node = events_node;
4707 return err;
4711 #if HAVE_LIBCURL
4712 /* Insert Base64 encoded data as element with text child.
4713 * @context is session context
4714 * @parent is XML node to append @element with @data as child
4715 * @ns is XML namespace of @element, use NULL to inherit from @parent
4716 * @element is UTF-8 encoded name of new element
4717 * @data is bit stream to encode into @element
4718 * @length is size of @data in bytes
4719 * @return standard error code and fill long error message if needed */
4720 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4721 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4722 const void *data, size_t length) {
4723 isds_error err = IE_SUCCESS;
4724 xmlNodePtr node;
4726 if (!context) return IE_INVALID_CONTEXT;
4727 if (!data && length > 0) return IE_INVAL;
4728 if (!parent || !element) return IE_INVAL;
4730 xmlChar *base64data = NULL;
4731 base64data = (xmlChar *) _isds_b64encode(data, length);
4732 if (!base64data) {
4733 isds_printf_message(context,
4734 ngettext("Not enough memory to encode %zd byte into Base64",
4735 "Not enough memory to encode %zd bytes into Base64",
4736 length),
4737 length);
4738 err = IE_NOMEM;
4739 goto leave;
4741 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4743 leave:
4744 free(base64data);
4745 return err;
4749 /* Convert isds_document structure into XML tree and append to dmFiles node.
4750 * @context is session context
4751 * @document is ISDS document
4752 * @dm_files is XML element the resulting tree will be appended to as a child.
4753 * @return error code, in case of error context' message is filled. */
4754 static isds_error insert_document(struct isds_ctx *context,
4755 struct isds_document *document, xmlNodePtr dm_files) {
4756 isds_error err = IE_SUCCESS;
4757 xmlNodePtr new_file = NULL, file = NULL, node;
4758 xmlAttrPtr attribute_node;
4760 if (!context) return IE_INVALID_CONTEXT;
4761 if (!document || !dm_files) return IE_INVAL;
4763 /* Allocate new dmFile */
4764 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4765 if (!new_file) {
4766 isds_printf_message(context, _("Could not allocate main dmFile"));
4767 err = IE_ERROR;
4768 goto leave;
4770 /* Append the new dmFile.
4771 * XXX: Main document must go first */
4772 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4773 file = xmlAddPrevSibling(dm_files->children, new_file);
4774 else
4775 file = xmlAddChild(dm_files, new_file);
4777 if (!file) {
4778 xmlFreeNode(new_file); new_file = NULL;
4779 isds_printf_message(context, _("Could not add dmFile child to "
4780 "%s element"), dm_files->name);
4781 err = IE_ERROR;
4782 goto leave;
4785 /* @dmMimeType is required */
4786 if (!document->dmMimeType) {
4787 isds_log_message(context,
4788 _("Document is missing mandatory MIME type definition"));
4789 err = IE_INVAL;
4790 goto leave;
4792 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4794 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4795 if (!string) {
4796 isds_printf_message(context,
4797 _("Document has unknown dmFileMetaType: %ld"),
4798 document->dmFileMetaType);
4799 err = IE_ENUM;
4800 goto leave;
4802 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4804 if (document->dmFileGuid) {
4805 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4807 if (document->dmUpFileGuid) {
4808 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4811 /* @dmFileDescr is required */
4812 if (!document->dmFileDescr) {
4813 isds_log_message(context,
4814 _("Document is missing mandatory description (title)"));
4815 err = IE_INVAL;
4816 goto leave;
4818 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4820 if (document->dmFormat) {
4821 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4825 /* Insert content (body) of the document. */
4826 if (document->is_xml) {
4827 /* XML document requested */
4829 /* Allocate new dmXMLContent */
4830 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4831 if (!xmlcontent) {
4832 isds_printf_message(context,
4833 _("Could not allocate dmXMLContent element"));
4834 err = IE_ERROR;
4835 goto leave;
4837 /* Append it */
4838 node = xmlAddChild(file, xmlcontent);
4839 if (!node) {
4840 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4841 isds_printf_message(context,
4842 _("Could not add dmXMLContent child to %s element"),
4843 file->name);
4844 err = IE_ERROR;
4845 goto leave;
4848 /* Copy non-empty node list */
4849 if (document->xml_node_list) {
4850 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4851 document->xml_node_list);
4852 if (!content) {
4853 isds_printf_message(context,
4854 _("Not enough memory to copy XML document"));
4855 err = IE_NOMEM;
4856 goto leave;
4859 if (!xmlAddChildList(node, content)) {
4860 xmlFreeNodeList(content);
4861 isds_printf_message(context,
4862 _("Error while adding XML document into dmXMLContent"));
4863 err = IE_XML;
4864 goto leave;
4866 /* XXX: We cannot free the content here because it's part of node's
4867 * document since now. It will be freed with it automatically. */
4869 } else {
4870 /* Binary document requested */
4871 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4872 document->data, document->data_length);
4873 if (err) goto leave;
4876 leave:
4877 return err;
4881 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4882 * The copy must be preallocated, the date are just appended into structure.
4883 * @context is ISDS context
4884 * @copy is message copy structure
4885 * @xpath_ctx is XPath context with current node as tMStatus */
4886 static isds_error append_TMStatus(struct isds_ctx *context,
4887 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4888 isds_error err = IE_SUCCESS;
4889 xmlXPathObjectPtr result = NULL;
4890 char *code = NULL, *message = NULL;
4892 if (!context) return IE_INVALID_CONTEXT;
4893 if (!copy || !xpath_ctx) return IE_INVAL;
4895 /* Free old values */
4896 zfree(copy->dmStatus);
4897 zfree(copy->dmID);
4899 /* Get error specific to this copy */
4900 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4901 if (!code) {
4902 isds_log_message(context,
4903 _("Missing isds:dmStatusCode under "
4904 "XSD:tMStatus type element"));
4905 err = IE_ISDS;
4906 goto leave;
4909 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4910 /* This copy failed */
4911 copy->error = IE_ISDS;
4912 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4913 if (message) {
4914 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4915 if (!copy->dmStatus) {
4916 copy->dmStatus = code;
4917 code = NULL;
4919 } else {
4920 copy->dmStatus = code;
4921 code = NULL;
4923 } else {
4924 /* This copy succeeded. In this case only, message ID is valid */
4925 copy->error = IE_SUCCESS;
4927 EXTRACT_STRING("isds:dmID", copy->dmID);
4928 if (!copy->dmID) {
4929 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4930 "but did not returned assigned message ID\n"));
4931 err = IE_ISDS;
4935 leave:
4936 free(code);
4937 free(message);
4938 xmlXPathFreeObject(result);
4939 return err;
4943 /* Insert struct isds_approval data (box approval) into XML tree
4944 * @context is session context
4945 * @approval is libisds structure with approval description. NULL is
4946 * acceptable.
4947 * @parent is XML element to append @approval to */
4948 static isds_error insert_GExtApproval(struct isds_ctx *context,
4949 const struct isds_approval *approval, xmlNodePtr parent) {
4951 isds_error err = IE_SUCCESS;
4952 xmlNodePtr node;
4954 if (!context) return IE_INVALID_CONTEXT;
4955 if (!parent) return IE_INVAL;
4957 if (!approval) return IE_SUCCESS;
4959 /* Build XSD:gExtApproval */
4960 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4961 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4963 leave:
4964 return err;
4968 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4969 * code
4970 * @context is session context
4971 * @service_name is name of SERVICE_DB_ACCESS
4972 * @response is reallocated server SOAP body response as XML document
4973 * @raw_response is reallocated bit stream with response body. Use
4974 * NULL if you don't care
4975 * @raw_response_length is size of @raw_response in bytes
4976 * @code is reallocated ISDS status code
4977 * @status_message is reallocated ISDS status message
4978 * @return error coded from lower layer, context message will be set up
4979 * appropriately. */
4980 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4981 const xmlChar *service_name,
4982 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4983 xmlChar **code, xmlChar **status_message) {
4985 isds_error err = IE_SUCCESS;
4986 char *service_name_locale = NULL;
4987 xmlNodePtr request = NULL, node;
4988 xmlNsPtr isds_ns = NULL;
4990 if (!context) return IE_INVALID_CONTEXT;
4991 if (!service_name) return IE_INVAL;
4992 if (!response || !code || !status_message) return IE_INVAL;
4993 if (!raw_response_length && raw_response) return IE_INVAL;
4995 /* Free output argument */
4996 xmlFreeDoc(*response); *response = NULL;
4997 if (raw_response) zfree(*raw_response);
4998 zfree(*code);
4999 zfree(*status_message);
5002 /* Check if connection is established
5003 * TODO: This check should be done downstairs. */
5004 if (!context->curl) return IE_CONNECTION_CLOSED;
5006 service_name_locale = _isds_utf82locale((char*)service_name);
5007 if (!service_name_locale) {
5008 err = IE_NOMEM;
5009 goto leave;
5012 /* Build request */
5013 request = xmlNewNode(NULL, service_name);
5014 if (!request) {
5015 isds_printf_message(context,
5016 _("Could not build %s request"), service_name_locale);
5017 err = IE_ERROR;
5018 goto leave;
5020 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5021 if(!isds_ns) {
5022 isds_log_message(context, _("Could not create ISDS name space"));
5023 err = IE_ERROR;
5024 goto leave;
5026 xmlSetNs(request, isds_ns);
5029 /* Add XSD:tDummyInput child */
5030 INSERT_STRING(request, "dbDummy", NULL);
5033 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5034 service_name_locale);
5036 /* Send request */
5037 err = _isds(context, SERVICE_DB_ACCESS, request, response,
5038 raw_response, raw_response_length);
5039 xmlFreeNode(request); request = NULL;
5041 if (err) {
5042 isds_log(ILF_ISDS, ILL_DEBUG,
5043 _("Processing ISDS response on %s request failed\n"),
5044 service_name_locale);
5045 goto leave;
5048 /* Check for response status */
5049 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
5050 code, status_message, NULL);
5051 if (err) {
5052 isds_log(ILF_ISDS, ILL_DEBUG,
5053 _("ISDS response on %s request is missing status\n"),
5054 service_name_locale);
5055 goto leave;
5058 /* Request processed, but nothing found */
5059 if (xmlStrcmp(*code, BAD_CAST "0000")) {
5060 char *code_locale = _isds_utf82locale((char*) *code);
5061 char *status_message_locale =
5062 _isds_utf82locale((char*) *status_message);
5063 isds_log(ILF_ISDS, ILL_DEBUG,
5064 _("Server refused %s request (code=%s, message=%s)\n"),
5065 service_name_locale, code_locale, status_message_locale);
5066 isds_log_message(context, status_message_locale);
5067 free(code_locale);
5068 free(status_message_locale);
5069 err = IE_ISDS;
5070 goto leave;
5073 leave:
5074 free(service_name_locale);
5075 xmlFreeNode(request);
5076 return err;
5078 #endif
5081 /* Get data about logged in user and his box.
5082 * @context is session context
5083 * @db_owner_info is reallocated box owner description. It will be freed on
5084 * error.
5085 * @return error code from lower layer, context message will be set up
5086 * appropriately. */
5087 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
5088 struct isds_DbOwnerInfo **db_owner_info) {
5089 isds_error err = IE_SUCCESS;
5090 #if HAVE_LIBCURL
5091 xmlDocPtr response = NULL;
5092 xmlChar *code = NULL, *message = NULL;
5093 xmlXPathContextPtr xpath_ctx = NULL;
5094 xmlXPathObjectPtr result = NULL;
5095 char *string = NULL;
5096 #endif
5098 if (!context) return IE_INVALID_CONTEXT;
5099 zfree(context->long_message);
5100 if (!db_owner_info) return IE_INVAL;
5101 isds_DbOwnerInfo_free(db_owner_info);
5103 #if HAVE_LIBCURL
5104 /* Check if connection is established */
5105 if (!context->curl) return IE_CONNECTION_CLOSED;
5108 /* Do request and check for success */
5109 err = build_send_check_dbdummy_request(context,
5110 BAD_CAST "GetOwnerInfoFromLogin",
5111 &response, NULL, NULL, &code, &message);
5112 if (err) goto leave;
5115 /* Extract data */
5116 /* Prepare structure */
5117 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5118 if (!*db_owner_info) {
5119 err = IE_NOMEM;
5120 goto leave;
5122 xpath_ctx = xmlXPathNewContext(response);
5123 if (!xpath_ctx) {
5124 err = IE_ERROR;
5125 goto leave;
5127 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5128 err = IE_ERROR;
5129 goto leave;
5132 /* Set context node */
5133 result = xmlXPathEvalExpression(BAD_CAST
5134 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5135 if (!result) {
5136 err = IE_ERROR;
5137 goto leave;
5139 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5140 isds_log_message(context, _("Missing dbOwnerInfo element"));
5141 err = IE_ISDS;
5142 goto leave;
5144 if (result->nodesetval->nodeNr > 1) {
5145 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5146 err = IE_ISDS;
5147 goto leave;
5149 xpath_ctx->node = result->nodesetval->nodeTab[0];
5150 xmlXPathFreeObject(result); result = NULL;
5152 /* Extract it */
5153 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5156 leave:
5157 if (err) {
5158 isds_DbOwnerInfo_free(db_owner_info);
5161 free(string);
5162 xmlXPathFreeObject(result);
5163 xmlXPathFreeContext(xpath_ctx);
5165 free(code);
5166 free(message);
5167 xmlFreeDoc(response);
5169 if (!err)
5170 isds_log(ILF_ISDS, ILL_DEBUG,
5171 _("GetOwnerInfoFromLogin request processed by server "
5172 "successfully.\n"));
5173 #else /* not HAVE_LIBCURL */
5174 err = IE_NOTSUP;
5175 #endif
5177 return err;
5181 /* Get data about logged in user. */
5182 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5183 struct isds_DbUserInfo **db_user_info) {
5184 isds_error err = IE_SUCCESS;
5185 #if HAVE_LIBCURL
5186 xmlDocPtr response = NULL;
5187 xmlChar *code = NULL, *message = NULL;
5188 xmlXPathContextPtr xpath_ctx = NULL;
5189 xmlXPathObjectPtr result = NULL;
5190 #endif
5192 if (!context) return IE_INVALID_CONTEXT;
5193 zfree(context->long_message);
5194 if (!db_user_info) return IE_INVAL;
5195 isds_DbUserInfo_free(db_user_info);
5197 #if HAVE_LIBCURL
5198 /* Check if connection is established */
5199 if (!context->curl) return IE_CONNECTION_CLOSED;
5202 /* Do request and check for success */
5203 err = build_send_check_dbdummy_request(context,
5204 BAD_CAST "GetUserInfoFromLogin",
5205 &response, NULL, NULL, &code, &message);
5206 if (err) goto leave;
5209 /* Extract data */
5210 /* Prepare structure */
5211 *db_user_info = calloc(1, sizeof(**db_user_info));
5212 if (!*db_user_info) {
5213 err = IE_NOMEM;
5214 goto leave;
5216 xpath_ctx = xmlXPathNewContext(response);
5217 if (!xpath_ctx) {
5218 err = IE_ERROR;
5219 goto leave;
5221 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5222 err = IE_ERROR;
5223 goto leave;
5226 /* Set context node */
5227 result = xmlXPathEvalExpression(BAD_CAST
5228 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5229 if (!result) {
5230 err = IE_ERROR;
5231 goto leave;
5233 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5234 isds_log_message(context, _("Missing dbUserInfo element"));
5235 err = IE_ISDS;
5236 goto leave;
5238 if (result->nodesetval->nodeNr > 1) {
5239 isds_log_message(context, _("Multiple dbUserInfo element"));
5240 err = IE_ISDS;
5241 goto leave;
5243 xpath_ctx->node = result->nodesetval->nodeTab[0];
5244 xmlXPathFreeObject(result); result = NULL;
5246 /* Extract it */
5247 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5249 leave:
5250 if (err) {
5251 isds_DbUserInfo_free(db_user_info);
5254 xmlXPathFreeObject(result);
5255 xmlXPathFreeContext(xpath_ctx);
5257 free(code);
5258 free(message);
5259 xmlFreeDoc(response);
5261 if (!err)
5262 isds_log(ILF_ISDS, ILL_DEBUG,
5263 _("GetUserInfoFromLogin request processed by server "
5264 "successfully.\n"));
5265 #else /* not HAVE_LIBCURL */
5266 err = IE_NOTSUP;
5267 #endif
5269 return err;
5273 /* Get expiration time of current password
5274 * @context is session context
5275 * @expiration is automatically reallocated time when password expires. If
5276 * password expiration is disabled, NULL will be returned. In case of error
5277 * it will be nulled too. */
5278 isds_error isds_get_password_expiration(struct isds_ctx *context,
5279 struct timeval **expiration) {
5280 isds_error err = IE_SUCCESS;
5281 #if HAVE_LIBCURL
5282 xmlDocPtr response = NULL;
5283 xmlChar *code = NULL, *message = NULL;
5284 xmlXPathContextPtr xpath_ctx = NULL;
5285 xmlXPathObjectPtr result = NULL;
5286 char *string = NULL;
5287 #endif
5289 if (!context) return IE_INVALID_CONTEXT;
5290 zfree(context->long_message);
5291 if (!expiration) return IE_INVAL;
5292 zfree(*expiration);
5294 #if HAVE_LIBCURL
5295 /* Check if connection is established */
5296 if (!context->curl) return IE_CONNECTION_CLOSED;
5299 /* Do request and check for success */
5300 err = build_send_check_dbdummy_request(context,
5301 BAD_CAST "GetPasswordInfo",
5302 &response, NULL, NULL, &code, &message);
5303 if (err) goto leave;
5306 /* Extract data */
5307 xpath_ctx = xmlXPathNewContext(response);
5308 if (!xpath_ctx) {
5309 err = IE_ERROR;
5310 goto leave;
5312 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5313 err = IE_ERROR;
5314 goto leave;
5317 /* Set context node */
5318 result = xmlXPathEvalExpression(BAD_CAST
5319 "/isds:GetPasswordInfoResponse", xpath_ctx);
5320 if (!result) {
5321 err = IE_ERROR;
5322 goto leave;
5324 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5325 isds_log_message(context,
5326 _("Missing GetPasswordInfoResponse element"));
5327 err = IE_ISDS;
5328 goto leave;
5330 if (result->nodesetval->nodeNr > 1) {
5331 isds_log_message(context,
5332 _("Multiple GetPasswordInfoResponse element"));
5333 err = IE_ISDS;
5334 goto leave;
5336 xpath_ctx->node = result->nodesetval->nodeTab[0];
5337 xmlXPathFreeObject(result); result = NULL;
5339 /* Extract expiration date */
5340 EXTRACT_STRING("isds:pswExpDate", string);
5341 if (string) {
5342 /* And convert it if any returned. Otherwise expiration is disabled. */
5343 err = timestring2timeval((xmlChar *) string, expiration);
5344 if (err) {
5345 char *string_locale = _isds_utf82locale(string);
5346 if (err == IE_DATE) err = IE_ISDS;
5347 isds_printf_message(context,
5348 _("Could not convert pswExpDate as ISO time: %s"),
5349 string_locale);
5350 free(string_locale);
5351 goto leave;
5355 leave:
5356 if (err) {
5357 if (*expiration) {
5358 zfree(*expiration);
5362 free(string);
5363 xmlXPathFreeObject(result);
5364 xmlXPathFreeContext(xpath_ctx);
5366 free(code);
5367 free(message);
5368 xmlFreeDoc(response);
5370 if (!err)
5371 isds_log(ILF_ISDS, ILL_DEBUG,
5372 _("GetPasswordInfo request processed by server "
5373 "successfully.\n"));
5374 #else /* not HAVE_LIBCURL */
5375 err = IE_NOTSUP;
5376 #endif
5378 return err;
5382 #if HAVE_LIBCURL
5383 /* Request delivering new TOTP code from ISDS through side channel before
5384 * changing password.
5385 * @context is session context
5386 * @password is current password.
5387 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5388 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5389 * function for more details.
5390 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5391 * NULL, if you don't care.
5392 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5393 * error code. */
5394 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5395 const char *password, struct isds_otp *otp, char **refnumber) {
5396 isds_error err = IE_SUCCESS;
5397 char *saved_url = NULL; /* No copy */
5398 #if HAVE_CURL_REAUTHORIZATION_BUG
5399 CURL *saved_curl = NULL; /* No copy */
5400 #endif
5401 xmlNsPtr isds_ns = NULL;
5402 xmlNodePtr request = NULL;
5403 xmlDocPtr response = NULL;
5404 xmlChar *code = NULL, *message = NULL;
5405 const xmlChar *codes[] = {
5406 BAD_CAST "2300",
5407 BAD_CAST "2301",
5408 BAD_CAST "2302"
5410 const char *meanings[] = {
5411 N_("Unexpected error"),
5412 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5413 N_("One-time code could not been sent. Try later again.")
5415 const isds_otp_resolution resolutions[] = {
5416 OTP_RESOLUTION_UNKNOWN,
5417 OTP_RESOLUTION_TO_FAST,
5418 OTP_RESOLUTION_TOTP_NOT_SENT
5421 if (NULL == context) return IE_INVALID_CONTEXT;
5422 zfree(context->long_message);
5423 if (NULL == password) {
5424 isds_log_message(context,
5425 _("Second argument (password) of isds_change_password() "
5426 "is NULL"));
5427 return IE_INVAL;
5430 /* Check if connection is established
5431 * TODO: This check should be done downstairs. */
5432 if (!context->curl) return IE_CONNECTION_CLOSED;
5434 if (!context->otp) {
5435 isds_log_message(context, _("This function requires OTP-authenticated "
5436 "context"));
5437 return IE_INVALID_CONTEXT;
5439 if (NULL == otp) {
5440 isds_log_message(context, _("If one-time password authentication "
5441 "method is in use, requesting new OTP code requires "
5442 "one-time credentials argument either"));
5443 return IE_INVAL;
5445 if (otp->method != OTP_TIME) {
5446 isds_log_message(context, _("Requesting new time-based OTP code from "
5447 "server requires one-time password authentication "
5448 "method"));
5449 return IE_INVAL;
5451 if (otp->otp_code != NULL) {
5452 isds_log_message(context, _("Requesting new time-based OTP code from "
5453 "server requires undefined OTP code member in "
5454 "one-time credentials argument"));
5455 return IE_INVAL;
5459 /* Build request */
5460 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5461 if (!request) {
5462 isds_log_message(context, _("Could not build SendSMSCode request"));
5463 return IE_ERROR;
5465 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5466 if(!isds_ns) {
5467 isds_log_message(context, _("Could not create ISDS name space"));
5468 xmlFreeNode(request);
5469 return IE_ERROR;
5471 xmlSetNs(request, isds_ns);
5473 /* Change URL temporarily for sending this request only */
5475 char *new_url = NULL;
5476 if ((err = _isds_build_url_from_context(context,
5477 "%.*sasws/changePassword", &new_url))) {
5478 goto leave;
5480 saved_url = context->url;
5481 context->url = new_url;
5484 /* Store credentials for sending this request only */
5485 context->otp_credentials = otp;
5486 _isds_discard_credentials(context, 0);
5487 if ((err = _isds_store_credentials(context, context->saved_username,
5488 password, NULL))) {
5489 _isds_discard_credentials(context, 0);
5490 goto leave;
5492 #if HAVE_CURL_REAUTHORIZATION_BUG
5493 saved_curl = context->curl;
5494 context->curl = curl_easy_init();
5495 if (NULL == context->curl) {
5496 err = IE_ERROR;
5497 goto leave;
5499 if (context->timeout) {
5500 err = isds_set_timeout(context, context->timeout);
5501 if (err) goto leave;
5503 #endif
5505 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5507 /* Sent request */
5508 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5510 /* Remove temporal credentials */
5511 _isds_discard_credentials(context, 0);
5512 /* Detach pointer to OTP credentials from context */
5513 context->otp_credentials = NULL;
5514 /* Keep context->otp true to keep signaling this is OTP session */
5516 /* Destroy request */
5517 xmlFreeNode(request); request = NULL;
5519 if (err) {
5520 isds_log(ILF_ISDS, ILL_DEBUG,
5521 _("Processing ISDS response on SendSMSCode request failed\n"));
5522 goto leave;
5525 /* Check for response status */
5526 err = isds_response_status(context, SERVICE_ASWS, response,
5527 &code, &message, (xmlChar **)refnumber);
5528 if (err) {
5529 isds_log(ILF_ISDS, ILL_DEBUG,
5530 _("ISDS response on SendSMSCode request is missing "
5531 "status\n"));
5532 goto leave;
5535 /* Check for error */
5536 if (xmlStrcmp(code, BAD_CAST "0000")) {
5537 char *code_locale = _isds_utf82locale((char*)code);
5538 char *message_locale = _isds_utf82locale((char*)message);
5539 size_t i;
5540 isds_log(ILF_ISDS, ILL_DEBUG,
5541 _("Server refused to send new code on SendSMSCode "
5542 "request (code=%s, message=%s)\n"),
5543 code_locale, message_locale);
5545 /* Check for known error codes */
5546 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5547 if (!xmlStrcmp(code, codes[i])) break;
5549 if (i < sizeof(codes)/sizeof(*codes)) {
5550 isds_log_message(context, _(meanings[i]));
5551 /* Mimic otp->resolution according to the code, specification does
5552 * prescribe OTP header to be available. */
5553 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5554 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5555 otp->resolution = resolutions[i];
5556 } else
5557 isds_log_message(context, message_locale);
5559 free(code_locale);
5560 free(message_locale);
5562 err = IE_ISDS;
5563 goto leave;
5566 /* Otherwise new code sent successfully */
5567 /* Mimic otp->resolution according to the code, specification does
5568 * prescribe OTP header to be available. */
5569 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5570 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5572 leave:
5573 if (NULL != saved_url) {
5574 /* Revert URL to original one */
5575 zfree(context->url);
5576 context->url = saved_url;
5578 #if HAVE_CURL_REAUTHORIZATION_BUG
5579 if (NULL != saved_curl) {
5580 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5581 context->curl = saved_curl;
5583 #endif
5585 free(code);
5586 free(message);
5587 xmlFreeDoc(response);
5588 xmlFreeNode(request);
5590 if (!err)
5591 isds_log(ILF_ISDS, ILL_DEBUG,
5592 _("New OTP code has been sent successfully on SendSMSCode "
5593 "request.\n"));
5594 return err;
5598 /* Convert response status code to isds_error code and set long message
5599 * @context is context to save long message to
5600 * @map is mapping from codes to errors and messages. Pass NULL for generic
5601 * handling.
5602 * @code is status code to translate
5603 * @message is non-localized status message to put into long message in case
5604 * of uknown error. It can be NULL if server did not provide any.
5605 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5606 * invalid invocation. */
5607 static isds_error statuscode2isds_error(struct isds_ctx *context,
5608 const struct code_map_isds_error *map,
5609 const xmlChar *code, const xmlChar *message) {
5610 if (NULL == code) {
5611 isds_log_message(context,
5612 _("NULL status code passed to statuscode2isds_error()"));
5613 return IE_INVAL;
5616 if (NULL != map) {
5617 /* Check for known error codes */
5618 for (int i=0; map->codes[i] != NULL; i++) {
5619 if (!xmlStrcmp(code, map->codes[i])) {
5620 isds_log_message(context, _(map->meanings[i]));
5621 return map->errors[i];
5626 /* Other error */
5627 if (xmlStrcmp(code, BAD_CAST "0000")) {
5628 char *message_locale = _isds_utf82locale((char*)message);
5629 if (NULL == message_locale)
5630 isds_log_message(context, _("ISDS server returned unknown error"));
5631 else
5632 isds_log_message(context, message_locale);
5633 free(message_locale);
5634 return IE_ISDS;
5637 return IE_SUCCESS;
5639 #endif
5642 /* Change user password in ISDS.
5643 * User must supply old password, new password will takes effect after some
5644 * time, current session can continue. Password must fulfill some constraints.
5645 * @context is session context
5646 * @old_password is current password.
5647 * @new_password is requested new password
5648 * @otp auxiliary data required if one-time password authentication is in use,
5649 * defines OTP code (if known) and returns fine grade resolution of OTP
5650 * procedure. Pass NULL, if one-time password authentication is not needed.
5651 * Please note the @otp argument must match OTP method used at log-in time. See
5652 * isds_login() function for more details.
5653 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5654 * NULL, if you don't care.
5655 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5656 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5657 * awaiting OTP code that has been delivered by side channel to the user. */
5658 isds_error isds_change_password(struct isds_ctx *context,
5659 const char *old_password, const char *new_password,
5660 struct isds_otp *otp, char **refnumber) {
5661 isds_error err = IE_SUCCESS;
5662 #if HAVE_LIBCURL
5663 char *saved_url = NULL; /* No copy */
5664 #if HAVE_CURL_REAUTHORIZATION_BUG
5665 CURL *saved_curl = NULL; /* No copy */
5666 #endif
5667 xmlNsPtr isds_ns = NULL;
5668 xmlNodePtr request = NULL, node;
5669 xmlDocPtr response = NULL;
5670 xmlChar *code = NULL, *message = NULL;
5671 const xmlChar *codes[] = {
5672 BAD_CAST "1066",
5673 BAD_CAST "1067",
5674 BAD_CAST "1079",
5675 BAD_CAST "1080",
5676 BAD_CAST "1081",
5677 BAD_CAST "1082",
5678 BAD_CAST "1083",
5679 BAD_CAST "1090",
5680 BAD_CAST "1091",
5681 BAD_CAST "2300",
5682 BAD_CAST "9204"
5684 const char *meanings[] = {
5685 N_("Password length must be between 8 and 32 characters"),
5686 N_("Password cannot be reused"), /* Server does not distinguish 1067
5687 and 1091 on ChangePasswordOTP */
5688 N_("Password contains forbidden character"),
5689 N_("Password must contain at least one upper-case letter, "
5690 "one lower-case, and one digit"),
5691 N_("Password cannot contain sequence of three identical characters"),
5692 N_("Password cannot contain user identifier"),
5693 N_("Password is too simmple"),
5694 N_("Old password is not valid"),
5695 N_("Password cannot be reused"),
5696 N_("Unexpected error"),
5697 N_("LDAP update error")
5699 #endif
5701 if (!context) return IE_INVALID_CONTEXT;
5702 zfree(context->long_message);
5703 if (NULL != refnumber)
5704 zfree(*refnumber);
5705 if (NULL == old_password) {
5706 isds_log_message(context,
5707 _("Second argument (old password) of isds_change_password() "
5708 "is NULL"));
5709 return IE_INVAL;
5711 if (NULL == otp && NULL == new_password) {
5712 isds_log_message(context,
5713 _("Third argument (new password) of isds_change_password() "
5714 "is NULL"));
5715 return IE_INVAL;
5718 #if HAVE_LIBCURL
5719 /* Check if connection is established
5720 * TODO: This check should be done downstairs. */
5721 if (!context->curl) return IE_CONNECTION_CLOSED;
5723 if (context->otp && NULL == otp) {
5724 isds_log_message(context, _("If one-time password authentication "
5725 "method is in use, changing password requires one-time "
5726 "credentials either"));
5727 return IE_INVAL;
5730 /* Build ChangeISDSPassword request */
5731 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5732 BAD_CAST "ChangePasswordOTP");
5733 if (!request) {
5734 isds_log_message(context, (NULL == otp) ?
5735 _("Could not build ChangeISDSPassword request") :
5736 _("Could not build ChangePasswordOTP request"));
5737 return IE_ERROR;
5739 isds_ns = xmlNewNs(request,
5740 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5741 NULL);
5742 if(!isds_ns) {
5743 isds_log_message(context, _("Could not create ISDS name space"));
5744 xmlFreeNode(request);
5745 return IE_ERROR;
5747 xmlSetNs(request, isds_ns);
5749 INSERT_STRING(request, "dbOldPassword", old_password);
5750 INSERT_STRING(request, "dbNewPassword", new_password);
5752 if (NULL != otp) {
5753 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5754 switch (otp->method) {
5755 case OTP_HMAC:
5756 isds_log(ILF_SEC, ILL_INFO,
5757 _("Selected authentication method: "
5758 "HMAC-based one-time password\n"));
5759 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5760 break;
5761 case OTP_TIME:
5762 isds_log(ILF_SEC, ILL_INFO,
5763 _("Selected authentication method: "
5764 "Time-based one-time password\n"));
5765 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5766 if (otp->otp_code == NULL) {
5767 isds_log(ILF_SEC, ILL_INFO,
5768 _("OTP code has not been provided by "
5769 "application, requesting server for "
5770 "new one.\n"));
5771 err = _isds_request_totp_code(context, old_password, otp,
5772 refnumber);
5773 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5774 goto leave;
5776 } else {
5777 isds_log(ILF_SEC, ILL_INFO,
5778 _("OTP code has been provided by "
5779 "application, not requesting server "
5780 "for new one.\n"));
5782 break;
5783 default:
5784 isds_log_message(context,
5785 _("Unknown one-time password authentication "
5786 "method requested by application"));
5787 err = IE_ENUM;
5788 goto leave;
5791 /* Change URL temporarily for sending this request only */
5793 char *new_url = NULL;
5794 if ((err = _isds_build_url_from_context(context,
5795 "%.*sasws/changePassword", &new_url))) {
5796 goto leave;
5798 saved_url = context->url;
5799 context->url = new_url;
5802 /* Store credentials for sending this request only */
5803 context->otp_credentials = otp;
5804 _isds_discard_credentials(context, 0);
5805 if ((err = _isds_store_credentials(context, context->saved_username,
5806 old_password, NULL))) {
5807 _isds_discard_credentials(context, 0);
5808 goto leave;
5810 #if HAVE_CURL_REAUTHORIZATION_BUG
5811 saved_curl = context->curl;
5812 context->curl = curl_easy_init();
5813 if (NULL == context->curl) {
5814 err = IE_ERROR;
5815 goto leave;
5817 if (context->timeout) {
5818 err = isds_set_timeout(context, context->timeout);
5819 if (err) goto leave;
5821 #endif
5824 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5825 _("Sending ChangeISDSPassword request to ISDS\n") :
5826 _("Sending ChangePasswordOTP request to ISDS\n"));
5828 /* Sent request */
5829 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5830 request, &response, NULL, NULL);
5832 if (otp) {
5833 /* Remove temporal credentials */
5834 _isds_discard_credentials(context, 0);
5835 /* Detach pointer to OTP credentials from context */
5836 context->otp_credentials = NULL;
5837 /* Keep context->otp true to keep signaling this is OTP session */
5840 /* Destroy request */
5841 xmlFreeNode(request); request = NULL;
5843 if (err) {
5844 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5845 _("Processing ISDS response on ChangeISDSPassword "
5846 "request failed\n") :
5847 _("Processing ISDS response on ChangePasswordOTP "
5848 "request failed\n"));
5849 goto leave;
5852 /* Check for response status */
5853 err = isds_response_status(context,
5854 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5855 &code, &message, (xmlChar **)refnumber);
5856 if (err) {
5857 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5858 _("ISDS response on ChangeISDSPassword request is missing "
5859 "status\n") :
5860 _("ISDS response on ChangePasswordOTP request is missing "
5861 "status\n"));
5862 goto leave;
5865 /* Check for known error codes */
5866 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5867 if (!xmlStrcmp(code, codes[i])) {
5868 char *code_locale = _isds_utf82locale((char*)code);
5869 char *message_locale = _isds_utf82locale((char*)message);
5870 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5871 _("Server refused to change password on ChangeISDSPassword "
5872 "request (code=%s, message=%s)\n") :
5873 _("Server refused to change password on ChangePasswordOTP "
5874 "request (code=%s, message=%s)\n"),
5875 code_locale, message_locale);
5876 free(code_locale);
5877 free(message_locale);
5878 isds_log_message(context, _(meanings[i]));
5879 err = IE_INVAL;
5880 goto leave;
5884 /* Other error */
5885 if (xmlStrcmp(code, BAD_CAST "0000")) {
5886 char *code_locale = _isds_utf82locale((char*)code);
5887 char *message_locale = _isds_utf82locale((char*)message);
5888 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5889 _("Server refused to change password on ChangeISDSPassword "
5890 "request (code=%s, message=%s)\n") :
5891 _("Server refused to change password on ChangePasswordOTP "
5892 "request (code=%s, message=%s)\n"),
5893 code_locale, message_locale);
5894 isds_log_message(context, message_locale);
5895 free(code_locale);
5896 free(message_locale);
5897 err = IE_ISDS;
5898 goto leave;
5901 /* Otherwise password changed successfully */
5903 leave:
5904 if (NULL != saved_url) {
5905 /* Revert URL to original one */
5906 zfree(context->url);
5907 context->url = saved_url;
5909 #if HAVE_CURL_REAUTHORIZATION_BUG
5910 if (NULL != saved_curl) {
5911 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5912 context->curl = saved_curl;
5914 #endif
5916 free(code);
5917 free(message);
5918 xmlFreeDoc(response);
5919 xmlFreeNode(request);
5921 if (!err)
5922 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5923 _("Password changed successfully on ChangeISDSPassword "
5924 "request.\n") :
5925 _("Password changed successfully on ChangePasswordOTP "
5926 "request.\n"));
5927 #else /* not HAVE_LIBCURL */
5928 err = IE_NOTSUP;
5929 #endif
5931 return err;
5935 #if HAVE_LIBCURL
5936 /* Generic middle part with request sending and response check.
5937 * It sends prepared request and checks for error code.
5938 * @context is ISDS session context.
5939 * @service is ISDS service handler
5940 * @service_name is name in scope of given @service
5941 * @request is XML tree with request. Will be freed to save memory.
5942 * @response is XML document outputting ISDS response.
5943 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5944 * @map is mapping from status code to library error. Pass NULL if no special
5945 * handling is requested.
5946 * NULL, if you don't care. */
5947 static isds_error send_destroy_request_check_response(
5948 struct isds_ctx *context,
5949 const isds_service service, const xmlChar *service_name,
5950 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5951 const struct code_map_isds_error *map) {
5952 isds_error err = IE_SUCCESS;
5953 char *service_name_locale = NULL;
5954 xmlChar *code = NULL, *message = NULL;
5957 if (!context) return IE_INVALID_CONTEXT;
5958 if (!service_name || *service_name == '\0' || !request || !*request ||
5959 !response)
5960 return IE_INVAL;
5962 /* Check if connection is established
5963 * TODO: This check should be done downstairs. */
5964 if (!context->curl) return IE_CONNECTION_CLOSED;
5966 service_name_locale = _isds_utf82locale((char*) service_name);
5967 if (!service_name_locale) {
5968 err = IE_NOMEM;
5969 goto leave;
5972 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5973 service_name_locale);
5975 /* Send request */
5976 err = _isds(context, service, *request, response, NULL, NULL);
5977 xmlFreeNode(*request); *request = NULL;
5979 if (err) {
5980 isds_log(ILF_ISDS, ILL_DEBUG,
5981 _("Processing ISDS response on %s request failed\n"),
5982 service_name_locale);
5983 goto leave;
5986 /* Check for response status */
5987 err = isds_response_status(context, service, *response,
5988 &code, &message, refnumber);
5989 if (err) {
5990 isds_log(ILF_ISDS, ILL_DEBUG,
5991 _("ISDS response on %s request is missing status\n"),
5992 service_name_locale);
5993 goto leave;
5996 err = statuscode2isds_error(context, map, code, message);
5998 /* Request processed, but server failed */
5999 if (xmlStrcmp(code, BAD_CAST "0000")) {
6000 char *code_locale = _isds_utf82locale((char*) code);
6001 char *message_locale = _isds_utf82locale((char*) message);
6002 isds_log(ILF_ISDS, ILL_DEBUG,
6003 _("Server refused %s request (code=%s, message=%s)\n"),
6004 service_name_locale, code_locale, message_locale);
6005 free(code_locale);
6006 free(message_locale);
6007 goto leave;
6011 leave:
6012 free(code);
6013 free(message);
6014 if (err && *response) {
6015 xmlFreeDoc(*response);
6016 *response = NULL;
6018 if (*request) {
6019 xmlFreeNode(*request);
6020 *request = NULL;
6022 free(service_name_locale);
6024 return err;
6028 /* Generic bottom half with request sending.
6029 * It sends prepared request, checks for error code, destroys response and
6030 * request and log success or failure.
6031 * @context is ISDS session context.
6032 * @service is ISDS service handler
6033 * @service_name is name in scope of given @service
6034 * @request is XML tree with request. Will be freed to save memory.
6035 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6036 * NULL, if you don't care. */
6037 static isds_error send_request_check_drop_response(
6038 struct isds_ctx *context,
6039 const isds_service service, const xmlChar *service_name,
6040 xmlNodePtr *request, xmlChar **refnumber) {
6041 isds_error err = IE_SUCCESS;
6042 xmlDocPtr response = NULL;
6045 if (!context) return IE_INVALID_CONTEXT;
6046 if (!service_name || *service_name == '\0' || !request || !*request)
6047 return IE_INVAL;
6049 /* Send request and check response*/
6050 err = send_destroy_request_check_response(context,
6051 service, service_name, request, &response, refnumber, NULL);
6053 xmlFreeDoc(response);
6055 if (*request) {
6056 xmlFreeNode(*request);
6057 *request = NULL;
6060 if (!err) {
6061 char *service_name_locale = _isds_utf82locale((char *) service_name);
6062 isds_log(ILF_ISDS, ILL_DEBUG,
6063 _("%s request processed by server successfully.\n"),
6064 service_name_locale);
6065 free(service_name_locale);
6068 return err;
6072 /* Insert isds_credentials_delivery structure into XML request if not NULL
6073 * @context is session context
6074 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6075 * credentials delivery. The email field is passed.
6076 * @parent is XML element where to insert */
6077 static isds_error insert_credentials_delivery(struct isds_ctx *context,
6078 const struct isds_credentials_delivery *credentials_delivery,
6079 xmlNodePtr parent) {
6080 isds_error err = IE_SUCCESS;
6081 xmlNodePtr node;
6083 if (!context) return IE_INVALID_CONTEXT;
6084 if (!parent) return IE_INVAL;
6086 if (credentials_delivery) {
6087 /* Following elements are valid only for services:
6088 * NewAccessData, AddDataBoxUser, CreateDataBox */
6089 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
6090 INSERT_STRING(parent, "email", credentials_delivery->email);
6093 leave:
6094 return err;
6098 /* Extract credentials delivery from ISDS response.
6099 * @context is session context
6100 * @credentials_delivery is pointer to valid structure to fill in returned
6101 * user's password (and new log-in name). If NULL, do not extract the data.
6102 * @response is pointer to XML document with ISDS response
6103 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6104 * @return IE_SUCCESS even if new user name has not been found because it's not
6105 * clear whether it's returned always. */
6106 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6107 struct isds_credentials_delivery *credentials_delivery,
6108 xmlDocPtr response, const char *request_name) {
6109 isds_error err = IE_SUCCESS;
6110 xmlXPathContextPtr xpath_ctx = NULL;
6111 xmlXPathObjectPtr result = NULL;
6112 char *xpath_query = NULL;
6114 if (!context) return IE_INVALID_CONTEXT;
6115 if (credentials_delivery) {
6116 zfree(credentials_delivery->token);
6117 zfree(credentials_delivery->new_user_name);
6119 if (!response || !request_name || !*request_name) return IE_INVAL;
6122 /* Extract optional token */
6123 if (credentials_delivery) {
6124 xpath_ctx = xmlXPathNewContext(response);
6125 if (!xpath_ctx) {
6126 err = IE_ERROR;
6127 goto leave;
6129 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6130 err = IE_ERROR;
6131 goto leave;
6134 /* Verify root element */
6135 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6136 request_name)) {
6137 err = IE_NOMEM;
6138 goto leave;
6140 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6141 if (!result) {
6142 err = IE_ERROR;
6143 goto leave;
6145 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6146 char *request_name_locale = _isds_utf82locale(request_name);
6147 isds_log(ILF_ISDS, ILL_WARNING,
6148 _("Wrong element in ISDS response for %s request "
6149 "while extracting credentials delivery details\n"),
6150 request_name_locale);
6151 free(request_name_locale);
6152 err = IE_ERROR;
6153 goto leave;
6155 xpath_ctx->node = result->nodesetval->nodeTab[0];
6158 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6159 * optional. */
6160 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6162 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6163 if (!credentials_delivery->token) {
6164 char *request_name_locale = _isds_utf82locale(request_name);
6165 isds_log(ILF_ISDS, ILL_ERR,
6166 _("ISDS did not return token on %s request "
6167 "even if requested\n"), request_name_locale);
6168 free(request_name_locale);
6169 err = IE_ERROR;
6173 leave:
6174 free(xpath_query);
6175 xmlXPathFreeObject(result);
6176 xmlXPathFreeContext(xpath_ctx);
6178 return err;
6182 /* Build XSD:tCreateDBInput request type for box creating.
6183 * @context is session context
6184 * @request outputs built XML tree
6185 * @service_name is request name of SERVICE_DB_MANIPULATION service
6186 * @box is box description to create including single primary user (in case of
6187 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6188 * ignored.
6189 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6190 * box, or contact address of PFO box owner)
6191 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6192 * @upper_box_id is optional ID of supper box if currently created box is
6193 * subordinated.
6194 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6195 * don't care.
6196 * @credentials_delivery is valid pointer if ISDS should return token that box
6197 * owner can use to obtain his new credentials in on-line way. Then valid email
6198 * member value should be supplied.
6199 * @approval is optional external approval of box manipulation */
6200 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6201 xmlNodePtr *request, const xmlChar *service_name,
6202 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6203 const xmlChar *former_names, const xmlChar *upper_box_id,
6204 const xmlChar *ceo_label,
6205 const struct isds_credentials_delivery *credentials_delivery,
6206 const struct isds_approval *approval) {
6207 isds_error err = IE_SUCCESS;
6208 xmlNsPtr isds_ns = NULL;
6209 xmlNodePtr node, dbPrimaryUsers;
6210 xmlChar *string = NULL;
6211 const struct isds_list *item;
6214 if (!context) return IE_INVALID_CONTEXT;
6215 if (!request || !service_name || service_name[0] == '\0' || !box)
6216 return IE_INVAL;
6219 /* Build CreateDataBox-similar request */
6220 *request = xmlNewNode(NULL, service_name);
6221 if (!*request) {
6222 char *service_name_locale = _isds_utf82locale((char*) service_name);
6223 isds_printf_message(context, _("Could build %s request"),
6224 service_name_locale);
6225 free(service_name_locale);
6226 return IE_ERROR;
6228 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6229 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6230 if (!isds_ns) {
6231 isds_log_message(context, _("Could not create ISDS1 name space"));
6232 xmlFreeNode(*request);
6233 return IE_ERROR;
6235 } else {
6236 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6237 if (!isds_ns) {
6238 isds_log_message(context, _("Could not create ISDS name space"));
6239 xmlFreeNode(*request);
6240 return IE_ERROR;
6243 xmlSetNs(*request, isds_ns);
6245 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6246 err = insert_DbOwnerInfo(context, box, 0, node);
6247 if (err) goto leave;
6249 /* Insert users */
6250 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6251 * verbose documentation allows none dbUserInfo */
6252 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6253 for (item = users; item; item = item->next) {
6254 if (item->data) {
6255 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6256 err = insert_DbUserInfo(context,
6257 (struct isds_DbUserInfo *) item->data, 1, node);
6258 if (err) goto leave;
6262 INSERT_STRING(*request, "dbFormerNames", former_names);
6263 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6264 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6266 err = insert_credentials_delivery(context, credentials_delivery, *request);
6267 if (err) goto leave;
6269 err = insert_GExtApproval(context, approval, *request);
6270 if (err) goto leave;
6272 leave:
6273 if (err) {
6274 xmlFreeNode(*request);
6275 *request = NULL;
6277 free(string);
6278 return err;
6280 #endif /* HAVE_LIBCURL */
6283 /* Create new box.
6284 * @context is session context
6285 * @box is box description to create including single primary user (in case of
6286 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6287 * ignored. It outputs box ID assigned by ISDS in dbID element.
6288 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6289 * box, or contact address of PFO box owner)
6290 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6291 * @upper_box_id is optional ID of supper box if currently created box is
6292 * subordinated.
6293 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6294 * @credentials_delivery is NULL if new password should be delivered off-line
6295 * to box owner. It is valid pointer if owner should obtain new password on-line
6296 * on dedicated web server. Then input @credentials_delivery.email value is
6297 * his e-mail address he must provide to dedicated web server together
6298 * with output reallocated @credentials_delivery.token member. Output
6299 * member @credentials_delivery.new_user_name is unused up on this call.
6300 * @approval is optional external approval of box manipulation
6301 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6302 * NULL, if you don't care.*/
6303 isds_error isds_add_box(struct isds_ctx *context,
6304 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6305 const char *former_names, const char *upper_box_id,
6306 const char *ceo_label,
6307 struct isds_credentials_delivery *credentials_delivery,
6308 const struct isds_approval *approval, char **refnumber) {
6309 isds_error err = IE_SUCCESS;
6310 #if HAVE_LIBCURL
6311 xmlNodePtr request = NULL;
6312 xmlDocPtr response = NULL;
6313 xmlXPathContextPtr xpath_ctx = NULL;
6314 xmlXPathObjectPtr result = NULL;
6315 #endif
6318 if (!context) return IE_INVALID_CONTEXT;
6319 zfree(context->long_message);
6320 if (credentials_delivery) {
6321 zfree(credentials_delivery->token);
6322 zfree(credentials_delivery->new_user_name);
6324 if (!box) return IE_INVAL;
6326 #if HAVE_LIBCURL
6327 /* Scratch box ID */
6328 zfree(box->dbID);
6330 /* Build CreateDataBox request */
6331 err = build_CreateDBInput_request(context,
6332 &request, BAD_CAST "CreateDataBox",
6333 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6334 (xmlChar *) ceo_label, credentials_delivery, approval);
6335 if (err) goto leave;
6337 /* Send it to server and process response */
6338 err = send_destroy_request_check_response(context,
6339 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6340 &response, (xmlChar **) refnumber, NULL);
6342 /* Extract box ID */
6343 xpath_ctx = xmlXPathNewContext(response);
6344 if (!xpath_ctx) {
6345 err = IE_ERROR;
6346 goto leave;
6348 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6349 err = IE_ERROR;
6350 goto leave;
6352 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6354 /* Extract optional token */
6355 err = extract_credentials_delivery(context, credentials_delivery, response,
6356 "CreateDataBox");
6358 leave:
6359 xmlXPathFreeObject(result);
6360 xmlXPathFreeContext(xpath_ctx);
6361 xmlFreeDoc(response);
6362 xmlFreeNode(request);
6364 if (!err) {
6365 isds_log(ILF_ISDS, ILL_DEBUG,
6366 _("CreateDataBox request processed by server successfully.\n"));
6368 #else /* not HAVE_LIBCURL */
6369 err = IE_NOTSUP;
6370 #endif
6372 return err;
6376 /* Notify ISDS about new PFO entity.
6377 * This function has no real effect.
6378 * @context is session context
6379 * @box is PFO description including single primary user. aifoIsds,
6380 * address->adCode, address->adDistrict members are ignored.
6381 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6382 * @former_names is optional undocumented string. Pass NULL if you don't care.
6383 * @upper_box_id is optional ID of supper box if currently created box is
6384 * subordinated.
6385 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6386 * @approval is optional external approval of box manipulation
6387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6388 * NULL, if you don't care.*/
6389 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6390 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6391 const char *former_names, const char *upper_box_id,
6392 const char *ceo_label, const struct isds_approval *approval,
6393 char **refnumber) {
6394 isds_error err = IE_SUCCESS;
6395 #if HAVE_LIBCURL
6396 xmlNodePtr request = NULL;
6397 #endif
6399 if (!context) return IE_INVALID_CONTEXT;
6400 zfree(context->long_message);
6401 if (!box) return IE_INVAL;
6403 #if HAVE_LIBCURL
6404 /* Build CreateDataBoxPFOInfo request */
6405 err = build_CreateDBInput_request(context,
6406 &request, BAD_CAST "CreateDataBoxPFOInfo",
6407 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6408 (xmlChar *) ceo_label, NULL, approval);
6409 if (err) goto leave;
6411 /* Send it to server and process response */
6412 err = send_request_check_drop_response(context,
6413 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6414 (xmlChar **) refnumber);
6415 /* XXX: XML Schema names output dbID element but textual documentation
6416 * states no box identifier is returned. */
6417 leave:
6418 xmlFreeNode(request);
6419 #else /* not HAVE_LIBCURL */
6420 err = IE_NOTSUP;
6421 #endif
6422 return err;
6426 /* Common implementation for removing given box.
6427 * @context is session context
6428 * @service_name is UTF-8 encoded name fo ISDS service
6429 * @box is box description to delete. aifoIsds, address->adCode,
6430 * address->adDistrict members are ignored.
6431 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6432 * carry sane value. If NULL, do not inject this information into request.
6433 * @approval is optional external approval of box manipulation
6434 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6435 * NULL, if you don't care.*/
6436 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6437 const xmlChar *service_name,
6438 const struct isds_DbOwnerInfo *box, const struct tm *since,
6439 const struct isds_approval *approval, char **refnumber) {
6440 isds_error err = IE_SUCCESS;
6441 #if HAVE_LIBCURL
6442 xmlNsPtr isds_ns = NULL;
6443 xmlNodePtr request = NULL;
6444 xmlNodePtr node;
6445 xmlChar *string = NULL;
6446 #endif
6449 if (!context) return IE_INVALID_CONTEXT;
6450 zfree(context->long_message);
6451 if (!service_name || !*service_name || !box) return IE_INVAL;
6454 #if HAVE_LIBCURL
6455 /* Build DeleteDataBox(Promptly) request */
6456 request = xmlNewNode(NULL, service_name);
6457 if (!request) {
6458 char *service_name_locale = _isds_utf82locale((char*)service_name);
6459 isds_printf_message(context,
6460 _("Could build %s request"), service_name_locale);
6461 free(service_name_locale);
6462 return IE_ERROR;
6464 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6465 if(!isds_ns) {
6466 isds_log_message(context, _("Could not create ISDS name space"));
6467 xmlFreeNode(request);
6468 return IE_ERROR;
6470 xmlSetNs(request, isds_ns);
6472 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6473 err = insert_DbOwnerInfo(context, box, 0, node);
6474 if (err) goto leave;
6476 if (since) {
6477 err = tm2datestring(since, &string);
6478 if (err) {
6479 isds_log_message(context,
6480 _("Could not convert `since' argument to ISO date string"));
6481 goto leave;
6483 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6484 zfree(string);
6487 err = insert_GExtApproval(context, approval, request);
6488 if (err) goto leave;
6491 /* Send it to server and process response */
6492 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6493 service_name, &request, (xmlChar **) refnumber);
6495 leave:
6496 xmlFreeNode(request);
6497 free(string);
6498 #else /* not HAVE_LIBCURL */
6499 err = IE_NOTSUP;
6500 #endif
6501 return err;
6505 /* Remove given box permanently.
6506 * @context is session context
6507 * @box is box description to delete. aifoIsds, address->adCode,
6508 * address->adDistrict members are ignored.
6509 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6510 * carry sane value.
6511 * @approval is optional external approval of box manipulation
6512 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6513 * NULL, if you don't care.*/
6514 isds_error isds_delete_box(struct isds_ctx *context,
6515 const struct isds_DbOwnerInfo *box, const struct tm *since,
6516 const struct isds_approval *approval, char **refnumber) {
6517 if (!context) return IE_INVALID_CONTEXT;
6518 zfree(context->long_message);
6519 if (!box || !since) return IE_INVAL;
6521 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6522 box, since, approval, refnumber);
6526 /* Undocumented function.
6527 * @context is session context
6528 * @box is box description to delete. aifoIsds, address->adCode,
6529 * address->adDistrict members are ignored.
6530 * @approval is optional external approval of box manipulation
6531 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6532 * NULL, if you don't care.*/
6533 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6534 const struct isds_DbOwnerInfo *box,
6535 const struct isds_approval *approval, char **refnumber) {
6536 if (!context) return IE_INVALID_CONTEXT;
6537 zfree(context->long_message);
6538 if (!box) return IE_INVAL;
6540 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6541 box, NULL, approval, refnumber);
6545 /* Update data about given box.
6546 * @context is session context
6547 * @old_box current box description. aifoIsds, address->adCode,
6548 * address->adDistrict members are ignored.
6549 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6550 * address->adDistrict members are ignored.
6551 * @approval is optional external approval of box manipulation
6552 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6553 * NULL, if you don't care.*/
6554 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6555 const struct isds_DbOwnerInfo *old_box,
6556 const struct isds_DbOwnerInfo *new_box,
6557 const struct isds_approval *approval, char **refnumber) {
6558 isds_error err = IE_SUCCESS;
6559 #if HAVE_LIBCURL
6560 xmlNsPtr isds_ns = NULL;
6561 xmlNodePtr request = NULL;
6562 xmlNodePtr node;
6563 #endif
6566 if (!context) return IE_INVALID_CONTEXT;
6567 zfree(context->long_message);
6568 if (!old_box || !new_box) return IE_INVAL;
6571 #if HAVE_LIBCURL
6572 /* Build UpdateDataBoxDescr request */
6573 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6574 if (!request) {
6575 isds_log_message(context,
6576 _("Could build UpdateDataBoxDescr request"));
6577 return IE_ERROR;
6579 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6580 if(!isds_ns) {
6581 isds_log_message(context, _("Could not create ISDS name space"));
6582 xmlFreeNode(request);
6583 return IE_ERROR;
6585 xmlSetNs(request, isds_ns);
6587 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6588 err = insert_DbOwnerInfo(context, old_box, 0, node);
6589 if (err) goto leave;
6591 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6592 err = insert_DbOwnerInfo(context, new_box, 0, node);
6593 if (err) goto leave;
6595 err = insert_GExtApproval(context, approval, request);
6596 if (err) goto leave;
6599 /* Send it to server and process response */
6600 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6601 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6603 leave:
6604 xmlFreeNode(request);
6605 #else /* not HAVE_LIBCURL */
6606 err = IE_NOTSUP;
6607 #endif
6609 return err;
6613 #if HAVE_LIBCURL
6614 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6615 * code
6616 * @context is session context
6617 * @service is SOAP service
6618 * @service_name is name of request in @service
6619 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6620 * @box_id is box ID of interest
6621 * @approval is optional external approval of box manipulation
6622 * @response is server SOAP body response as XML document
6623 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6624 * NULL, if you don't care.
6625 * @return error coded from lower layer, context message will be set up
6626 * appropriately. */
6627 static isds_error build_send_dbid_request_check_response(
6628 struct isds_ctx *context, const isds_service service,
6629 const xmlChar *service_name, const xmlChar *box_id_element,
6630 const xmlChar *box_id, const struct isds_approval *approval,
6631 xmlDocPtr *response, xmlChar **refnumber) {
6633 isds_error err = IE_SUCCESS;
6634 char *service_name_locale = NULL, *box_id_locale = NULL;
6635 xmlNodePtr request = NULL, node;
6636 xmlNsPtr isds_ns = NULL;
6638 if (!context) return IE_INVALID_CONTEXT;
6639 if (!service_name || !box_id) return IE_INVAL;
6640 if (!response) return IE_INVAL;
6642 /* Free output argument */
6643 xmlFreeDoc(*response); *response = NULL;
6645 /* Prepare strings */
6646 service_name_locale = _isds_utf82locale((char*)service_name);
6647 if (!service_name_locale) {
6648 err = IE_NOMEM;
6649 goto leave;
6651 box_id_locale = _isds_utf82locale((char*)box_id);
6652 if (!box_id_locale) {
6653 err = IE_NOMEM;
6654 goto leave;
6657 /* Build request */
6658 request = xmlNewNode(NULL, service_name);
6659 if (!request) {
6660 isds_printf_message(context,
6661 _("Could not build %s request for %s box"), service_name_locale,
6662 box_id_locale);
6663 err = IE_ERROR;
6664 goto leave;
6666 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6667 if(!isds_ns) {
6668 isds_log_message(context, _("Could not create ISDS name space"));
6669 err = IE_ERROR;
6670 goto leave;
6672 xmlSetNs(request, isds_ns);
6674 /* Add XSD:tIdDbInput children */
6675 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6676 INSERT_STRING(request, box_id_element, box_id);
6677 err = insert_GExtApproval(context, approval, request);
6678 if (err) goto leave;
6680 /* Send request and check response*/
6681 err = send_destroy_request_check_response(context,
6682 service, service_name, &request, response, refnumber, NULL);
6684 leave:
6685 free(service_name_locale);
6686 free(box_id_locale);
6687 xmlFreeNode(request);
6688 return err;
6690 #endif /* HAVE_LIBCURL */
6693 /* Get data about all users assigned to given box.
6694 * @context is session context
6695 * @box_id is box ID
6696 * @users is automatically reallocated list of struct isds_DbUserInfo */
6697 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6698 struct isds_list **users) {
6699 isds_error err = IE_SUCCESS;
6700 #if HAVE_LIBCURL
6701 xmlDocPtr response = NULL;
6702 xmlXPathContextPtr xpath_ctx = NULL;
6703 xmlXPathObjectPtr result = NULL;
6704 int i;
6705 struct isds_list *item, *prev_item = NULL;
6706 #endif
6708 if (!context) return IE_INVALID_CONTEXT;
6709 zfree(context->long_message);
6710 if (!users || !box_id) return IE_INVAL;
6711 isds_list_free(users);
6714 #if HAVE_LIBCURL
6715 /* Do request and check for success */
6716 err = build_send_dbid_request_check_response(context,
6717 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6718 BAD_CAST box_id, NULL, &response, NULL);
6719 if (err) goto leave;
6722 /* Extract data */
6723 /* Prepare structure */
6724 xpath_ctx = xmlXPathNewContext(response);
6725 if (!xpath_ctx) {
6726 err = IE_ERROR;
6727 goto leave;
6729 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6730 err = IE_ERROR;
6731 goto leave;
6734 /* Set context node */
6735 result = xmlXPathEvalExpression(BAD_CAST
6736 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6737 xpath_ctx);
6738 if (!result) {
6739 err = IE_ERROR;
6740 goto leave;
6742 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6743 /* Iterate over all users */
6744 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6746 /* Prepare structure */
6747 item = calloc(1, sizeof(*item));
6748 if (!item) {
6749 err = IE_NOMEM;
6750 goto leave;
6752 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6753 if (i == 0) *users = item;
6754 else prev_item->next = item;
6755 prev_item = item;
6757 /* Extract it */
6758 xpath_ctx->node = result->nodesetval->nodeTab[i];
6759 err = extract_DbUserInfo(context,
6760 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6761 if (err) goto leave;
6765 leave:
6766 if (err) {
6767 isds_list_free(users);
6770 xmlXPathFreeObject(result);
6771 xmlXPathFreeContext(xpath_ctx);
6772 xmlFreeDoc(response);
6774 if (!err)
6775 isds_log(ILF_ISDS, ILL_DEBUG,
6776 _("GetDataBoxUsers request processed by server "
6777 "successfully.\n"));
6778 #else /* not HAVE_LIBCURL */
6779 err = IE_NOTSUP;
6780 #endif
6782 return err;
6786 /* Update data about user assigned to given box.
6787 * @context is session context
6788 * @box is box identification. aifoIsds, address->adCode,
6789 * address->adDistrict members are ignored.
6790 * @old_user identifies user to update, aifo_ticket member is ignored
6791 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6792 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6793 * NULL, if you don't care.*/
6794 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6795 const struct isds_DbOwnerInfo *box,
6796 const struct isds_DbUserInfo *old_user,
6797 const struct isds_DbUserInfo *new_user,
6798 char **refnumber) {
6799 isds_error err = IE_SUCCESS;
6800 #if HAVE_LIBCURL
6801 xmlNsPtr isds_ns = NULL;
6802 xmlNodePtr request = NULL;
6803 xmlNodePtr node;
6804 #endif
6807 if (!context) return IE_INVALID_CONTEXT;
6808 zfree(context->long_message);
6809 if (!box || !old_user || !new_user) return IE_INVAL;
6812 #if HAVE_LIBCURL
6813 /* Build UpdateDataBoxUser request */
6814 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6815 if (!request) {
6816 isds_log_message(context,
6817 _("Could build UpdateDataBoxUser request"));
6818 return IE_ERROR;
6820 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6821 if(!isds_ns) {
6822 isds_log_message(context, _("Could not create ISDS name space"));
6823 xmlFreeNode(request);
6824 return IE_ERROR;
6826 xmlSetNs(request, isds_ns);
6828 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6829 err = insert_DbOwnerInfo(context, box, 0, node);
6830 if (err) goto leave;
6832 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6833 err = insert_DbUserInfo(context, old_user, 0, node);
6834 if (err) goto leave;
6836 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6837 err = insert_DbUserInfo(context, new_user, 0, node);
6838 if (err) goto leave;
6840 /* Send it to server and process response */
6841 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6842 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6844 leave:
6845 xmlFreeNode(request);
6846 #else /* not HAVE_LIBCURL */
6847 err = IE_NOTSUP;
6848 #endif
6850 return err;
6854 /* Undocumented function.
6855 * @context is session context
6856 * @box_id is UTF-8 encoded box identifier
6857 * @token is UTF-8 encoded temporary password
6858 * @user_id outputs UTF-8 encoded reallocated user identifier
6859 * @password outpus UTF-8 encoded reallocated user password
6860 * Output arguments will be nulled in case of error */
6861 isds_error isds_activate(struct isds_ctx *context,
6862 const char *box_id, const char *token,
6863 char **user_id, char **password) {
6864 isds_error err = IE_SUCCESS;
6865 #if HAVE_LIBCURL
6866 xmlNsPtr isds_ns = NULL;
6867 xmlNodePtr request = NULL, node;
6868 xmlDocPtr response = NULL;
6869 xmlXPathContextPtr xpath_ctx = NULL;
6870 xmlXPathObjectPtr result = NULL;
6871 #endif
6874 if (!context) return IE_INVALID_CONTEXT;
6875 zfree(context->long_message);
6877 if (user_id) zfree(*user_id);
6878 if (password) zfree(*password);
6880 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6883 #if HAVE_LIBCURL
6884 /* Build Activate request */
6885 request = xmlNewNode(NULL, BAD_CAST "Activate");
6886 if (!request) {
6887 isds_log_message(context, _("Could build Activate request"));
6888 return IE_ERROR;
6890 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6891 if(!isds_ns) {
6892 isds_log_message(context, _("Could not create ISDS name space"));
6893 xmlFreeNode(request);
6894 return IE_ERROR;
6896 xmlSetNs(request, isds_ns);
6898 INSERT_STRING(request, "dbAccessDataId", token);
6899 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6900 INSERT_STRING(request, "dbID", box_id);
6903 /* Send request and check response*/
6904 err = send_destroy_request_check_response(context,
6905 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6906 &response, NULL, NULL);
6907 if (err) goto leave;
6910 /* Extract data */
6911 xpath_ctx = xmlXPathNewContext(response);
6912 if (!xpath_ctx) {
6913 err = IE_ERROR;
6914 goto leave;
6916 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6917 err = IE_ERROR;
6918 goto leave;
6920 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6921 xpath_ctx);
6922 if (!result) {
6923 err = IE_ERROR;
6924 goto leave;
6926 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6927 isds_log_message(context, _("Missing ActivateResponse element"));
6928 err = IE_ISDS;
6929 goto leave;
6931 if (result->nodesetval->nodeNr > 1) {
6932 isds_log_message(context, _("Multiple ActivateResponse element"));
6933 err = IE_ISDS;
6934 goto leave;
6936 xpath_ctx->node = result->nodesetval->nodeTab[0];
6937 xmlXPathFreeObject(result); result = NULL;
6939 EXTRACT_STRING("isds:userId", *user_id);
6940 if (!*user_id)
6941 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6942 "but did not return `userId' element.\n"));
6944 EXTRACT_STRING("isds:password", *password);
6945 if (!*password)
6946 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6947 "but did not return `password' element.\n"));
6949 leave:
6950 xmlXPathFreeObject(result);
6951 xmlXPathFreeContext(xpath_ctx);
6952 xmlFreeDoc(response);
6953 xmlFreeNode(request);
6955 if (!err)
6956 isds_log(ILF_ISDS, ILL_DEBUG,
6957 _("Activate request processed by server successfully.\n"));
6958 #else /* not HAVE_LIBCURL */
6959 err = IE_NOTSUP;
6960 #endif
6962 return err;
6966 /* Reset credentials of user assigned to given box.
6967 * @context is session context
6968 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6969 * members are ignored.
6970 * @user identifies user to reset password, aifo_ticket member is ignored
6971 * @fee_paid is true if fee has been paid, false otherwise
6972 * @approval is optional external approval of box manipulation
6973 * @credentials_delivery is NULL if new password should be delivered off-line
6974 * to the user. It is valid pointer if user should obtain new password on-line
6975 * on dedicated web server. Then input @credentials_delivery.email value is
6976 * user's e-mail address user must provide to dedicated web server together
6977 * with @credentials_delivery.token. The output reallocated token user needs
6978 * to use to authorize on the web server to view his new password. Output
6979 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6980 * ISDS changed up on this call. (No reason why server could change the name
6981 * is known now.)
6982 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6983 * NULL, if you don't care.*/
6984 isds_error isds_reset_password(struct isds_ctx *context,
6985 const struct isds_DbOwnerInfo *box,
6986 const struct isds_DbUserInfo *user,
6987 const _Bool fee_paid, const struct isds_approval *approval,
6988 struct isds_credentials_delivery *credentials_delivery,
6989 char **refnumber) {
6990 isds_error err = IE_SUCCESS;
6991 #if HAVE_LIBCURL
6992 xmlNsPtr isds_ns = NULL;
6993 xmlNodePtr request = NULL, node;
6994 xmlDocPtr response = NULL;
6995 #endif
6998 if (!context) return IE_INVALID_CONTEXT;
6999 zfree(context->long_message);
7001 if (credentials_delivery) {
7002 zfree(credentials_delivery->token);
7003 zfree(credentials_delivery->new_user_name);
7005 if (!box || !user) return IE_INVAL;
7008 #if HAVE_LIBCURL
7009 /* Build NewAccessData request */
7010 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
7011 if (!request) {
7012 isds_log_message(context,
7013 _("Could build NewAccessData request"));
7014 return IE_ERROR;
7016 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7017 if(!isds_ns) {
7018 isds_log_message(context, _("Could not create ISDS name space"));
7019 xmlFreeNode(request);
7020 return IE_ERROR;
7022 xmlSetNs(request, isds_ns);
7024 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7025 err = insert_DbOwnerInfo(context, box, 0, node);
7026 if (err) goto leave;
7028 INSERT_ELEMENT(node, request, "dbUserInfo");
7029 err = insert_DbUserInfo(context, user, 0, node);
7030 if (err) goto leave;
7032 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
7034 err = insert_credentials_delivery(context, credentials_delivery, request);
7035 if (err) goto leave;
7037 err = insert_GExtApproval(context, approval, request);
7038 if (err) goto leave;
7040 /* Send request and check response*/
7041 err = send_destroy_request_check_response(context,
7042 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
7043 &response, (xmlChar **) refnumber, NULL);
7044 if (err) goto leave;
7047 /* Extract optional token */
7048 err = extract_credentials_delivery(context, credentials_delivery,
7049 response, "NewAccessData");
7051 leave:
7052 xmlFreeDoc(response);
7053 xmlFreeNode(request);
7055 if (!err)
7056 isds_log(ILF_ISDS, ILL_DEBUG,
7057 _("NewAccessData request processed by server "
7058 "successfully.\n"));
7059 #else /* not HAVE_LIBCURL */
7060 err = IE_NOTSUP;
7061 #endif
7063 return err;
7067 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7068 * code, destroy response and log success.
7069 * @context is ISDS session context.
7070 * @service_name is name of SERVICE_DB_MANIPULATION service
7071 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7072 * members are ignored.
7073 * @user identifies user to remove
7074 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7075 * @credentials_delivery is NULL if new user's password should be delivered
7076 * off-line to the user. It is valid pointer if user should obtain new
7077 * password on-line on dedicated web server. Then input
7078 * @credentials_delivery.email value is user's e-mail address user must
7079 * provide to dedicated web server together with @credentials_delivery.token.
7080 * The output reallocated token user needs to use to authorize on the web
7081 * server to view his new password. Output reallocated
7082 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7083 * assingned or changed up on this call.
7084 * @approval is optional external approval of box manipulation
7085 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7086 * NULL, if you don't care. */
7087 static isds_error build_send_manipulationboxuser_request_check_drop_response(
7088 struct isds_ctx *context, const xmlChar *service_name,
7089 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7090 _Bool honor_aifo_ticket,
7091 struct isds_credentials_delivery *credentials_delivery,
7092 const struct isds_approval *approval, xmlChar **refnumber) {
7093 isds_error err = IE_SUCCESS;
7094 #if HAVE_LIBCURL
7095 xmlNsPtr isds_ns = NULL;
7096 xmlNodePtr request = NULL, node;
7097 xmlDocPtr response = NULL;
7098 #endif
7101 if (!context) return IE_INVALID_CONTEXT;
7102 zfree(context->long_message);
7103 if (credentials_delivery) {
7104 zfree(credentials_delivery->token);
7105 zfree(credentials_delivery->new_user_name);
7107 if (!service_name || service_name[0] == '\0' || !box || !user)
7108 return IE_INVAL;
7111 #if HAVE_LIBCURL
7112 /* Build NewAccessData or similar request */
7113 request = xmlNewNode(NULL, service_name);
7114 if (!request) {
7115 char *service_name_locale = _isds_utf82locale((char *) service_name);
7116 isds_printf_message(context, _("Could not build %s request"),
7117 service_name_locale);
7118 free(service_name_locale);
7119 return IE_ERROR;
7121 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7122 if(!isds_ns) {
7123 isds_log_message(context, _("Could not create ISDS name space"));
7124 xmlFreeNode(request);
7125 return IE_ERROR;
7127 xmlSetNs(request, isds_ns);
7129 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7130 err = insert_DbOwnerInfo(context, box, 0, node);
7131 if (err) goto leave;
7133 INSERT_ELEMENT(node, request, "dbUserInfo");
7134 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7135 if (err) goto leave;
7137 err = insert_credentials_delivery(context, credentials_delivery, request);
7138 if (err) goto leave;
7140 err = insert_GExtApproval(context, approval, request);
7141 if (err) goto leave;
7144 /* Send request and check response*/
7145 err = send_destroy_request_check_response(context,
7146 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7147 refnumber, NULL);
7149 xmlFreeNode(request);
7150 request = NULL;
7152 /* Pick up credentials_delivery if requested */
7153 err = extract_credentials_delivery(context, credentials_delivery, response,
7154 (char *)service_name);
7156 leave:
7157 xmlFreeDoc(response);
7158 if (request) xmlFreeNode(request);
7160 if (!err) {
7161 char *service_name_locale = _isds_utf82locale((char *) service_name);
7162 isds_log(ILF_ISDS, ILL_DEBUG,
7163 _("%s request processed by server successfully.\n"),
7164 service_name_locale);
7165 free(service_name_locale);
7167 #else /* not HAVE_LIBCURL */
7168 err = IE_NOTSUP;
7169 #endif
7171 return err;
7175 /* Assign new user to given box.
7176 * @context is session context
7177 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7178 * members are ignored.
7179 * @user defines new user to add
7180 * @credentials_delivery is NULL if new user's password should be delivered
7181 * off-line to the user. It is valid pointer if user should obtain new
7182 * password on-line on dedicated web server. Then input
7183 * @credentials_delivery.email value is user's e-mail address user must
7184 * provide to dedicated web server together with @credentials_delivery.token.
7185 * The output reallocated token user needs to use to authorize on the web
7186 * server to view his new password. Output reallocated
7187 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7188 * assingned up on this call.
7189 * @approval is optional external approval of box manipulation
7190 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7191 * NULL, if you don't care.*/
7192 isds_error isds_add_user(struct isds_ctx *context,
7193 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7194 struct isds_credentials_delivery *credentials_delivery,
7195 const struct isds_approval *approval, char **refnumber) {
7196 return build_send_manipulationboxuser_request_check_drop_response(context,
7197 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7198 approval, (xmlChar **) refnumber);
7202 /* Remove user assigned to given box.
7203 * @context is session context
7204 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7205 * members are ignored.
7206 * @user identifies user to remove, aifo_ticket member is ignored
7207 * @approval is optional external approval of box manipulation
7208 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7209 * NULL, if you don't care.*/
7210 isds_error isds_delete_user(struct isds_ctx *context,
7211 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7212 const struct isds_approval *approval, char **refnumber) {
7213 return build_send_manipulationboxuser_request_check_drop_response(context,
7214 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7215 (xmlChar **) refnumber);
7219 /* Get list of boxes in ZIP archive.
7220 * @context is session context
7221 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7222 * System recognizes following values currently: ALL (all boxes), UPG
7223 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7224 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7225 * commercial messages). This argument is a string because specification
7226 * states new values can appear in the future. Not all list types are
7227 * available to all users.
7228 * @buffer is automatically reallocated memory to store the list of boxes. The
7229 * list is zipped CSV file.
7230 * @buffer_length is size of @buffer data in bytes.
7231 * In case of error @buffer will be freed and @buffer_length will be
7232 * undefined.*/
7233 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7234 const char *list_identifier, void **buffer, size_t *buffer_length) {
7235 isds_error err = IE_SUCCESS;
7236 #if HAVE_LIBCURL
7237 xmlNsPtr isds_ns = NULL;
7238 xmlNodePtr request = NULL, node;
7239 xmlDocPtr response = NULL;
7240 xmlXPathContextPtr xpath_ctx = NULL;
7241 xmlXPathObjectPtr result = NULL;
7242 char *string = NULL;
7243 #endif
7246 if (!context) return IE_INVALID_CONTEXT;
7247 zfree(context->long_message);
7248 if (buffer) zfree(*buffer);
7249 if (!buffer || !buffer_length) return IE_INVAL;
7252 #if HAVE_LIBCURL
7253 /* Check if connection is established
7254 * TODO: This check should be done downstairs. */
7255 if (!context->curl) return IE_CONNECTION_CLOSED;
7258 /* Build AuthenticateMessage request */
7259 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7260 if (!request) {
7261 isds_log_message(context,
7262 _("Could not build GetDataBoxList request"));
7263 return IE_ERROR;
7265 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7266 if(!isds_ns) {
7267 isds_log_message(context, _("Could not create ISDS name space"));
7268 xmlFreeNode(request);
7269 return IE_ERROR;
7271 xmlSetNs(request, isds_ns);
7272 INSERT_STRING(request, "dblType", list_identifier);
7274 /* Send request to server and process response */
7275 err = send_destroy_request_check_response(context,
7276 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7277 &response, NULL, NULL);
7278 if (err) goto leave;
7281 /* Extract Base-64 encoded ZIP file */
7282 xpath_ctx = xmlXPathNewContext(response);
7283 if (!xpath_ctx) {
7284 err = IE_ERROR;
7285 goto leave;
7287 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7288 err = IE_ERROR;
7289 goto leave;
7291 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7293 /* Decode non-empty archive */
7294 if (string && string[0] != '\0') {
7295 *buffer_length = _isds_b64decode(string, buffer);
7296 if (*buffer_length == (size_t) -1) {
7297 isds_printf_message(context,
7298 _("Error while Base64-decoding box list archive"));
7299 err = IE_ERROR;
7300 goto leave;
7305 leave:
7306 free(string);
7307 xmlXPathFreeObject(result);
7308 xmlXPathFreeContext(xpath_ctx);
7309 xmlFreeDoc(response);
7310 xmlFreeNode(request);
7312 if (!err) {
7313 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7314 "processed by server successfully.\n"));
7316 #else /* not HAVE_LIBCURL */
7317 err = IE_NOTSUP;
7318 #endif
7320 return err;
7324 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7325 * send it, check for error code, extract list of results, destroy response
7326 * and log success.
7327 * @context is ISDS session context.
7328 * @service_name is name of SERVICE_DB_SEARCH service
7329 * @pfo_service is false if tDbOwnerInfo request should be built from
7330 * @criteria and corresponding result extracted. It is true if
7331 * tDbPersonalOwnerInfoRequest request should be built. The request and
7332 * response differ subset of significant isds_DbOwnerInfo structure members.
7333 * @criteria is filter. You should fill in at least some members.
7334 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7335 * members will be ignored. If @pfo_service is true, dbType, ic,
7336 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7337 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7338 * ignored.
7339 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7340 * possibly empty. Input NULL or valid old structure. The same memebers as
7341 * in described for @criteria argument will be NULL according to @pfo_service
7342 * switch.
7343 * @return:
7344 * IE_SUCCESS if search succeeded, @boxes contains useful data
7345 * IE_NOEXIST if no such box exists, @boxes will be NULL
7346 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7347 * contains still valid data
7348 * other code if something bad happens. @boxes will be NULL. */
7349 static isds_error build_send_findbox_request_check_parse_drop_response(
7350 struct isds_ctx *context, const xmlChar *service_name,
7351 _Bool pfo_service, const struct isds_DbOwnerInfo *criteria,
7352 struct isds_list **boxes) {
7353 isds_error err = IE_SUCCESS;
7354 #if HAVE_LIBCURL
7355 char *service_name_locale = NULL;
7356 _Bool truncated = 0;
7357 xmlNsPtr isds_ns = NULL;
7358 xmlNodePtr request = NULL;
7359 xmlDocPtr response = NULL;
7360 xmlChar *code = NULL, *message = NULL;
7361 xmlNodePtr db_owner_info;
7362 xmlXPathContextPtr xpath_ctx = NULL;
7363 xmlXPathObjectPtr result = NULL;
7364 xmlChar *string = NULL;
7365 #endif
7368 if (!context) return IE_INVALID_CONTEXT;
7369 zfree(context->long_message);
7370 if (!boxes) return IE_INVAL;
7371 isds_list_free(boxes);
7373 if (!criteria) {
7374 return IE_INVAL;
7377 #if HAVE_LIBCURL
7378 /* Check if connection is established
7379 * TODO: This check should be done downstairs. */
7380 if (!context->curl) return IE_CONNECTION_CLOSED;
7381 service_name_locale = _isds_utf82locale((char *) service_name);
7383 /* Build request */
7384 request = xmlNewNode(NULL, service_name);
7385 if (!request) {
7386 isds_printf_message(context, _("Could not build %s request"),
7387 service_name_locale);
7388 free(service_name_locale);
7389 return IE_ERROR;
7391 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7392 if(!isds_ns) {
7393 isds_log_message(context, _("Could not create ISDS name space"));
7394 free(service_name_locale);
7395 xmlFreeNode(request);
7396 return IE_ERROR;
7398 xmlSetNs(request, isds_ns);
7399 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7400 if (!db_owner_info) {
7401 isds_printf_message(context,
7402 _("Could not add dbOwnerInfo child to %s element"),
7403 service_name_locale);
7404 free(service_name_locale);
7405 xmlFreeNode(request);
7406 return IE_ERROR;
7409 err = insert_DbOwnerInfo(context, criteria, pfo_service, db_owner_info);
7410 if (err) goto leave;
7413 /* Send request */
7414 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
7415 service_name_locale);
7416 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7418 /* Destroy request */
7419 xmlFreeNode(request); request = NULL;
7421 if (err) {
7422 isds_log(ILF_ISDS, ILL_DEBUG,
7423 _("Processing ISDS response on %s request failed\n"),
7424 service_name_locale);
7425 goto leave;
7428 /* Check for response status */
7429 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7430 &code, &message, NULL);
7431 if (err) {
7432 isds_log(ILF_ISDS, ILL_DEBUG,
7433 _("ISDS response on %s request is missing status\n"),
7434 service_name_locale);
7435 goto leave;
7438 /* Request processed, but nothing found */
7439 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7440 !xmlStrcmp(code, BAD_CAST "5001")) {
7441 char *code_locale = _isds_utf82locale((char*)code);
7442 char *message_locale = _isds_utf82locale((char*)message);
7443 isds_log(ILF_ISDS, ILL_DEBUG,
7444 _("Server did not find any box on %s request "
7445 "(code=%s, message=%s)\n"), service_name_locale,
7446 code_locale, message_locale);
7447 isds_log_message(context, message_locale);
7448 free(code_locale);
7449 free(message_locale);
7450 err = IE_NOEXIST;
7451 goto leave;
7454 /* Warning, not an error */
7455 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7456 char *code_locale = _isds_utf82locale((char*)code);
7457 char *message_locale = _isds_utf82locale((char*)message);
7458 isds_log(ILF_ISDS, ILL_DEBUG,
7459 _("Server truncated response on %s request "
7460 "(code=%s, message=%s)\n"), service_name_locale,
7461 code_locale, message_locale);
7462 isds_log_message(context, message_locale);
7463 free(code_locale);
7464 free(message_locale);
7465 truncated = 1;
7468 /* Other error */
7469 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7470 char *code_locale = _isds_utf82locale((char*)code);
7471 char *message_locale = _isds_utf82locale((char*)message);
7472 isds_log(ILF_ISDS, ILL_DEBUG,
7473 _("Server refused %s request (code=%s, message=%s)\n"),
7474 service_name_locale, code_locale, message_locale);
7475 isds_log_message(context, message_locale);
7476 free(code_locale);
7477 free(message_locale);
7478 err = IE_ISDS;
7479 goto leave;
7482 xpath_ctx = xmlXPathNewContext(response);
7483 if (!xpath_ctx) {
7484 err = IE_ERROR;
7485 goto leave;
7487 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7488 err = IE_ERROR;
7489 goto leave;
7492 /* Extract boxes if they present */
7493 if (-1 == isds_asprintf((char **)&string,
7494 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7495 service_name)) {
7496 err = IE_NOMEM;
7497 goto leave;
7499 result = xmlXPathEvalExpression(string, xpath_ctx);
7500 zfree(string);
7501 if (!result) {
7502 err = IE_ERROR;
7503 goto leave;
7505 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7506 struct isds_list *item, *prev_item = NULL;
7507 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7508 item = calloc(1, sizeof(*item));
7509 if (!item) {
7510 err = IE_NOMEM;
7511 goto leave;
7514 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7515 if (i == 0) *boxes = item;
7516 else prev_item->next = item;
7517 prev_item = item;
7519 xpath_ctx->node = result->nodesetval->nodeTab[i];
7520 err = extract_DbOwnerInfo(context,
7521 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7522 if (err) goto leave;
7526 leave:
7527 if (err) {
7528 isds_list_free(boxes);
7529 } else {
7530 if (truncated) err = IE_2BIG;
7533 free(string);
7534 xmlFreeNode(request);
7535 xmlXPathFreeObject(result);
7536 xmlXPathFreeContext(xpath_ctx);
7538 free(code);
7539 free(message);
7540 xmlFreeDoc(response);
7542 if (!err)
7543 isds_log(ILF_ISDS, ILL_DEBUG,
7544 _("%s request processed by server successfully.\n"),
7545 service_name_locale);
7546 free(service_name_locale);
7547 #else /* not HAVE_LIBCURL */
7548 err = IE_NOTSUP;
7549 #endif
7551 return err;
7555 /* Find boxes suiting given criteria.
7556 * @criteria is filter. You should fill in at least some members. aifoIsds,
7557 * address->adCode, address->adDistrict members are ignored.
7558 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7559 * possibly empty. Input NULL or valid old structure.
7560 * @return:
7561 * IE_SUCCESS if search succeeded, @boxes contains useful data
7562 * IE_NOEXIST if no such box exists, @boxes will be NULL
7563 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7564 * contains still valid data
7565 * other code if something bad happens. @boxes will be NULL. */
7566 isds_error isds_FindDataBox(struct isds_ctx *context,
7567 const struct isds_DbOwnerInfo *criteria,
7568 struct isds_list **boxes) {
7569 return build_send_findbox_request_check_parse_drop_response(context,
7570 BAD_CAST "FindDataBox", 0, criteria, boxes);
7574 /* Find accessible FO-type boxes suiting given criteria.
7575 * @criteria is filter. You should fill in at least some members. dbType, ic,
7576 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7577 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7578 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7579 * possibly empty. Input NULL or valid old structure.
7580 * @return:
7581 * IE_SUCCESS if search succeeded, @boxes contains useful data
7582 * IE_NOEXIST if no such box exists, @boxes will be NULL
7583 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7584 * contains still valid data
7585 * other code if something bad happens. @boxes will be NULL. */
7586 isds_error isds_FindPersonalDataBox(struct isds_ctx *context,
7587 const struct isds_DbOwnerInfo *criteria,
7588 struct isds_list **boxes) {
7589 return build_send_findbox_request_check_parse_drop_response(context,
7590 BAD_CAST "FindPersonalDataBox", 1, criteria, boxes);
7594 #if HAVE_LIBCURL
7595 /* Convert a string with match markers into a plain string with list of
7596 * pointers to the matches
7597 * @string is an UTF-8 encoded non-constant string with match markers
7598 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7599 * The markers will be removed from the string.
7600 * @starts is a reallocated list of static pointers into the @string pointing
7601 * to places where match start markers occured.
7602 * @ends is a reallocated list of static pointers into the @string pointing
7603 * to places where match end markers occured.
7604 * @return IE_SUCCESS in case of no failure. */
7605 static isds_error interpret_matches(xmlChar *string,
7606 struct isds_list **starts, struct isds_list **ends) {
7607 isds_error err = IE_SUCCESS;
7608 xmlChar *pointer, *destination, *source;
7609 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7611 isds_list_free(starts);
7612 isds_list_free(ends);
7613 if (NULL == starts || NULL == ends) return IE_INVAL;
7614 if (NULL == string) return IE_SUCCESS;
7616 for (pointer = string; *pointer != '\0';) {
7617 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7618 /* Remove the start marker */
7619 for (source = pointer + 14, destination = pointer;
7620 *source != '\0'; source++, destination++) {
7621 *destination = *source;
7623 *destination = '\0';
7624 /* Append the pointer into the list */
7625 item = calloc(1, sizeof(*item));
7626 if (!item) {
7627 err = IE_NOMEM;
7628 goto leave;
7630 item->destructor = (void (*)(void **))NULL;
7631 item->data = pointer;
7632 if (NULL == prev_start) *starts = item;
7633 else prev_start->next = item;
7634 prev_start = item;
7635 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7636 /* Remove the end marker */
7637 for (source = pointer + 12, destination = pointer;
7638 *source != '\0'; source++, destination++) {
7639 *destination = *source;
7641 *destination = '\0';
7642 /* Append the pointer into the list */
7643 item = calloc(1, sizeof(*item));
7644 if (!item) {
7645 err = IE_NOMEM;
7646 goto leave;
7648 item->destructor = (void (*)(void **))NULL;
7649 item->data = pointer;
7650 if (NULL == prev_end) *ends = item;
7651 else prev_end->next = item;
7652 prev_end = item;
7653 } else {
7654 pointer++;
7658 leave:
7659 if (err) {
7660 isds_list_free(starts);
7661 isds_list_free(ends);
7663 return err;
7667 /* Convert isds:dbResult XML tree into structure
7668 * @context is ISDS context.
7669 * @fulltext_result is automatically reallocated found box structure.
7670 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7671 * @collect_matches is true to interpret match markers.
7672 * In case of error @result will be freed. */
7673 static isds_error extract_dbResult(struct isds_ctx *context,
7674 struct isds_fulltext_result **fulltext_result,
7675 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7676 isds_error err = IE_SUCCESS;
7677 xmlXPathObjectPtr result = NULL;
7678 char *string = NULL;
7680 if (NULL == context) return IE_INVALID_CONTEXT;
7681 if (NULL == fulltext_result) return IE_INVAL;
7682 isds_fulltext_result_free(fulltext_result);
7683 if (!xpath_ctx) return IE_INVAL;
7686 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7687 if (NULL == *fulltext_result) {
7688 err = IE_NOMEM;
7689 goto leave;
7692 /* Extract data */
7693 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7695 EXTRACT_STRING("isds:dbType", string);
7696 if (NULL == string) {
7697 err = IE_ISDS;
7698 isds_log_message(context, _("Empty isds:dbType element"));
7699 goto leave;
7701 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7702 if (err) {
7703 if (err == IE_ENUM) {
7704 err = IE_ISDS;
7705 char *string_locale = _isds_utf82locale(string);
7706 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7707 string_locale);
7708 free(string_locale);
7710 goto leave;
7712 zfree(string);
7714 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7715 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7717 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7718 if (err) goto leave;
7720 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7721 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7722 (*fulltext_result)->dbEffectiveOVM);
7724 EXTRACT_STRING("isds:dbSendOptions", string);
7725 if (NULL == string) {
7726 err = IE_ISDS;
7727 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7728 goto leave;
7730 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7731 (*fulltext_result)->active = 1;
7732 (*fulltext_result)->public_sending = 1;
7733 (*fulltext_result)->commercial_sending = 0;
7734 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7735 (*fulltext_result)->active = 1;
7736 (*fulltext_result)->public_sending = 1;
7737 (*fulltext_result)->commercial_sending = 1;
7738 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7739 (*fulltext_result)->active = 1;
7740 (*fulltext_result)->public_sending = 0;
7741 (*fulltext_result)->commercial_sending = 1;
7742 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7743 (*fulltext_result)->active = 1;
7744 (*fulltext_result)->public_sending = 0;
7745 (*fulltext_result)->commercial_sending = 0;
7746 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7747 (*fulltext_result)->active = 0;
7748 (*fulltext_result)->public_sending = 0;
7749 (*fulltext_result)->commercial_sending = 0;
7750 } else {
7751 err = IE_ISDS;
7752 char *string_locale = _isds_utf82locale(string);
7753 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7754 string_locale);
7755 free(string_locale);
7756 goto leave;
7758 zfree(string);
7760 /* Interpret match marks */
7761 if (collect_matches) {
7762 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7763 &((*fulltext_result)->name_match_start),
7764 &((*fulltext_result)->name_match_end));
7765 if (err) goto leave;
7766 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7767 &((*fulltext_result)->address_match_start),
7768 &((*fulltext_result)->address_match_end));
7769 if (err) goto leave;
7772 leave:
7773 if (err) isds_fulltext_result_free(fulltext_result);
7774 free(string);
7775 xmlXPathFreeObject(result);
7776 return err;
7778 #endif /* HAVE_LIBCURL */
7781 /* Find boxes matching a given full-text criteria.
7782 * @context is a session context
7783 * @query is a non-empty string which consists of words to search
7784 * @target selects box attributes to search for @query words. Pass NULL if you
7785 * don't care.
7786 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7787 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7788 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7789 * which is DBTYPE_SYSTEM.
7790 * @page_size defines count of boxes to constitute a response page. It counts
7791 * from zero. Pass NULL to let server to use a default value (50 now).
7792 * @page_number defines ordinar number of the response page to return. It
7793 * counts from zero. Pass NULL to let server to use a default value (0 now).
7794 * @track_matches points to true for marking @query words found in the box
7795 * attributes. It points to false for not marking. Pass NULL to let the server
7796 * to use default value (false now).
7797 * @total_matching_boxes outputs reallocated number of all boxes matching the
7798 * query. Will be pointer to NULL if server did not provide the value.
7799 * Pass NULL if you don't care.
7800 * @current_page_beginning outputs reallocated ordinar number of the first box
7801 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7802 * server did not provide the value. Pass NULL if you don't care.
7803 * @current_page_size outputs reallocated count of boxes in the this @boxes
7804 * page. It will be pointer to NULL if the server did not provide the value.
7805 * Pass NULL if you don't care.
7806 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7807 * is the last one, false if more boxes match, NULL if the server did not
7808 * provude the value. Pass NULL if you don't care.
7809 * @boxes outputs reallocated list of isds_fulltext_result structures,
7810 * possibly empty.
7811 * @return:
7812 * IE_SUCCESS if search succeeded
7813 * IE_2BIG if @page_size is too large
7814 * other code if something bad happens; output arguments will be NULL. */
7815 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7816 const char *query,
7817 const isds_fulltext_target *target,
7818 const isds_DbType *box_type,
7819 const unsigned long int *page_size,
7820 const unsigned long int *page_number,
7821 const _Bool *track_matches,
7822 unsigned long int **total_matching_boxes,
7823 unsigned long int **current_page_beginning,
7824 unsigned long int **current_page_size,
7825 _Bool **last_page,
7826 struct isds_list **boxes) {
7827 isds_error err = IE_SUCCESS;
7828 #if HAVE_LIBCURL
7829 xmlNsPtr isds_ns = NULL;
7830 xmlNodePtr request = NULL;
7831 xmlDocPtr response = NULL;
7832 xmlNodePtr node;
7833 xmlXPathContextPtr xpath_ctx = NULL;
7834 xmlXPathObjectPtr result = NULL;
7835 const xmlChar *static_string = NULL;
7836 xmlChar *string = NULL;
7838 const xmlChar *codes[] = {
7839 BAD_CAST "1004",
7840 BAD_CAST "1152",
7841 BAD_CAST "1153",
7842 BAD_CAST "1154",
7843 BAD_CAST "1155",
7844 BAD_CAST "1156",
7845 BAD_CAST "9002",
7846 NULL
7848 const char *meanings[] = {
7849 N_("You are not allowed to perform the search"),
7850 N_("The query string is empty"),
7851 N_("Searched box ID is malformed"),
7852 N_("Searched organization ID is malformed"),
7853 N_("Invalid input"),
7854 N_("Requested page size is too large"),
7855 N_("Search engine internal error")
7857 const isds_error errors[] = {
7858 IE_ISDS,
7859 IE_INVAL,
7860 IE_INVAL,
7861 IE_INVAL,
7862 IE_INVAL,
7863 IE_2BIG,
7864 IE_ISDS
7866 struct code_map_isds_error map = {
7867 .codes = codes,
7868 .meanings = meanings,
7869 .errors = errors
7871 #endif
7874 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7875 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7876 if (NULL != current_page_size) zfree(*current_page_size);
7877 if (NULL != last_page) zfree(*last_page);
7878 isds_list_free(boxes);
7880 if (NULL == context) return IE_INVALID_CONTEXT;
7881 zfree(context->long_message);
7883 if (NULL == boxes) return IE_INVAL;
7885 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7886 isds_log_message(context, _("Query string must be non-empty"));
7887 return IE_INVAL;
7890 #if HAVE_LIBCURL
7891 /* Check if connection is established
7892 * TODO: This check should be done downstairs. */
7893 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7895 /* Build FindDataBox request */
7896 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7897 if (NULL == request) {
7898 isds_log_message(context,
7899 _("Could not build ISDSSearch2 request"));
7900 return IE_ERROR;
7902 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7903 if(NULL == isds_ns) {
7904 isds_log_message(context, _("Could not create ISDS name space"));
7905 xmlFreeNode(request);
7906 return IE_ERROR;
7908 xmlSetNs(request, isds_ns);
7910 INSERT_STRING(request, "searchText", query);
7912 if (NULL != target) {
7913 static_string = isds_fulltext_target2string(*(target));
7914 if (NULL == static_string) {
7915 isds_printf_message(context, _("Invalid target value: %d"),
7916 *(target));
7917 err = IE_ENUM;
7918 goto leave;
7921 INSERT_STRING(request, "searchType", static_string);
7922 static_string = NULL;
7924 if (NULL != box_type) {
7925 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7926 if (DBTYPE_SYSTEM == *box_type) {
7927 static_string = BAD_CAST "ALL";
7928 } else if (DBTYPE_OVM_MAIN == *box_type) {
7929 static_string = BAD_CAST "OVM_MAIN";
7930 } else {
7931 static_string = isds_DbType2string(*(box_type));
7932 if (NULL == static_string) {
7933 isds_printf_message(context, _("Invalid box type value: %d"),
7934 *(box_type));
7935 err = IE_ENUM;
7936 goto leave;
7940 INSERT_STRING(request, "searchScope", static_string);
7941 static_string = NULL;
7943 INSERT_ULONGINT(request, "page", page_number, string);
7944 INSERT_ULONGINT(request, "pageSize", page_size, string);
7945 INSERT_BOOLEAN(request, "highlighting", track_matches);
7947 /* Send request and check response */
7948 err = send_destroy_request_check_response(context,
7949 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7950 &request, &response, NULL, &map);
7951 if (err) goto leave;
7953 /* Parse response */
7954 xpath_ctx = xmlXPathNewContext(response);
7955 if (NULL == xpath_ctx) {
7956 err = IE_ERROR;
7957 goto leave;
7959 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7960 err = IE_ERROR;
7961 goto leave;
7963 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7964 xpath_ctx);
7965 if (!result) {
7966 err = IE_ERROR;
7967 goto leave;
7969 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7970 isds_log_message(context, _("Missing ISDSSearch2 element"));
7971 err = IE_ISDS;
7972 goto leave;
7974 if (result->nodesetval->nodeNr > 1) {
7975 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7976 err = IE_ISDS;
7977 goto leave;
7979 xpath_ctx->node = result->nodesetval->nodeTab[0];
7980 xmlXPathFreeObject(result); result = NULL;
7983 /* Extract counters */
7984 if (NULL != total_matching_boxes) {
7985 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7987 if (NULL != current_page_size) {
7988 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7990 if (NULL != current_page_beginning) {
7991 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7993 if (NULL != last_page) {
7994 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7996 xmlXPathFreeObject(result); result = NULL;
7998 /* Extract boxes if they present */
7999 result = xmlXPathEvalExpression(BAD_CAST
8000 "isds:dbResults/isds:dbResult", xpath_ctx);
8001 if (NULL == result) {
8002 err = IE_ERROR;
8003 goto leave;
8005 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8006 struct isds_list *item, *prev_item = NULL;
8007 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8008 item = calloc(1, sizeof(*item));
8009 if (!item) {
8010 err = IE_NOMEM;
8011 goto leave;
8014 item->destructor = (void (*)(void **))isds_fulltext_result_free;
8015 if (i == 0) *boxes = item;
8016 else prev_item->next = item;
8017 prev_item = item;
8019 xpath_ctx->node = result->nodesetval->nodeTab[i];
8020 err = extract_dbResult(context,
8021 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
8022 (NULL == track_matches) ? 0 : *track_matches);
8023 if (err) goto leave;
8027 leave:
8028 if (err) {
8029 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
8030 if (NULL != current_page_beginning) zfree(*current_page_beginning);
8031 if (NULL != current_page_size) zfree(*current_page_size);
8032 if (NULL != last_page) zfree(*last_page);
8033 isds_list_free(boxes);
8036 free(string);
8037 xmlFreeNode(request);
8038 xmlXPathFreeObject(result);
8039 xmlXPathFreeContext(xpath_ctx);
8040 xmlFreeDoc(response);
8042 if (!err)
8043 isds_log(ILF_ISDS, ILL_DEBUG,
8044 _("ISDSSearch2 request processed by server successfully.\n"));
8045 #else /* not HAVE_LIBCURL */
8046 err = IE_NOTSUP;
8047 #endif
8049 return err;
8053 /* Get status of a box.
8054 * @context is ISDS session context.
8055 * @box_id is UTF-8 encoded box identifier as zero terminated string
8056 * @box_status is return value of box status.
8057 * @return:
8058 * IE_SUCCESS if box has been found and its status retrieved
8059 * IE_NOEXIST if box is not known to ISDS server
8060 * or other appropriate error.
8061 * You can use isds_DbState to enumerate box status. However out of enum
8062 * range value can be returned too. This is feature because ISDS
8063 * specification leaves the set of values open.
8064 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8065 * the box has been deleted, but ISDS still lists its former existence. */
8066 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
8067 long int *box_status) {
8068 isds_error err = IE_SUCCESS;
8069 #if HAVE_LIBCURL
8070 xmlNsPtr isds_ns = NULL;
8071 xmlNodePtr request = NULL, db_id;
8072 xmlDocPtr response = NULL;
8073 xmlXPathContextPtr xpath_ctx = NULL;
8074 xmlXPathObjectPtr result = NULL;
8075 xmlChar *string = NULL;
8077 const xmlChar *codes[] = {
8078 BAD_CAST "5001",
8079 BAD_CAST "1007",
8080 BAD_CAST "2011",
8081 NULL
8083 const char *meanings[] = {
8084 "The box does not exist",
8085 "Box ID is malformed",
8086 "Box ID malformed",
8088 const isds_error errors[] = {
8089 IE_NOEXIST,
8090 IE_INVAL,
8091 IE_INVAL,
8093 struct code_map_isds_error map = {
8094 .codes = codes,
8095 .meanings = meanings,
8096 .errors = errors
8098 #endif
8100 if (!context) return IE_INVALID_CONTEXT;
8101 zfree(context->long_message);
8102 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
8104 #if HAVE_LIBCURL
8105 /* Check if connection is established
8106 * TODO: This check should be done downstairs. */
8107 if (!context->curl) return IE_CONNECTION_CLOSED;
8110 /* Build CheckDataBox request */
8111 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
8112 if (!request) {
8113 isds_log_message(context,
8114 _("Could build CheckDataBox request"));
8115 return IE_ERROR;
8117 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8118 if(!isds_ns) {
8119 isds_log_message(context, _("Could not create ISDS name space"));
8120 xmlFreeNode(request);
8121 return IE_ERROR;
8123 xmlSetNs(request, isds_ns);
8124 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
8125 if (!db_id) {
8126 isds_log_message(context, _("Could not add dbID child to "
8127 "CheckDataBox element"));
8128 xmlFreeNode(request);
8129 return IE_ERROR;
8133 /* Send request and check response*/
8134 err = send_destroy_request_check_response(context,
8135 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
8136 &request, &response, NULL, &map);
8137 if (err) goto leave;
8140 /* Extract data */
8141 xpath_ctx = xmlXPathNewContext(response);
8142 if (!xpath_ctx) {
8143 err = IE_ERROR;
8144 goto leave;
8146 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8147 err = IE_ERROR;
8148 goto leave;
8150 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
8151 xpath_ctx);
8152 if (!result) {
8153 err = IE_ERROR;
8154 goto leave;
8156 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8157 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
8158 err = IE_ISDS;
8159 goto leave;
8161 if (result->nodesetval->nodeNr > 1) {
8162 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
8163 err = IE_ISDS;
8164 goto leave;
8166 xpath_ctx->node = result->nodesetval->nodeTab[0];
8167 xmlXPathFreeObject(result); result = NULL;
8169 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8172 leave:
8173 free(string);
8174 xmlXPathFreeObject(result);
8175 xmlXPathFreeContext(xpath_ctx);
8177 xmlFreeDoc(response);
8179 if (!err)
8180 isds_log(ILF_ISDS, ILL_DEBUG,
8181 _("CheckDataBox request processed by server successfully.\n"));
8182 #else /* not HAVE_LIBCURL */
8183 err = IE_NOTSUP;
8184 #endif
8186 return err;
8190 #if HAVE_LIBCURL
8191 /* Convert XSD:tdbPeriod XML tree into structure
8192 * @context is ISDS context.
8193 * @period is automatically reallocated found box status period structure.
8194 * @xpath_ctx is XPath context with current node as element of
8195 * XSD:tDbPeriod type.
8196 * In case of error @period will be freed. */
8197 static isds_error extract_Period(struct isds_ctx *context,
8198 struct isds_box_state_period **period, xmlXPathContextPtr xpath_ctx) {
8199 isds_error err = IE_SUCCESS;
8200 xmlXPathObjectPtr result = NULL;
8201 char *string = NULL;
8202 long int *dbState_ptr;
8204 if (NULL == context) return IE_INVALID_CONTEXT;
8205 if (NULL == period) return IE_INVAL;
8206 isds_box_state_period_free(period);
8207 if (!xpath_ctx) return IE_INVAL;
8210 *period = calloc(1, sizeof(**period));
8211 if (NULL == *period) {
8212 err = IE_NOMEM;
8213 goto leave;
8216 /* Extract data */
8217 EXTRACT_STRING("isds:PeriodFrom", string);
8218 if (NULL == string) {
8219 err = IE_XML;
8220 isds_log_message(context,
8221 _("Could not find PeriodFrom element value"));
8222 goto leave;
8224 err = timestring2static_timeval((xmlChar *) string,
8225 &((*period)->from));
8226 if (err) {
8227 char *string_locale = _isds_utf82locale(string);
8228 if (err == IE_DATE) err = IE_ISDS;
8229 isds_printf_message(context,
8230 _("Could not convert PeriodFrom as ISO time: %s"),
8231 string_locale);
8232 free(string_locale);
8233 goto leave;
8235 zfree(string);
8237 EXTRACT_STRING("isds:PeriodTo", string);
8238 if (NULL == string) {
8239 err = IE_XML;
8240 isds_log_message(context,
8241 _("Could not find PeriodTo element value"));
8242 goto leave;
8244 err = timestring2static_timeval((xmlChar *) string,
8245 &((*period)->to));
8246 if (err) {
8247 char *string_locale = _isds_utf82locale(string);
8248 if (err == IE_DATE) err = IE_ISDS;
8249 isds_printf_message(context,
8250 _("Could not convert PeriodTo as ISO time: %s"),
8251 string_locale);
8252 free(string_locale);
8253 goto leave;
8255 zfree(string);
8257 dbState_ptr = &((*period)->dbState);
8258 EXTRACT_LONGINT("isds:DbState", dbState_ptr, 1);
8260 leave:
8261 if (err) isds_box_state_period_free(period);
8262 free(string);
8263 xmlXPathFreeObject(result);
8264 return err;
8266 #endif /* HAVE_LIBCURL */
8269 /* Get history of box state changes.
8270 * @context is ISDS session context.
8271 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8272 * @from_time is first second of history to return in @history. Server ignores
8273 * subseconds. NULL means time of creating the box.
8274 * @to_time is last second of history to return in @history. Server ignores
8275 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8276 * interval is closed from both ends. NULL means now.
8277 * @history outputs auto-reallocated list of pointers to struct
8278 * isds_box_state_period. Each item describes a continues time when the box
8279 * was in one state. The state is 1 for accessible box. Otherwise the box
8280 * is inaccessible (priviledged users will get exact box state as enumerated
8281 * in isds_DbState, other users 0).
8282 * @return:
8283 * IE_SUCCESS if the history has been obtained correctly,
8284 * or other appropriate error. Please note that server allows to retrieve
8285 * the history only to some users. */
8286 isds_error isds_get_box_state_history(struct isds_ctx *context,
8287 const char *box_id,
8288 const struct timeval *from_time, const struct timeval *to_time,
8289 struct isds_list **history) {
8290 isds_error err = IE_SUCCESS;
8291 #if HAVE_LIBCURL
8292 char *box_id_locale = NULL;
8293 xmlNodePtr request = NULL, node;
8294 xmlNsPtr isds_ns = NULL;
8295 xmlChar *string = NULL;
8297 xmlDocPtr response = NULL;
8298 xmlXPathContextPtr xpath_ctx = NULL;
8299 xmlXPathObjectPtr result = NULL;
8300 #endif
8302 if (!context) return IE_INVALID_CONTEXT;
8303 zfree(context->long_message);
8305 /* Free output argument */
8306 isds_list_free(history);
8308 #if HAVE_LIBCURL
8309 /* Check if connection is established */
8310 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8312 /* ??? XML schema allows empty box ID, textual documentation
8313 * requries the value. */
8314 /* Allow undefined box_id */
8315 if (NULL != box_id) {
8316 box_id_locale = _isds_utf82locale((char*)box_id);
8317 if (NULL == box_id_locale) {
8318 err = IE_NOMEM;
8319 goto leave;
8323 /* Build request */
8324 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxActivityStatus");
8325 if (NULL == request) {
8326 isds_printf_message(context,
8327 _("Could not build GetDataBoxActivityStatus request "
8328 "for %s box"),
8329 box_id_locale);
8330 err = IE_ERROR;
8331 goto leave;
8333 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8334 if(!isds_ns) {
8335 isds_log_message(context, _("Could not create ISDS name space"));
8336 err = IE_ERROR;
8337 goto leave;
8339 xmlSetNs(request, isds_ns);
8341 /* Add mandatory XSD:tIdDbInput child */
8342 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8343 /* Add times elements only when defined */
8344 /* ???: XML schema requires the values, textual documentation does not. */
8345 if (from_time) {
8346 err = timeval2timestring(from_time, &string);
8347 if (err) {
8348 isds_log_message(context,
8349 _("Could not convert `from_time' argument to ISO time "
8350 "string"));
8351 goto leave;
8353 INSERT_STRING(request, "baFrom", string);
8354 zfree(string);
8356 if (to_time) {
8357 err = timeval2timestring(to_time, &string);
8358 if (err) {
8359 isds_log_message(context,
8360 _("Could not convert `to_time' argument to ISO time "
8361 "string"));
8362 goto leave;
8364 INSERT_STRING(request, "baTo", string);
8365 zfree(string);
8368 /* Send request and check response*/
8369 err = send_destroy_request_check_response(context,
8370 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxActivityStatus",
8371 &request, &response, NULL, NULL);
8372 if (err) goto leave;
8375 /* Extract data */
8376 /* Set context to the root */
8377 xpath_ctx = xmlXPathNewContext(response);
8378 if (!xpath_ctx) {
8379 err = IE_ERROR;
8380 goto leave;
8382 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8383 err = IE_ERROR;
8384 goto leave;
8386 result = xmlXPathEvalExpression(BAD_CAST "/isds:GetDataBoxActivityStatusResponse",
8387 xpath_ctx);
8388 if (!result) {
8389 err = IE_ERROR;
8390 goto leave;
8392 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8393 isds_log_message(context, _("Missing GetDataBoxActivityStatusResponse element"));
8394 err = IE_ISDS;
8395 goto leave;
8397 if (result->nodesetval->nodeNr > 1) {
8398 isds_log_message(context, _("Multiple GetDataBoxActivityStatusResponse element"));
8399 err = IE_ISDS;
8400 goto leave;
8402 xpath_ctx->node = result->nodesetval->nodeTab[0];
8403 xmlXPathFreeObject(result); result = NULL;
8405 /* Ignore dbID, it's the same as the input argument. */
8407 /* Extract records */
8408 if (NULL == history) goto leave;
8409 result = xmlXPathEvalExpression(BAD_CAST "isds:Periods/isds:Period",
8410 xpath_ctx);
8411 if (!result) {
8412 err = IE_ERROR;
8413 goto leave;
8415 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8416 struct isds_list *prev_item = NULL;
8418 /* Iterate over all records */
8419 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8420 struct isds_list *item;
8422 /* Prepare structure */
8423 item = calloc(1, sizeof(*item));
8424 if (!item) {
8425 err = IE_NOMEM;
8426 goto leave;
8428 item->destructor = (void(*)(void**))isds_box_state_period_free;
8429 if (i == 0) *history = item;
8430 else prev_item->next = item;
8431 prev_item = item;
8433 /* Extract it */
8434 xpath_ctx->node = result->nodesetval->nodeTab[i];
8435 err = extract_Period(context,
8436 (struct isds_box_state_period **) (&item->data),
8437 xpath_ctx);
8438 if (err) goto leave;
8442 leave:
8443 if (!err) {
8444 isds_log(ILF_ISDS, ILL_DEBUG,
8445 _("GetDataBoxActivityStatus request for %s box "
8446 "processed by server successfully.\n"), box_id_locale);
8448 if (err) {
8449 isds_list_free(history);
8452 free(box_id_locale);
8453 xmlXPathFreeObject(result);
8454 xmlXPathFreeContext(xpath_ctx);
8455 xmlFreeDoc(response);
8457 #else /* not HAVE_LIBCURL */
8458 err = IE_NOTSUP;
8459 #endif
8461 return err;
8465 /* Get list of permissions to send commercial messages.
8466 * @context is ISDS session context.
8467 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8468 * @permissions is a reallocated list of permissions (struct
8469 * isds_commercial_permission*) to send commercial messages from @box_id. The
8470 * order of permissions is significant as the server applies the permissions
8471 * and associated pre-paid credits in the order. Empty list means no
8472 * permission.
8473 * @return:
8474 * IE_SUCCESS if the list has been obtained correctly,
8475 * or other appropriate error. */
8476 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8477 const char *box_id, struct isds_list **permissions) {
8478 isds_error err = IE_SUCCESS;
8479 #if HAVE_LIBCURL
8480 xmlDocPtr response = NULL;
8481 xmlXPathContextPtr xpath_ctx = NULL;
8482 xmlXPathObjectPtr result = NULL;
8483 #endif
8485 if (!context) return IE_INVALID_CONTEXT;
8486 zfree(context->long_message);
8487 if (NULL == permissions) return IE_INVAL;
8488 isds_list_free(permissions);
8489 if (NULL == box_id) return IE_INVAL;
8491 #if HAVE_LIBCURL
8492 /* Check if connection is established */
8493 if (!context->curl) return IE_CONNECTION_CLOSED;
8495 /* Do request and check for success */
8496 err = build_send_dbid_request_check_response(context,
8497 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8498 BAD_CAST box_id, NULL, &response, NULL);
8499 if (!err) {
8500 isds_log(ILF_ISDS, ILL_DEBUG,
8501 _("PDZInfo request processed by server successfully.\n"));
8504 /* Extract data */
8505 /* Prepare structure */
8506 xpath_ctx = xmlXPathNewContext(response);
8507 if (!xpath_ctx) {
8508 err = IE_ERROR;
8509 goto leave;
8511 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8512 err = IE_ERROR;
8513 goto leave;
8516 /* Set context node */
8517 result = xmlXPathEvalExpression(BAD_CAST
8518 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8519 xpath_ctx);
8520 if (!result) {
8521 err = IE_ERROR;
8522 goto leave;
8524 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8525 struct isds_list *prev_item = NULL;
8527 /* Iterate over all permission records */
8528 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8529 struct isds_list *item;
8531 /* Prepare structure */
8532 item = calloc(1, sizeof(*item));
8533 if (!item) {
8534 err = IE_NOMEM;
8535 goto leave;
8537 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8538 if (i == 0) *permissions = item;
8539 else prev_item->next = item;
8540 prev_item = item;
8542 /* Extract it */
8543 xpath_ctx->node = result->nodesetval->nodeTab[i];
8544 err = extract_DbPDZRecord(context,
8545 (struct isds_commercial_permission **) (&item->data),
8546 xpath_ctx);
8547 if (err) goto leave;
8551 leave:
8552 if (err) {
8553 isds_list_free(permissions);
8556 xmlXPathFreeObject(result);
8557 xmlXPathFreeContext(xpath_ctx);
8558 xmlFreeDoc(response);
8560 #else /* not HAVE_LIBCURL */
8561 err = IE_NOTSUP;
8562 #endif
8564 return err;
8568 /* Get details about credit for sending pre-paid commercial messages.
8569 * @context is ISDS session context.
8570 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8571 * @from_date is first day of credit history to return in @history. Only
8572 * tm_year, tm_mon and tm_mday carry sane value.
8573 * @to_date is last day of credit history to return in @history. Only
8574 * tm_year, tm_mon and tm_mday carry sane value.
8575 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8576 * if you don't care. This and all other credit values are integers in
8577 * hundredths of Czech Crowns.
8578 * @email outputs notification e-mail address where notifications about credit
8579 * are sent. This is automatically reallocated string. Pass NULL if you don't
8580 * care. It can return NULL if no address is defined.
8581 * @history outputs auto-reallocated list of pointers to struct
8582 * isds_credit_event. Events in closed interval @from_time to @to_time are
8583 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8584 * are sorted by time.
8585 * @return:
8586 * IE_SUCCESS if the credit details have been obtained correctly,
8587 * or other appropriate error. Please note that server allows to retrieve
8588 * only limited history of events. */
8589 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8590 const char *box_id,
8591 const struct tm *from_date, const struct tm *to_date,
8592 long int *credit, char **email, struct isds_list **history) {
8593 isds_error err = IE_SUCCESS;
8594 #if HAVE_LIBCURL
8595 char *box_id_locale = NULL;
8596 xmlNodePtr request = NULL, node;
8597 xmlNsPtr isds_ns = NULL;
8598 xmlChar *string = NULL;
8600 xmlDocPtr response = NULL;
8601 xmlXPathContextPtr xpath_ctx = NULL;
8602 xmlXPathObjectPtr result = NULL;
8604 const xmlChar *codes[] = {
8605 BAD_CAST "1004",
8606 BAD_CAST "2011",
8607 BAD_CAST "1093",
8608 BAD_CAST "1137",
8609 BAD_CAST "1058",
8610 NULL
8612 const char *meanings[] = {
8613 "Insufficient priviledges for the box",
8614 "The box does not exist",
8615 "Date is too long (history is not available after 15 months)",
8616 "Interval is too long (limit is 3 months)",
8617 "Invalid date"
8619 const isds_error errors[] = {
8620 IE_ISDS,
8621 IE_NOEXIST,
8622 IE_DATE,
8623 IE_DATE,
8624 IE_DATE,
8626 struct code_map_isds_error map = {
8627 .codes = codes,
8628 .meanings = meanings,
8629 .errors = errors
8631 #endif
8633 if (!context) return IE_INVALID_CONTEXT;
8634 zfree(context->long_message);
8636 /* Free output argument */
8637 if (NULL != credit) *credit = 0;
8638 if (NULL != email) zfree(*email);
8639 isds_list_free(history);
8641 if (NULL == box_id) return IE_INVAL;
8643 #if HAVE_LIBCURL
8644 /* Check if connection is established */
8645 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8647 box_id_locale = _isds_utf82locale((char*)box_id);
8648 if (NULL == box_id_locale) {
8649 err = IE_NOMEM;
8650 goto leave;
8653 /* Build request */
8654 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8655 if (NULL == request) {
8656 isds_printf_message(context,
8657 _("Could not build DataBoxCreditInfo request for %s box"),
8658 box_id_locale);
8659 err = IE_ERROR;
8660 goto leave;
8662 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8663 if(!isds_ns) {
8664 isds_log_message(context, _("Could not create ISDS name space"));
8665 err = IE_ERROR;
8666 goto leave;
8668 xmlSetNs(request, isds_ns);
8670 /* Add mandatory XSD:tIdDbInput child */
8671 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8672 /* Add mandatory dates elements with optional values */
8673 if (from_date) {
8674 err = tm2datestring(from_date, &string);
8675 if (err) {
8676 isds_log_message(context,
8677 _("Could not convert `from_date' argument to ISO date "
8678 "string"));
8679 goto leave;
8681 INSERT_STRING(request, "ciFromDate", string);
8682 zfree(string);
8683 } else {
8684 INSERT_STRING(request, "ciFromDate", NULL);
8686 if (to_date) {
8687 err = tm2datestring(to_date, &string);
8688 if (err) {
8689 isds_log_message(context,
8690 _("Could not convert `to_date' argument to ISO date "
8691 "string"));
8692 goto leave;
8694 INSERT_STRING(request, "ciTodate", string);
8695 zfree(string);
8696 } else {
8697 INSERT_STRING(request, "ciTodate", NULL);
8700 /* Send request and check response*/
8701 err = send_destroy_request_check_response(context,
8702 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8703 &request, &response, NULL, &map);
8704 if (err) goto leave;
8707 /* Extract data */
8708 /* Set context to the root */
8709 xpath_ctx = xmlXPathNewContext(response);
8710 if (!xpath_ctx) {
8711 err = IE_ERROR;
8712 goto leave;
8714 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8715 err = IE_ERROR;
8716 goto leave;
8718 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8719 xpath_ctx);
8720 if (!result) {
8721 err = IE_ERROR;
8722 goto leave;
8724 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8725 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8726 err = IE_ISDS;
8727 goto leave;
8729 if (result->nodesetval->nodeNr > 1) {
8730 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8731 err = IE_ISDS;
8732 goto leave;
8734 xpath_ctx->node = result->nodesetval->nodeTab[0];
8735 xmlXPathFreeObject(result); result = NULL;
8737 /* Extract common data */
8738 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8739 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8741 /* Extract records */
8742 if (NULL == history) goto leave;
8743 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8744 xpath_ctx);
8745 if (!result) {
8746 err = IE_ERROR;
8747 goto leave;
8749 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8750 struct isds_list *prev_item = NULL;
8752 /* Iterate over all records */
8753 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8754 struct isds_list *item;
8756 /* Prepare structure */
8757 item = calloc(1, sizeof(*item));
8758 if (!item) {
8759 err = IE_NOMEM;
8760 goto leave;
8762 item->destructor = (void(*)(void**))isds_credit_event_free;
8763 if (i == 0) *history = item;
8764 else prev_item->next = item;
8765 prev_item = item;
8767 /* Extract it */
8768 xpath_ctx->node = result->nodesetval->nodeTab[i];
8769 err = extract_CiRecord(context,
8770 (struct isds_credit_event **) (&item->data),
8771 xpath_ctx);
8772 if (err) goto leave;
8776 leave:
8777 if (!err) {
8778 isds_log(ILF_ISDS, ILL_DEBUG,
8779 _("DataBoxCreditInfo request processed by server successfully.\n"));
8781 if (err) {
8782 isds_list_free(history);
8783 if (NULL != email) zfree(*email)
8786 free(box_id_locale);
8787 xmlXPathFreeObject(result);
8788 xmlXPathFreeContext(xpath_ctx);
8789 xmlFreeDoc(response);
8791 #else /* not HAVE_LIBCURL */
8792 err = IE_NOTSUP;
8793 #endif
8795 return err;
8799 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8800 * code, destroy response and log success.
8801 * @context is ISDS session context.
8802 * @service_name is name of SERVICE_DB_MANIPULATION service
8803 * @box_id is UTF-8 encoded box identifier as zero terminated string
8804 * @approval is optional external approval of box manipulation
8805 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8806 * NULL, if you don't care. */
8807 static isds_error build_send_manipulationdbid_request_check_drop_response(
8808 struct isds_ctx *context, const xmlChar *service_name,
8809 const xmlChar *box_id, const struct isds_approval *approval,
8810 xmlChar **refnumber) {
8811 isds_error err = IE_SUCCESS;
8812 #if HAVE_LIBCURL
8813 xmlDocPtr response = NULL;
8814 #endif
8816 if (!context) return IE_INVALID_CONTEXT;
8817 zfree(context->long_message);
8818 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8820 #if HAVE_LIBCURL
8821 /* Check if connection is established */
8822 if (!context->curl) return IE_CONNECTION_CLOSED;
8824 /* Do request and check for success */
8825 err = build_send_dbid_request_check_response(context,
8826 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8827 &response, refnumber);
8828 xmlFreeDoc(response);
8830 if (!err) {
8831 char *service_name_locale = _isds_utf82locale((char *) service_name);
8832 isds_log(ILF_ISDS, ILL_DEBUG,
8833 _("%s request processed by server successfully.\n"),
8834 service_name_locale);
8835 free(service_name_locale);
8837 #else /* not HAVE_LIBCURL */
8838 err = IE_NOTSUP;
8839 #endif
8841 return err;
8845 /* Switch box into state where box can receive commercial messages (off by
8846 * default)
8847 * @context is ISDS session context.
8848 * @box_id is UTF-8 encoded box identifier as zero terminated string
8849 * @allow is true for enable, false for disable commercial messages income
8850 * @approval is optional external approval of box manipulation
8851 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8852 * NULL, if you don't care. */
8853 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8854 const char *box_id, const _Bool allow,
8855 const struct isds_approval *approval, char **refnumber) {
8856 return build_send_manipulationdbid_request_check_drop_response(context,
8857 (allow) ? BAD_CAST "SetOpenAddressing" :
8858 BAD_CAST "ClearOpenAddressing",
8859 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8863 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8864 * message acceptance). This is just a box permission. Sender must apply
8865 * such role by sending each message.
8866 * @context is ISDS session context.
8867 * @box_id is UTF-8 encoded box identifier as zero terminated string
8868 * @allow is true for enable, false for disable OVM role permission
8869 * @approval is optional external approval of box manipulation
8870 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8871 * NULL, if you don't care. */
8872 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8873 const char *box_id, const _Bool allow,
8874 const struct isds_approval *approval, char **refnumber) {
8875 return build_send_manipulationdbid_request_check_drop_response(context,
8876 (allow) ? BAD_CAST "SetEffectiveOVM" :
8877 BAD_CAST "ClearEffectiveOVM",
8878 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8882 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8883 * code, destroy response and log success.
8884 * @context is ISDS session context.
8885 * @service_name is name of SERVICE_DB_MANIPULATION service
8886 * @owner is structure describing box. aifoIsds, address->adCode,
8887 * address->adDistrict members are ignored.
8888 * @approval is optional external approval of box manipulation
8889 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8890 * NULL, if you don't care. */
8891 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8892 struct isds_ctx *context, const xmlChar *service_name,
8893 const struct isds_DbOwnerInfo *owner,
8894 const struct isds_approval *approval, xmlChar **refnumber) {
8895 isds_error err = IE_SUCCESS;
8896 #if HAVE_LIBCURL
8897 char *service_name_locale = NULL;
8898 xmlNodePtr request = NULL, db_owner_info;
8899 xmlNsPtr isds_ns = NULL;
8900 #endif
8903 if (!context) return IE_INVALID_CONTEXT;
8904 zfree(context->long_message);
8905 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8907 #if HAVE_LIBCURL
8908 service_name_locale = _isds_utf82locale((char*)service_name);
8909 if (!service_name_locale) {
8910 err = IE_NOMEM;
8911 goto leave;
8914 /* Build request */
8915 request = xmlNewNode(NULL, service_name);
8916 if (!request) {
8917 isds_printf_message(context,
8918 _("Could not build %s request"), service_name_locale);
8919 err = IE_ERROR;
8920 goto leave;
8922 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8923 if(!isds_ns) {
8924 isds_log_message(context, _("Could not create ISDS name space"));
8925 err = IE_ERROR;
8926 goto leave;
8928 xmlSetNs(request, isds_ns);
8931 /* Add XSD:tOwnerInfoInput child*/
8932 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8933 err = insert_DbOwnerInfo(context, owner, 0, db_owner_info);
8934 if (err) goto leave;
8936 /* Add XSD:gExtApproval*/
8937 err = insert_GExtApproval(context, approval, request);
8938 if (err) goto leave;
8940 /* Send it to server and process response */
8941 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8942 service_name, &request, refnumber);
8944 leave:
8945 xmlFreeNode(request);
8946 free(service_name_locale);
8947 #else /* not HAVE_LIBCURL */
8948 err = IE_NOTSUP;
8949 #endif
8951 return err;
8955 /* Switch box accessibility state on request of box owner.
8956 * Despite the name, owner must do the request off-line. This function is
8957 * designed for such off-line meeting points (e.g. Czech POINT).
8958 * @context is ISDS session context.
8959 * @box identifies box to switch accessibility state. aifoIsds,
8960 * address->adCode, address->adDistrict members are ignored.
8961 * @allow is true for making accessible, false to disallow access.
8962 * @approval is optional external approval of box manipulation
8963 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8964 * NULL, if you don't care. */
8965 isds_error isds_switch_box_accessibility_on_owner_request(
8966 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8967 const _Bool allow, const struct isds_approval *approval,
8968 char **refnumber) {
8969 return build_send_manipulationdbowner_request_check_drop_response(context,
8970 (allow) ? BAD_CAST "EnableOwnDataBox" :
8971 BAD_CAST "DisableOwnDataBox",
8972 box, approval, (xmlChar **) refnumber);
8976 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8977 * date.
8978 * @context is ISDS session context.
8979 * @box identifies box to switch accessibility state. aifoIsds,
8980 * address->adCode, address->adDistrict members are ignored.
8981 * @since is date since accessibility has been denied. This can be past too.
8982 * Only tm_year, tm_mon and tm_mday carry sane value.
8983 * @approval is optional external approval of box manipulation
8984 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8985 * NULL, if you don't care. */
8986 isds_error isds_disable_box_accessibility_externaly(
8987 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8988 const struct tm *since, const struct isds_approval *approval,
8989 char **refnumber) {
8990 isds_error err = IE_SUCCESS;
8991 #if HAVE_LIBCURL
8992 char *service_name_locale = NULL;
8993 xmlNodePtr request = NULL, node;
8994 xmlNsPtr isds_ns = NULL;
8995 xmlChar *string = NULL;
8996 #endif
8999 if (!context) return IE_INVALID_CONTEXT;
9000 zfree(context->long_message);
9001 if (!box || !since) return IE_INVAL;
9003 #if HAVE_LIBCURL
9004 /* Build request */
9005 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
9006 if (!request) {
9007 isds_printf_message(context,
9008 _("Could not build %s request"), "DisableDataBoxExternally");
9009 err = IE_ERROR;
9010 goto leave;
9012 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9013 if(!isds_ns) {
9014 isds_log_message(context, _("Could not create ISDS name space"));
9015 err = IE_ERROR;
9016 goto leave;
9018 xmlSetNs(request, isds_ns);
9021 /* Add @box identification */
9022 INSERT_ELEMENT(node, request, "dbOwnerInfo");
9023 err = insert_DbOwnerInfo(context, box, 0, node);
9024 if (err) goto leave;
9026 /* Add @since date */
9027 err = tm2datestring(since, &string);
9028 if(err) {
9029 isds_log_message(context,
9030 _("Could not convert `since' argument to ISO date string"));
9031 goto leave;
9033 INSERT_STRING(request, "dbOwnerDisableDate", string);
9034 zfree(string);
9036 /* Add @approval */
9037 err = insert_GExtApproval(context, approval, request);
9038 if (err) goto leave;
9040 /* Send it to server and process response */
9041 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
9042 BAD_CAST "DisableDataBoxExternally", &request,
9043 (xmlChar **) refnumber);
9045 leave:
9046 free(string);
9047 xmlFreeNode(request);
9048 free(service_name_locale);
9049 #else /* not HAVE_LIBCURL */
9050 err = IE_NOTSUP;
9051 #endif
9053 return err;
9057 #if HAVE_LIBCURL
9058 /* Insert struct isds_message data (envelope (recipient data optional) and
9059 * documents into XML tree
9060 * @context is session context
9061 * @outgoing_message is libisds structure with message data
9062 * @create_message is XML CreateMessage or CreateMultipleMessage element
9063 * @process_recipient true for recipient data serialization, false for no
9064 * serialization */
9065 static isds_error insert_envelope_files(struct isds_ctx *context,
9066 const struct isds_message *outgoing_message, xmlNodePtr create_message,
9067 const _Bool process_recipient) {
9069 isds_error err = IE_SUCCESS;
9070 xmlNodePtr envelope, dm_files, node;
9071 xmlChar *string = NULL;
9073 if (!context) return IE_INVALID_CONTEXT;
9074 if (!outgoing_message || !create_message) return IE_INVAL;
9077 /* Build envelope */
9078 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
9079 if (!envelope) {
9080 isds_printf_message(context, _("Could not add dmEnvelope child to "
9081 "%s element"), create_message->name);
9082 return IE_ERROR;
9085 if (!outgoing_message->envelope) {
9086 isds_log_message(context, _("Outgoing message is missing envelope"));
9087 err = IE_INVAL;
9088 goto leave;
9091 /* Insert optional message type */
9092 err = insert_message_type(context, outgoing_message->envelope->dmType,
9093 envelope);
9094 if (err) goto leave;
9096 INSERT_STRING(envelope, "dmSenderOrgUnit",
9097 outgoing_message->envelope->dmSenderOrgUnit);
9098 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
9099 outgoing_message->envelope->dmSenderOrgUnitNum, string);
9101 if (process_recipient) {
9102 if (!outgoing_message->envelope->dbIDRecipient) {
9103 isds_log_message(context,
9104 _("Outgoing message is missing recipient box identifier"));
9105 err = IE_INVAL;
9106 goto leave;
9108 INSERT_STRING(envelope, "dbIDRecipient",
9109 outgoing_message->envelope->dbIDRecipient);
9111 INSERT_STRING(envelope, "dmRecipientOrgUnit",
9112 outgoing_message->envelope->dmRecipientOrgUnit);
9113 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
9114 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
9115 INSERT_STRING(envelope, "dmToHands",
9116 outgoing_message->envelope->dmToHands);
9119 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
9120 "dmAnnotation");
9121 INSERT_STRING(envelope, "dmAnnotation",
9122 outgoing_message->envelope->dmAnnotation);
9124 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
9125 0, 50, "dmRecipientRefNumber");
9126 INSERT_STRING(envelope, "dmRecipientRefNumber",
9127 outgoing_message->envelope->dmRecipientRefNumber);
9129 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
9130 0, 50, "dmSenderRefNumber");
9131 INSERT_STRING(envelope, "dmSenderRefNumber",
9132 outgoing_message->envelope->dmSenderRefNumber);
9134 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
9135 0, 50, "dmRecipientIdent");
9136 INSERT_STRING(envelope, "dmRecipientIdent",
9137 outgoing_message->envelope->dmRecipientIdent);
9139 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
9140 0, 50, "dmSenderIdent");
9141 INSERT_STRING(envelope, "dmSenderIdent",
9142 outgoing_message->envelope->dmSenderIdent);
9144 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
9145 outgoing_message->envelope->dmLegalTitleLaw, string);
9146 INSERT_LONGINT(envelope, "dmLegalTitleYear",
9147 outgoing_message->envelope->dmLegalTitleYear, string);
9148 INSERT_STRING(envelope, "dmLegalTitleSect",
9149 outgoing_message->envelope->dmLegalTitleSect);
9150 INSERT_STRING(envelope, "dmLegalTitlePar",
9151 outgoing_message->envelope->dmLegalTitlePar);
9152 INSERT_STRING(envelope, "dmLegalTitlePoint",
9153 outgoing_message->envelope->dmLegalTitlePoint);
9155 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
9156 outgoing_message->envelope->dmPersonalDelivery);
9157 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
9158 outgoing_message->envelope->dmAllowSubstDelivery);
9160 /* ???: Should we require value for dbEffectiveOVM sender?
9161 * ISDS has default as true */
9162 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
9163 INSERT_BOOLEAN(envelope, "dmPublishOwnID",
9164 outgoing_message->envelope->dmPublishOwnID);
9167 /* Append dmFiles */
9168 if (!outgoing_message->documents) {
9169 isds_log_message(context,
9170 _("Outgoing message is missing list of documents"));
9171 err = IE_INVAL;
9172 goto leave;
9174 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
9175 if (!dm_files) {
9176 isds_printf_message(context, _("Could not add dmFiles child to "
9177 "%s element"), create_message->name);
9178 err = IE_ERROR;
9179 goto leave;
9182 /* Check for document hierarchy */
9183 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
9184 if (err) goto leave;
9186 /* Process each document */
9187 for (struct isds_list *item =
9188 (struct isds_list *) outgoing_message->documents;
9189 item; item = item->next) {
9190 if (!item->data) {
9191 isds_log_message(context,
9192 _("List of documents contains empty item"));
9193 err = IE_INVAL;
9194 goto leave;
9196 /* FIXME: Check for dmFileMetaType and for document references.
9197 * Only first document can be of MAIN type */
9198 err = insert_document(context, (struct isds_document*) item->data,
9199 dm_files);
9201 if (err) goto leave;
9204 leave:
9205 free(string);
9206 return err;
9208 #endif /* HAVE_LIBCURL */
9211 /* Send a message via ISDS to a recipient
9212 * @context is session context
9213 * @outgoing_message is message to send; Some members are mandatory (like
9214 * dbIDRecipient), some are optional and some are irrelevant (especially data
9215 * about sender). Included pointer to isds_list documents must contain at
9216 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9217 * members will be filled with valid data from ISDS. Exact list of write
9218 * members is subject to change. Currently dmID is changed.
9219 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9220 isds_error isds_send_message(struct isds_ctx *context,
9221 struct isds_message *outgoing_message) {
9223 isds_error err = IE_SUCCESS;
9224 #if HAVE_LIBCURL
9225 xmlNsPtr isds_ns = NULL;
9226 xmlNodePtr request = NULL;
9227 xmlDocPtr response = NULL;
9228 xmlChar *code = NULL, *message = NULL;
9229 xmlXPathContextPtr xpath_ctx = NULL;
9230 xmlXPathObjectPtr result = NULL;
9231 /*_Bool message_is_complete = 0;*/
9232 #endif
9234 if (!context) return IE_INVALID_CONTEXT;
9235 zfree(context->long_message);
9236 if (!outgoing_message) return IE_INVAL;
9238 #if HAVE_LIBCURL
9239 /* Check if connection is established
9240 * TODO: This check should be done downstairs. */
9241 if (!context->curl) return IE_CONNECTION_CLOSED;
9244 /* Build CreateMessage request */
9245 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
9246 if (!request) {
9247 isds_log_message(context,
9248 _("Could not build CreateMessage request"));
9249 return IE_ERROR;
9251 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9252 if(!isds_ns) {
9253 isds_log_message(context, _("Could not create ISDS name space"));
9254 xmlFreeNode(request);
9255 return IE_ERROR;
9257 xmlSetNs(request, isds_ns);
9259 /* Append envelope and files */
9260 err = insert_envelope_files(context, outgoing_message, request, 1);
9261 if (err) goto leave;
9264 /* Signal we can serialize message since now */
9265 /*message_is_complete = 1;*/
9268 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
9270 /* Sent request */
9271 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9273 /* Don't' destroy request, we want to provide it to application later */
9275 if (err) {
9276 isds_log(ILF_ISDS, ILL_DEBUG,
9277 _("Processing ISDS response on CreateMessage "
9278 "request failed\n"));
9279 goto leave;
9282 /* Check for response status */
9283 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9284 &code, &message, NULL);
9285 if (err) {
9286 isds_log(ILF_ISDS, ILL_DEBUG,
9287 _("ISDS response on CreateMessage request "
9288 "is missing status\n"));
9289 goto leave;
9292 /* Request processed, but refused by server or server failed */
9293 if (xmlStrcmp(code, BAD_CAST "0000")) {
9294 char *box_id_locale =
9295 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9296 char *code_locale = _isds_utf82locale((char*)code);
9297 char *message_locale = _isds_utf82locale((char*)message);
9298 isds_log(ILF_ISDS, ILL_DEBUG,
9299 _("Server did not accept message for %s on CreateMessage "
9300 "request (code=%s, message=%s)\n"),
9301 box_id_locale, code_locale, message_locale);
9302 isds_log_message(context, message_locale);
9303 free(box_id_locale);
9304 free(code_locale);
9305 free(message_locale);
9306 err = IE_ISDS;
9307 goto leave;
9311 /* Extract data */
9312 xpath_ctx = xmlXPathNewContext(response);
9313 if (!xpath_ctx) {
9314 err = IE_ERROR;
9315 goto leave;
9317 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9318 err = IE_ERROR;
9319 goto leave;
9321 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
9322 xpath_ctx);
9323 if (!result) {
9324 err = IE_ERROR;
9325 goto leave;
9327 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9328 isds_log_message(context, _("Missing CreateMessageResponse element"));
9329 err = IE_ISDS;
9330 goto leave;
9332 if (result->nodesetval->nodeNr > 1) {
9333 isds_log_message(context, _("Multiple CreateMessageResponse element"));
9334 err = IE_ISDS;
9335 goto leave;
9337 xpath_ctx->node = result->nodesetval->nodeTab[0];
9338 xmlXPathFreeObject(result); result = NULL;
9340 if (outgoing_message->envelope->dmID) {
9341 free(outgoing_message->envelope->dmID);
9342 outgoing_message->envelope->dmID = NULL;
9344 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
9345 if (!outgoing_message->envelope->dmID) {
9346 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
9347 "but did not return assigned message ID\n"));
9350 leave:
9351 /* TODO: Serialize message into structure member raw */
9352 /* XXX: Each web service transport message in different format.
9353 * Therefore it's not possible to save them directly.
9354 * To save them, one must figure out common format.
9355 * We can leave it on application, or we can implement the ESS format. */
9356 /*if (message_is_complete) {
9357 if (outgoing_message->envelope->dmID) {
9359 /* Add assigned message ID as first child*/
9360 /*xmlNodePtr dmid_text = xmlNewText(
9361 (xmlChar *) outgoing_message->envelope->dmID);
9362 if (!dmid_text) goto serialization_failed;
9364 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9365 BAD_CAST "dmID");
9366 if (!dmid_element) {
9367 xmlFreeNode(dmid_text);
9368 goto serialization_failed;
9371 xmlNodePtr dmid_element_with_text =
9372 xmlAddChild(dmid_element, dmid_text);
9373 if (!dmid_element_with_text) {
9374 xmlFreeNode(dmid_element);
9375 xmlFreeNode(dmid_text);
9376 goto serialization_failed;
9379 node = xmlAddPrevSibling(envelope->childern,
9380 dmid_element_with_text);
9381 if (!node) {
9382 xmlFreeNodeList(dmid_element_with_text);
9383 goto serialization_failed;
9387 /* Serialize message with ID into raw */
9388 /*buffer = serialize_element(envelope)*/
9389 /* }
9391 serialization_failed:
9395 /* Clean up */
9396 xmlXPathFreeObject(result);
9397 xmlXPathFreeContext(xpath_ctx);
9399 free(code);
9400 free(message);
9401 xmlFreeDoc(response);
9402 xmlFreeNode(request);
9404 if (!err)
9405 isds_log(ILF_ISDS, ILL_DEBUG,
9406 _("CreateMessage request processed by server "
9407 "successfully.\n"));
9408 #else /* not HAVE_LIBCURL */
9409 err = IE_NOTSUP;
9410 #endif
9412 return err;
9416 /* Send a message via ISDS to a multiple recipients
9417 * @context is session context
9418 * @outgoing_message is message to send; Some members are mandatory,
9419 * some are optional and some are irrelevant (especially data
9420 * about sender). Data about recipient will be substituted by ISDS from
9421 * @copies. Included pointer to isds_list documents must
9422 * contain at least one document of FILEMETATYPE_MAIN.
9423 * @copies is list of isds_message_copy structures addressing all desired
9424 * recipients. This is read-write structure, some members will be filled with
9425 * valid data from ISDS (message IDs, error codes, error descriptions).
9426 * @return
9427 * ISDS_SUCCESS if all messages have been sent
9428 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9429 * succeeded messages can be identified by copies->data->error),
9430 * or other error code if something other goes wrong. */
9431 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
9432 const struct isds_message *outgoing_message,
9433 struct isds_list *copies) {
9435 isds_error err = IE_SUCCESS;
9436 #if HAVE_LIBCURL
9437 isds_error append_err;
9438 xmlNsPtr isds_ns = NULL;
9439 xmlNodePtr request = NULL, recipients, recipient, node;
9440 struct isds_list *item;
9441 struct isds_message_copy *copy;
9442 xmlDocPtr response = NULL;
9443 xmlChar *code = NULL, *message = NULL;
9444 xmlXPathContextPtr xpath_ctx = NULL;
9445 xmlXPathObjectPtr result = NULL;
9446 xmlChar *string = NULL;
9447 int i;
9448 #endif
9450 if (!context) return IE_INVALID_CONTEXT;
9451 zfree(context->long_message);
9452 if (!outgoing_message || !copies) return IE_INVAL;
9454 #if HAVE_LIBCURL
9455 /* Check if connection is established
9456 * TODO: This check should be done downstairs. */
9457 if (!context->curl) return IE_CONNECTION_CLOSED;
9460 /* Build CreateMultipleMessage request */
9461 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9462 if (!request) {
9463 isds_log_message(context,
9464 _("Could not build CreateMultipleMessage request"));
9465 return IE_ERROR;
9467 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9468 if(!isds_ns) {
9469 isds_log_message(context, _("Could not create ISDS name space"));
9470 xmlFreeNode(request);
9471 return IE_ERROR;
9473 xmlSetNs(request, isds_ns);
9476 /* Build recipients */
9477 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9478 if (!recipients) {
9479 isds_log_message(context, _("Could not add dmRecipients child to "
9480 "CreateMultipleMessage element"));
9481 xmlFreeNode(request);
9482 return IE_ERROR;
9485 /* Insert each recipient */
9486 for (item = copies; item; item = item->next) {
9487 copy = (struct isds_message_copy *) item->data;
9488 if (!copy) {
9489 isds_log_message(context,
9490 _("`copies' list item contains empty data"));
9491 err = IE_INVAL;
9492 goto leave;
9495 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9496 if (!recipient) {
9497 isds_log_message(context, _("Could not add dmRecipient child to "
9498 "dmRecipients element"));
9499 err = IE_ERROR;
9500 goto leave;
9503 if (!copy->dbIDRecipient) {
9504 isds_log_message(context,
9505 _("Message copy is missing recipient box identifier"));
9506 err = IE_INVAL;
9507 goto leave;
9509 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9510 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9511 copy->dmRecipientOrgUnit);
9512 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9513 copy->dmRecipientOrgUnitNum, string);
9514 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9517 /* Append envelope and files */
9518 err = insert_envelope_files(context, outgoing_message, request, 0);
9519 if (err) goto leave;
9522 isds_log(ILF_ISDS, ILL_DEBUG,
9523 _("Sending CreateMultipleMessage request to ISDS\n"));
9525 /* Sent request */
9526 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9527 if (err) {
9528 isds_log(ILF_ISDS, ILL_DEBUG,
9529 _("Processing ISDS response on CreateMultipleMessage "
9530 "request failed\n"));
9531 goto leave;
9534 /* Check for response status */
9535 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9536 &code, &message, NULL);
9537 if (err) {
9538 isds_log(ILF_ISDS, ILL_DEBUG,
9539 _("ISDS response on CreateMultipleMessage request "
9540 "is missing status\n"));
9541 goto leave;
9544 /* Request processed, but some copies failed */
9545 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9546 char *box_id_locale =
9547 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9548 char *code_locale = _isds_utf82locale((char*)code);
9549 char *message_locale = _isds_utf82locale((char*)message);
9550 isds_log(ILF_ISDS, ILL_DEBUG,
9551 _("Server did accept message for multiple recipients "
9552 "on CreateMultipleMessage request but delivery to "
9553 "some of them failed (code=%s, message=%s)\n"),
9554 box_id_locale, code_locale, message_locale);
9555 isds_log_message(context, message_locale);
9556 free(box_id_locale);
9557 free(code_locale);
9558 free(message_locale);
9559 err = IE_PARTIAL_SUCCESS;
9562 /* Request refused by server as whole */
9563 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9564 char *box_id_locale =
9565 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9566 char *code_locale = _isds_utf82locale((char*)code);
9567 char *message_locale = _isds_utf82locale((char*)message);
9568 isds_log(ILF_ISDS, ILL_DEBUG,
9569 _("Server did not accept message for multiple recipients "
9570 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9571 box_id_locale, code_locale, message_locale);
9572 isds_log_message(context, message_locale);
9573 free(box_id_locale);
9574 free(code_locale);
9575 free(message_locale);
9576 err = IE_ISDS;
9577 goto leave;
9581 /* Extract data */
9582 xpath_ctx = xmlXPathNewContext(response);
9583 if (!xpath_ctx) {
9584 err = IE_ERROR;
9585 goto leave;
9587 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9588 err = IE_ERROR;
9589 goto leave;
9591 result = xmlXPathEvalExpression(
9592 BAD_CAST "/isds:CreateMultipleMessageResponse"
9593 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9594 xpath_ctx);
9595 if (!result) {
9596 err = IE_ERROR;
9597 goto leave;
9599 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9600 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9601 err = IE_ISDS;
9602 goto leave;
9605 /* Extract message ID and delivery status for each copy */
9606 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9607 item = item->next, i++) {
9608 copy = (struct isds_message_copy *) item->data;
9609 xpath_ctx->node = result->nodesetval->nodeTab[i];
9611 append_err = append_TMStatus(context, copy, xpath_ctx);
9612 if (append_err) {
9613 err = append_err;
9614 goto leave;
9617 if (item || i < result->nodesetval->nodeNr) {
9618 isds_printf_message(context, _("ISDS returned unexpected number of "
9619 "message copy delivery states: %d"),
9620 result->nodesetval->nodeNr);
9621 err = IE_ISDS;
9622 goto leave;
9626 leave:
9627 /* Clean up */
9628 free(string);
9629 xmlXPathFreeObject(result);
9630 xmlXPathFreeContext(xpath_ctx);
9632 free(code);
9633 free(message);
9634 xmlFreeDoc(response);
9635 xmlFreeNode(request);
9637 if (!err)
9638 isds_log(ILF_ISDS, ILL_DEBUG,
9639 _("CreateMultipleMessageResponse request processed by server "
9640 "successfully.\n"));
9641 #else /* not HAVE_LIBCURL */
9642 err = IE_NOTSUP;
9643 #endif
9645 return err;
9649 /* Get list of messages. This is common core for getting sent or received
9650 * messages.
9651 * Any criterion argument can be NULL, if you don't care about it.
9652 * @context is session context. Must not be NULL.
9653 * @outgoing_direction is true if you want list of outgoing messages,
9654 * it's false if you want incoming messages.
9655 * @from_time is minimal time and date of message sending inclusive.
9656 * @to_time is maximal time and date of message sending inclusive
9657 * @organization_unit_number is number of sender/recipient respectively.
9658 * @status_filter is bit field of isds_message_status values. Use special
9659 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9660 * all values, you can use bit-wise arithmetic if you want.)
9661 * @offset is index of first message we are interested in. First message is 1.
9662 * Set to 0 (or 1) if you don't care.
9663 * @number is maximal length of list you want to get as input value, outputs
9664 * number of messages matching these criteria. Can be NULL if you don't care
9665 * (applies to output value either).
9666 * @messages is automatically reallocated list of isds_message's. Be ware that
9667 * it returns only brief overview (envelope and some other fields) about each
9668 * message, not the complete message. FIXME: Specify exact fields.
9669 * The list is sorted by delivery time in ascending order.
9670 * Use NULL if you don't care about don't need the data (useful if you want to
9671 * know only the @number). If you provide &NULL, list will be allocated on
9672 * heap, if you provide pointer to non-NULL, list will be freed automatically
9673 * at first. Also in case of error the list will be NULLed.
9674 * @return IE_SUCCESS or appropriate error code. */
9675 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9676 _Bool outgoing_direction,
9677 const struct timeval *from_time, const struct timeval *to_time,
9678 const long int *organization_unit_number,
9679 const unsigned int status_filter,
9680 const unsigned long int offset, unsigned long int *number,
9681 struct isds_list **messages) {
9683 isds_error err = IE_SUCCESS;
9684 #if HAVE_LIBCURL
9685 xmlNsPtr isds_ns = NULL;
9686 xmlNodePtr request = NULL, node;
9687 xmlDocPtr response = NULL;
9688 xmlChar *code = NULL, *message = NULL;
9689 xmlXPathContextPtr xpath_ctx = NULL;
9690 xmlXPathObjectPtr result = NULL;
9691 xmlChar *string = NULL;
9692 int count = 0;
9693 #endif
9695 if (!context) return IE_INVALID_CONTEXT;
9696 zfree(context->long_message);
9698 /* Free former message list if any */
9699 if (messages) isds_list_free(messages);
9701 #if HAVE_LIBCURL
9702 /* Check if connection is established
9703 * TODO: This check should be done downstairs. */
9704 if (!context->curl) return IE_CONNECTION_CLOSED;
9706 /* Build GetListOf*Messages request */
9707 request = xmlNewNode(NULL,
9708 (outgoing_direction) ?
9709 BAD_CAST "GetListOfSentMessages" :
9710 BAD_CAST "GetListOfReceivedMessages"
9712 if (!request) {
9713 isds_log_message(context,
9714 (outgoing_direction) ?
9715 _("Could not build GetListOfSentMessages request") :
9716 _("Could not build GetListOfReceivedMessages request")
9718 return IE_ERROR;
9720 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9721 if(!isds_ns) {
9722 isds_log_message(context, _("Could not create ISDS name space"));
9723 xmlFreeNode(request);
9724 return IE_ERROR;
9726 xmlSetNs(request, isds_ns);
9729 if (from_time) {
9730 err = timeval2timestring(from_time, &string);
9731 if (err) goto leave;
9733 INSERT_STRING(request, "dmFromTime", string);
9734 free(string); string = NULL;
9736 if (to_time) {
9737 err = timeval2timestring(to_time, &string);
9738 if (err) goto leave;
9740 INSERT_STRING(request, "dmToTime", string);
9741 free(string); string = NULL;
9743 if (outgoing_direction) {
9744 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9745 organization_unit_number, string);
9746 } else {
9747 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9748 organization_unit_number, string);
9751 if (status_filter > MESSAGESTATE_ANY) {
9752 isds_printf_message(context,
9753 _("Invalid message state filter value: %ld"), status_filter);
9754 err = IE_INVAL;
9755 goto leave;
9757 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9759 if (offset > 0 ) {
9760 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9761 } else {
9762 INSERT_STRING(request, "dmOffset", "1");
9765 /* number 0 means no limit */
9766 if (number && *number == 0) {
9767 INSERT_STRING(request, "dmLimit", NULL);
9768 } else {
9769 INSERT_ULONGINT(request, "dmLimit", number, string);
9773 isds_log(ILF_ISDS, ILL_DEBUG,
9774 (outgoing_direction) ?
9775 _("Sending GetListOfSentMessages request to ISDS\n") :
9776 _("Sending GetListOfReceivedMessages request to ISDS\n")
9779 /* Sent request */
9780 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9781 xmlFreeNode(request); request = NULL;
9783 if (err) {
9784 isds_log(ILF_ISDS, ILL_DEBUG,
9785 (outgoing_direction) ?
9786 _("Processing ISDS response on GetListOfSentMessages "
9787 "request failed\n") :
9788 _("Processing ISDS response on GetListOfReceivedMessages "
9789 "request failed\n")
9791 goto leave;
9794 /* Check for response status */
9795 err = isds_response_status(context, SERVICE_DM_INFO, response,
9796 &code, &message, NULL);
9797 if (err) {
9798 isds_log(ILF_ISDS, ILL_DEBUG,
9799 (outgoing_direction) ?
9800 _("ISDS response on GetListOfSentMessages request "
9801 "is missing status\n") :
9802 _("ISDS response on GetListOfReceivedMessages request "
9803 "is missing status\n")
9805 goto leave;
9808 /* Request processed, but nothing found */
9809 if (xmlStrcmp(code, BAD_CAST "0000")) {
9810 char *code_locale = _isds_utf82locale((char*)code);
9811 char *message_locale = _isds_utf82locale((char*)message);
9812 isds_log(ILF_ISDS, ILL_DEBUG,
9813 (outgoing_direction) ?
9814 _("Server refused GetListOfSentMessages request "
9815 "(code=%s, message=%s)\n") :
9816 _("Server refused GetListOfReceivedMessages request "
9817 "(code=%s, message=%s)\n"),
9818 code_locale, message_locale);
9819 isds_log_message(context, message_locale);
9820 free(code_locale);
9821 free(message_locale);
9822 err = IE_ISDS;
9823 goto leave;
9827 /* Extract data */
9828 xpath_ctx = xmlXPathNewContext(response);
9829 if (!xpath_ctx) {
9830 err = IE_ERROR;
9831 goto leave;
9833 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9834 err = IE_ERROR;
9835 goto leave;
9837 result = xmlXPathEvalExpression(
9838 (outgoing_direction) ?
9839 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9840 "isds:dmRecords/isds:dmRecord" :
9841 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9842 "isds:dmRecords/isds:dmRecord",
9843 xpath_ctx);
9844 if (!result) {
9845 err = IE_ERROR;
9846 goto leave;
9849 /* Fill output arguments in */
9850 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9851 struct isds_envelope *envelope;
9852 struct isds_list *item = NULL, *last_item = NULL;
9854 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9855 /* Create new message */
9856 item = calloc(1, sizeof(*item));
9857 if (!item) {
9858 err = IE_NOMEM;
9859 goto leave;
9861 item->destructor = (void(*)(void**)) &isds_message_free;
9862 item->data = calloc(1, sizeof(struct isds_message));
9863 if (!item->data) {
9864 isds_list_free(&item);
9865 err = IE_NOMEM;
9866 goto leave;
9869 /* Extract envelope data */
9870 xpath_ctx->node = result->nodesetval->nodeTab[count];
9871 envelope = NULL;
9872 err = extract_DmRecord(context, &envelope, xpath_ctx);
9873 if (err) {
9874 isds_list_free(&item);
9875 goto leave;
9878 /* Attach extracted envelope */
9879 ((struct isds_message *) item->data)->envelope = envelope;
9881 /* Append new message into the list */
9882 if (!*messages) {
9883 *messages = last_item = item;
9884 } else {
9885 last_item->next = item;
9886 last_item = item;
9890 if (number) *number = count;
9892 leave:
9893 if (err) {
9894 isds_list_free(messages);
9897 free(string);
9898 xmlXPathFreeObject(result);
9899 xmlXPathFreeContext(xpath_ctx);
9901 free(code);
9902 free(message);
9903 xmlFreeDoc(response);
9904 xmlFreeNode(request);
9906 if (!err)
9907 isds_log(ILF_ISDS, ILL_DEBUG,
9908 (outgoing_direction) ?
9909 _("GetListOfSentMessages request processed by server "
9910 "successfully.\n") :
9911 _("GetListOfReceivedMessages request processed by server "
9912 "successfully.\n")
9914 #else /* not HAVE_LIBCURL */
9915 err = IE_NOTSUP;
9916 #endif
9917 return err;
9921 /* Get list of outgoing (already sent) messages.
9922 * Any criterion argument can be NULL, if you don't care about it.
9923 * @context is session context. Must not be NULL.
9924 * @from_time is minimal time and date of message sending inclusive.
9925 * @to_time is maximal time and date of message sending inclusive
9926 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9927 * @status_filter is bit field of isds_message_status values. Use special
9928 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9929 * all values, you can use bit-wise arithmetic if you want.)
9930 * @offset is index of first message we are interested in. First message is 1.
9931 * Set to 0 (or 1) if you don't care.
9932 * @number is maximal length of list you want to get as input value, outputs
9933 * number of messages matching these criteria. Can be NULL if you don't care
9934 * (applies to output value either).
9935 * @messages is automatically reallocated list of isds_message's. Be ware that
9936 * it returns only brief overview (envelope and some other fields) about each
9937 * message, not the complete message. FIXME: Specify exact fields.
9938 * The list is sorted by delivery time in ascending order.
9939 * Use NULL if you don't care about the meta data (useful if you want to know
9940 * only the @number). If you provide &NULL, list will be allocated on heap,
9941 * if you provide pointer to non-NULL, list will be freed automatically at
9942 * first. Also in case of error the list will be NULLed.
9943 * @return IE_SUCCESS or appropriate error code. */
9944 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9945 const struct timeval *from_time, const struct timeval *to_time,
9946 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9947 const unsigned long int offset, unsigned long int *number,
9948 struct isds_list **messages) {
9950 return isds_get_list_of_messages(
9951 context, 1,
9952 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9953 offset, number,
9954 messages);
9958 /* Get list of incoming (addressed to you) messages.
9959 * Any criterion argument can be NULL, if you don't care about it.
9960 * @context is session context. Must not be NULL.
9961 * @from_time is minimal time and date of message sending inclusive.
9962 * @to_time is maximal time and date of message sending inclusive
9963 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9964 * @status_filter is bit field of isds_message_status values. Use special
9965 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9966 * all values, you can use bit-wise arithmetic if you want.)
9967 * @offset is index of first message we are interested in. First message is 1.
9968 * Set to 0 (or 1) if you don't care.
9969 * @number is maximal length of list you want to get as input value, outputs
9970 * number of messages matching these criteria. Can be NULL if you don't care
9971 * (applies to output value either).
9972 * @messages is automatically reallocated list of isds_message's. Be ware that
9973 * it returns only brief overview (envelope and some other fields) about each
9974 * message, not the complete message. FIXME: Specify exact fields.
9975 * Use NULL if you don't care about the meta data (useful if you want to know
9976 * only the @number). If you provide &NULL, list will be allocated on heap,
9977 * if you provide pointer to non-NULL, list will be freed automatically at
9978 * first. Also in case of error the list will be NULLed.
9979 * @return IE_SUCCESS or appropriate error code. */
9980 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9981 const struct timeval *from_time, const struct timeval *to_time,
9982 const long int *dmRecipientOrgUnitNum,
9983 const unsigned int status_filter,
9984 const unsigned long int offset, unsigned long int *number,
9985 struct isds_list **messages) {
9987 return isds_get_list_of_messages(
9988 context, 0,
9989 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9990 offset, number,
9991 messages);
9995 /* Get list of sent message state changes.
9996 * Any criterion argument can be NULL, if you don't care about it.
9997 * @context is session context. Must not be NULL.
9998 * @from_time is minimal time and date of status changes inclusive
9999 * @to_time is maximal time and date of status changes inclusive
10000 * @changed_states is automatically reallocated list of
10001 * isds_message_status_change's. If you provide &NULL, list will be allocated
10002 * on heap, if you provide pointer to non-NULL, list will be freed
10003 * automatically at first. Also in case of error the list will be NULLed.
10004 * XXX: The list item ordering is not specified.
10005 * XXX: Server provides only `recent' changes.
10006 * @return IE_SUCCESS or appropriate error code. */
10007 isds_error isds_get_list_of_sent_message_state_changes(
10008 struct isds_ctx *context,
10009 const struct timeval *from_time, const struct timeval *to_time,
10010 struct isds_list **changed_states) {
10012 isds_error err = IE_SUCCESS;
10013 #if HAVE_LIBCURL
10014 xmlNsPtr isds_ns = NULL;
10015 xmlNodePtr request = NULL, node;
10016 xmlDocPtr response = NULL;
10017 xmlXPathContextPtr xpath_ctx = NULL;
10018 xmlXPathObjectPtr result = NULL;
10019 xmlChar *string = NULL;
10020 int count = 0;
10021 #endif
10023 if (!context) return IE_INVALID_CONTEXT;
10024 zfree(context->long_message);
10026 /* Free former message list if any */
10027 isds_list_free(changed_states);
10029 #if HAVE_LIBCURL
10030 /* Check if connection is established
10031 * TODO: This check should be done downstairs. */
10032 if (!context->curl) return IE_CONNECTION_CLOSED;
10034 /* Build GetMessageStateChanges request */
10035 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
10036 if (!request) {
10037 isds_log_message(context,
10038 _("Could not build GetMessageStateChanges request"));
10039 return IE_ERROR;
10041 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10042 if(!isds_ns) {
10043 isds_log_message(context, _("Could not create ISDS name space"));
10044 xmlFreeNode(request);
10045 return IE_ERROR;
10047 xmlSetNs(request, isds_ns);
10050 if (from_time) {
10051 err = timeval2timestring(from_time, &string);
10052 if (err) goto leave;
10054 INSERT_STRING(request, "dmFromTime", string);
10055 zfree(string);
10057 if (to_time) {
10058 err = timeval2timestring(to_time, &string);
10059 if (err) goto leave;
10061 INSERT_STRING(request, "dmToTime", string);
10062 zfree(string);
10065 /* Sent request */
10066 err = send_destroy_request_check_response(context,
10067 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
10068 &response, NULL, NULL);
10069 if (err) goto leave;
10072 /* Extract data */
10073 xpath_ctx = xmlXPathNewContext(response);
10074 if (!xpath_ctx) {
10075 err = IE_ERROR;
10076 goto leave;
10078 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10079 err = IE_ERROR;
10080 goto leave;
10082 result = xmlXPathEvalExpression(
10083 BAD_CAST "/isds:GetMessageStateChangesResponse/"
10084 "isds:dmRecords/isds:dmRecord", xpath_ctx);
10085 if (!result) {
10086 err = IE_ERROR;
10087 goto leave;
10090 /* Fill output arguments in */
10091 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10092 struct isds_list *item = NULL, *last_item = NULL;
10094 for (count = 0; count < result->nodesetval->nodeNr; count++) {
10095 /* Create new status change */
10096 item = calloc(1, sizeof(*item));
10097 if (!item) {
10098 err = IE_NOMEM;
10099 goto leave;
10101 item->destructor =
10102 (void(*)(void**)) &isds_message_status_change_free;
10104 /* Extract message status change */
10105 xpath_ctx->node = result->nodesetval->nodeTab[count];
10106 err = extract_StateChangesRecord(context,
10107 (struct isds_message_status_change **) &item->data,
10108 xpath_ctx);
10109 if (err) {
10110 isds_list_free(&item);
10111 goto leave;
10114 /* Append new message status change into the list */
10115 if (!*changed_states) {
10116 *changed_states = last_item = item;
10117 } else {
10118 last_item->next = item;
10119 last_item = item;
10124 leave:
10125 if (err) {
10126 isds_list_free(changed_states);
10129 free(string);
10130 xmlXPathFreeObject(result);
10131 xmlXPathFreeContext(xpath_ctx);
10132 xmlFreeDoc(response);
10133 xmlFreeNode(request);
10135 if (!err)
10136 isds_log(ILF_ISDS, ILL_DEBUG,
10137 _("GetMessageStateChanges request processed by server "
10138 "successfully.\n"));
10139 #else /* not HAVE_LIBCURL */
10140 err = IE_NOTSUP;
10141 #endif
10142 return err;
10146 #if HAVE_LIBCURL
10147 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10148 * code
10149 * @context is session context
10150 * @service is ISDS WS service handler
10151 * @service_name is name of SERVICE_DM_OPERATIONS
10152 * @message_id is message ID to send as service argument to ISDS
10153 * @response is reallocated server SOAP body response as XML document
10154 * @raw_response is reallocated bit stream with response body. Use
10155 * NULL if you don't care
10156 * @raw_response_length is size of @raw_response in bytes
10157 * @code is reallocated ISDS status code
10158 * @status_message is reallocated ISDS status message
10159 * @return error coded from lower layer, context message will be set up
10160 * appropriately. */
10161 static isds_error build_send_check_message_request(struct isds_ctx *context,
10162 const isds_service service, const xmlChar *service_name,
10163 const char *message_id,
10164 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
10165 xmlChar **code, xmlChar **status_message) {
10167 isds_error err = IE_SUCCESS;
10168 char *service_name_locale = NULL, *message_id_locale = NULL;
10169 xmlNodePtr request = NULL, node;
10170 xmlNsPtr isds_ns = NULL;
10172 if (!context) return IE_INVALID_CONTEXT;
10173 if (!service_name || !message_id) return IE_INVAL;
10174 if (!response || !code || !status_message) return IE_INVAL;
10175 if (!raw_response_length && raw_response) return IE_INVAL;
10177 /* Free output argument */
10178 xmlFreeDoc(*response); *response = NULL;
10179 if (raw_response) zfree(*raw_response);
10180 zfree(*code);
10181 zfree(*status_message);
10184 /* Check if connection is established
10185 * TODO: This check should be done downstairs. */
10186 if (!context->curl) return IE_CONNECTION_CLOSED;
10188 service_name_locale = _isds_utf82locale((char*)service_name);
10189 message_id_locale = _isds_utf82locale(message_id);
10190 if (!service_name_locale || !message_id_locale) {
10191 err = IE_NOMEM;
10192 goto leave;
10195 /* Build request */
10196 request = xmlNewNode(NULL, service_name);
10197 if (!request) {
10198 isds_printf_message(context,
10199 _("Could not build %s request for %s message ID"),
10200 service_name_locale, message_id_locale);
10201 err = IE_ERROR;
10202 goto leave;
10204 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10205 if(!isds_ns) {
10206 isds_log_message(context, _("Could not create ISDS name space"));
10207 err = IE_ERROR;
10208 goto leave;
10210 xmlSetNs(request, isds_ns);
10213 /* Add requested ID */
10214 err = validate_message_id_length(context, (xmlChar *) message_id);
10215 if (err) goto leave;
10216 INSERT_STRING(request, "dmID", message_id);
10219 isds_log(ILF_ISDS, ILL_DEBUG,
10220 _("Sending %s request for %s message ID to ISDS\n"),
10221 service_name_locale, message_id_locale);
10223 /* Send request */
10224 err = _isds(context, service, request, response,
10225 raw_response, raw_response_length);
10226 xmlFreeNode(request); request = NULL;
10228 if (err) {
10229 isds_log(ILF_ISDS, ILL_DEBUG,
10230 _("Processing ISDS response on %s request failed\n"),
10231 service_name_locale);
10232 goto leave;
10235 /* Check for response status */
10236 err = isds_response_status(context, service, *response,
10237 code, status_message, NULL);
10238 if (err) {
10239 isds_log(ILF_ISDS, ILL_DEBUG,
10240 _("ISDS response on %s request is missing status\n"),
10241 service_name_locale);
10242 goto leave;
10245 /* Request processed, but nothing found */
10246 if (xmlStrcmp(*code, BAD_CAST "0000")) {
10247 char *code_locale = _isds_utf82locale((char*) *code);
10248 char *status_message_locale = _isds_utf82locale((char*) *status_message);
10249 isds_log(ILF_ISDS, ILL_DEBUG,
10250 _("Server refused %s request for %s message ID "
10251 "(code=%s, message=%s)\n"),
10252 service_name_locale, message_id_locale,
10253 code_locale, status_message_locale);
10254 isds_log_message(context, status_message_locale);
10255 free(code_locale);
10256 free(status_message_locale);
10257 err = IE_ISDS;
10258 goto leave;
10261 leave:
10262 free(message_id_locale);
10263 free(service_name_locale);
10264 xmlFreeNode(request);
10265 return err;
10269 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10270 * signed data and free ISDS response.
10271 * @context is session context
10272 * @message_id is UTF-8 encoded message ID for logging purpose
10273 * @response is parsed XML document. It will be freed and NULLed in the middle
10274 * of function run to save memory. This is not guaranteed in case of error.
10275 * @request_name is name of ISDS request used to construct response root
10276 * element name and for logging purpose.
10277 * @raw is reallocated output buffer with DER encoded CMS data
10278 * @raw_length is size of @raw buffer in bytes
10279 * @returns standard error codes, in case of error, @raw will be freed and
10280 * NULLed, @response sometimes. */
10281 static isds_error find_extract_signed_data_free_response(
10282 struct isds_ctx *context, const xmlChar *message_id,
10283 xmlDocPtr *response, const xmlChar *request_name,
10284 void **raw, size_t *raw_length) {
10286 isds_error err = IE_SUCCESS;
10287 char *xpath_expression = NULL;
10288 xmlXPathContextPtr xpath_ctx = NULL;
10289 xmlXPathObjectPtr result = NULL;
10290 char *encoded_structure = NULL;
10292 if (!context) return IE_INVALID_CONTEXT;
10293 if (!raw) return IE_INVAL;
10294 zfree(*raw);
10295 if (!message_id || !response || !*response || !request_name || !raw_length)
10296 return IE_INVAL;
10298 /* Build XPath expression */
10299 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
10300 "Response/isds:dmSignature");
10301 if (!xpath_expression) return IE_NOMEM;
10303 /* Extract data */
10304 xpath_ctx = xmlXPathNewContext(*response);
10305 if (!xpath_ctx) {
10306 err = IE_ERROR;
10307 goto leave;
10309 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10310 err = IE_ERROR;
10311 goto leave;
10313 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
10314 if (!result) {
10315 err = IE_ERROR;
10316 goto leave;
10318 /* Empty response */
10319 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10320 char *message_id_locale = _isds_utf82locale((char*) message_id);
10321 isds_printf_message(context,
10322 _("Server did not return any signed data for message ID `%s' "
10323 "on %s request"),
10324 message_id_locale, request_name);
10325 free(message_id_locale);
10326 err = IE_ISDS;
10327 goto leave;
10329 /* More responses */
10330 if (result->nodesetval->nodeNr > 1) {
10331 char *message_id_locale = _isds_utf82locale((char*) message_id);
10332 isds_printf_message(context,
10333 _("Server did return more signed data for message ID `%s' "
10334 "on %s request"),
10335 message_id_locale, request_name);
10336 free(message_id_locale);
10337 err = IE_ISDS;
10338 goto leave;
10340 /* One response */
10341 xpath_ctx->node = result->nodesetval->nodeTab[0];
10343 /* Extract PKCS#7 structure */
10344 EXTRACT_STRING(".", encoded_structure);
10345 if (!encoded_structure) {
10346 isds_log_message(context, _("dmSignature element is empty"));
10349 /* Here we have delivery info as standalone CMS in encoded_structure.
10350 * We don't need any other data, free them: */
10351 xmlXPathFreeObject(result); result = NULL;
10352 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
10353 xmlFreeDoc(*response); *response = NULL;
10356 /* Decode PKCS#7 to DER format */
10357 *raw_length = _isds_b64decode(encoded_structure, raw);
10358 if (*raw_length == (size_t) -1) {
10359 isds_log_message(context,
10360 _("Error while Base64-decoding PKCS#7 structure"));
10361 err = IE_ERROR;
10362 goto leave;
10365 leave:
10366 if (err) {
10367 zfree(*raw);
10368 raw_length = 0;
10371 free(encoded_structure);
10372 xmlXPathFreeObject(result);
10373 xmlXPathFreeContext(xpath_ctx);
10374 free(xpath_expression);
10376 return err;
10378 #endif /* HAVE_LIBCURL */
10381 /* Download incoming message envelope identified by ID.
10382 * @context is session context
10383 * @message_id is message identifier (you can get them from
10384 * isds_get_list_of_received_messages())
10385 * @message is automatically reallocated message retrieved from ISDS.
10386 * It will miss documents per se. Use isds_get_received_message(), if you are
10387 * interested in documents (content) too.
10388 * Returned hash and timestamp require documents to be verifiable. */
10389 isds_error isds_get_received_envelope(struct isds_ctx *context,
10390 const char *message_id, struct isds_message **message) {
10392 isds_error err = IE_SUCCESS;
10393 #if HAVE_LIBCURL
10394 xmlDocPtr response = NULL;
10395 xmlChar *code = NULL, *status_message = NULL;
10396 xmlXPathContextPtr xpath_ctx = NULL;
10397 xmlXPathObjectPtr result = NULL;
10398 #endif
10400 if (!context) return IE_INVALID_CONTEXT;
10401 zfree(context->long_message);
10403 /* Free former message if any */
10404 if (!message) return IE_INVAL;
10405 isds_message_free(message);
10407 #if HAVE_LIBCURL
10408 /* Do request and check for success */
10409 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10410 BAD_CAST "MessageEnvelopeDownload", message_id,
10411 &response, NULL, NULL, &code, &status_message);
10412 if (err) goto leave;
10414 /* Extract data */
10415 xpath_ctx = xmlXPathNewContext(response);
10416 if (!xpath_ctx) {
10417 err = IE_ERROR;
10418 goto leave;
10420 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10421 err = IE_ERROR;
10422 goto leave;
10424 result = xmlXPathEvalExpression(
10425 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
10426 "isds:dmReturnedMessageEnvelope",
10427 xpath_ctx);
10428 if (!result) {
10429 err = IE_ERROR;
10430 goto leave;
10432 /* Empty response */
10433 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10434 char *message_id_locale = _isds_utf82locale((char*) message_id);
10435 isds_printf_message(context,
10436 _("Server did not return any envelope for ID `%s' "
10437 "on MessageEnvelopeDownload request"), message_id_locale);
10438 free(message_id_locale);
10439 err = IE_ISDS;
10440 goto leave;
10442 /* More envelops */
10443 if (result->nodesetval->nodeNr > 1) {
10444 char *message_id_locale = _isds_utf82locale((char*) message_id);
10445 isds_printf_message(context,
10446 _("Server did return more envelopes for ID `%s' "
10447 "on MessageEnvelopeDownload request"), message_id_locale);
10448 free(message_id_locale);
10449 err = IE_ISDS;
10450 goto leave;
10452 /* One message */
10453 xpath_ctx->node = result->nodesetval->nodeTab[0];
10455 /* Extract the envelope (= message without documents, hence 0) */
10456 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10457 if (err) goto leave;
10459 /* Save XML blob */
10460 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10461 &(*message)->raw_length);
10463 leave:
10464 if (err) {
10465 isds_message_free(message);
10468 xmlXPathFreeObject(result);
10469 xmlXPathFreeContext(xpath_ctx);
10471 free(code);
10472 free(status_message);
10473 if (!*message || !(*message)->xml) {
10474 xmlFreeDoc(response);
10477 if (!err)
10478 isds_log(ILF_ISDS, ILL_DEBUG,
10479 _("MessageEnvelopeDownload request processed by server "
10480 "successfully.\n")
10482 #else /* not HAVE_LIBCURL */
10483 err = IE_NOTSUP;
10484 #endif
10485 return err;
10489 /* Load delivery info of any format from buffer.
10490 * @context is session context
10491 * @raw_type advertises format of @buffer content. Only delivery info types
10492 * are accepted.
10493 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10494 * retrieve such data from message->raw after calling
10495 * isds_get_signed_delivery_info().
10496 * @length is length of buffer in bytes.
10497 * @message is automatically reallocated message parsed from @buffer.
10498 * @strategy selects how buffer will be attached into raw isds_message member.
10499 * */
10500 isds_error isds_load_delivery_info(struct isds_ctx *context,
10501 const isds_raw_type raw_type,
10502 const void *buffer, const size_t length,
10503 struct isds_message **message, const isds_buffer_strategy strategy) {
10505 isds_error err = IE_SUCCESS;
10506 message_ns_type message_ns;
10507 xmlDocPtr message_doc = NULL;
10508 xmlXPathContextPtr xpath_ctx = NULL;
10509 xmlXPathObjectPtr result = NULL;
10510 void *xml_stream = NULL;
10511 size_t xml_stream_length = 0;
10513 if (!context) return IE_INVALID_CONTEXT;
10514 zfree(context->long_message);
10515 if (!message) return IE_INVAL;
10516 isds_message_free(message);
10517 if (!buffer) return IE_INVAL;
10520 /* Select buffer format and extract XML from CMS*/
10521 switch (raw_type) {
10522 case RAWTYPE_DELIVERYINFO:
10523 message_ns = MESSAGE_NS_UNSIGNED;
10524 xml_stream = (void *) buffer;
10525 xml_stream_length = length;
10526 break;
10528 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10529 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10530 xml_stream = (void *) buffer;
10531 xml_stream_length = length;
10532 break;
10534 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10535 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10536 err = _isds_extract_cms_data(context, buffer, length,
10537 &xml_stream, &xml_stream_length);
10538 if (err) goto leave;
10539 break;
10541 default:
10542 isds_log_message(context, _("Bad raw delivery representation type"));
10543 return IE_INVAL;
10544 break;
10547 if (_isds_sizet2int(xml_stream_length) >= 0) {
10548 isds_log(ILF_ISDS, ILL_DEBUG,
10549 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10550 _isds_sizet2int(xml_stream_length), xml_stream);
10553 /* Convert delivery info XML stream into XPath context */
10554 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10555 if (!message_doc) {
10556 err = IE_XML;
10557 goto leave;
10559 xpath_ctx = xmlXPathNewContext(message_doc);
10560 if (!xpath_ctx) {
10561 err = IE_ERROR;
10562 goto leave;
10564 /* XXX: Name spaces mangled for signed delivery info:
10565 * http://isds.czechpoint.cz/v20/delivery:
10567 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10568 * <q:dmDelivery>
10569 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10570 * <p:dmID>170272</p:dmID>
10571 * ...
10572 * </p:dmDm>
10573 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10574 * ...
10575 * </q:dmEvents>...</q:dmEvents>
10576 * </q:dmDelivery>
10577 * </q:GetDeliveryInfoResponse>
10578 * */
10579 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10580 err = IE_ERROR;
10581 goto leave;
10583 result = xmlXPathEvalExpression(
10584 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10585 xpath_ctx);
10586 if (!result) {
10587 err = IE_ERROR;
10588 goto leave;
10590 /* Empty delivery info */
10591 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10592 isds_printf_message(context,
10593 _("XML document is not sisds:dmDelivery document"));
10594 err = IE_ISDS;
10595 goto leave;
10597 /* More delivery info's */
10598 if (result->nodesetval->nodeNr > 1) {
10599 isds_printf_message(context,
10600 _("XML document has more sisds:dmDelivery elements"));
10601 err = IE_ISDS;
10602 goto leave;
10604 /* One delivery info */
10605 xpath_ctx->node = result->nodesetval->nodeTab[0];
10607 /* Extract the envelope (= message without documents, hence 0).
10608 * XXX: extract_TReturnedMessage() can obtain attachments size,
10609 * but delivery info carries none. It's coded as option elements,
10610 * so it should work. */
10611 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10612 if (err) goto leave;
10614 /* Extract events */
10615 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10616 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10617 if (err) { err = IE_ERROR; goto leave; }
10618 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10619 if (err) goto leave;
10621 /* Append raw CMS structure into message */
10622 (*message)->raw_type = raw_type;
10623 switch (strategy) {
10624 case BUFFER_DONT_STORE:
10625 break;
10626 case BUFFER_COPY:
10627 (*message)->raw = malloc(length);
10628 if (!(*message)->raw) {
10629 err = IE_NOMEM;
10630 goto leave;
10632 memcpy((*message)->raw, buffer, length);
10633 (*message)->raw_length = length;
10634 break;
10635 case BUFFER_MOVE:
10636 (*message)->raw = (void *) buffer;
10637 (*message)->raw_length = length;
10638 break;
10639 default:
10640 err = IE_ENUM;
10641 goto leave;
10644 leave:
10645 if (err) {
10646 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10647 isds_message_free(message);
10650 xmlXPathFreeObject(result);
10651 xmlXPathFreeContext(xpath_ctx);
10652 if (!*message || !(*message)->xml) {
10653 xmlFreeDoc(message_doc);
10655 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10657 if (!err)
10658 isds_log(ILF_ISDS, ILL_DEBUG,
10659 _("Delivery info loaded successfully.\n"));
10660 return err;
10664 /* Download signed delivery info-sheet of given message identified by ID.
10665 * @context is session context
10666 * @message_id is message identifier (you can get them from
10667 * isds_get_list_of_{sent,received}_messages())
10668 * @message is automatically reallocated message retrieved from ISDS.
10669 * It will miss documents per se. Use isds_get_signed_received_message(),
10670 * if you are interested in documents (content). OTOH, only this function
10671 * can get list events message has gone through. */
10672 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10673 const char *message_id, struct isds_message **message) {
10675 isds_error err = IE_SUCCESS;
10676 #if HAVE_LIBCURL
10677 xmlDocPtr response = NULL;
10678 xmlChar *code = NULL, *status_message = NULL;
10679 void *raw = NULL;
10680 size_t raw_length = 0;
10681 #endif
10683 if (!context) return IE_INVALID_CONTEXT;
10684 zfree(context->long_message);
10686 /* Free former message if any */
10687 if (!message) return IE_INVAL;
10688 isds_message_free(message);
10690 #if HAVE_LIBCURL
10691 /* Do request and check for success */
10692 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10693 BAD_CAST "GetSignedDeliveryInfo", message_id,
10694 &response, NULL, NULL, &code, &status_message);
10695 if (err) goto leave;
10697 /* Find signed delivery info, extract it into raw and maybe free
10698 * response */
10699 err = find_extract_signed_data_free_response(context,
10700 (xmlChar *)message_id, &response,
10701 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10702 if (err) goto leave;
10704 /* Parse delivery info */
10705 err = isds_load_delivery_info(context,
10706 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10707 message, BUFFER_MOVE);
10708 if (err) goto leave;
10710 raw = NULL;
10712 leave:
10713 if (err) {
10714 isds_message_free(message);
10717 free(raw);
10718 free(code);
10719 free(status_message);
10720 xmlFreeDoc(response);
10722 if (!err)
10723 isds_log(ILF_ISDS, ILL_DEBUG,
10724 _("GetSignedDeliveryInfo request processed by server "
10725 "successfully.\n")
10727 #else /* not HAVE_LIBCURL */
10728 err = IE_NOTSUP;
10729 #endif
10730 return err;
10734 /* Download delivery info-sheet of given message identified by ID.
10735 * @context is session context
10736 * @message_id is message identifier (you can get them from
10737 * isds_get_list_of_{sent,received}_messages())
10738 * @message is automatically reallocated message retrieved from ISDS.
10739 * It will miss documents per se. Use isds_get_received_message(), if you are
10740 * interested in documents (content). OTOH, only this function can get list
10741 * of events message has gone through. */
10742 isds_error isds_get_delivery_info(struct isds_ctx *context,
10743 const char *message_id, struct isds_message **message) {
10745 isds_error err = IE_SUCCESS;
10746 #if HAVE_LIBCURL
10747 xmlDocPtr response = NULL;
10748 xmlChar *code = NULL, *status_message = NULL;
10749 xmlNodePtr delivery_node = NULL;
10750 void *raw = NULL;
10751 size_t raw_length = 0;
10752 #endif
10754 if (!context) return IE_INVALID_CONTEXT;
10755 zfree(context->long_message);
10757 /* Free former message if any */
10758 if (!message) return IE_INVAL;
10759 isds_message_free(message);
10761 #if HAVE_LIBCURL
10762 /* Do request and check for success */
10763 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10764 BAD_CAST "GetDeliveryInfo", message_id,
10765 &response, NULL, NULL, &code, &status_message);
10766 if (err) goto leave;
10769 /* Serialize delivery info */
10770 delivery_node = xmlDocGetRootElement(response);
10771 if (!delivery_node) {
10772 char *message_id_locale = _isds_utf82locale((char*) message_id);
10773 isds_printf_message(context,
10774 _("Server did not return any delivery info for ID `%s' "
10775 "on GetDeliveryInfo request"), message_id_locale);
10776 free(message_id_locale);
10777 err = IE_ISDS;
10778 goto leave;
10780 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10781 if (err) goto leave;
10783 /* Parse delivery info */
10784 /* TODO: Here we parse the response second time. We could single delivery
10785 * parser from isds_load_delivery_info() to make things faster. */
10786 err = isds_load_delivery_info(context,
10787 RAWTYPE_DELIVERYINFO, raw, raw_length,
10788 message, BUFFER_MOVE);
10789 if (err) goto leave;
10791 raw = NULL;
10794 leave:
10795 if (err) {
10796 isds_message_free(message);
10799 free(raw);
10800 free(code);
10801 free(status_message);
10802 xmlFreeDoc(response);
10804 if (!err)
10805 isds_log(ILF_ISDS, ILL_DEBUG,
10806 _("GetDeliveryInfo request processed by server "
10807 "successfully.\n")
10809 #else /* not HAVE_LIBCURL */
10810 err = IE_NOTSUP;
10811 #endif
10812 return err;
10816 /* Download incoming message identified by ID.
10817 * @context is session context
10818 * @message_id is message identifier (you can get them from
10819 * isds_get_list_of_received_messages())
10820 * @message is automatically reallocated message retrieved from ISDS */
10821 isds_error isds_get_received_message(struct isds_ctx *context,
10822 const char *message_id, struct isds_message **message) {
10824 isds_error err = IE_SUCCESS;
10825 #if HAVE_LIBCURL
10826 xmlDocPtr response = NULL;
10827 void *xml_stream = NULL;
10828 size_t xml_stream_length;
10829 xmlChar *code = NULL, *status_message = NULL;
10830 xmlXPathContextPtr xpath_ctx = NULL;
10831 xmlXPathObjectPtr result = NULL;
10832 char *phys_path = NULL;
10833 size_t phys_start, phys_end;
10834 #endif
10836 if (!context) return IE_INVALID_CONTEXT;
10837 zfree(context->long_message);
10839 /* Free former message if any */
10840 if (NULL == message) return IE_INVAL;
10841 if (message) isds_message_free(message);
10843 #if HAVE_LIBCURL
10844 /* Do request and check for success */
10845 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10846 BAD_CAST "MessageDownload", message_id,
10847 &response, &xml_stream, &xml_stream_length,
10848 &code, &status_message);
10849 if (err) goto leave;
10851 /* Extract data */
10852 xpath_ctx = xmlXPathNewContext(response);
10853 if (!xpath_ctx) {
10854 err = IE_ERROR;
10855 goto leave;
10857 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10858 err = IE_ERROR;
10859 goto leave;
10861 result = xmlXPathEvalExpression(
10862 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10863 xpath_ctx);
10864 if (!result) {
10865 err = IE_ERROR;
10866 goto leave;
10868 /* Empty response */
10869 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10870 char *message_id_locale = _isds_utf82locale((char*) message_id);
10871 isds_printf_message(context,
10872 _("Server did not return any message for ID `%s' "
10873 "on MessageDownload request"), message_id_locale);
10874 free(message_id_locale);
10875 err = IE_ISDS;
10876 goto leave;
10878 /* More messages */
10879 if (result->nodesetval->nodeNr > 1) {
10880 char *message_id_locale = _isds_utf82locale((char*) message_id);
10881 isds_printf_message(context,
10882 _("Server did return more messages for ID `%s' "
10883 "on MessageDownload request"), message_id_locale);
10884 free(message_id_locale);
10885 err = IE_ISDS;
10886 goto leave;
10888 /* One message */
10889 xpath_ctx->node = result->nodesetval->nodeTab[0];
10891 /* Extract the message */
10892 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10893 if (err) goto leave;
10895 /* Locate raw XML blob */
10896 phys_path = strdup(
10897 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10898 PHYSXML_ELEMENT_SEPARATOR
10899 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10900 PHYSXML_ELEMENT_SEPARATOR
10901 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10903 if (!phys_path) {
10904 err = IE_NOMEM;
10905 goto leave;
10907 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10908 phys_path, &phys_start, &phys_end);
10909 zfree(phys_path);
10910 if (err) {
10911 isds_log_message(context,
10912 _("Substring with isds:MessageDownloadResponse element "
10913 "could not be located in raw SOAP message"));
10914 goto leave;
10916 /* Save XML blob */
10917 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10918 &(*message)->raw_length);*/
10919 /* TODO: Store name space declarations from ancestors */
10920 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10921 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10922 (*message)->raw_length = phys_end - phys_start + 1;
10923 (*message)->raw = malloc((*message)->raw_length);
10924 if (!(*message)->raw) {
10925 err = IE_NOMEM;
10926 goto leave;
10928 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10931 leave:
10932 if (err) {
10933 isds_message_free(message);
10936 free(phys_path);
10938 xmlXPathFreeObject(result);
10939 xmlXPathFreeContext(xpath_ctx);
10941 free(code);
10942 free(status_message);
10943 free(xml_stream);
10944 if (!*message || !(*message)->xml) {
10945 xmlFreeDoc(response);
10948 if (!err)
10949 isds_log(ILF_ISDS, ILL_DEBUG,
10950 _("MessageDownload request processed by server "
10951 "successfully.\n")
10953 #else /* not HAVE_LIBCURL */
10954 err = IE_NOTSUP;
10955 #endif
10956 return err;
10960 /* Load message of any type from buffer.
10961 * @context is session context
10962 * @raw_type defines content type of @buffer. Only message types are allowed.
10963 * @buffer is message raw representation. Format (CMS, plain signed,
10964 * message direction) is defined in @raw_type. You can retrieve such data
10965 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10966 * @length is length of buffer in bytes.
10967 * @message is automatically reallocated message parsed from @buffer.
10968 * @strategy selects how buffer will be attached into raw isds_message member.
10969 * */
10970 isds_error isds_load_message(struct isds_ctx *context,
10971 const isds_raw_type raw_type, const void *buffer, const size_t length,
10972 struct isds_message **message, const isds_buffer_strategy strategy) {
10974 isds_error err = IE_SUCCESS;
10975 void *xml_stream = NULL;
10976 size_t xml_stream_length = 0;
10977 message_ns_type message_ns;
10978 xmlDocPtr message_doc = NULL;
10979 xmlXPathContextPtr xpath_ctx = NULL;
10980 xmlXPathObjectPtr result = NULL;
10982 if (!context) return IE_INVALID_CONTEXT;
10983 zfree(context->long_message);
10984 if (!message) return IE_INVAL;
10985 isds_message_free(message);
10986 if (!buffer) return IE_INVAL;
10989 /* Select buffer format and extract XML from CMS*/
10990 switch (raw_type) {
10991 case RAWTYPE_INCOMING_MESSAGE:
10992 message_ns = MESSAGE_NS_UNSIGNED;
10993 xml_stream = (void *) buffer;
10994 xml_stream_length = length;
10995 break;
10997 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10998 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10999 xml_stream = (void *) buffer;
11000 xml_stream_length = length;
11001 break;
11003 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11004 message_ns = MESSAGE_NS_SIGNED_INCOMING;
11005 err = _isds_extract_cms_data(context, buffer, length,
11006 &xml_stream, &xml_stream_length);
11007 if (err) goto leave;
11008 break;
11010 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11011 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11012 xml_stream = (void *) buffer;
11013 xml_stream_length = length;
11014 break;
11016 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11017 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11018 err = _isds_extract_cms_data(context, buffer, length,
11019 &xml_stream, &xml_stream_length);
11020 if (err) goto leave;
11021 break;
11023 default:
11024 isds_log_message(context, _("Bad raw message representation type"));
11025 return IE_INVAL;
11026 break;
11029 if (_isds_sizet2int(xml_stream_length) >= 0) {
11030 isds_log(ILF_ISDS, ILL_DEBUG,
11031 _("Loading message:\n%.*s\nEnd of message\n"),
11032 _isds_sizet2int(xml_stream_length), xml_stream);
11035 /* Convert messages XML stream into XPath context */
11036 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
11037 if (!message_doc) {
11038 err = IE_XML;
11039 goto leave;
11041 xpath_ctx = xmlXPathNewContext(message_doc);
11042 if (!xpath_ctx) {
11043 err = IE_ERROR;
11044 goto leave;
11046 /* XXX: Standard name space for unsigned incoming direction:
11047 * http://isds.czechpoint.cz/v20/
11049 * XXX: Name spaces mangled for signed outgoing direction:
11050 * http://isds.czechpoint.cz/v20/SentMessage:
11052 * <q:MessageDownloadResponse
11053 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11054 * <q:dmReturnedMessage>
11055 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11056 * <p:dmID>151916</p:dmID>
11057 * ...
11058 * </p:dmDm>
11059 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11060 * ...
11061 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11062 * </q:dmReturnedMessage>
11063 * </q:MessageDownloadResponse>
11065 * XXX: Name spaces mangled for signed incoming direction:
11066 * http://isds.czechpoint.cz/v20/message:
11068 * <q:MessageDownloadResponse
11069 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11070 * <q:dmReturnedMessage>
11071 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11072 * <p:dmID>151916</p:dmID>
11073 * ...
11074 * </p:dmDm>
11075 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11076 * ...
11077 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11078 * </q:dmReturnedMessage>
11079 * </q:MessageDownloadResponse>
11081 * Stupidity of ISDS developers is unlimited */
11082 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
11083 err = IE_ERROR;
11084 goto leave;
11086 result = xmlXPathEvalExpression(
11087 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11088 xpath_ctx);
11089 if (!result) {
11090 err = IE_ERROR;
11091 goto leave;
11093 /* Empty message */
11094 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11095 isds_printf_message(context,
11096 _("XML document does not contain "
11097 "sisds:dmReturnedMessage element"));
11098 err = IE_ISDS;
11099 goto leave;
11101 /* More messages */
11102 if (result->nodesetval->nodeNr > 1) {
11103 isds_printf_message(context,
11104 _("XML document has more sisds:dmReturnedMessage elements"));
11105 err = IE_ISDS;
11106 goto leave;
11108 /* One message */
11109 xpath_ctx->node = result->nodesetval->nodeTab[0];
11111 /* Extract the message */
11112 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
11113 if (err) goto leave;
11115 /* Append raw buffer into message */
11116 (*message)->raw_type = raw_type;
11117 switch (strategy) {
11118 case BUFFER_DONT_STORE:
11119 break;
11120 case BUFFER_COPY:
11121 (*message)->raw = malloc(length);
11122 if (!(*message)->raw) {
11123 err = IE_NOMEM;
11124 goto leave;
11126 memcpy((*message)->raw, buffer, length);
11127 (*message)->raw_length = length;
11128 break;
11129 case BUFFER_MOVE:
11130 (*message)->raw = (void *) buffer;
11131 (*message)->raw_length = length;
11132 break;
11133 default:
11134 err = IE_ENUM;
11135 goto leave;
11139 leave:
11140 if (err) {
11141 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
11142 isds_message_free(message);
11145 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11146 xmlXPathFreeObject(result);
11147 xmlXPathFreeContext(xpath_ctx);
11148 if (!*message || !(*message)->xml) {
11149 xmlFreeDoc(message_doc);
11152 if (!err)
11153 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
11154 return err;
11158 /* Determine type of raw message or delivery info according some heuristics.
11159 * It does not validate the raw blob.
11160 * @context is session context
11161 * @raw_type returns content type of @buffer. Valid only if exit code of this
11162 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11163 * reallocated memory.
11164 * @buffer is message raw representation.
11165 * @length is length of buffer in bytes. */
11166 isds_error isds_guess_raw_type(struct isds_ctx *context,
11167 isds_raw_type *raw_type, const void *buffer, const size_t length) {
11168 isds_error err;
11169 void *xml_stream = NULL;
11170 size_t xml_stream_length = 0;
11171 xmlDocPtr document = NULL;
11172 xmlNodePtr root = NULL;
11174 if (!context) return IE_INVALID_CONTEXT;
11175 zfree(context->long_message);
11176 if (length == 0 || !buffer) return IE_INVAL;
11177 if (!raw_type) return IE_INVAL;
11179 /* Try CMS */
11180 err = _isds_extract_cms_data(context, buffer, length,
11181 &xml_stream, &xml_stream_length);
11182 if (err) {
11183 xml_stream = (void *) buffer;
11184 xml_stream_length = (size_t) length;
11185 err = IE_SUCCESS;
11188 /* Try XML */
11189 document = xmlParseMemory(xml_stream, xml_stream_length);
11190 if (!document) {
11191 isds_printf_message(context,
11192 _("Could not parse data as XML document"));
11193 err = IE_NOTSUP;
11194 goto leave;
11197 /* Get root element */
11198 root = xmlDocGetRootElement(document);
11199 if (!root) {
11200 isds_printf_message(context,
11201 _("XML document is missing root element"));
11202 err = IE_XML;
11203 goto leave;
11206 if (!root->ns || !root->ns->href) {
11207 isds_printf_message(context,
11208 _("Root element does not belong to any name space"));
11209 err = IE_NOTSUP;
11210 goto leave;
11213 /* Test name space */
11214 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
11215 if (xml_stream == buffer)
11216 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
11217 else
11218 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
11219 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
11220 if (xml_stream == buffer)
11221 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
11222 else
11223 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
11224 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
11225 if (xml_stream == buffer)
11226 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
11227 else
11228 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
11229 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
11230 if (xml_stream != buffer) {
11231 isds_printf_message(context,
11232 _("Document in ISDS name space is encapsulated into CMS" ));
11233 err = IE_NOTSUP;
11234 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
11235 *raw_type = RAWTYPE_INCOMING_MESSAGE;
11236 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
11237 *raw_type = RAWTYPE_DELIVERYINFO;
11238 else {
11239 isds_printf_message(context,
11240 _("Unknown root element in ISDS name space"));
11241 err = IE_NOTSUP;
11243 } else {
11244 isds_printf_message(context,
11245 _("Unknown name space"));
11246 err = IE_NOTSUP;
11249 leave:
11250 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11251 xmlFreeDoc(document);
11252 return err;
11256 /* Download signed incoming/outgoing message identified by ID.
11257 * @context is session context
11258 * @output is true for outgoing message, false for incoming message
11259 * @message_id is message identifier (you can get them from
11260 * isds_get_list_of_{sent,received}_messages())
11261 * @message is automatically reallocated message retrieved from ISDS. The raw
11262 * member will be filled with PKCS#7 structure in DER format. */
11263 static isds_error isds_get_signed_message(struct isds_ctx *context,
11264 const _Bool outgoing, const char *message_id,
11265 struct isds_message **message) {
11267 isds_error err = IE_SUCCESS;
11268 #if HAVE_LIBCURL
11269 xmlDocPtr response = NULL;
11270 xmlChar *code = NULL, *status_message = NULL;
11271 xmlXPathContextPtr xpath_ctx = NULL;
11272 xmlXPathObjectPtr result = NULL;
11273 char *encoded_structure = NULL;
11274 void *raw = NULL;
11275 size_t raw_length = 0;
11276 #endif
11278 if (!context) return IE_INVALID_CONTEXT;
11279 zfree(context->long_message);
11280 if (!message) return IE_INVAL;
11281 isds_message_free(message);
11283 #if HAVE_LIBCURL
11284 /* Do request and check for success */
11285 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
11286 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11287 BAD_CAST "SignedMessageDownload",
11288 message_id, &response, NULL, NULL, &code, &status_message);
11289 if (err) goto leave;
11291 /* Find signed message, extract it into raw and maybe free
11292 * response */
11293 err = find_extract_signed_data_free_response(context,
11294 (xmlChar *)message_id, &response,
11295 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11296 BAD_CAST "SignedMessageDownload",
11297 &raw, &raw_length);
11298 if (err) goto leave;
11300 /* Parse message */
11301 err = isds_load_message(context,
11302 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
11303 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
11304 raw, raw_length, message, BUFFER_MOVE);
11305 if (err) goto leave;
11307 raw = NULL;
11309 leave:
11310 if (err) {
11311 isds_message_free(message);
11314 free(encoded_structure);
11315 xmlXPathFreeObject(result);
11316 xmlXPathFreeContext(xpath_ctx);
11317 free(raw);
11319 free(code);
11320 free(status_message);
11321 xmlFreeDoc(response);
11323 if (!err)
11324 isds_log(ILF_ISDS, ILL_DEBUG,
11325 (outgoing) ?
11326 _("SignedSentMessageDownload request processed by server "
11327 "successfully.\n") :
11328 _("SignedMessageDownload request processed by server "
11329 "successfully.\n")
11331 #else /* not HAVE_LIBCURL */
11332 err = IE_NOTSUP;
11333 #endif
11334 return err;
11338 /* Download signed incoming 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_received_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_received_message(struct isds_ctx *context,
11345 const char *message_id, struct isds_message **message) {
11346 return isds_get_signed_message(context, 0, message_id, message);
11350 /* Download signed outgoing message identified by ID.
11351 * @context is session context
11352 * @message_id is message identifier (you can get them from
11353 * isds_get_list_of_sent_messages())
11354 * @message is automatically reallocated message retrieved from ISDS. The raw
11355 * member will be filled with PKCS#7 structure in DER format. */
11356 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
11357 const char *message_id, struct isds_message **message) {
11358 return isds_get_signed_message(context, 1, message_id, message);
11362 /* Get type and name of user who sent a message identified by ID.
11363 * @context is session context
11364 * @message_id is message identifier
11365 * @sender_type is pointer to automatically allocated type of sender detected
11366 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11367 * library or to the server, NULL will be returned. Pass NULL if you don't
11368 * care about it.
11369 * @raw_sender_type is automatically reallocated UTF-8 string describing
11370 * sender type or NULL if not known to server. Pass NULL if you don't care.
11371 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11372 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11373 isds_error isds_get_message_sender(struct isds_ctx *context,
11374 const char *message_id, isds_sender_type **sender_type,
11375 char **raw_sender_type, char **sender_name) {
11376 isds_error err = IE_SUCCESS;
11377 #if HAVE_LIBCURL
11378 xmlDocPtr response = NULL;
11379 xmlChar *code = NULL, *status_message = NULL;
11380 xmlXPathContextPtr xpath_ctx = NULL;
11381 xmlXPathObjectPtr result = NULL;
11382 char *type_string = NULL;
11383 #endif
11385 if (!context) return IE_INVALID_CONTEXT;
11386 zfree(context->long_message);
11387 if (sender_type) zfree(*sender_type);
11388 if (raw_sender_type) zfree(*raw_sender_type);
11389 if (sender_name) zfree(*sender_name);
11390 if (!message_id) return IE_INVAL;
11392 #if HAVE_LIBCURL
11393 /* Do request and check for success */
11394 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11395 BAD_CAST "GetMessageAuthor",
11396 message_id, &response, NULL, NULL, &code, &status_message);
11397 if (err) goto leave;
11399 /* Extract data */
11400 xpath_ctx = xmlXPathNewContext(response);
11401 if (!xpath_ctx) {
11402 err = IE_ERROR;
11403 goto leave;
11405 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11406 err = IE_ERROR;
11407 goto leave;
11409 result = xmlXPathEvalExpression(
11410 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
11411 if (!result) {
11412 err = IE_ERROR;
11413 goto leave;
11415 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11416 isds_log_message(context,
11417 _("Missing GetMessageAuthorResponse element"));
11418 err = IE_ISDS;
11419 goto leave;
11421 if (result->nodesetval->nodeNr > 1) {
11422 isds_log_message(context,
11423 _("Multiple GetMessageAuthorResponse element"));
11424 err = IE_ISDS;
11425 goto leave;
11427 xpath_ctx->node = result->nodesetval->nodeTab[0];
11428 xmlXPathFreeObject(result); result = NULL;
11430 /* Fill output arguments in */
11431 EXTRACT_STRING("isds:userType", type_string);
11432 if (NULL != type_string) {
11433 if (NULL != sender_type) {
11434 *sender_type = calloc(1, sizeof(**sender_type));
11435 if (NULL == *sender_type) {
11436 err = IE_NOMEM;
11437 goto leave;
11440 err = string2isds_sender_type((xmlChar *)type_string,
11441 *sender_type);
11442 if (err) {
11443 zfree(*sender_type);
11444 if (err == IE_ENUM) {
11445 err = IE_SUCCESS;
11446 char *type_string_locale = _isds_utf82locale(type_string);
11447 isds_log(ILF_ISDS, ILL_WARNING,
11448 _("Unknown isds:userType value: %s"),
11449 type_string_locale);
11450 free(type_string_locale);
11455 if (NULL != sender_name)
11456 EXTRACT_STRING("isds:authorName", *sender_name);
11458 leave:
11459 if (err) {
11460 if (NULL != sender_type) zfree(*sender_type);
11461 zfree(type_string);
11462 if (NULL != sender_name) zfree(*sender_name);
11464 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11466 xmlXPathFreeObject(result);
11467 xmlXPathFreeContext(xpath_ctx);
11469 free(code);
11470 free(status_message);
11471 xmlFreeDoc(response);
11473 if (!err)
11474 isds_log(ILF_ISDS, ILL_DEBUG,
11475 _("GetMessageAuthor request processed by server "
11476 "successfully.\n"));
11477 #else /* not HAVE_LIBCURL */
11478 err = IE_NOTSUP;
11479 #endif
11480 return err;
11484 /* Retrieve hash of message identified by ID stored in ISDS.
11485 * @context is session context
11486 * @message_id is message identifier
11487 * @hash is automatically reallocated message hash downloaded from ISDS.
11488 * Message must exist in system and must not be deleted. */
11489 isds_error isds_download_message_hash(struct isds_ctx *context,
11490 const char *message_id, struct isds_hash **hash) {
11492 isds_error err = IE_SUCCESS;
11493 #if HAVE_LIBCURL
11494 xmlDocPtr response = NULL;
11495 xmlChar *code = NULL, *status_message = NULL;
11496 xmlXPathContextPtr xpath_ctx = NULL;
11497 xmlXPathObjectPtr result = NULL;
11498 #endif
11500 if (!context) return IE_INVALID_CONTEXT;
11501 zfree(context->long_message);
11503 isds_hash_free(hash);
11505 #if HAVE_LIBCURL
11506 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11507 BAD_CAST "VerifyMessage", message_id,
11508 &response, NULL, NULL, &code, &status_message);
11509 if (err) goto leave;
11512 /* Extract data */
11513 xpath_ctx = xmlXPathNewContext(response);
11514 if (!xpath_ctx) {
11515 err = IE_ERROR;
11516 goto leave;
11518 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11519 err = IE_ERROR;
11520 goto leave;
11522 result = xmlXPathEvalExpression(
11523 BAD_CAST "/isds:VerifyMessageResponse",
11524 xpath_ctx);
11525 if (!result) {
11526 err = IE_ERROR;
11527 goto leave;
11529 /* Empty response */
11530 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11531 char *message_id_locale = _isds_utf82locale((char*) message_id);
11532 isds_printf_message(context,
11533 _("Server did not return any response for ID `%s' "
11534 "on VerifyMessage request"), message_id_locale);
11535 free(message_id_locale);
11536 err = IE_ISDS;
11537 goto leave;
11539 /* More responses */
11540 if (result->nodesetval->nodeNr > 1) {
11541 char *message_id_locale = _isds_utf82locale((char*) message_id);
11542 isds_printf_message(context,
11543 _("Server did return more responses for ID `%s' "
11544 "on VerifyMessage request"), message_id_locale);
11545 free(message_id_locale);
11546 err = IE_ISDS;
11547 goto leave;
11549 /* One response */
11550 xpath_ctx->node = result->nodesetval->nodeTab[0];
11552 /* Extract the hash */
11553 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11555 leave:
11556 if (err) {
11557 isds_hash_free(hash);
11560 xmlXPathFreeObject(result);
11561 xmlXPathFreeContext(xpath_ctx);
11563 free(code);
11564 free(status_message);
11565 xmlFreeDoc(response);
11567 if (!err)
11568 isds_log(ILF_ISDS, ILL_DEBUG,
11569 _("VerifyMessage request processed by server "
11570 "successfully.\n")
11572 #else /* not HAVE_LIBCURL */
11573 err = IE_NOTSUP;
11574 #endif
11575 return err;
11579 /* Erase message specified by @message_id from long term storage. Other
11580 * message cannot be erased on user request.
11581 * @context is session context
11582 * @message_id is message identifier.
11583 * @incoming is true for incoming message, false for outgoing message.
11584 * @return
11585 * IE_SUCCESS if message has ben removed
11586 * IE_INVAL if message does not exist in long term storage or message
11587 * belongs to different box
11588 * TODO: IE_NOEPRM if user has no permission to erase a message */
11589 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11590 const char *message_id, _Bool incoming) {
11591 isds_error err = IE_SUCCESS;
11592 #if HAVE_LIBCURL
11593 xmlNodePtr request = NULL, node;
11594 xmlNsPtr isds_ns = NULL;
11595 xmlDocPtr response = NULL;
11596 xmlChar *code = NULL, *status_message = NULL;
11597 #endif
11599 if (!context) return IE_INVALID_CONTEXT;
11600 zfree(context->long_message);
11601 if (NULL == message_id) return IE_INVAL;
11603 #if HAVE_LIBCURL
11604 /* Check if connection is established
11605 * TODO: This check should be done downstairs. */
11606 if (!context->curl) return IE_CONNECTION_CLOSED;
11608 /* Build request */
11609 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11610 if (!request) {
11611 isds_log_message(context,
11612 _("Could build EraseMessage request"));
11613 return IE_ERROR;
11615 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11616 if(!isds_ns) {
11617 isds_log_message(context, _("Could not create ISDS name space"));
11618 xmlFreeNode(request);
11619 return IE_ERROR;
11621 xmlSetNs(request, isds_ns);
11623 err = validate_message_id_length(context, (xmlChar *) message_id);
11624 if (err) goto leave;
11625 INSERT_STRING(request, "dmID", message_id);
11627 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11630 /* Send request */
11631 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11632 "message ID %s to ISDS\n"), message_id);
11633 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11634 xmlFreeNode(request); request = NULL;
11636 if (err) {
11637 isds_log(ILF_ISDS, ILL_DEBUG,
11638 _("Processing ISDS response on EraseMessage request "
11639 "failed\n"));
11640 goto leave;
11643 /* Check for response status */
11644 err = isds_response_status(context, SERVICE_DM_INFO, response,
11645 &code, &status_message, NULL);
11646 if (err) {
11647 isds_log(ILF_ISDS, ILL_DEBUG,
11648 _("ISDS response on EraseMessage request is missing "
11649 "status\n"));
11650 goto leave;
11653 /* Check server status code */
11654 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11655 isds_log_message(context, _("Message to erase belongs to other box"));
11656 err = IE_INVAL;
11657 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11658 isds_log_message(context, _("Message to erase is not saved in "
11659 "long term storage or the direction does not match"));
11660 err = IE_INVAL;
11661 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11662 char *code_locale = _isds_utf82locale((char*) code);
11663 char *message_locale = _isds_utf82locale((char*) status_message);
11664 isds_log(ILF_ISDS, ILL_DEBUG,
11665 _("Server refused EraseMessage request "
11666 "(code=%s, message=%s)\n"),
11667 code_locale, message_locale);
11668 isds_log_message(context, message_locale);
11669 free(code_locale);
11670 free(message_locale);
11671 err = IE_ISDS;
11672 goto leave;
11675 leave:
11676 free(code);
11677 free(status_message);
11678 xmlFreeDoc(response);
11679 xmlFreeNode(request);
11681 if (!err)
11682 isds_log(ILF_ISDS, ILL_DEBUG,
11683 _("EraseMessage request processed by server "
11684 "successfully.\n")
11686 #else /* not HAVE_LIBCURL */
11687 err = IE_NOTSUP;
11688 #endif
11689 return err;
11693 /* Mark message as read. This is a transactional commit function to acknowledge
11694 * to ISDS the message has been downloaded and processed by client properly.
11695 * @context is session context
11696 * @message_id is message identifier. */
11697 isds_error isds_mark_message_read(struct isds_ctx *context,
11698 const char *message_id) {
11700 isds_error err = IE_SUCCESS;
11701 #if HAVE_LIBCURL
11702 xmlDocPtr response = NULL;
11703 xmlChar *code = NULL, *status_message = NULL;
11704 #endif
11706 if (!context) return IE_INVALID_CONTEXT;
11707 zfree(context->long_message);
11709 #if HAVE_LIBCURL
11710 /* Do request and check for success */
11711 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11712 BAD_CAST "MarkMessageAsDownloaded", message_id,
11713 &response, NULL, NULL, &code, &status_message);
11715 free(code);
11716 free(status_message);
11717 xmlFreeDoc(response);
11719 if (!err)
11720 isds_log(ILF_ISDS, ILL_DEBUG,
11721 _("MarkMessageAsDownloaded request processed by server "
11722 "successfully.\n")
11724 #else /* not HAVE_LIBCURL */
11725 err = IE_NOTSUP;
11726 #endif
11727 return err;
11731 /* Mark message as received by recipient. This is applicable only to
11732 * commercial message. Use envelope->dmType message member to distinguish
11733 * commercial message from government message. Government message is
11734 * received automatically (by law), commercial message on recipient request.
11735 * @context is session context
11736 * @message_id is message identifier. */
11737 isds_error isds_mark_message_received(struct isds_ctx *context,
11738 const char *message_id) {
11740 isds_error err = IE_SUCCESS;
11741 #if HAVE_LIBCURL
11742 xmlDocPtr response = NULL;
11743 xmlChar *code = NULL, *status_message = NULL;
11744 #endif
11746 if (!context) return IE_INVALID_CONTEXT;
11747 zfree(context->long_message);
11749 #if HAVE_LIBCURL
11750 /* Do request and check for success */
11751 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11752 BAD_CAST "ConfirmDelivery", message_id,
11753 &response, NULL, NULL, &code, &status_message);
11755 free(code);
11756 free(status_message);
11757 xmlFreeDoc(response);
11759 if (!err)
11760 isds_log(ILF_ISDS, ILL_DEBUG,
11761 _("ConfirmDelivery request processed by server "
11762 "successfully.\n")
11764 #else /* not HAVE_LIBCURL */
11765 err = IE_NOTSUP;
11766 #endif
11767 return err;
11771 /* Send document for authorized conversion into Czech POINT system.
11772 * This is public anonymous service, no log-in necessary. Special context is
11773 * used to reuse keep-a-live HTTPS connection.
11774 * @context is Czech POINT session context. DO NOT use context connected to
11775 * ISDS server. Use new context or context used by this function previously.
11776 * @document is document to convert. Only data, data_length, dmFileDescr and
11777 * is_xml members are significant. Be ware that not all document formats can be
11778 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11779 * @id is reallocated identifier assigned by Czech POINT system to
11780 * your document on submit. Use is to tell it to Czech POINT officer.
11781 * @date is reallocated document submit date (submitted documents
11782 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11783 * value. */
11784 isds_error czp_convert_document(struct isds_ctx *context,
11785 const struct isds_document *document,
11786 char **id, struct tm **date) {
11787 isds_error err = IE_SUCCESS;
11788 #if HAVE_LIBCURL
11789 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11790 xmlNodePtr request = NULL, node;
11791 xmlDocPtr response = NULL;
11793 xmlXPathContextPtr xpath_ctx = NULL;
11794 xmlXPathObjectPtr result = NULL;
11795 long int status = -1;
11796 long int *status_ptr = &status;
11797 char *string = NULL;
11798 #endif
11801 if (!context) return IE_INVALID_CONTEXT;
11802 zfree(context->long_message);
11803 if (!document || !id || !date) return IE_INVAL;
11805 if (document->is_xml) {
11806 isds_log_message(context,
11807 _("XML documents cannot be submitted to conversion"));
11808 return IE_NOTSUP;
11811 /* Free output arguments */
11812 zfree(*id);
11813 zfree(*date);
11815 #if HAVE_LIBCURL
11816 /* Store configuration */
11817 context->type = CTX_TYPE_CZP;
11818 free(context->url);
11819 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11820 if (!(context->url))
11821 return IE_NOMEM;
11823 /* Prepare CURL handle if not yet connected */
11824 if (!context->curl) {
11825 context->curl = curl_easy_init();
11826 if (!(context->curl))
11827 return IE_ERROR;
11830 /* Build conversion request */
11831 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11832 if (!request) {
11833 isds_log_message(context,
11834 _("Could not build Czech POINT conversion request"));
11835 return IE_ERROR;
11837 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11838 if(!deposit_ns) {
11839 isds_log_message(context,
11840 _("Could not create Czech POINT deposit name space"));
11841 xmlFreeNode(request);
11842 return IE_ERROR;
11844 xmlSetNs(request, deposit_ns);
11846 /* Insert children. They are in empty namespace! */
11847 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11848 if(!empty_ns) {
11849 isds_log_message(context, _("Could not create empty name space"));
11850 err = IE_ERROR;
11851 goto leave;
11853 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11854 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11855 document->dmFileDescr);
11857 /* Document encoded in Base64 */
11858 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11859 document->data, document->data_length);
11860 if (err) goto leave;
11862 isds_log(ILF_ISDS, ILL_DEBUG,
11863 _("Submitting document for conversion into Czech POINT deposit"));
11865 /* Send conversion request */
11866 err = _czp_czpdeposit(context, request, &response);
11867 xmlFreeNode(request); request = NULL;
11869 if (err) {
11870 czp_do_close_connection(context);
11871 goto leave;
11875 /* Extract response */
11876 xpath_ctx = xmlXPathNewContext(response);
11877 if (!xpath_ctx) {
11878 err = IE_ERROR;
11879 goto leave;
11881 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11882 err = IE_ERROR;
11883 goto leave;
11885 result = xmlXPathEvalExpression(
11886 BAD_CAST "/deposit:saveDocumentResponse/return",
11887 xpath_ctx);
11888 if (!result) {
11889 err = IE_ERROR;
11890 goto leave;
11892 /* Empty response */
11893 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11894 isds_printf_message(context,
11895 _("Missing `return' element in Czech POINT deposit response"));
11896 err = IE_ISDS;
11897 goto leave;
11899 /* More responses */
11900 if (result->nodesetval->nodeNr > 1) {
11901 isds_printf_message(context,
11902 _("Multiple `return' element in Czech POINT deposit response"));
11903 err = IE_ISDS;
11904 goto leave;
11906 /* One response */
11907 xpath_ctx->node = result->nodesetval->nodeTab[0];
11909 /* Get status */
11910 EXTRACT_LONGINT("status", status_ptr, 1);
11911 if (status) {
11912 EXTRACT_STRING("statusMsg", string);
11913 char *string_locale = _isds_utf82locale(string);
11914 isds_printf_message(context,
11915 _("Czech POINT deposit refused document for conversion "
11916 "(code=%ld, message=%s)"),
11917 status, string_locale);
11918 free(string_locale);
11919 err = IE_ISDS;
11920 goto leave;
11923 /* Get document ID */
11924 EXTRACT_STRING("documentID", *id);
11926 /* Get submit date */
11927 EXTRACT_STRING("dateInserted", string);
11928 if (string) {
11929 *date = calloc(1, sizeof(**date));
11930 if (!*date) {
11931 err = IE_NOMEM;
11932 goto leave;
11934 err = _isds_datestring2tm((xmlChar *)string, *date);
11935 if (err) {
11936 if (err == IE_NOTSUP) {
11937 err = IE_ISDS;
11938 char *string_locale = _isds_utf82locale(string);
11939 isds_printf_message(context,
11940 _("Invalid dateInserted value: %s"), string_locale);
11941 free(string_locale);
11943 goto leave;
11947 leave:
11948 free(string);
11949 xmlXPathFreeObject(result);
11950 xmlXPathFreeContext(xpath_ctx);
11952 xmlFreeDoc(response);
11953 xmlFreeNode(request);
11955 if (!err) {
11956 char *id_locale = _isds_utf82locale((char *) *id);
11957 isds_log(ILF_ISDS, ILL_DEBUG,
11958 _("Document %s has been submitted for conversion "
11959 "to server successfully\n"), id_locale);
11960 free(id_locale);
11962 #else /* not HAVE_LIBCURL */
11963 err = IE_NOTSUP;
11964 #endif
11965 return err;
11969 /* Close possibly opened connection to Czech POINT document deposit.
11970 * @context is Czech POINT session context. */
11971 isds_error czp_close_connection(struct isds_ctx *context) {
11972 if (!context) return IE_INVALID_CONTEXT;
11973 zfree(context->long_message);
11974 #if HAVE_LIBCURL
11975 return czp_do_close_connection(context);
11976 #else
11977 return IE_NOTSUP;
11978 #endif
11982 /* Send request for new box creation in testing ISDS instance.
11983 * It's not possible to request for a production box currently, as it
11984 * communicates via e-mail.
11985 * XXX: This function does not work either. Server complains about invalid
11986 * e-mail address.
11987 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11988 * this function
11989 * @context is special session context for box creation request. DO NOT use
11990 * standard context as it could reveal your password. Use fresh new context or
11991 * context previously used by this function.
11992 * @box is box description to create including single primary user (in case of
11993 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11994 * ignored. It outputs box ID assigned by ISDS in dbID element.
11995 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11996 * box, or contact address of PFO box owner). The email member is mandatory as
11997 * it will be used to deliver credentials.
11998 * @former_names is former name of box owner. Pass NULL if you don't care.
11999 * @approval is optional external approval of box manipulation
12000 * @refnumber is reallocated serial number of request assigned by ISDS. Use
12001 * NULL, if you don't care.*/
12002 isds_error isds_request_new_testing_box(struct isds_ctx *context,
12003 struct isds_DbOwnerInfo *box, const struct isds_list *users,
12004 const char *former_names, const struct isds_approval *approval,
12005 char **refnumber) {
12006 isds_error err = IE_SUCCESS;
12007 #if HAVE_LIBCURL
12008 xmlNodePtr request = NULL;
12009 xmlDocPtr response = NULL;
12010 xmlXPathContextPtr xpath_ctx = NULL;
12011 xmlXPathObjectPtr result = NULL;
12012 #endif
12015 if (!context) return IE_INVALID_CONTEXT;
12016 zfree(context->long_message);
12017 if (!box) return IE_INVAL;
12019 #if HAVE_LIBCURL
12020 if (!box->email || box->email[0] == '\0') {
12021 isds_log_message(context, _("E-mail field is mandatory"));
12022 return IE_INVAL;
12025 /* Scratch box ID */
12026 zfree(box->dbID);
12028 /* Store configuration */
12029 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
12030 free(context->url);
12031 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
12032 if (!(context->url))
12033 return IE_NOMEM;
12035 /* Prepare CURL handle if not yet connected */
12036 if (!context->curl) {
12037 context->curl = curl_easy_init();
12038 if (!(context->curl))
12039 return IE_ERROR;
12042 /* Build CreateDataBox request */
12043 err = build_CreateDBInput_request(context,
12044 &request, BAD_CAST "CreateDataBox",
12045 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
12046 if (err) goto leave;
12048 /* Send it to server and process response */
12049 err = send_destroy_request_check_response(context,
12050 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
12051 &response, (xmlChar **) refnumber, NULL);
12052 if (err) goto leave;
12054 /* Extract box ID */
12055 xpath_ctx = xmlXPathNewContext(response);
12056 if (!xpath_ctx) {
12057 err = IE_ERROR;
12058 goto leave;
12060 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12061 err = IE_ERROR;
12062 goto leave;
12064 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
12066 leave:
12067 xmlXPathFreeObject(result);
12068 xmlXPathFreeContext(xpath_ctx);
12069 xmlFreeDoc(response);
12070 xmlFreeNode(request);
12072 if (!err) {
12073 isds_log(ILF_ISDS, ILL_DEBUG,
12074 _("CreateDataBox request processed by server successfully.\n"));
12076 #else /* not HAVE_LIBCURL */
12077 err = IE_NOTSUP;
12078 #endif
12080 return err;
12084 /* Submit CMS signed message to ISDS to verify its originality. This is
12085 * stronger form of isds_verify_message_hash() because ISDS does more checks
12086 * than simple one (potentialy old weak) hash comparison.
12087 * @context is session context
12088 * @message is memory with raw CMS signed message bit stream
12089 * @length is @message size in bytes
12090 * @return
12091 * IE_SUCCESS if message originates in ISDS
12092 * IE_NOTEQUAL if message is unknown to ISDS
12093 * other code for other errors */
12094 isds_error isds_authenticate_message(struct isds_ctx *context,
12095 const void *message, size_t length) {
12096 isds_error err = IE_SUCCESS;
12097 #if HAVE_LIBCURL
12098 xmlNsPtr isds_ns = NULL;
12099 xmlNodePtr request = NULL;
12100 xmlDocPtr response = NULL;
12101 xmlXPathContextPtr xpath_ctx = NULL;
12102 xmlXPathObjectPtr result = NULL;
12103 _Bool *authentic = NULL;
12104 #endif
12106 if (!context) return IE_INVALID_CONTEXT;
12107 zfree(context->long_message);
12108 if (!message || length == 0) return IE_INVAL;
12110 #if HAVE_LIBCURL
12111 /* Check if connection is established
12112 * TODO: This check should be done downstairs. */
12113 if (!context->curl) return IE_CONNECTION_CLOSED;
12116 /* Build AuthenticateMessage request */
12117 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
12118 if (!request) {
12119 isds_log_message(context,
12120 _("Could not build AuthenticateMessage request"));
12121 return IE_ERROR;
12123 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12124 if(!isds_ns) {
12125 isds_log_message(context, _("Could not create ISDS name space"));
12126 xmlFreeNode(request);
12127 return IE_ERROR;
12129 xmlSetNs(request, isds_ns);
12131 /* Insert Base64 encoded message */
12132 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
12133 message, length);
12134 if (err) goto leave;
12136 /* Send request to server and process response */
12137 err = send_destroy_request_check_response(context,
12138 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
12139 &response, NULL, NULL);
12140 if (err) goto leave;
12143 /* ISDS has decided */
12144 xpath_ctx = xmlXPathNewContext(response);
12145 if (!xpath_ctx) {
12146 err = IE_ERROR;
12147 goto leave;
12149 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12150 err = IE_ERROR;
12151 goto leave;
12154 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
12156 if (!authentic) {
12157 isds_log_message(context,
12158 _("Server did not return any response on "
12159 "AuthenticateMessage request"));
12160 err = IE_ISDS;
12161 goto leave;
12163 if (*authentic) {
12164 isds_log(ILF_ISDS, ILL_DEBUG,
12165 _("ISDS authenticated the message successfully\n"));
12166 } else {
12167 isds_log_message(context, _("ISDS does not know the message"));
12168 err = IE_NOTEQUAL;
12172 leave:
12173 free(authentic);
12174 xmlXPathFreeObject(result);
12175 xmlXPathFreeContext(xpath_ctx);
12177 xmlFreeDoc(response);
12178 xmlFreeNode(request);
12179 #else /* not HAVE_LIBCURL */
12180 err = IE_NOTSUP;
12181 #endif
12183 return err;
12187 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12188 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12189 * be re-signed.
12190 * @context is session context
12191 * @input_data is memory with raw CMS signed message or delivery info bit
12192 * stream to re-sign
12193 * @input_length is @input_data size in bytes
12194 * @output_data is pointer to auto-allocated memory where to store re-signed
12195 * input data blob. Caller must free it.
12196 * @output_data is pointer where to store @output_data size in bytes
12197 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12198 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12199 * @return
12200 * IE_SUCCESS if CMS blob has been re-signed successfully
12201 * other code for other errors */
12202 isds_error isds_resign_message(struct isds_ctx *context,
12203 const void *input_data, size_t input_length,
12204 void **output_data, size_t *output_length, struct tm **valid_to) {
12205 isds_error err = IE_SUCCESS;
12206 #if HAVE_LIBCURL
12207 xmlNsPtr isds_ns = NULL;
12208 xmlNodePtr request = NULL;
12209 xmlDocPtr response = NULL;
12210 xmlXPathContextPtr xpath_ctx = NULL;
12211 xmlXPathObjectPtr result = NULL;
12212 char *string = NULL;
12213 const xmlChar *codes[] = {
12214 BAD_CAST "2200",
12215 BAD_CAST "2201",
12216 BAD_CAST "2204",
12217 BAD_CAST "2207",
12218 NULL
12220 const char *meanings[] = {
12221 "Message is bad",
12222 "Message is not original",
12223 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12224 "Time stamp could not been generated in time"
12226 const isds_error errors[] = {
12227 IE_INVAL,
12228 IE_NOTUNIQ,
12229 IE_INVAL,
12230 IE_ISDS,
12232 struct code_map_isds_error map = {
12233 .codes = codes,
12234 .meanings = meanings,
12235 .errors = errors
12237 #endif
12239 if (NULL != output_data) *output_data = NULL;
12240 if (NULL != output_length) *output_length = 0;
12241 if (NULL != valid_to) *valid_to = NULL;
12243 if (NULL == context) return IE_INVALID_CONTEXT;
12244 zfree(context->long_message);
12245 if (NULL == input_data || 0 == input_length) {
12246 isds_log_message(context, _("Empty CMS blob on input"));
12247 return IE_INVAL;
12249 if (NULL == output_data || NULL == output_length) {
12250 isds_log_message(context,
12251 _("NULL pointer provided for output CMS blob"));
12252 return IE_INVAL;
12255 #if HAVE_LIBCURL
12256 /* Check if connection is established
12257 * TODO: This check should be done downstairs. */
12258 if (!context->curl) return IE_CONNECTION_CLOSED;
12261 /* Build Re-signISDSDocument request */
12262 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
12263 if (!request) {
12264 isds_log_message(context,
12265 _("Could not build Re-signISDSDocument request"));
12266 return IE_ERROR;
12268 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12269 if(!isds_ns) {
12270 isds_log_message(context, _("Could not create ISDS name space"));
12271 xmlFreeNode(request);
12272 return IE_ERROR;
12274 xmlSetNs(request, isds_ns);
12276 /* Insert Base64 encoded CMS blob */
12277 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
12278 input_data, input_length);
12279 if (err) goto leave;
12281 /* Send request to server and process response */
12282 err = send_destroy_request_check_response(context,
12283 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
12284 &response, NULL, &map);
12285 if (err) goto leave;
12288 /* Extract re-signed data */
12289 xpath_ctx = xmlXPathNewContext(response);
12290 if (!xpath_ctx) {
12291 err = IE_ERROR;
12292 goto leave;
12294 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12295 err = IE_ERROR;
12296 goto leave;
12298 result = xmlXPathEvalExpression(
12299 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
12300 if (!result) {
12301 err = IE_ERROR;
12302 goto leave;
12304 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
12305 isds_log_message(context,
12306 _("Missing Re-signISDSDocumentResponse element"));
12307 err = IE_ISDS;
12308 goto leave;
12310 if (result->nodesetval->nodeNr > 1) {
12311 isds_log_message(context,
12312 _("Multiple Re-signISDSDocumentResponse element"));
12313 err = IE_ISDS;
12314 goto leave;
12316 xpath_ctx->node = result->nodesetval->nodeTab[0];
12317 xmlXPathFreeObject(result); result = NULL;
12319 EXTRACT_STRING("isds:dmResultDoc", string);
12320 /* Decode non-empty data */
12321 if (NULL != string && string[0] != '\0') {
12322 *output_length = _isds_b64decode(string, output_data);
12323 if (*output_length == (size_t) -1) {
12324 isds_log_message(context,
12325 _("Error while Base64-decoding re-signed data"));
12326 err = IE_ERROR;
12327 goto leave;
12329 } else {
12330 isds_log_message(context, _("Server did not send re-signed data"));
12331 err = IE_ISDS;
12332 goto leave;
12334 zfree(string);
12336 if (NULL != valid_to) {
12337 /* Get time stamp expiration date */
12338 EXTRACT_STRING("isds:dmValidTo", string);
12339 if (NULL != string) {
12340 *valid_to = calloc(1, sizeof(**valid_to));
12341 if (!*valid_to) {
12342 err = IE_NOMEM;
12343 goto leave;
12345 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
12346 if (err) {
12347 if (err == IE_NOTSUP) {
12348 err = IE_ISDS;
12349 char *string_locale = _isds_utf82locale(string);
12350 isds_printf_message(context,
12351 _("Invalid dmValidTo value: %s"), string_locale);
12352 free(string_locale);
12354 goto leave;
12359 leave:
12360 free(string);
12362 xmlXPathFreeObject(result);
12363 xmlXPathFreeContext(xpath_ctx);
12365 xmlFreeDoc(response);
12366 xmlFreeNode(request);
12367 #else /* not HAVE_LIBCURL */
12368 err = IE_NOTSUP;
12369 #endif
12371 return err;
12374 #undef INSERT_ELEMENT
12375 #undef CHECK_FOR_STRING_LENGTH
12376 #undef INSERT_STRING_ATTRIBUTE
12377 #undef INSERT_ULONGINTNOPTR
12378 #undef INSERT_ULONGINT
12379 #undef INSERT_LONGINT
12380 #undef INSERT_BOOLEAN
12381 #undef INSERT_SCALAR_BOOLEAN
12382 #undef INSERT_STRING
12383 #undef INSERT_STRING_WITH_NS
12384 #undef EXTRACT_STRING_ATTRIBUTE
12385 #undef EXTRACT_ULONGINT
12386 #undef EXTRACT_LONGINT
12387 #undef EXTRACT_BOOLEAN
12388 #undef EXTRACT_STRING
12391 /* Compute hash of message from raw representation and store it into envelope.
12392 * Original hash structure will be destroyed in envelope.
12393 * @context is session context
12394 * @message is message carrying raw XML message blob
12395 * @algorithm is desired hash algorithm to use */
12396 isds_error isds_compute_message_hash(struct isds_ctx *context,
12397 struct isds_message *message, const isds_hash_algorithm algorithm) {
12398 isds_error err = IE_SUCCESS;
12399 const char *nsuri;
12400 void *xml_stream = NULL;
12401 size_t xml_stream_length;
12402 size_t phys_start, phys_end;
12403 char *phys_path = NULL;
12404 struct isds_hash *new_hash = NULL;
12407 if (!context) return IE_INVALID_CONTEXT;
12408 zfree(context->long_message);
12409 if (!message) return IE_INVAL;
12411 if (!message->raw) {
12412 isds_log_message(context,
12413 _("Message does not carry raw representation"));
12414 return IE_INVAL;
12417 switch (message->raw_type) {
12418 case RAWTYPE_INCOMING_MESSAGE:
12419 nsuri = ISDS_NS;
12420 xml_stream = message->raw;
12421 xml_stream_length = message->raw_length;
12422 break;
12424 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
12425 nsuri = SISDS_INCOMING_NS;
12426 xml_stream = message->raw;
12427 xml_stream_length = message->raw_length;
12428 break;
12430 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
12431 nsuri = SISDS_INCOMING_NS;
12432 err = _isds_extract_cms_data(context,
12433 message->raw, message->raw_length,
12434 &xml_stream, &xml_stream_length);
12435 if (err) goto leave;
12436 break;
12438 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
12439 nsuri = SISDS_OUTGOING_NS;
12440 xml_stream = message->raw;
12441 xml_stream_length = message->raw_length;
12442 break;
12444 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12445 nsuri = SISDS_OUTGOING_NS;
12446 err = _isds_extract_cms_data(context,
12447 message->raw, message->raw_length,
12448 &xml_stream, &xml_stream_length);
12449 if (err) goto leave;
12450 break;
12452 default:
12453 isds_log_message(context, _("Bad raw representation type"));
12454 return IE_INVAL;
12455 break;
12459 /* XXX: Hash is computed from original string representing isds:dmDm
12460 * subtree. That means no encoding, white space, xmlns attributes changes.
12461 * In other words, input for hash can be invalid XML stream. */
12462 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12463 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12464 PHYSXML_ELEMENT_SEPARATOR,
12465 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12466 PHYSXML_ELEMENT_SEPARATOR
12467 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12468 err = IE_NOMEM;
12469 goto leave;
12471 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12472 phys_path, &phys_start, &phys_end);
12473 zfree(phys_path);
12474 if (err) {
12475 isds_log_message(context,
12476 _("Substring with isds:dmDM element could not be located "
12477 "in raw message"));
12478 goto leave;
12482 /* Compute hash */
12483 new_hash = calloc(1, sizeof(*new_hash));
12484 if (!new_hash) {
12485 err = IE_NOMEM;
12486 goto leave;
12488 new_hash->algorithm = algorithm;
12489 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12490 new_hash);
12491 if (err) {
12492 isds_log_message(context, _("Could not compute message hash"));
12493 goto leave;
12496 /* Save computed hash */
12497 if (!message->envelope) {
12498 message->envelope = calloc(1, sizeof(*message->envelope));
12499 if (!message->envelope) {
12500 err = IE_NOMEM;
12501 goto leave;
12504 isds_hash_free(&message->envelope->hash);
12505 message->envelope->hash = new_hash;
12507 leave:
12508 if (err) {
12509 isds_hash_free(&new_hash);
12512 free(phys_path);
12513 if (xml_stream != message->raw) free(xml_stream);
12514 return err;
12518 /* Compare two hashes.
12519 * @h1 is first hash
12520 * @h2 is another hash
12521 * @return
12522 * IE_SUCCESS if hashes equal
12523 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12524 * IE_ENUM if not comparable, but both structures defined
12525 * IE_INVAL if some of the structures are undefined (NULL)
12526 * IE_ERROR if internal error occurs */
12527 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12528 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12529 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12530 if (h1->length != h2->length) return IE_ERROR;
12531 if (h1->length > 0 && !h1->value) return IE_ERROR;
12532 if (h2->length > 0 && !h2->value) return IE_ERROR;
12534 for (size_t i = 0; i < h1->length; i++) {
12535 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12536 return IE_NOTEQUAL;
12538 return IE_SUCCESS;
12542 /* Check message has gone through ISDS by comparing message hash stored in
12543 * ISDS and locally computed hash. You must provide message with valid raw
12544 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12545 * This is convenient wrapper for isds_download_message_hash(),
12546 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12547 * @context is session context
12548 * @message is message with valid raw and envelope member; envelope->hash
12549 * member will be changed during function run. Use envelope on heap only.
12550 * @return
12551 * IE_SUCCESS if message originates in ISDS
12552 * IE_NOTEQUAL if message is unknown to ISDS
12553 * other code for other errors */
12554 isds_error isds_verify_message_hash(struct isds_ctx *context,
12555 struct isds_message *message) {
12556 isds_error err = IE_SUCCESS;
12557 struct isds_hash *downloaded_hash = NULL;
12559 if (!context) return IE_INVALID_CONTEXT;
12560 zfree(context->long_message);
12561 if (!message) return IE_INVAL;
12563 if (!message->envelope) {
12564 isds_log_message(context,
12565 _("Given message structure is missing envelope"));
12566 return IE_INVAL;
12568 if (!message->raw) {
12569 isds_log_message(context,
12570 _("Given message structure is missing raw representation"));
12571 return IE_INVAL;
12574 err = isds_download_message_hash(context, message->envelope->dmID,
12575 &downloaded_hash);
12576 if (err) goto leave;
12578 err = isds_compute_message_hash(context, message,
12579 downloaded_hash->algorithm);
12580 if (err) goto leave;
12582 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12584 leave:
12585 isds_hash_free(&downloaded_hash);
12586 return err;
12590 /* Search for document by document ID in list of documents. IDs are compared
12591 * as UTF-8 string.
12592 * @documents is list of isds_documents
12593 * @id is document identifier
12594 * @return first matching document or NULL. */
12595 const struct isds_document *isds_find_document_by_id(
12596 const struct isds_list *documents, const char *id) {
12597 const struct isds_list *item;
12598 const struct isds_document *document;
12600 for (item = documents; item; item = item->next) {
12601 document = (struct isds_document *) item->data;
12602 if (!document) continue;
12604 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12605 return document;
12608 return NULL;
12612 /* Normalize @mime_type to be proper MIME type.
12613 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12614 * guess regular MIME type (e.g. "application/pdf").
12615 * @mime_type is UTF-8 encoded MIME type to fix
12616 * @return original @mime_type if no better interpretation exists, or
12617 * constant static UTF-8 encoded string with proper MIME type. */
12618 const char *isds_normalize_mime_type(const char *mime_type) {
12619 if (!mime_type) return NULL;
12621 for (size_t offset = 0;
12622 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12623 offset += 2) {
12624 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12625 extension_map_mime[offset]))
12626 return (const char *) extension_map_mime[offset + 1];
12629 return mime_type;
12633 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12634 struct isds_message **message);
12635 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12636 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12637 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12638 struct isds_address **address);
12640 int isds_message_free(struct isds_message **message);
12641 int isds_address_free(struct isds_address **address);
12645 /* Makes known all relevant namespaces to given XPath context
12646 * @xpath_ctx is XPath context
12647 * @message_ns selects proper message name space. Unsigned and signed
12648 * messages and delivery info's differ in prefix and URI. */
12649 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12650 const message_ns_type message_ns) {
12651 const xmlChar *message_namespace = NULL;
12653 if (!xpath_ctx) return IE_ERROR;
12655 switch(message_ns) {
12656 case MESSAGE_NS_1:
12657 message_namespace = BAD_CAST ISDS1_NS; break;
12658 case MESSAGE_NS_UNSIGNED:
12659 message_namespace = BAD_CAST ISDS_NS; break;
12660 case MESSAGE_NS_SIGNED_INCOMING:
12661 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12662 case MESSAGE_NS_SIGNED_OUTGOING:
12663 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12664 case MESSAGE_NS_SIGNED_DELIVERY:
12665 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12666 default:
12667 return IE_ENUM;
12670 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12671 return IE_ERROR;
12672 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12673 return IE_ERROR;
12674 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12675 return IE_ERROR;
12676 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12677 return IE_ERROR;
12678 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12679 return IE_ERROR;
12680 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12681 return IE_ERROR;
12682 return IE_SUCCESS;