Work around a -Wuse-after-free misdetection in GCC 12
[libisds.git] / src / isds.c
blobac8426f37af5b50a5e507ce37ca9af709640e586
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/";
35 const char isds_mep_locator[] = "https://www.mojedatovaschranka.cz/";
37 /* Base URL of production ISDS instance */
38 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
39 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
40 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
41 const char isds_mep_testing_locator[] = "https://www.czebox.cz/";
43 /* Extension to MIME type map */
44 static const xmlChar *extension_map_mime[] = {
45 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
46 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
47 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
48 BAD_CAST "doc", BAD_CAST "application/msword",
49 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
50 "wordprocessingml.document",
51 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
52 BAD_CAST "prj", BAD_CAST "application/octet-stream",
53 BAD_CAST "qix", BAD_CAST "application/octet-stream",
54 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
55 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
56 BAD_CAST "shp", BAD_CAST "application/octet-stream",
57 BAD_CAST "shx", BAD_CAST "application/octet-stream",
58 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
59 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
60 BAD_CAST "edi", BAD_CAST "application/edifact",
61 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
62 BAD_CAST "gfs", BAD_CAST "application/xml",
63 BAD_CAST "gml", BAD_CAST "application/xml",
64 BAD_CAST "gif", BAD_CAST "image/gif",
65 BAD_CAST "htm", BAD_CAST "text/html",
66 BAD_CAST "html", BAD_CAST "text/html",
67 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
68 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
69 BAD_CAST "jfif", BAD_CAST "image/jpeg",
70 BAD_CAST "jpg", BAD_CAST "image/jpeg",
71 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
72 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
73 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
74 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
75 BAD_CAST "mpg", BAD_CAST "video/mpeg",
76 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
77 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
78 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
79 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
80 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
81 BAD_CAST "pdf", BAD_CAST "application/pdf",
82 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
83 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
84 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
85 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
86 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
87 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
88 BAD_CAST "png", BAD_CAST "image/png",
89 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
90 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
91 "presentationml.presentation",
92 BAD_CAST "rtf", BAD_CAST "application/rtf",
93 BAD_CAST "tif", BAD_CAST "image/tiff",
94 BAD_CAST "tiff", BAD_CAST "image/tiff",
95 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
96 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
97 BAD_CAST "txt", BAD_CAST "text/plain",
98 BAD_CAST "wav", BAD_CAST "audio/wav",
99 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
100 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
101 "spreadsheetml.sheet",
102 BAD_CAST "xml", BAD_CAST "application/xml",
103 BAD_CAST "xsd", BAD_CAST "application/xml",
104 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
107 /* Structure type to hold conversion table from status code to isds_error and
108 * long message */
109 struct code_map_isds_error {
110 const xmlChar **codes; /* NULL terminated array of status codes */
111 const char **meanings; /* Mapping to non-localized long messages */
112 const isds_error *errors; /* Mapping to isds_error code */
115 /* Deallocate structure isds_pki_credentials and NULL it.
116 * Pass-phrase is discarded.
117 * @pki credentials to to free */
118 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
119 if(!pki || !*pki) return;
121 free((*pki)->engine);
122 free((*pki)->certificate);
123 free((*pki)->key);
125 if ((*pki)->passphrase) {
126 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
127 free((*pki)->passphrase);
130 zfree((*pki));
134 /* Free isds_list with all member data.
135 * @list list to free, on return will be NULL */
136 void isds_list_free(struct isds_list **list) {
137 struct isds_list *item, *next_item;
139 if (!list || !*list) return;
141 for(item = *list; item; item = next_item) {
142 if (item->destructor) (item->destructor)(&(item->data));
143 next_item = item->next;
144 free(item);
147 *list = NULL;
151 /* Deallocate structure isds_hash and NULL it.
152 * @hash hash to to free */
153 void isds_hash_free(struct isds_hash **hash) {
154 if(!hash || !*hash) return;
155 free((*hash)->value);
156 zfree((*hash));
160 /* Deallocate structure isds_PersonName recursively and NULL it */
161 void isds_PersonName_free(struct isds_PersonName **person_name) {
162 if (!person_name || !*person_name) return;
164 free((*person_name)->pnFirstName);
165 free((*person_name)->pnMiddleName);
166 free((*person_name)->pnLastName);
167 free((*person_name)->pnLastNameAtBirth);
169 free(*person_name);
170 *person_name = NULL;
174 /* Deallocate structure isds_BirthInfo recursively and NULL it */
175 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
176 if (!birth_info || !*birth_info) return;
178 free((*birth_info)->biDate);
179 free((*birth_info)->biCity);
180 free((*birth_info)->biCounty);
181 free((*birth_info)->biState);
183 free(*birth_info);
184 *birth_info = NULL;
188 /* Deallocate structure isds_Address recursively and NULL it */
189 void isds_Address_free(struct isds_Address **address) {
190 if (!address || !*address) return;
192 free((*address)->adCode);
193 free((*address)->adCity);
194 free((*address)->adDistrict);
195 free((*address)->adStreet);
196 free((*address)->adNumberInStreet);
197 free((*address)->adNumberInMunicipality);
198 free((*address)->adZipCode);
199 free((*address)->adState);
201 free(*address);
202 *address = NULL;
206 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
207 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
208 if (!db_owner_info || !*db_owner_info) return;
210 free((*db_owner_info)->dbID);
211 free((*db_owner_info)->dbType);
212 free((*db_owner_info)->ic);
213 isds_PersonName_free(&((*db_owner_info)->personName));
214 free((*db_owner_info)->firmName);
215 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
216 isds_Address_free(&((*db_owner_info)->address));
217 free((*db_owner_info)->nationality);
218 free((*db_owner_info)->email);
219 free((*db_owner_info)->telNumber);
220 free((*db_owner_info)->identifier);
221 free((*db_owner_info)->aifoIsds);
222 free((*db_owner_info)->registryCode);
223 free((*db_owner_info)->dbState);
224 free((*db_owner_info)->dbEffectiveOVM);
225 free((*db_owner_info)->dbOpenAddressing);
227 free(*db_owner_info);
228 *db_owner_info = NULL;
231 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
232 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
233 if (!db_user_info || !*db_user_info) return;
235 free((*db_user_info)->userID);
236 free((*db_user_info)->userType);
237 free((*db_user_info)->userPrivils);
238 isds_PersonName_free(&((*db_user_info)->personName));
239 isds_Address_free(&((*db_user_info)->address));
240 free((*db_user_info)->biDate);
241 free((*db_user_info)->ic);
242 free((*db_user_info)->firmName);
243 free((*db_user_info)->caStreet);
244 free((*db_user_info)->caCity);
245 free((*db_user_info)->caZipCode);
246 free((*db_user_info)->caState);
247 free((*db_user_info)->aifo_ticket);
249 zfree(*db_user_info);
253 /* Deallocate struct isds_event recursively and NULL it */
254 void isds_event_free(struct isds_event **event) {
255 if (!event || !*event) return;
257 free((*event)->time);
258 free((*event)->type);
259 free((*event)->description);
260 zfree(*event);
264 /* Deallocate struct isds_envelope recursively and NULL it */
265 void isds_envelope_free(struct isds_envelope **envelope) {
266 if (!envelope || !*envelope) return;
268 free((*envelope)->dmID);
269 free((*envelope)->dbIDSender);
270 free((*envelope)->dmSender);
271 free((*envelope)->dmSenderAddress);
272 free((*envelope)->dmSenderType);
273 free((*envelope)->dmRecipient);
274 free((*envelope)->dmRecipientAddress);
275 free((*envelope)->dmAmbiguousRecipient);
277 free((*envelope)->dmOrdinal);
278 free((*envelope)->dmMessageStatus);
279 free((*envelope)->dmAttachmentSize);
280 free((*envelope)->dmDeliveryTime);
281 free((*envelope)->dmAcceptanceTime);
282 isds_hash_free(&(*envelope)->hash);
283 free((*envelope)->timestamp);
284 isds_list_free(&(*envelope)->events);
286 free((*envelope)->dmSenderOrgUnit);
287 free((*envelope)->dmSenderOrgUnitNum);
288 free((*envelope)->dbIDRecipient);
289 free((*envelope)->dmRecipientOrgUnit);
290 free((*envelope)->dmRecipientOrgUnitNum);
291 free((*envelope)->dmToHands);
292 free((*envelope)->dmAnnotation);
293 free((*envelope)->dmRecipientRefNumber);
294 free((*envelope)->dmSenderRefNumber);
295 free((*envelope)->dmRecipientIdent);
296 free((*envelope)->dmSenderIdent);
298 free((*envelope)->dmLegalTitleLaw);
299 free((*envelope)->dmLegalTitleYear);
300 free((*envelope)->dmLegalTitleSect);
301 free((*envelope)->dmLegalTitlePar);
302 free((*envelope)->dmLegalTitlePoint);
304 free((*envelope)->dmPersonalDelivery);
305 free((*envelope)->dmAllowSubstDelivery);
306 free((*envelope)->dmType);
308 free((*envelope)->dmOVM);
309 free((*envelope)->dmPublishOwnID);
311 free(*envelope);
312 *envelope = NULL;
316 /* Deallocate struct isds_message recursively and NULL it */
317 void isds_message_free(struct isds_message **message) {
318 if (!message || !*message) return;
320 free((*message)->raw);
321 isds_envelope_free(&((*message)->envelope));
322 isds_list_free(&((*message)->documents));
323 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
325 free(*message);
326 *message = NULL;
330 /* Deallocate struct isds_document recursively and NULL it */
331 void isds_document_free(struct isds_document **document) {
332 if (!document || !*document) return;
334 if (!(*document)->is_xml) {
335 free((*document)->data);
337 free((*document)->dmMimeType);
338 free((*document)->dmFileGuid);
339 free((*document)->dmUpFileGuid);
340 free((*document)->dmFileDescr);
341 free((*document)->dmFormat);
343 free(*document);
344 *document = NULL;
348 /* Deallocate struct isds_message_copy recursively and NULL it */
349 void isds_message_copy_free(struct isds_message_copy **copy) {
350 if (!copy || !*copy) return;
352 free((*copy)->dbIDRecipient);
353 free((*copy)->dmRecipientOrgUnit);
354 free((*copy)->dmRecipientOrgUnitNum);
355 free((*copy)->dmToHands);
357 free((*copy)->dmStatus);
358 free((*copy)->dmID);
360 zfree(*copy);
364 /* Deallocate struct isds_message_status_change recursively and NULL it */
365 void isds_message_status_change_free(
366 struct isds_message_status_change **message_status_change) {
367 if (!message_status_change || !*message_status_change) return;
369 free((*message_status_change)->dmID);
370 free((*message_status_change)->time);
371 free((*message_status_change)->dmMessageStatus);
373 zfree(*message_status_change);
377 /* Deallocate struct isds_approval recursively and NULL it */
378 void isds_approval_free(struct isds_approval **approval) {
379 if (!approval || !*approval) return;
381 free((*approval)->refference);
383 zfree(*approval);
387 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
388 * The email string is deallocated too. */
389 void isds_credentials_delivery_free(
390 struct isds_credentials_delivery **credentials_delivery) {
391 if (!credentials_delivery || !*credentials_delivery) return;
393 free((*credentials_delivery)->email);
394 free((*credentials_delivery)->token);
395 free((*credentials_delivery)->new_user_name);
397 zfree(*credentials_delivery);
401 /* Deallocate struct isds_commercial_permission recursively and NULL it */
402 void isds_commercial_permission_free(
403 struct isds_commercial_permission **permission) {
404 if (NULL == permission || NULL == *permission) return;
406 free((*permission)->recipient);
407 free((*permission)->payer);
408 free((*permission)->expiration);
409 free((*permission)->count);
410 free((*permission)->reply_identifier);
412 zfree(*permission);
416 /* Deallocate struct isds_credit_event recursively and NULL it */
417 void isds_credit_event_free(struct isds_credit_event **event) {
418 if (NULL == event || NULL == *event) return;
420 free((*event)->time);
421 switch ((*event)->type) {
422 case ISDS_CREDIT_CHARGED:
423 free((*event)->details.charged.transaction);
424 break;
425 case ISDS_CREDIT_DISCHARGED:
426 free((*event)->details.discharged.transaction);
427 break;
428 case ISDS_CREDIT_MESSAGE_SENT:
429 free((*event)->details.message_sent.recipient);
430 free((*event)->details.message_sent.message_id);
431 break;
432 case ISDS_CREDIT_STORAGE_SET:
433 free((*event)->details.storage_set.new_valid_from);
434 free((*event)->details.storage_set.new_valid_to);
435 free((*event)->details.storage_set.old_capacity);
436 free((*event)->details.storage_set.old_valid_from);
437 free((*event)->details.storage_set.old_valid_to);
438 free((*event)->details.storage_set.initiator);
439 break;
440 case ISDS_CREDIT_EXPIRED:
441 break;
444 zfree(*event);
448 /* Deallocate struct isds_fulltext_result recursively and NULL it */
449 void isds_fulltext_result_free(
450 struct isds_fulltext_result **result) {
451 if (NULL == result || NULL == *result) return;
453 free((*result)->dbID);
454 free((*result)->name);
455 isds_list_free(&((*result)->name_match_start));
456 isds_list_free(&((*result)->name_match_end));
457 free((*result)->address);
458 isds_list_free(&((*result)->address_match_start));
459 isds_list_free(&((*result)->address_match_end));
460 free((*result)->ic);
461 free((*result)->biDate);
463 zfree(*result);
467 /* Deallocate struct isds_box_state_period recursively and NULL it */
468 void isds_box_state_period_free(struct isds_box_state_period **period) {
469 if (NULL == period || NULL == *period) return;
470 zfree(*period);
474 /* *DUP_OR_ERROR macros needs error label */
475 #define STRDUP_OR_ERROR(new, template) { \
476 if (!template) { \
477 (new) = NULL; \
478 } else { \
479 (new) = strdup(template); \
480 if (!new) goto error; \
484 #define FLATDUP_OR_ERROR(new, template) { \
485 if (!template) { \
486 (new) = NULL; \
487 } else { \
488 (new) = malloc(sizeof(*(new))); \
489 if (!new) goto error; \
490 memcpy((new), (template), sizeof(*(template))); \
494 /* Copy structure isds_pki_credentials recursively. */
495 struct isds_pki_credentials *isds_pki_credentials_duplicate(
496 const struct isds_pki_credentials *template) {
497 struct isds_pki_credentials *new = NULL;
499 if(!template) return NULL;
501 new = calloc(1, sizeof(*new));
502 if (!new) return NULL;
504 STRDUP_OR_ERROR(new->engine, template->engine);
505 new->certificate_format = template->certificate_format;
506 STRDUP_OR_ERROR(new->certificate, template->certificate);
507 new->key_format = template->key_format;
508 STRDUP_OR_ERROR(new->key, template->key);
509 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
511 return new;
513 error:
514 isds_pki_credentials_free(&new);
515 return NULL;
519 /* Copy structure isds_PersonName recursively */
520 struct isds_PersonName *isds_PersonName_duplicate(
521 const struct isds_PersonName *src) {
522 struct isds_PersonName *new = NULL;
524 if (!src) return NULL;
526 new = calloc(1, sizeof(*new));
527 if (!new) return NULL;
529 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
530 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
531 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
532 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
534 return new;
536 error:
537 isds_PersonName_free(&new);
538 return NULL;
542 /* Copy structure isds_BirthInfo recursively */
543 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
544 const struct isds_BirthInfo *template) {
545 struct isds_BirthInfo *new = NULL;
547 if (!template) return NULL;
549 new = calloc(1, sizeof(*new));
550 if (!new) return NULL;
552 FLATDUP_OR_ERROR(new->biDate, template->biDate);
553 STRDUP_OR_ERROR(new->biCity, template->biCity);
554 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
555 STRDUP_OR_ERROR(new->biState, template->biState);
557 return new;
559 error:
560 isds_BirthInfo_free(&new);
561 return NULL;
565 /* Copy structure isds_Address recursively */
566 struct isds_Address *isds_Address_duplicate(
567 const struct isds_Address *src) {
568 struct isds_Address *new = NULL;
570 if (!src) return NULL;
572 new = calloc(1, sizeof(*new));
573 if (!new) return NULL;
575 FLATDUP_OR_ERROR(new->adCode, src->adCode);
576 STRDUP_OR_ERROR(new->adCity, src->adCity);
577 STRDUP_OR_ERROR(new->adDistrict, src->adDistrict);
578 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
579 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
580 STRDUP_OR_ERROR(new->adNumberInMunicipality,
581 src->adNumberInMunicipality);
582 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
583 STRDUP_OR_ERROR(new->adState, src->adState);
585 return new;
587 error:
588 isds_Address_free(&new);
589 return NULL;
593 /* Copy structure isds_DbOwnerInfo recursively */
594 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
595 const struct isds_DbOwnerInfo *src) {
596 struct isds_DbOwnerInfo *new = NULL;
597 if (!src) return NULL;
599 new = calloc(1, sizeof(*new));
600 if (!new) return NULL;
602 STRDUP_OR_ERROR(new->dbID, src->dbID);
603 FLATDUP_OR_ERROR(new->dbType, src->dbType);
604 STRDUP_OR_ERROR(new->ic, src->ic);
606 if (src->personName) {
607 if (!(new->personName =
608 isds_PersonName_duplicate(src->personName)))
609 goto error;
612 STRDUP_OR_ERROR(new->firmName, src->firmName);
614 if (src->birthInfo) {
615 if (!(new->birthInfo =
616 isds_BirthInfo_duplicate(src->birthInfo)))
617 goto error;
620 if (src->address) {
621 if (!(new->address = isds_Address_duplicate(src->address)))
622 goto error;
625 STRDUP_OR_ERROR(new->nationality, src->nationality);
626 STRDUP_OR_ERROR(new->email, src->email);
627 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
628 STRDUP_OR_ERROR(new->identifier, src->identifier);
629 FLATDUP_OR_ERROR(new->aifoIsds, src->aifoIsds);
630 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
631 FLATDUP_OR_ERROR(new->dbState, src->dbState);
632 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
633 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
635 return new;
637 error:
638 isds_DbOwnerInfo_free(&new);
639 return NULL;
643 /* Copy structure isds_DbUserInfo recursively */
644 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
645 const struct isds_DbUserInfo *src) {
646 struct isds_DbUserInfo *new = NULL;
647 if (!src) return NULL;
649 new = calloc(1, sizeof(*new));
650 if (!new) return NULL;
652 STRDUP_OR_ERROR(new->userID, src->userID);
653 FLATDUP_OR_ERROR(new->userType, src->userType);
654 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
656 if (src->personName) {
657 if (!(new->personName =
658 isds_PersonName_duplicate(src->personName)))
659 goto error;
662 if (src->address) {
663 if (!(new->address = isds_Address_duplicate(src->address)))
664 goto error;
667 FLATDUP_OR_ERROR(new->biDate, src->biDate);
668 STRDUP_OR_ERROR(new->ic, src->ic);
669 STRDUP_OR_ERROR(new->firmName, src->firmName);
670 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
671 STRDUP_OR_ERROR(new->caCity, src->caCity);
672 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
673 STRDUP_OR_ERROR(new->caState, src->caState);
674 STRDUP_OR_ERROR(new->aifo_ticket, src->aifo_ticket);
676 return new;
678 error:
679 isds_DbUserInfo_free(&new);
680 return NULL;
684 /* Copy structure isds_box_state_period recursively */
685 struct isds_box_state_period *isds_box_state_period_duplicate(
686 const struct isds_box_state_period *src) {
687 struct isds_box_state_period *new = NULL;
688 if (!src) return NULL;
690 new = calloc(1, sizeof(*new));
691 if (!new) return NULL;
693 memcpy(&new->from, &src->from, sizeof(src->from));
694 memcpy(&new->to, &src->to, sizeof(src->to));
695 new->dbState = src->dbState;
697 return new;
700 #undef FLATDUP_OR_ERROR
701 #undef STRDUP_OR_ERROR
704 /* Logs libxml2 errors. Should be registered to libxml2 library.
705 * @ctx is unused currently
706 * @msg is printf-like formated message from libxml2 (UTF-8?)
707 * @... are variadic arguments for @msg */
708 static void log_xml(void *ctx, const char *msg, ...) {
709 va_list ap;
710 char *text = NULL;
712 /* Silent warning for unused function argument.
713 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
714 (void)ctx;
716 if (!msg) return;
718 va_start(ap, msg);
719 isds_vasprintf(&text, msg, ap);
720 va_end(ap);
722 if (text)
723 isds_log(ILF_XML, ILL_ERR, "%s", text);
724 free(text);
728 /* Initialize ISDS library.
729 * Global function, must be called before other functions.
730 * If it fails you can not use ISDS library and must call isds_cleanup() to
731 * free partially initialized global variables. */
732 isds_error isds_init(void) {
733 /* NULL global variables */
734 log_facilities = ILF_ALL;
735 log_level = ILL_WARNING;
736 log_callback = NULL;
737 log_callback_data = NULL;
739 #if ENABLE_NLS
740 /* Initialize gettext */
741 bindtextdomain(PACKAGE, LOCALEDIR);
742 #endif
744 #if HAVE_LIBCURL
745 /* Initialize CURL */
746 if (curl_global_init(CURL_GLOBAL_ALL)) {
747 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
748 return IE_ERROR;
750 #endif /* HAVE_LIBCURL */
752 /* Initialise cryptographic back-ends. */
753 if (IE_SUCCESS != _isds_init_crypto()) {
754 isds_log(ILF_ISDS, ILL_CRIT,
755 _("Initialization of cryptographic back-end failed\n"));
756 return IE_ERROR;
759 /* This can _exit() current program. Find not so assertive check. */
760 LIBXML_TEST_VERSION;
761 xmlSetGenericErrorFunc(NULL, log_xml);
763 /* Check expat */
764 if (_isds_init_expat(&version_expat)) {
765 isds_log(ILF_ISDS, ILL_CRIT,
766 _("expat library initialization failed\n"));
767 return IE_ERROR;
770 /* Allocate global variables */
773 return IE_SUCCESS;
777 /* Deinitialize ISDS library.
778 * Global function, must be called as last library function. */
779 isds_error isds_cleanup(void) {
780 /* XML */
781 xmlCleanupParser();
783 #if HAVE_LIBCURL
784 /* Curl */
785 curl_global_cleanup();
786 #endif
788 return IE_SUCCESS;
792 /* Return version string of this library. Version of dependencies can be
793 * embedded. Do no try to parse it. You must free it. */
794 char *isds_version(void) {
795 char *buffer = NULL;
797 isds_asprintf(&buffer,
798 #if HAVE_LIBCURL
799 # ifndef USE_OPENSSL_BACKEND
800 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
801 # else
802 _("%s (%s, %s, %s, libxml2 %s)"),
803 # endif
804 #else
805 # ifndef USE_OPENSSL_BACKEND
806 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
807 # else
808 _("%s (%s, %s, libxml2 %s)"),
809 # endif
810 #endif
811 PACKAGE_VERSION,
812 #if HAVE_LIBCURL
813 curl_version(),
814 #endif
815 #ifndef USE_OPENSSL_BACKEND
816 version_gpgme, version_gcrypt,
817 #else
818 version_openssl,
819 #endif
820 version_expat, xmlParserVersion);
821 return buffer;
825 /* Return text description of ISDS error */
826 const char *isds_strerror(const isds_error error) {
827 switch (error) {
828 case IE_SUCCESS:
829 return(_("Success")); break;
830 case IE_ERROR:
831 return(_("Unspecified error")); break;
832 case IE_NOTSUP:
833 return(_("Not supported")); break;
834 case IE_INVAL:
835 return(_("Invalid value")); break;
836 case IE_INVALID_CONTEXT:
837 return(_("Invalid context")); break;
838 case IE_NOT_LOGGED_IN:
839 return(_("Not logged in")); break;
840 case IE_CONNECTION_CLOSED:
841 return(_("Connection closed")); break;
842 case IE_TIMED_OUT:
843 return(_("Timed out")); break;
844 case IE_NOEXIST:
845 return(_("Not exist")); break;
846 case IE_NOMEM:
847 return(_("Out of memory")); break;
848 case IE_NETWORK:
849 return(_("Network problem")); break;
850 case IE_HTTP:
851 return(_("HTTP problem")); break;
852 case IE_SOAP:
853 return(_("SOAP problem")); break;
854 case IE_XML:
855 return(_("XML problem")); break;
856 case IE_ISDS:
857 return(_("ISDS server problem")); break;
858 case IE_ENUM:
859 return(_("Invalid enum value")); break;
860 case IE_DATE:
861 return(_("Invalid date value")); break;
862 case IE_2BIG:
863 return(_("Too big")); break;
864 case IE_2SMALL:
865 return(_("Too small")); break;
866 case IE_NOTUNIQ:
867 return(_("Value not unique")); break;
868 case IE_NOTEQUAL:
869 return(_("Values not equal")); break;
870 case IE_PARTIAL_SUCCESS:
871 return(_("Some suboperations failed")); break;
872 case IE_ABORTED:
873 return(_("Operation aborted")); break;
874 case IE_SECURITY:
875 return(_("Security problem")); break;
876 default:
877 return(_("Unknown error"));
882 /* Create ISDS context.
883 * Each context can be used for different sessions to (possibly) different
884 * ISDS server with different credentials. */
885 struct isds_ctx *isds_ctx_create(void) {
886 struct isds_ctx *context;
887 context = malloc(sizeof(*context));
888 if (context) memset(context, 0, sizeof(*context));
889 return context;
892 #if HAVE_LIBCURL
893 /* Close possibly opened connection to Czech POINT document deposit without
894 * resetting long_message buffer.
895 * XXX: Do not use czp_close_connection() if you do not want to destroy log
896 * message.
897 * @context is Czech POINT session context. */
898 static isds_error czp_do_close_connection(struct isds_ctx *context) {
899 if (!context) return IE_INVALID_CONTEXT;
900 _isds_close_connection(context);
901 return IE_SUCCESS;
905 /* Discard credentials.
906 * @context is ISDS context
907 * @discard_saved_username is true for removing saved username, false for
908 * keeping it.
909 * Only that. It does not cause log out, connection close or similar. */
910 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
911 _Bool discard_saved_username) {
912 if(!context) return IE_INVALID_CONTEXT;
914 if (context->username) {
915 memset(context->username, 0, strlen(context->username));
916 zfree(context->username);
918 if (context->password) {
919 memset(context->password, 0, strlen(context->password));
920 zfree(context->password);
922 isds_pki_credentials_free(&context->pki_credentials);
923 if (discard_saved_username && context->saved_username) {
924 memset(context->saved_username, 0, strlen(context->saved_username));
925 zfree(context->saved_username);
928 return IE_SUCCESS;
930 #endif /* HAVE_LIBCURL */
933 /* Destroy ISDS context and free memory.
934 * @context will be NULLed on success. */
935 isds_error isds_ctx_free(struct isds_ctx **context) {
936 if (!context || !*context) {
937 return IE_INVALID_CONTEXT;
940 #if HAVE_LIBCURL
941 /* Discard credentials and close connection */
942 switch ((*context)->type) {
943 case CTX_TYPE_NONE: break;
944 case CTX_TYPE_ISDS: isds_logout(*context); break;
945 case CTX_TYPE_CZP:
946 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
947 czp_do_close_connection(*context); break;
950 /* For sure */
951 _isds_discard_credentials(*context, 1);
953 /* Free other structures */
954 free((*context)->url);
955 free((*context)->tls_verify_server);
956 free((*context)->tls_ca_file);
957 free((*context)->tls_ca_dir);
958 free((*context)->tls_crl_file);
959 #endif /* HAVE_LIBCURL */
960 free((*context)->long_message);
962 free(*context);
963 *context = NULL;
964 return IE_SUCCESS;
968 /* Return long message text produced by library function, e.g. detailed error
969 * message. Returned pointer is only valid until new library function is
970 * called for the same context. Could be NULL, especially if NULL context is
971 * supplied. Return string is locale encoded. */
972 char *isds_long_message(const struct isds_ctx *context) {
973 if (!context) return NULL;
974 return context->long_message;
978 /* Stores message into context' long_message buffer.
979 * Application can pick the message up using isds_long_message().
980 * NULL @message truncates the buffer but does not deallocate it.
981 * @message is coded in locale encoding */
982 _hidden isds_error isds_log_message(struct isds_ctx *context,
983 const char *message) {
984 char *buffer;
985 size_t length;
987 if (!context) return IE_INVALID_CONTEXT;
989 /* FIXME: Check for integer overflow */
990 length = 1 + ((message) ? strlen(message) : 0);
991 buffer = realloc(context->long_message, length);
992 if (!buffer) return IE_NOMEM;
994 if (message)
995 strcpy(buffer, message);
996 else
997 *buffer = '\0';
999 context->long_message = buffer;
1000 return IE_SUCCESS;
1004 /* Appends message into context' long_message buffer.
1005 * Application can pick the message up using isds_long_message().
1006 * NULL message has void effect. */
1007 _hidden isds_error isds_append_message(struct isds_ctx *context,
1008 const char *message) {
1009 char *buffer;
1010 size_t old_length, length;
1012 if (!context) return IE_INVALID_CONTEXT;
1013 if (!message) return IE_SUCCESS;
1014 if (!context->long_message)
1015 return isds_log_message(context, message);
1017 old_length = strlen(context->long_message);
1018 /* FIXME: Check for integer overflow */
1019 length = 1 + old_length + strlen(message);
1020 buffer = realloc(context->long_message, length);
1021 if (!buffer) return IE_NOMEM;
1023 strcpy(buffer + old_length, message);
1025 context->long_message = buffer;
1026 return IE_SUCCESS;
1030 /* Stores formatted message into context' long_message buffer.
1031 * Application can pick the message up using isds_long_message(). */
1032 _hidden isds_error isds_printf_message(struct isds_ctx *context,
1033 const char *format, ...) {
1034 va_list ap;
1035 int length;
1037 if (!context) return IE_INVALID_CONTEXT;
1038 va_start(ap, format);
1039 length = isds_vasprintf(&(context->long_message), format, ap);
1040 va_end(ap);
1042 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1046 /* Set logging up.
1047 * @facilities is bit mask of isds_log_facility values,
1048 * @level is verbosity level. */
1049 void isds_set_logging(const unsigned int facilities,
1050 const isds_log_level level) {
1051 log_facilities = facilities;
1052 log_level = level;
1056 /* Register callback function libisds calls when new global log message is
1057 * produced by library. Library logs to stderr by default.
1058 * @callback is function provided by application libisds will call. See type
1059 * definition for @callback argument explanation. Pass NULL to revert logging to
1060 * default behaviour.
1061 * @data is application specific data @callback gets as last argument */
1062 void isds_set_log_callback(isds_log_callback callback, void *data) {
1063 log_callback = callback;
1064 log_callback_data = data;
1068 /* Log @message in class @facility with log @level into global log. @message
1069 * is printf(3) formatting string, variadic arguments may be necessary.
1070 * For debugging purposes. */
1071 _hidden isds_error isds_log(const isds_log_facility facility,
1072 const isds_log_level level, const char *message, ...) {
1073 va_list ap;
1074 char *buffer = NULL;
1075 int length;
1077 if (level > log_level) return IE_SUCCESS;
1078 if (!(log_facilities & facility)) return IE_SUCCESS;
1079 if (!message) return IE_INVAL;
1081 if (log_callback) {
1082 /* Pass message to application supplied callback function */
1083 va_start(ap, message);
1084 length = isds_vasprintf(&buffer, message, ap);
1085 va_end(ap);
1087 if (length == -1) {
1088 return IE_ERROR;
1090 if (length > 0) {
1091 log_callback(facility, level, buffer, length, log_callback_data);
1093 free(buffer);
1094 } else {
1095 /* Default: Log it to stderr */
1096 va_start(ap, message);
1097 vfprintf(stderr, message, ap);
1098 va_end(ap);
1099 /* Line buffered printf is default.
1100 * fflush(stderr);*/
1103 return IE_SUCCESS;
1107 /* Set timeout in milliseconds for each network job like connecting to server
1108 * or sending message. Use 0 to disable timeout limits. */
1109 isds_error isds_set_timeout(struct isds_ctx *context,
1110 const unsigned int timeout) {
1111 if (!context) return IE_INVALID_CONTEXT;
1112 zfree(context->long_message);
1114 #if HAVE_LIBCURL
1115 context->timeout = timeout;
1117 if (context->curl) {
1118 CURLcode curl_err;
1120 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1121 if (!curl_err)
1122 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1123 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1124 context->timeout);
1125 #else
1126 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1127 context->timeout / 1000);
1128 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1129 if (curl_err) return IE_ERROR;
1132 return IE_SUCCESS;
1133 #else /* not HAVE_LIBCURL */
1134 return IE_NOTSUP;
1135 #endif
1139 /* Register callback function libisds calls periodically during HTTP data
1140 * transfer.
1141 * @context is session context
1142 * @callback is function provided by application libisds will call. See type
1143 * definition for @callback argument explanation.
1144 * @data is application specific data @callback gets as last argument */
1145 isds_error isds_set_progress_callback(struct isds_ctx *context,
1146 isds_progress_callback callback, void *data) {
1147 if (!context) return IE_INVALID_CONTEXT;
1148 zfree(context->long_message);
1150 #if HAVE_LIBCURL
1151 context->progress_callback = callback;
1152 context->progress_callback_data = data;
1154 return IE_SUCCESS;
1155 #else /* not HAVE_LIBCURL */
1156 return IE_NOTSUP;
1157 #endif
1161 /* Change context settings.
1162 * @context is context which setting will be applied to
1163 * @option is name of option. It determines the type of last argument. See
1164 * isds_option definition for more info.
1165 * @... is value of new setting. Type is determined by @option
1166 * */
1167 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1168 ...) {
1169 isds_error err = IE_SUCCESS;
1170 va_list ap;
1171 #if HAVE_LIBCURL
1172 char *pointer, *string;
1173 #endif
1175 if (!context) return IE_INVALID_CONTEXT;
1176 zfree(context->long_message);
1178 va_start(ap, option);
1180 #define REPLACE_VA_BOOLEAN(destination) { \
1181 if (!(destination)) { \
1182 (destination) = malloc(sizeof(*(destination))); \
1183 if (!(destination)) { \
1184 err = IE_NOMEM; goto leave; \
1187 *(destination) = (_Bool) !!va_arg(ap, int); \
1190 #define REPLACE_VA_STRING(destination) { \
1191 string = va_arg(ap, char *); \
1192 if (string) { \
1193 pointer = realloc((destination), 1 + strlen(string)); \
1194 if (!pointer) { err = IE_NOMEM; goto leave; } \
1195 strcpy(pointer, string); \
1196 (destination) = pointer; \
1197 } else { \
1198 free(destination); \
1199 (destination) = NULL; \
1203 switch (option) {
1204 case IOPT_TLS_VERIFY_SERVER:
1205 #if HAVE_LIBCURL
1206 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1207 #else
1208 err = IE_NOTSUP; goto leave;
1209 #endif
1210 break;
1211 case IOPT_TLS_CA_FILE:
1212 #if HAVE_LIBCURL
1213 REPLACE_VA_STRING(context->tls_ca_file);
1214 #else
1215 err = IE_NOTSUP; goto leave;
1216 #endif
1217 break;
1218 case IOPT_TLS_CA_DIRECTORY:
1219 #if HAVE_LIBCURL
1220 REPLACE_VA_STRING(context->tls_ca_dir);
1221 #else
1222 err = IE_NOTSUP; goto leave;
1223 #endif
1224 break;
1225 case IOPT_TLS_CRL_FILE:
1226 #if HAVE_LIBCURL
1227 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1228 REPLACE_VA_STRING(context->tls_crl_file);
1229 #else
1230 isds_log_message(context,
1231 _("Curl library does not support CRL definition"));
1232 err = IE_NOTSUP;
1233 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1234 #else
1235 err = IE_NOTSUP; goto leave;
1236 #endif /* not HAVE_LIBCURL */
1237 break;
1238 case IOPT_NORMALIZE_MIME_TYPE:
1239 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1240 break;
1242 default:
1243 err = IE_ENUM; goto leave;
1246 #undef REPLACE_VA_STRING
1247 #undef REPLACE_VA_BOOLEAN
1249 leave:
1250 va_end(ap);
1251 return err;
1255 #if HAVE_LIBCURL
1256 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1257 * Destination for NULL argument will not be touched.
1258 * Destination pointers must be freed before calling this function.
1259 * If @username is @context->saved_username, the saved_username will not be
1260 * replaced. The saved_username is clobbered only if context has set otp
1261 * member.
1262 * Return IE_SUCCESS on success. */
1263 static isds_error _isds_store_credentials(struct isds_ctx *context,
1264 const char *username, const char *password,
1265 const struct isds_pki_credentials *pki_credentials) {
1266 if (NULL == context) return IE_INVALID_CONTEXT;
1268 /* FIXME: mlock password
1269 * (I have a library) */
1271 if (username) {
1272 context->username = strdup(username);
1273 if (context->otp && context->saved_username != username)
1274 context->saved_username = strdup(username);
1276 if (password) {
1277 if (NULL == context->otp_credentials)
1278 context->password = strdup(password);
1279 else
1280 context->password = _isds_astrcat(password,
1281 context->otp_credentials->otp_code);
1283 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1285 if ((NULL != username && NULL == context->username) ||
1286 (NULL != password && NULL == context->password) ||
1287 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1288 (context->otp && NULL != context->username &&
1289 NULL == context->saved_username)) {
1290 return IE_NOMEM;
1293 return IE_SUCCESS;
1295 #endif
1298 /* Connect and log into ISDS server.
1299 * All required arguments will be copied, you do not have to keep them after
1300 * that.
1301 * ISDS supports six different authentication methods. Exact method is
1302 * selected on @username, @password, @pki_credentials, and @otp arguments:
1303 * - If @pki_credentials == NULL, @username and @password must be supplied
1304 * and then
1305 * - If @otp == NULL, simple authentication by username and password will
1306 * be proceeded.
1307 * - If @otp != NULL, authentication by username and password and OTP
1308 * will be used.
1309 * - If @pki_credentials != NULL, then
1310 * - If @username == NULL, only certificate will be used
1311 * - If @username != NULL, then
1312 * - If @password == NULL, then certificate will be used and
1313 * @username shifts meaning to box ID. This is used for hosted
1314 * services.
1315 * - Otherwise all three arguments will be used.
1316 * Please note, that different cases require different certificate type
1317 * (system qualified one or commercial non qualified one). This library
1318 * does not check such political issues. Please see ISDS Specification
1319 * for more details.
1320 * @url is base address of ISDS web service. Pass extern isds_locator
1321 * variable to use production ISDS instance without client certificate
1322 * authentication (or extern isds_cert_locator with client certificate
1323 * authentication or extern isds_otp_locators with OTP authentication).
1324 * Passing NULL has the same effect, autoselection between isds_locator,
1325 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1326 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1327 * isds_otp_testing_locator) variable to select testing instance.
1328 * @username is user name of ISDS user or box ID
1329 * @password is user's secret password
1330 * @pki_credentials defines public key cryptographic material to use in client
1331 * authentication.
1332 * @otp selects one-time password authentication method to use, defines OTP
1333 * code (if known) and returns fine grade resolution of OTP procedure.
1334 * @return:
1335 * IE_SUCCESS if authentication succeeds
1336 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1337 * requested, fine grade reason will be set into @otp->resolution. Error
1338 * message from server can be obtained by isds_long_message() call.
1339 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1340 * server has sent OTP code through side channel. Application is expected to
1341 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1342 * this call to complete second phase of TOTP authentication;
1343 * or other appropriate error. */
1344 isds_error isds_login(struct isds_ctx *context, const char *url,
1345 const char *username, const char *password,
1346 const struct isds_pki_credentials *pki_credentials,
1347 struct isds_otp *otp) {
1348 #if HAVE_LIBCURL
1349 isds_error err = IE_NOT_LOGGED_IN;
1350 isds_error soap_err;
1351 xmlNsPtr isds_ns = NULL;
1352 xmlNodePtr request = NULL;
1353 #endif /* HAVE_LIBCURL */
1355 if (!context) return IE_INVALID_CONTEXT;
1356 zfree(context->long_message);
1358 #if HAVE_LIBCURL
1359 /* Close connection if already logged in */
1360 if (context->curl) {
1361 _isds_close_connection(context);
1364 /* Store configuration */
1365 context->type = CTX_TYPE_ISDS;
1366 zfree(context->url);
1368 /* Mangle base URI according to requested authentication method */
1369 if (NULL == pki_credentials) {
1370 isds_log(ILF_SEC, ILL_INFO,
1371 _("Selected authentication method: no certificate, "
1372 "username and password\n"));
1373 if (!username || !password) {
1374 isds_log_message(context,
1375 _("Both username and password must be supplied"));
1376 return IE_INVAL;
1378 context->otp_credentials = otp;
1379 context->otp = (NULL != context->otp_credentials);
1381 if (!context->otp) {
1382 /* Default locator is official system (without certificate or
1383 * OTP) */
1384 context->url = strdup((NULL != url) ? url : isds_locator);
1385 } else {
1386 const char *authenticator_uri = NULL;
1387 if (!url) url = isds_otp_locator;
1388 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1389 switch (context->otp_credentials->method) {
1390 case OTP_HMAC:
1391 isds_log(ILF_SEC, ILL_INFO,
1392 _("Selected authentication method: "
1393 "HMAC-based one-time password\n"));
1394 authenticator_uri =
1395 "%sas/processLogin?type=hotp&uri=%sapps/";
1396 break;
1397 case OTP_TIME:
1398 isds_log(ILF_SEC, ILL_INFO,
1399 _("Selected authentication method: "
1400 "Time-based one-time password\n"));
1401 if (context->otp_credentials->otp_code == NULL) {
1402 isds_log(ILF_SEC, ILL_INFO,
1403 _("OTP code has not been provided by "
1404 "application, requesting server for "
1405 "new one.\n"));
1406 authenticator_uri =
1407 "%sas/processLogin?type=totp&sendSms=true&"
1408 "uri=%sapps/";
1409 } else {
1410 isds_log(ILF_SEC, ILL_INFO,
1411 _("OTP code has been provided by "
1412 "application, not requesting server "
1413 "for new one.\n"));
1414 authenticator_uri =
1415 "%sas/processLogin?type=totp&"
1416 "uri=%sapps/";
1418 break;
1419 default:
1420 isds_log_message(context,
1421 _("Unknown one-time password authentication "
1422 "method requested by application"));
1423 return IE_ENUM;
1425 if (-1 == isds_asprintf(&context->url, authenticator_uri, url, url))
1426 return IE_NOMEM;
1428 } else {
1429 /* Default locator is official system (with client certificate) */
1430 context->otp = 0;
1431 context->otp_credentials = NULL;
1432 if (!url) url = isds_cert_locator;
1434 if (!username) {
1435 isds_log(ILF_SEC, ILL_INFO,
1436 _("Selected authentication method: system certificate, "
1437 "no username and no password\n"));
1438 password = NULL;
1439 context->url = _isds_astrcat(url, "cert/");
1440 } else {
1441 if (!password) {
1442 isds_log(ILF_SEC, ILL_INFO,
1443 _("Selected authentication method: system certificate, "
1444 "box ID and no password\n"));
1445 context->url = _isds_astrcat(url, "hspis/");
1446 } else {
1447 isds_log(ILF_SEC, ILL_INFO,
1448 _("Selected authentication method: commercial "
1449 "certificate, username and password\n"));
1450 context->url = _isds_astrcat(url, "certds/");
1454 if (!(context->url))
1455 return IE_NOMEM;
1457 /* Prepare CURL handle */
1458 context->curl = curl_easy_init();
1459 if (!(context->curl))
1460 return IE_ERROR;
1462 /* Build log-in request */
1463 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1464 if (!request) {
1465 isds_log_message(context, _("Could not build ISDS log-in request"));
1466 return IE_ERROR;
1468 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1469 if(!isds_ns) {
1470 isds_log_message(context, _("Could not create ISDS name space"));
1471 xmlFreeNode(request);
1472 return IE_ERROR;
1474 xmlSetNs(request, isds_ns);
1476 /* Store credentials */
1477 _isds_discard_credentials(context, 1);
1478 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1479 _isds_discard_credentials(context, 1);
1480 xmlFreeNode(request);
1481 return IE_NOMEM;
1484 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1485 username, url);
1487 /* XXX: ISDS documentation does not specify response body for
1488 * DummyOperation request. However real server sends back
1489 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1490 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1491 * SOAP body content, e.g. the dmStatus element. */
1493 /* Send log-in request */
1494 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1496 if (context->otp) {
1497 /* Revert context URL from OTP authentication service URL to OTP web
1498 * service base URL for subsequent calls. Potential isds_login() retry
1499 * will re-set context URL again. */
1500 zfree(context->url);
1501 context->url = _isds_astrcat(url, "apps/");
1502 if (context->url == NULL) {
1503 soap_err = IE_NOMEM;
1505 /* Detach pointer to OTP credentials from context */
1506 context->otp_credentials = NULL;
1509 /* Remove credentials */
1510 _isds_discard_credentials(context, 0);
1512 /* Destroy log-in request */
1513 xmlFreeNode(request);
1515 if (soap_err) {
1516 _isds_close_connection(context);
1517 return soap_err;
1520 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1521 * authentication succeeded if soap_err == IE_SUCCESS */
1522 err = IE_SUCCESS;
1524 if (!err)
1525 isds_log(ILF_ISDS, ILL_DEBUG,
1526 _("User %s has been logged into server %s successfully\n"),
1527 username, url);
1528 return err;
1529 #else /* not HAVE_LIBCURL */
1530 return IE_NOTSUP;
1531 #endif
1535 /* Connect and log into ISDS server using the MEP login method.
1536 * All arguments are copied, you don't have to keep them after successful
1537 * return.
1538 * @url is base address of ISDS web service. Pass extern isds_mep_locator to use
1539 * the production ISDS environment (pass extern isds_mep_testing_locator to
1540 * access the testing environment). Passing null causes the production
1541 * environment locator to be used.
1542 * @username is the username of ISDS user or box ID
1543 * @code is the communication code. The code is generated when enabling
1544 * the mobile key authentication and can be found in the web-based portal
1545 * of the data-box service.
1546 * @return:
1547 * IE_SUCCESS if authentication succeeds
1548 * IE_NOT_LOGGED_IN if authentication fails
1549 * IE_PARTIAL_SUCCESS if MEP authentication has been requested, fine-grade
1550 * resolution is returned via @mep->resolution, keep arguments unchanged and
1551 * repeat the function call as long as IE_PARTIAL_SUCCESS is being returned;
1552 * or other appropriate error. */
1553 isds_error isds_login_mep(struct isds_ctx *context, const char *url,
1554 const char *username, const char *code, struct isds_mep *mep) {
1555 #if HAVE_LIBCURL
1556 isds_error err = IE_NOT_LOGGED_IN;
1557 isds_error soap_err;
1558 xmlNsPtr isds_ns = NULL;
1559 xmlNodePtr request = NULL;
1560 #endif /* HAVE_LIBCURL */
1562 if (NULL == context) {
1563 return IE_INVALID_CONTEXT;
1565 zfree(context->long_message);
1567 #if HAVE_LIBCURL
1568 context->type = CTX_TYPE_ISDS;
1570 if ((NULL != username) && (NULL != code) && (NULL != mep)) {
1571 isds_log(ILF_SEC, ILL_INFO,
1572 _("Selected authentication method: username and mobile key\n"));
1573 } else {
1574 isds_log_message(context,
1575 "Username, communication code and mep context must be supplied.\n");
1576 return IE_INVAL;
1578 /* Close connection if already logged in, but don't close the connection
1579 * if continuing to negotiate MEP authentication.*/
1580 if ((NULL != context->curl) && (NULL == mep->intermediate_uri)) {
1581 _isds_close_connection(context);
1584 context->mep_credentials = mep;
1585 context->mep = (NULL != context->mep_credentials);
1587 if (context->mep) {
1588 if (NULL == url) {
1589 url = isds_mep_locator;
1591 mep->resolution = MEP_RESOLUTION_UNKNOWN;
1592 const char *authenticator_uri =
1593 "%sas/processLogin?type=mep-ws&applicationName=%s&"
1594 "uri=%sapps/";
1595 const char *app_name = context->mep_credentials->app_name;
1596 if (NULL == app_name) {
1597 app_name = "";
1599 char *escaped_app_name = curl_easy_escape(context->curl, app_name, 0);
1600 if (NULL == escaped_app_name) {
1601 return IE_NOMEM;
1603 if (-1 == isds_asprintf(&context->url, authenticator_uri, url,
1604 escaped_app_name, url)) {
1605 curl_free(escaped_app_name);
1606 return IE_NOMEM;
1608 curl_free(escaped_app_name);
1610 if (NULL == context->url) {
1611 return IE_NOMEM;
1614 /* Prepare CURL handle */
1615 if (NULL == context->curl) {
1616 context->curl = curl_easy_init();
1618 if (NULL == context->curl) {
1619 return IE_ERROR;
1622 /* Build log-in request */
1623 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1624 if (NULL == request) {
1625 isds_log_message(context, _("Could not build ISDS log-in request"));
1626 return IE_ERROR;
1628 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1629 if(NULL == isds_ns) {
1630 isds_log_message(context, _("Could not create ISDS name space"));
1631 xmlFreeNode(request);
1632 return IE_ERROR;
1634 xmlSetNs(request, isds_ns);
1636 /* Store credentials, use mobile key code for password. */
1637 _isds_discard_credentials(context, 1);
1638 if (IE_SUCCESS != _isds_store_credentials(context, username, code, NULL)) {
1639 _isds_discard_credentials(context, 1);
1640 xmlFreeNode(request);
1641 return IE_NOMEM;
1644 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1645 username, url);
1647 /* XXX: ISDS documentation does not specify response body for
1648 * DummyOperation request. However real server sends back
1649 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1650 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1651 * SOAP body content, e.g. the dmStatus element. */
1653 /* Send log-in request */
1654 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1656 if (context->mep) {
1657 /* Revert context URL from mobile key authentication service to web
1658 * service base URL for subsequent calls. */
1659 zfree(context->url);
1660 context->url = _isds_astrcat(url, "apps/");
1661 if (context->url == NULL) {
1662 soap_err = IE_NOMEM;
1664 /* Detach credentials pointer from context. */
1665 context->mep_credentials = NULL;
1668 /* Remove credentials */
1669 _isds_discard_credentials(context, 0);
1671 /* Destroy log-in request */
1672 xmlFreeNode(request);
1674 if ((IE_SUCCESS != soap_err) && (context->mep && (IE_PARTIAL_SUCCESS != soap_err))) {
1675 /* Don't close connection when using MEP authentication. */
1676 _isds_close_connection(context);
1677 return soap_err;
1680 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1681 * authentication succeeded if soap_err == IE_SUCCESS */
1682 err = soap_err;
1684 if (IE_SUCCESS == err) {
1685 isds_log(ILF_ISDS, ILL_DEBUG,
1686 _("User %s has been logged into server %s successfully\n"),
1687 username, url);
1689 return err;
1690 #else /* not HAVE_LIBCURL */
1691 return IE_NOTSUP;
1692 #endif
1696 /* Log out from ISDS server discards credentials and connection configuration. */
1697 isds_error isds_logout(struct isds_ctx *context) {
1698 if (!context) return IE_INVALID_CONTEXT;
1699 zfree(context->long_message);
1701 #if HAVE_LIBCURL
1702 if (context->curl) {
1703 if (context->otp || context->mep) {
1704 isds_error err = _isds_invalidate_otp_cookie(context);
1705 if (err) return err;
1708 /* Close connection */
1709 _isds_close_connection(context);
1711 /* Discard credentials for sure. They should not survive isds_login(),
1712 * even successful .*/
1713 _isds_discard_credentials(context, 1);
1715 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1716 } else {
1717 _isds_discard_credentials(context, 1);
1719 zfree(context->url);
1720 return IE_SUCCESS;
1721 #else /* not HAVE_LIBCURL */
1722 return IE_NOTSUP;
1723 #endif
1727 /* Verify connection to ISDS is alive and server is responding.
1728 * Send dummy request to ISDS and expect dummy response. */
1729 isds_error isds_ping(struct isds_ctx *context) {
1730 #if HAVE_LIBCURL
1731 isds_error soap_err;
1732 xmlNsPtr isds_ns = NULL;
1733 xmlNodePtr request = NULL;
1734 #endif /* HAVE_LIBCURL */
1736 if (!context) return IE_INVALID_CONTEXT;
1737 zfree(context->long_message);
1739 #if HAVE_LIBCURL
1740 /* Check if connection is established */
1741 if (!context->curl) return IE_CONNECTION_CLOSED;
1744 /* Build dummy request */
1745 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1746 if (!request) {
1747 isds_log_message(context, _("Could build ISDS dummy request"));
1748 return IE_ERROR;
1750 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1751 if(!isds_ns) {
1752 isds_log_message(context, _("Could not create ISDS name space"));
1753 xmlFreeNode(request);
1754 return IE_ERROR;
1756 xmlSetNs(request, isds_ns);
1758 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1760 /* XXX: ISDS documentation does not specify response body for
1761 * DummyOperation request. However real server sends back
1762 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1763 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1764 * SOAP body content, e.g. the dmStatus element. */
1766 /* Send dummy request */
1767 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1769 /* Destroy log-in request */
1770 xmlFreeNode(request);
1772 if (soap_err) {
1773 isds_log(ILF_ISDS, ILL_DEBUG,
1774 _("ISDS server could not be contacted\n"));
1775 return soap_err;
1778 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1779 * authentication succeeded if soap_err == IE_SUCCESS */
1782 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1784 return IE_SUCCESS;
1785 #else /* not HAVE_LIBCURL */
1786 return IE_NOTSUP;
1787 #endif
1791 /* Send bogus request to ISDS.
1792 * Just for test purposes */
1793 isds_error isds_bogus_request(struct isds_ctx *context) {
1794 #if HAVE_LIBCURL
1795 isds_error err;
1796 xmlNsPtr isds_ns = NULL;
1797 xmlNodePtr request = NULL;
1798 xmlDocPtr response = NULL;
1799 xmlChar *code = NULL, *message = NULL;
1800 #endif
1802 if (!context) return IE_INVALID_CONTEXT;
1803 zfree(context->long_message);
1805 #if HAVE_LIBCURL
1806 /* Check if connection is established */
1807 if (!context->curl) {
1808 /* Testing printf message */
1809 isds_printf_message(context, "%s", _("I said connection closed"));
1810 return IE_CONNECTION_CLOSED;
1814 /* Build dummy request */
1815 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1816 if (!request) {
1817 isds_log_message(context, _("Could build ISDS bogus request"));
1818 return IE_ERROR;
1820 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1821 if(!isds_ns) {
1822 isds_log_message(context, _("Could not create ISDS name space"));
1823 xmlFreeNode(request);
1824 return IE_ERROR;
1826 xmlSetNs(request, isds_ns);
1828 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1830 /* Sent bogus request */
1831 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1833 /* Destroy request */
1834 xmlFreeNode(request);
1836 if (err) {
1837 isds_log(ILF_ISDS, ILL_DEBUG,
1838 _("Processing ISDS response on bogus request failed\n"));
1839 xmlFreeDoc(response);
1840 return err;
1843 /* Check for response status */
1844 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1845 &code, &message, NULL);
1846 if (err) {
1847 isds_log(ILF_ISDS, ILL_DEBUG,
1848 _("ISDS response on bogus request is missing status\n"));
1849 free(code);
1850 free(message);
1851 xmlFreeDoc(response);
1852 return err;
1854 if (xmlStrcmp(code, BAD_CAST "0000")) {
1855 char *code_locale = _isds_utf82locale((char*)code);
1856 char *message_locale = _isds_utf82locale((char*)message);
1857 isds_log(ILF_ISDS, ILL_DEBUG,
1858 _("Server refused bogus request (code=%s, message=%s)\n"),
1859 code_locale, message_locale);
1860 /* XXX: Literal error messages from ISDS are Czech messages
1861 * (English sometimes) in UTF-8. It's hard to catch them for
1862 * translation. Successfully gettextized would return in locale
1863 * encoding, unsuccessfully translated would pass in UTF-8. */
1864 isds_log_message(context, message_locale);
1865 free(code_locale);
1866 free(message_locale);
1867 free(code);
1868 free(message);
1869 xmlFreeDoc(response);
1870 return IE_ISDS;
1874 free(code);
1875 free(message);
1876 xmlFreeDoc(response);
1878 isds_log(ILF_ISDS, ILL_DEBUG,
1879 _("Bogus message accepted by server. This should not happen.\n"));
1881 return IE_SUCCESS;
1882 #else /* not HAVE_LIBCURL */
1883 return IE_NOTSUP;
1884 #endif
1888 #if HAVE_LIBCURL
1889 /* Serialize XML subtree to buffer preserving XML indentation.
1890 * @context is session context
1891 * @subtree is XML element to be serialized (with children)
1892 * @buffer is automatically reallocated buffer where serialize to
1893 * @length is size of serialized stream in bytes
1894 * @return standard error code, free @buffer in case of error */
1895 static isds_error serialize_subtree(struct isds_ctx *context,
1896 xmlNodePtr subtree, void **buffer, size_t *length) {
1897 isds_error err = IE_SUCCESS;
1898 xmlBufferPtr xml_buffer = NULL;
1899 xmlSaveCtxtPtr save_ctx = NULL;
1900 xmlDocPtr subtree_doc = NULL;
1901 xmlNodePtr subtree_copy;
1902 xmlNsPtr isds_ns;
1903 void *new_buffer;
1905 if (!context) return IE_INVALID_CONTEXT;
1906 if (!buffer) return IE_INVAL;
1907 zfree(*buffer);
1908 if (!subtree || !length) return IE_INVAL;
1910 /* Make temporary XML document with @subtree root element */
1911 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1912 * It can result in not well-formed on invalid XML tree (e.g. name space
1913 * prefix definition can miss. */
1914 /*FIXME */
1916 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1917 if (!subtree_doc) {
1918 isds_log_message(context, _("Could not build temporary document"));
1919 err = IE_ERROR;
1920 goto leave;
1923 /* XXX: Copy subtree and attach the copy to document.
1924 * One node can not bee attached into more document at the same time.
1925 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1926 * automatically.
1927 * XXX: Check xmlSaveTree() too. */
1928 subtree_copy = xmlCopyNodeList(subtree);
1929 if (!subtree_copy) {
1930 isds_log_message(context, _("Could not copy subtree"));
1931 err = IE_ERROR;
1932 goto leave;
1934 xmlDocSetRootElement(subtree_doc, subtree_copy);
1936 /* Only this way we get namespace definition as @xmlns:isds,
1937 * otherwise we get namespace prefix without definition */
1938 /* FIXME: Don't overwrite original default namespace */
1939 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1940 if(!isds_ns) {
1941 isds_log_message(context, _("Could not create ISDS name space"));
1942 err = IE_ERROR;
1943 goto leave;
1945 xmlSetNs(subtree_copy, isds_ns);
1948 /* Serialize the document into buffer */
1949 xml_buffer = xmlBufferCreate();
1950 if (!xml_buffer) {
1951 isds_log_message(context, _("Could not create xmlBuffer"));
1952 err = IE_ERROR;
1953 goto leave;
1955 /* Last argument 0 means to not format the XML tree */
1956 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1957 if (!save_ctx) {
1958 isds_log_message(context, _("Could not create XML serializer"));
1959 err = IE_ERROR;
1960 goto leave;
1962 /* XXX: According LibXML documentation, this function does not return
1963 * meaningful value yet */
1964 xmlSaveDoc(save_ctx, subtree_doc);
1965 if (-1 == xmlSaveFlush(save_ctx)) {
1966 isds_log_message(context,
1967 _("Could not serialize XML subtree"));
1968 err = IE_ERROR;
1969 goto leave;
1971 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1972 * even after xmlSaveFlush(). Thus close it here */
1973 xmlSaveClose(save_ctx); save_ctx = NULL;
1976 /* Store and detach buffer from xml_buffer */
1977 *buffer = xml_buffer->content;
1978 *length = xml_buffer->use;
1979 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1981 /* Shrink buffer */
1982 new_buffer = realloc(*buffer, *length);
1983 if (new_buffer) *buffer = new_buffer;
1985 leave:
1986 if (err) {
1987 zfree(*buffer);
1988 *length = 0;
1991 xmlSaveClose(save_ctx);
1992 xmlBufferFree(xml_buffer);
1993 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1994 return err;
1996 #endif /* HAVE_LIBCURL */
1999 #if 0
2000 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
2001 * @context is session context
2002 * @document is original document where @nodeset points to
2003 * @nodeset is XPath node set to dump (recursively)
2004 * @buffer is automatically reallocated buffer where serialize to
2005 * @length is size of serialized stream in bytes
2006 * @return standard error code, free @buffer in case of error */
2007 static isds_error dump_nodeset(struct isds_ctx *context,
2008 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
2009 void **buffer, size_t *length) {
2010 isds_error err = IE_SUCCESS;
2011 xmlBufferPtr xml_buffer = NULL;
2012 void *new_buffer;
2014 if (!context) return IE_INVALID_CONTEXT;
2015 if (!buffer) return IE_INVAL;
2016 zfree(*buffer);
2017 if (!document || !nodeset || !length) return IE_INVAL;
2018 *length = 0;
2020 /* Empty node set results into NULL buffer */
2021 if (xmlXPathNodeSetIsEmpty(nodeset)) {
2022 goto leave;
2025 /* Resulting the document into buffer */
2026 xml_buffer = xmlBufferCreate();
2027 if (!xml_buffer) {
2028 isds_log_message(context, _("Could not create xmlBuffer"));
2029 err = IE_ERROR;
2030 goto leave;
2033 /* Iterate over all nodes */
2034 for (int i = 0; i < nodeset->nodeNr; i++) {
2035 /* Serialize node.
2036 * XXX: xmlNodeDump() appends to xml_buffer. */
2037 if (-1 ==
2038 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
2039 isds_log_message(context, _("Could not dump XML node"));
2040 err = IE_ERROR;
2041 goto leave;
2045 /* Store and detach buffer from xml_buffer */
2046 *buffer = xml_buffer->content;
2047 *length = xml_buffer->use;
2048 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
2050 /* Shrink buffer */
2051 new_buffer = realloc(*buffer, *length);
2052 if (new_buffer) *buffer = new_buffer;
2055 leave:
2056 if (err) {
2057 zfree(*buffer);
2058 *length = 0;
2061 xmlBufferFree(xml_buffer);
2062 return err;
2064 #endif
2066 #if 0
2067 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
2068 * @context is session context
2069 * @document is original document where @nodeset points to
2070 * @nodeset is XPath node set to dump (recursively)
2071 * @buffer is automatically reallocated buffer where serialize to
2072 * @length is size of serialized stream in bytes
2073 * @return standard error code, free @buffer in case of error */
2074 static isds_error dump_nodeset(struct isds_ctx *context,
2075 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
2076 void **buffer, size_t *length) {
2077 isds_error err = IE_SUCCESS;
2078 xmlBufferPtr xml_buffer = NULL;
2079 xmlSaveCtxtPtr save_ctx = NULL;
2080 void *new_buffer;
2082 if (!context) return IE_INVALID_CONTEXT;
2083 if (!buffer) return IE_INVAL;
2084 zfree(*buffer);
2085 if (!document || !nodeset || !length) return IE_INVAL;
2086 *length = 0;
2088 /* Empty node set results into NULL buffer */
2089 if (xmlXPathNodeSetIsEmpty(nodeset)) {
2090 goto leave;
2093 /* Resulting the document into buffer */
2094 xml_buffer = xmlBufferCreate();
2095 if (!xml_buffer) {
2096 isds_log_message(context, _("Could not create xmlBuffer"));
2097 err = IE_ERROR;
2098 goto leave;
2100 if (xmlSubstituteEntitiesDefault(1)) {
2101 isds_log_message(context, _("Could not disable attribute escaping"));
2102 err = IE_ERROR;
2103 goto leave;
2105 /* Last argument means:
2106 * 0 to not format the XML tree
2107 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
2108 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
2109 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
2110 if (!save_ctx) {
2111 isds_log_message(context, _("Could not create XML serializer"));
2112 err = IE_ERROR;
2113 goto leave;
2115 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
2116 isds_log_message(context, _("Could not disable attribute escaping"));
2117 err = IE_ERROR;
2118 goto leave;
2122 /* Iterate over all nodes */
2123 for (int i = 0; i < nodeset->nodeNr; i++) {
2124 /* Serialize node.
2125 * XXX: xmlNodeDump() appends to xml_buffer. */
2126 /*if (-1 ==
2127 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
2129 /* XXX: According LibXML documentation, this function does not return
2130 * meaningful value yet */
2131 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
2132 if (-1 == xmlSaveFlush(save_ctx)) {
2133 isds_log_message(context,
2134 _("Could not serialize XML subtree"));
2135 err = IE_ERROR;
2136 goto leave;
2140 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
2141 * even after xmlSaveFlush(). Thus close it here */
2142 xmlSaveClose(save_ctx); save_ctx = NULL;
2144 /* Store and detach buffer from xml_buffer */
2145 *buffer = xml_buffer->content;
2146 *length = xml_buffer->use;
2147 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
2149 /* Shrink buffer */
2150 new_buffer = realloc(*buffer, *length);
2151 if (new_buffer) *buffer = new_buffer;
2153 leave:
2154 if (err) {
2155 zfree(*buffer);
2156 *length = 0;
2159 xmlSaveClose(save_ctx);
2160 xmlBufferFree(xml_buffer);
2161 return err;
2163 #endif
2166 #if HAVE_LIBCURL
2167 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
2168 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
2169 if (!string || !type) return IE_INVAL;
2171 if (!xmlStrcmp(string, BAD_CAST "FO"))
2172 *type = DBTYPE_FO;
2173 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
2174 *type = DBTYPE_PFO;
2175 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
2176 *type = DBTYPE_PFO_ADVOK;
2177 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
2178 *type = DBTYPE_PFO_DANPOR;
2179 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
2180 *type = DBTYPE_PFO_INSSPR;
2181 else if (!xmlStrcmp(string, BAD_CAST "PFO_AUDITOR"))
2182 *type = DBTYPE_PFO_AUDITOR;
2183 else if (!xmlStrcmp(string, BAD_CAST "PO"))
2184 *type = DBTYPE_PO;
2185 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
2186 *type = DBTYPE_PO_ZAK;
2187 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
2188 *type = DBTYPE_PO_REQ;
2189 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
2190 *type = DBTYPE_OVM;
2191 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
2192 *type = DBTYPE_OVM_NOTAR;
2193 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
2194 *type = DBTYPE_OVM_EXEKUT;
2195 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2196 *type = DBTYPE_OVM_REQ;
2197 else if (!xmlStrcmp(string, BAD_CAST "OVM_FO"))
2198 *type = DBTYPE_OVM_FO;
2199 else if (!xmlStrcmp(string, BAD_CAST "OVM_PFO"))
2200 *type = DBTYPE_OVM_PFO;
2201 else if (!xmlStrcmp(string, BAD_CAST "OVM_PO"))
2202 *type = DBTYPE_OVM_PO;
2203 else
2204 return IE_ENUM;
2205 return IE_SUCCESS;
2209 /* Convert ISDS dbType enum @type to UTF-8 string.
2210 * @Return pointer to static string, or NULL if unknown enum value */
2211 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2212 switch(type) {
2213 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2214 * of view of generic public SOAP interface. */
2215 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2216 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2217 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2218 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2219 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2220 case DBTYPE_PFO_AUDITOR: return(BAD_CAST "PFO_AUDITOR"); break;
2221 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2222 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2223 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2224 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2225 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2226 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2227 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2228 case DBTYPE_OVM_FO: return(BAD_CAST "OVM_FO"); break;
2229 case DBTYPE_OVM_PFO: return(BAD_CAST "OVM_PFO"); break;
2230 case DBTYPE_OVM_PO: return(BAD_CAST "OVM_PO"); break;
2231 default: return NULL; break;
2236 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2237 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2238 if (!string || !type) return IE_INVAL;
2240 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2241 *type = USERTYPE_PRIMARY;
2242 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2243 *type = USERTYPE_ENTRUSTED;
2244 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2245 *type = USERTYPE_ADMINISTRATOR;
2246 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2247 *type = USERTYPE_OFFICIAL;
2248 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2249 *type = USERTYPE_OFFICIAL_CERT;
2250 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2251 *type = USERTYPE_LIQUIDATOR;
2252 else if (!xmlStrcmp(string, BAD_CAST "RECEIVER"))
2253 *type = USERTYPE_RECEIVER;
2254 else if (!xmlStrcmp(string, BAD_CAST "GUARDIAN"))
2255 *type = USERTYPE_GUARDIAN;
2256 else
2257 return IE_ENUM;
2258 return IE_SUCCESS;
2262 /* Convert ISDS userType enum @type to UTF-8 string.
2263 * @Return pointer to static string, or NULL if unknown enum value */
2264 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2265 switch(type) {
2266 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2267 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2268 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2269 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2270 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2271 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2272 case USERTYPE_RECEIVER: return(BAD_CAST "RECEIVER"); break;
2273 case USERTYPE_GUARDIAN: return(BAD_CAST "GUARDIAN"); break;
2274 default: return NULL; break;
2279 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2280 static isds_error string2isds_sender_type(const xmlChar *string,
2281 isds_sender_type *type) {
2282 if (!string || !type) return IE_INVAL;
2284 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2285 *type = SENDERTYPE_PRIMARY;
2286 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2287 *type = SENDERTYPE_ENTRUSTED;
2288 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2289 *type = SENDERTYPE_ADMINISTRATOR;
2290 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2291 *type = SENDERTYPE_OFFICIAL;
2292 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2293 *type = SENDERTYPE_VIRTUAL;
2294 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2295 *type = SENDERTYPE_OFFICIAL_CERT;
2296 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2297 *type = SENDERTYPE_LIQUIDATOR;
2298 else if (!xmlStrcmp(string, BAD_CAST "RECEIVER"))
2299 *type = SENDERTYPE_RECEIVER;
2300 else if (!xmlStrcmp(string, BAD_CAST "GUARDIAN"))
2301 *type = SENDERTYPE_GUARDIAN;
2302 else
2303 return IE_ENUM;
2304 return IE_SUCCESS;
2308 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2309 static isds_error string2isds_payment_type(const xmlChar *string,
2310 isds_payment_type *type) {
2311 if (!string || !type) return IE_INVAL;
2313 if (!xmlStrcmp(string, BAD_CAST "K"))
2314 *type = PAYMENT_SENDER;
2315 else if (!xmlStrcmp(string, BAD_CAST "O"))
2316 *type = PAYMENT_RESPONSE;
2317 else if (!xmlStrcmp(string, BAD_CAST "G"))
2318 *type = PAYMENT_SPONSOR;
2319 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2320 *type = PAYMENT_SPONSOR_LIMITED;
2321 else if (!xmlStrcmp(string, BAD_CAST "D"))
2322 *type = PAYMENT_SPONSOR_EXTERNAL;
2323 else if (!xmlStrcmp(string, BAD_CAST "E"))
2324 *type = PAYMENT_STAMP;
2325 else
2326 return IE_ENUM;
2327 return IE_SUCCESS;
2331 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2332 * ciEventType is integer but we convert it from string representation
2333 * directly. */
2334 static isds_error string2isds_credit_event_type(const xmlChar *string,
2335 isds_credit_event_type *type) {
2336 if (!string || !type) return IE_INVAL;
2338 if (!xmlStrcmp(string, BAD_CAST "1"))
2339 *type = ISDS_CREDIT_CHARGED;
2340 else if (!xmlStrcmp(string, BAD_CAST "2"))
2341 *type = ISDS_CREDIT_DISCHARGED;
2342 else if (!xmlStrcmp(string, BAD_CAST "3"))
2343 *type = ISDS_CREDIT_MESSAGE_SENT;
2344 else if (!xmlStrcmp(string, BAD_CAST "4"))
2345 *type = ISDS_CREDIT_STORAGE_SET;
2346 else if (!xmlStrcmp(string, BAD_CAST "5"))
2347 *type = ISDS_CREDIT_EXPIRED;
2348 else
2349 return IE_ENUM;
2350 return IE_SUCCESS;
2354 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2355 * @Return pointer to static string, or NULL if unknown enum value */
2356 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2357 switch(type) {
2358 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2359 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2360 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2361 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2362 default: return NULL; break;
2367 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2368 * ISDSSearch2/searchType value.
2369 * @Return pointer to static string, or NULL if unknown enum value */
2370 static const xmlChar *isds_fulltext_target2string(
2371 const isds_fulltext_target type) {
2372 switch(type) {
2373 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2374 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2375 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2376 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2377 default: return NULL; break;
2380 #endif /* HAVE_LIBCURL */
2383 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2384 * @Return IE_ENUM if @string is not valid enum member */
2385 static isds_error string2isds_FileMetaType(const xmlChar *string,
2386 isds_FileMetaType *type) {
2387 if (!string || !type) return IE_INVAL;
2389 if (!xmlStrcmp(string, BAD_CAST "main"))
2390 *type = FILEMETATYPE_MAIN;
2391 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2392 *type = FILEMETATYPE_ENCLOSURE;
2393 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2394 *type = FILEMETATYPE_SIGNATURE;
2395 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2396 *type = FILEMETATYPE_META;
2397 else
2398 return IE_ENUM;
2399 return IE_SUCCESS;
2403 /* Convert UTF-8 @string to ISDS hash @algorithm.
2404 * @Return IE_ENUM if @string is not valid enum member */
2405 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2406 isds_hash_algorithm *algorithm) {
2407 if (!string || !algorithm) return IE_INVAL;
2409 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2410 *algorithm = HASH_ALGORITHM_MD5;
2411 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2412 *algorithm = HASH_ALGORITHM_SHA_1;
2413 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2414 *algorithm = HASH_ALGORITHM_SHA_224;
2415 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2416 *algorithm = HASH_ALGORITHM_SHA_256;
2417 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2418 *algorithm = HASH_ALGORITHM_SHA_384;
2419 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2420 *algorithm = HASH_ALGORITHM_SHA_512;
2421 else
2422 return IE_ENUM;
2423 return IE_SUCCESS;
2427 #if HAVE_LIBCURL
2428 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2429 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2430 if (!time || !string) return IE_INVAL;
2432 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2433 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2434 return IE_ERROR;
2436 return IE_SUCCESS;
2440 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2441 * respects the @time microseconds too. */
2442 static isds_error timeval2timestring(const struct timeval *time,
2443 xmlChar **string) {
2444 struct tm broken;
2445 time_t seconds_as_time_t;
2447 if (!time || !string) return IE_INVAL;
2449 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2450 * 32-bit long in Microsoft API. Convert value to the type expected by
2451 * gmtime_r(). */
2452 seconds_as_time_t = time->tv_sec;
2453 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2454 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2456 /* TODO: small negative year should be formatted as "-0012". This is not
2457 * true for glibc "%04d". We should implement it.
2458 * time->tv_usec type is su_seconds_t which is required to be signed
2459 * integer to accomodate values from range [-1, 1000000].
2460 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2461 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2462 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2463 * of the range check above. */
2464 if (-1 == isds_asprintf((char **) string,
2465 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2466 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2467 broken.tm_hour, broken.tm_min, broken.tm_sec,
2468 (int32_t)time->tv_usec))
2469 return IE_ERROR;
2471 return IE_SUCCESS;
2473 #endif /* HAVE_LIBCURL */
2476 /* Convert UTF-8 ISO 8601 date-time @string to static struct timeval.
2477 * It respects microseconds too. Microseconds are rounded half up.
2478 * In case of error, @time will be undefined. */
2479 static isds_error timestring2static_timeval(const xmlChar *string,
2480 struct timeval *time) {
2481 struct tm broken;
2482 char *offset, *delim, *endptr;
2483 const int subsecond_resolution = 6;
2484 char subseconds[subsecond_resolution + 1];
2485 _Bool round_up = 0;
2486 int offset_hours, offset_minutes;
2487 int i;
2488 long int long_number;
2489 #ifdef _WIN32
2490 int tmp;
2491 #endif
2493 if (!time) return IE_INVAL;
2494 if (!string) {
2495 return IE_INVAL;
2498 memset(&broken, 0, sizeof(broken));
2499 memset(time, 0, sizeof(*time));
2502 /* xsd:date is ISO 8601 string, thus ASCII */
2503 /*TODO: negative year */
2505 #ifdef _WIN32
2506 i = 0;
2507 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2508 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2509 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2510 &i)) < 6) {
2511 return IE_DATE;
2514 broken.tm_year -= 1900;
2515 broken.tm_mon--;
2516 broken.tm_isdst = -1;
2517 offset = (char*)string + i;
2518 #else
2519 /* Parse date and time without subseconds and offset */
2520 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2521 if (!offset) {
2522 return IE_DATE;
2524 #endif
2526 /* Get subseconds */
2527 if (*offset == '.' ) {
2528 offset++;
2530 /* Copy first 6 digits, pad it with zeros.
2531 * Current server implementation uses only millisecond resolution. */
2532 /* TODO: isdigit() is locale sensitive */
2533 for (i = 0;
2534 i < subsecond_resolution && isdigit(*offset);
2535 i++, offset++) {
2536 subseconds[i] = *offset;
2538 if (subsecond_resolution == i && isdigit(*offset)) {
2539 /* Check 7th digit for rounding */
2540 if (*offset >= '5') round_up = 1;
2541 offset++;
2543 for (; i < subsecond_resolution; i++) {
2544 subseconds[i] = '0';
2546 subseconds[subsecond_resolution] = '\0';
2548 /* Convert it into integer */
2549 long_number = strtol(subseconds, &endptr, 10);
2550 if (*endptr != '\0' || long_number == LONG_MIN ||
2551 long_number == LONG_MAX) {
2552 return IE_DATE;
2554 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2555 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2556 * microseconds" and "the type shall be a signed integer capable of
2557 * storing values at least in the range [-1, 1000000]. */
2558 if (long_number < -1 || long_number >= 1000000) {
2559 return IE_DATE;
2561 time->tv_usec = long_number;
2563 /* Round the subseconds */
2564 if (round_up) {
2565 if (999999 == time->tv_usec) {
2566 time->tv_usec = 0;
2567 broken.tm_sec++;
2568 } else {
2569 time->tv_usec++;
2573 /* move to the zone offset delimiter or signal NULL*/
2574 delim = strchr(offset, '-');
2575 if (!delim)
2576 delim = strchr(offset, '+');
2577 if (!delim)
2578 delim = strchr(offset, 'Z');
2579 offset = delim;
2582 /* Get zone offset */
2583 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2584 * "" equals to "Z" and it means UTC zone. */
2585 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2586 * colon separator */
2587 if (offset && (*offset == '-' || *offset == '+')) {
2588 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2589 return IE_DATE;
2591 if (*offset == '+') {
2592 broken.tm_hour -= offset_hours;
2593 broken.tm_min -= offset_minutes;
2594 } else {
2595 broken.tm_hour += offset_hours;
2596 broken.tm_min += offset_minutes;
2600 /* Convert to time_t */
2601 time->tv_sec = _isds_timegm(&broken);
2602 if (time->tv_sec == (time_t) -1) {
2603 return IE_DATE;
2606 return IE_SUCCESS;
2610 /* Convert UTF-8 ISO 8601 date-time @string to reallocated struct timeval.
2611 * It respects microseconds too. Microseconds are rounded half up.
2612 * In case of error, @time will be freed. */
2613 static isds_error timestring2timeval(const xmlChar *string,
2614 struct timeval **time) {
2615 isds_error error;
2617 if (!time) return IE_INVAL;
2618 if (!string) {
2619 zfree(*time);
2620 return IE_INVAL;
2623 if (!*time) {
2624 *time = calloc(1, sizeof(**time));
2625 if (!*time) return IE_NOMEM;
2626 } else {
2627 memset(*time, 0, sizeof(**time));
2630 error = timestring2static_timeval(string, *time);
2631 if (error) {
2632 zfree(*time);
2635 return error;
2639 /* Convert unsigned int into isds_message_status.
2640 * @context is session context
2641 * @number is pointer to number value. NULL will be treated as invalid value.
2642 * @status is automatically reallocated status
2643 * @return IE_SUCCESS, or error code and free status */
2644 static isds_error uint2isds_message_status(struct isds_ctx *context,
2645 const unsigned long int *number, isds_message_status **status) {
2646 if (!context) return IE_INVALID_CONTEXT;
2647 if (!status) return IE_INVAL;
2649 free(*status); *status = NULL;
2650 if (!number) return IE_INVAL;
2652 if (*number < 1 || *number > 10) {
2653 isds_printf_message(context, _("Invalid message status value: %lu"),
2654 *number);
2655 return IE_ENUM;
2658 *status = malloc(sizeof(**status));
2659 if (!*status) return IE_NOMEM;
2661 **status = 1 << *number;
2662 return IE_SUCCESS;
2666 /* Convert event description string into isds_event members type and
2667 * description
2668 * @string is raw event description starting with event prefix
2669 * @event is structure where to store type and stripped description to
2670 * @return standard error code, unknown prefix is not classified as an error.
2671 * */
2672 static isds_error eventstring2event(const xmlChar *string,
2673 struct isds_event* event) {
2674 const xmlChar *known_prefixes[] = {
2675 BAD_CAST "EV0:",
2676 BAD_CAST "EV1:",
2677 BAD_CAST "EV2:",
2678 BAD_CAST "EV3:",
2679 BAD_CAST "EV4:",
2680 BAD_CAST "EV5:",
2681 BAD_CAST "EV8:",
2682 BAD_CAST "EV11:",
2683 BAD_CAST "EV12:",
2684 BAD_CAST "EV13:"
2686 const isds_event_type types[] = {
2687 EVENT_ENTERED_SYSTEM,
2688 EVENT_ACCEPTED_BY_RECIPIENT,
2689 EVENT_ACCEPTED_BY_FICTION,
2690 EVENT_UNDELIVERABLE,
2691 EVENT_COMMERCIAL_ACCEPTED,
2692 EVENT_DELIVERED,
2693 EVENT_UNDELIVERED_AV_CHECK,
2694 EVENT_PRIMARY_LOGIN,
2695 EVENT_ENTRUSTED_LOGIN,
2696 EVENT_SYSCERT_LOGIN
2698 unsigned int index;
2699 size_t length;
2701 if (!string || !event) return IE_INVAL;
2703 if (!event->type) {
2704 event->type = malloc(sizeof(*event->type));
2705 if (!(event->type)) return IE_NOMEM;
2707 zfree(event->description);
2709 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2710 index++) {
2711 length = xmlUTF8Strlen(known_prefixes[index]);
2713 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2714 /* Prefix is known */
2715 *event->type = types[index];
2717 /* Strip prefix from description and spaces */
2718 /* TODO: Recognize all white spaces from UCS blank class and
2719 * operate on UTF-8 chars. */
2720 for (; string[length] != '\0' && string[length] == ' '; length++);
2721 event->description = strdup((char *) (string + length));
2722 if (!(event->description)) return IE_NOMEM;
2724 return IE_SUCCESS;
2728 /* Unknown event prefix.
2729 * XSD allows any string */
2730 char *string_locale = _isds_utf82locale((char *) string);
2731 isds_log(ILF_ISDS, ILL_WARNING,
2732 _("Unknown delivery info event prefix: %s\n"), string_locale);
2733 free(string_locale);
2735 *event->type = EVENT_UKNOWN;
2736 event->description = strdup((char *) string);
2737 if (!(event->description)) return IE_NOMEM;
2739 return IE_SUCCESS;
2743 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2744 * and leave label */
2745 #define EXTRACT_STRING(element, string) { \
2746 xmlXPathFreeObject(result); \
2747 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2748 if (NULL == (result)) { \
2749 err = IE_ERROR; \
2750 goto leave; \
2752 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2753 if (result->nodesetval->nodeNr > 1) { \
2754 isds_printf_message(context, _("Multiple %s element"), element); \
2755 err = IE_ERROR; \
2756 goto leave; \
2758 (string) = (char *) \
2759 xmlXPathCastNodeSetToString(result->nodesetval); \
2760 if (NULL == (string)) { \
2761 err = IE_ERROR; \
2762 goto leave; \
2767 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2769 char *string = NULL; \
2770 EXTRACT_STRING(element, string); \
2772 if (string) { \
2773 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2774 if (!(booleanPtr)) { \
2775 free(string); \
2776 err = IE_NOMEM; \
2777 goto leave; \
2780 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2781 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2782 *(booleanPtr) = 1; \
2783 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2784 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2785 *(booleanPtr) = 0; \
2786 else { \
2787 char *string_locale = _isds_utf82locale((char*)string); \
2788 isds_printf_message(context, \
2789 _("%s value is not valid boolean: %s"), \
2790 element, string_locale); \
2791 free(string_locale); \
2792 free(string); \
2793 err = IE_ERROR; \
2794 goto leave; \
2797 free(string); \
2801 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2803 char *string = NULL; \
2804 EXTRACT_STRING(element, string); \
2806 if (NULL == string) { \
2807 isds_printf_message(context, _("%s element is empty"), element); \
2808 err = IE_ERROR; \
2809 goto leave; \
2811 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2812 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2813 (boolean) = 1; \
2814 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2815 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2816 (boolean) = 0; \
2817 else { \
2818 char *string_locale = _isds_utf82locale((char*)string); \
2819 isds_printf_message(context, \
2820 _("%s value is not valid boolean: %s"), \
2821 element, string_locale); \
2822 free(string_locale); \
2823 free(string); \
2824 err = IE_ERROR; \
2825 goto leave; \
2828 free(string); \
2831 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2833 char *string = NULL; \
2834 EXTRACT_STRING(element, string); \
2835 if (string) { \
2836 long int number; \
2837 char *endptr; \
2839 number = strtol((char*)string, &endptr, 10); \
2841 if (*endptr != '\0') { \
2842 char *string_locale = _isds_utf82locale((char *)string); \
2843 isds_printf_message(context, \
2844 _("%s is not valid integer: %s"), \
2845 element, string_locale); \
2846 free(string_locale); \
2847 free(string); \
2848 err = IE_ISDS; \
2849 goto leave; \
2852 if (number == LONG_MIN || number == LONG_MAX) { \
2853 char *string_locale = _isds_utf82locale((char *)string); \
2854 isds_printf_message(context, \
2855 _("%s value out of range of long int: %s"), \
2856 element, string_locale); \
2857 free(string_locale); \
2858 free(string); \
2859 err = IE_ERROR; \
2860 goto leave; \
2863 free(string); string = NULL; \
2865 if (!(preallocated)) { \
2866 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2867 if (!(longintPtr)) { \
2868 err = IE_NOMEM; \
2869 goto leave; \
2872 *(longintPtr) = number; \
2876 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2878 char *string = NULL; \
2879 EXTRACT_STRING(element, string); \
2880 if (string) { \
2881 long int number; \
2882 char *endptr; \
2884 number = strtol((char*)string, &endptr, 10); \
2886 if (*endptr != '\0') { \
2887 char *string_locale = _isds_utf82locale((char *)string); \
2888 isds_printf_message(context, \
2889 _("%s is not valid integer: %s"), \
2890 element, string_locale); \
2891 free(string_locale); \
2892 free(string); \
2893 err = IE_ISDS; \
2894 goto leave; \
2897 if (number == LONG_MIN || number == LONG_MAX) { \
2898 char *string_locale = _isds_utf82locale((char *)string); \
2899 isds_printf_message(context, \
2900 _("%s value out of range of long int: %s"), \
2901 element, string_locale); \
2902 free(string_locale); \
2903 free(string); \
2904 err = IE_ERROR; \
2905 goto leave; \
2908 free(string); string = NULL; \
2909 if (number < 0) { \
2910 isds_printf_message(context, \
2911 _("%s value is negative: %ld"), element, number); \
2912 err = IE_ERROR; \
2913 goto leave; \
2916 if (!(preallocated)) { \
2917 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2918 if (!(ulongintPtr)) { \
2919 err = IE_NOMEM; \
2920 goto leave; \
2923 *(ulongintPtr) = number; \
2927 #define EXTRACT_DATE(element, tmPtr) { \
2928 char *string = NULL; \
2929 EXTRACT_STRING(element, string); \
2930 if (NULL != string) { \
2931 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2932 if (NULL == (tmPtr)) { \
2933 free(string); \
2934 err = IE_NOMEM; \
2935 goto leave; \
2937 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2938 if (err) { \
2939 if (err == IE_NOTSUP) { \
2940 err = IE_ISDS; \
2941 char *string_locale = _isds_utf82locale(string); \
2942 char *element_locale = _isds_utf82locale(element); \
2943 isds_printf_message(context, _("Invalid %s value: %s"), \
2944 element_locale, string_locale); \
2945 free(string_locale); \
2946 free(element_locale); \
2948 free(string); \
2949 goto leave; \
2951 free(string); \
2955 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2956 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2957 NULL); \
2958 if ((required) && (!string)) { \
2959 char *attribute_locale = _isds_utf82locale(attribute); \
2960 char *element_locale = \
2961 _isds_utf82locale((char *)xpath_ctx->node->name); \
2962 isds_printf_message(context, \
2963 _("Could not extract required %s attribute value from " \
2964 "%s element"), attribute_locale, element_locale); \
2965 free(element_locale); \
2966 free(attribute_locale); \
2967 err = IE_ERROR; \
2968 goto leave; \
2973 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2975 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2976 (xmlChar *) (string)); \
2977 if (!node) { \
2978 isds_printf_message(context, \
2979 _("Could not add %s child to %s element"), \
2980 element, (parent)->name); \
2981 err = IE_ERROR; \
2982 goto leave; \
2986 #define INSERT_STRING(parent, element, string) \
2987 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2989 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2991 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2992 else { INSERT_STRING(parent, element, "false"); } \
2995 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2997 if (booleanPtr) { \
2998 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2999 } else { \
3000 INSERT_STRING(parent, element, NULL); \
3004 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
3005 if ((longintPtr)) { \
3006 /* FIXME: locale sensitive */ \
3007 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
3008 err = IE_NOMEM; \
3009 goto leave; \
3011 INSERT_STRING(parent, element, buffer) \
3012 free(buffer); (buffer) = NULL; \
3013 } else { INSERT_STRING(parent, element, NULL) } \
3016 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
3017 if ((ulongintPtr)) { \
3018 /* FIXME: locale sensitive */ \
3019 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
3020 err = IE_NOMEM; \
3021 goto leave; \
3023 INSERT_STRING(parent, element, buffer) \
3024 free(buffer); (buffer) = NULL; \
3025 } else { INSERT_STRING(parent, element, NULL) } \
3028 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
3030 /* FIXME: locale sensitive */ \
3031 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
3032 err = IE_NOMEM; \
3033 goto leave; \
3035 INSERT_STRING(parent, element, buffer) \
3036 free(buffer); (buffer) = NULL; \
3039 /* Requires attribute_node variable, do not free it. Can be used to reffer to
3040 * new attribute. */
3041 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
3043 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
3044 (xmlChar *) (string)); \
3045 if (!attribute_node) { \
3046 isds_printf_message(context, _("Could not add %s " \
3047 "attribute to %s element"), \
3048 (attribute), (parent)->name); \
3049 err = IE_ERROR; \
3050 goto leave; \
3054 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
3055 if (string) { \
3056 int length = xmlUTF8Strlen((xmlChar *) (string)); \
3057 if (length > (maximum)) { \
3058 isds_printf_message(context, \
3059 ngettext("%s has more than %d characters", \
3060 "%s has more than %d characters", (maximum)), \
3061 (name), (maximum)); \
3062 err = IE_2BIG; \
3063 goto leave; \
3065 if (length < (minimum)) { \
3066 isds_printf_message(context, \
3067 ngettext("%s has less than %d characters", \
3068 "%s has less than %d characters", (minimum)), \
3069 (name), (minimum)); \
3070 err = IE_2SMALL; \
3071 goto leave; \
3076 #define INSERT_ELEMENT(child, parent, element) \
3078 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
3079 if (!(child)) { \
3080 isds_printf_message(context, \
3081 _("Could not add %s child to %s element"), \
3082 (element), (parent)->name); \
3083 err = IE_ERROR; \
3084 goto leave; \
3089 /* Find child element by name in given XPath context and switch context onto
3090 * it. The child must be uniq and must exist. Otherwise fails.
3091 * @context is ISDS context
3092 * @child is child element name
3093 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
3094 * into it child. In error case, the @xpath_ctx keeps original value. */
3095 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
3096 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
3097 isds_error err = IE_SUCCESS;
3098 xmlXPathObjectPtr result = NULL;
3100 if (!context) return IE_INVALID_CONTEXT;
3101 if (!child || !xpath_ctx) return IE_INVAL;
3103 /* Find child */
3104 result = xmlXPathEvalExpression(child, xpath_ctx);
3105 if (!result) {
3106 err = IE_XML;
3107 goto leave;
3110 /* No match */
3111 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3112 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
3113 char *child_locale = _isds_utf82locale((char*) child);
3114 isds_printf_message(context,
3115 _("%s element does not contain %s child"),
3116 parent_locale, child_locale);
3117 free(child_locale);
3118 free(parent_locale);
3119 err = IE_NOEXIST;
3120 goto leave;
3123 /* More matches */
3124 if (result->nodesetval->nodeNr > 1) {
3125 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
3126 char *child_locale = _isds_utf82locale((char*) child);
3127 isds_printf_message(context,
3128 _("%s element contains multiple %s children"),
3129 parent_locale, child_locale);
3130 free(child_locale);
3131 free(parent_locale);
3132 err = IE_NOTUNIQ;
3133 goto leave;
3136 /* Switch context */
3137 xpath_ctx->node = result->nodesetval->nodeTab[0];
3139 leave:
3140 xmlXPathFreeObject(result);
3141 return err;
3146 #if HAVE_LIBCURL
3147 /* Find and convert XSD:gPersonName group in current node into structure
3148 * @context is ISDS context
3149 * @personName is automatically reallocated person name structure. If no member
3150 * value is found, will be freed.
3151 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
3152 * elements
3153 * In case of error @personName will be freed. */
3154 static isds_error extract_gPersonName(struct isds_ctx *context,
3155 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
3156 isds_error err = IE_SUCCESS;
3157 xmlXPathObjectPtr result = NULL;
3159 if (!context) return IE_INVALID_CONTEXT;
3160 if (!personName) return IE_INVAL;
3161 isds_PersonName_free(personName);
3162 if (!xpath_ctx) return IE_INVAL;
3165 *personName = calloc(1, sizeof(**personName));
3166 if (!*personName) {
3167 err = IE_NOMEM;
3168 goto leave;
3171 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
3172 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
3173 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
3174 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
3176 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
3177 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
3178 isds_PersonName_free(personName);
3180 leave:
3181 if (err) isds_PersonName_free(personName);
3182 xmlXPathFreeObject(result);
3183 return err;
3187 /* Find and convert XSD:gAddress group extended with relevant
3188 * tdbPersonalOwnerinfo members in current node into structure
3189 * @context is ISDS context
3190 * @address is automatically reallocated address structure. If no member
3191 * value is found, will be freed.
3192 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
3193 * elements
3194 * In case of error @address will be freed. */
3195 static isds_error extract_gAddress(struct isds_ctx *context,
3196 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
3197 isds_error err = IE_SUCCESS;
3198 xmlXPathObjectPtr result = NULL;
3200 if (!context) return IE_INVALID_CONTEXT;
3201 if (!address) return IE_INVAL;
3202 isds_Address_free(address);
3203 if (!xpath_ctx) return IE_INVAL;
3206 *address = calloc(1, sizeof(**address));
3207 if (!*address) {
3208 err = IE_NOMEM;
3209 goto leave;
3212 EXTRACT_LONGINT("isds:adCode", (*address)->adCode, 0);
3213 EXTRACT_STRING("isds:adCity", (*address)->adCity);
3214 EXTRACT_STRING("isds:adDistrict", (*address)->adDistrict);
3215 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
3216 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
3217 EXTRACT_STRING("isds:adNumberInMunicipality",
3218 (*address)->adNumberInMunicipality);
3219 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
3220 EXTRACT_STRING("isds:adState", (*address)->adState);
3222 if (!(*address)->adCity && !(*address)->adStreet &&
3223 !(*address)->adNumberInStreet &&
3224 !(*address)->adNumberInMunicipality &&
3225 !(*address)->adZipCode && !(*address)->adState)
3226 isds_Address_free(address);
3228 leave:
3229 if (err) isds_Address_free(address);
3230 xmlXPathFreeObject(result);
3231 return err;
3235 /* Find and convert isds:biDate element in current node into structure
3236 * @context is ISDS context
3237 * @biDate is automatically reallocated birth date structure. If no member
3238 * value is found, will be freed.
3239 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3240 * element
3241 * In case of error @biDate will be freed. */
3242 static isds_error extract_BiDate(struct isds_ctx *context,
3243 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3244 isds_error err = IE_SUCCESS;
3245 xmlXPathObjectPtr result = NULL;
3246 char *string = NULL;
3248 if (!context) return IE_INVALID_CONTEXT;
3249 if (!biDate) return IE_INVAL;
3250 zfree(*biDate);
3251 if (!xpath_ctx) return IE_INVAL;
3253 EXTRACT_STRING("isds:biDate", string);
3254 if (string) {
3255 *biDate = calloc(1, sizeof(**biDate));
3256 if (!*biDate) {
3257 err = IE_NOMEM;
3258 goto leave;
3260 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3261 if (err) {
3262 if (err == IE_NOTSUP) {
3263 err = IE_ISDS;
3264 char *string_locale = _isds_utf82locale(string);
3265 isds_printf_message(context,
3266 _("Invalid isds:biDate value: %s"), string_locale);
3267 free(string_locale);
3269 goto leave;
3273 leave:
3274 if (err) zfree(*biDate);
3275 free(string);
3276 xmlXPathFreeObject(result);
3277 return err;
3281 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3282 * @context is ISDS context
3283 * @db_owner_info is automatically reallocated box owner info structure
3284 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3285 * XSD:tdbPersonalOwenerInfo element
3286 * In case of error @db_owner_info will be freed. */
3287 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3288 struct isds_DbOwnerInfo **db_owner_info,
3289 xmlXPathContextPtr xpath_ctx) {
3290 isds_error err = IE_SUCCESS;
3291 xmlXPathObjectPtr result = NULL;
3292 char *string = NULL;
3294 if (!context) return IE_INVALID_CONTEXT;
3295 if (!db_owner_info) return IE_INVAL;
3296 isds_DbOwnerInfo_free(db_owner_info);
3297 if (!xpath_ctx) return IE_INVAL;
3300 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3301 if (!*db_owner_info) {
3302 err = IE_NOMEM;
3303 goto leave;
3306 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3308 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info)->aifoIsds);
3310 EXTRACT_STRING("isds:dbType", string);
3311 if (string) {
3312 (*db_owner_info)->dbType =
3313 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3314 if (!(*db_owner_info)->dbType) {
3315 err = IE_NOMEM;
3316 goto leave;
3318 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3319 if (err) {
3320 zfree((*db_owner_info)->dbType);
3321 if (err == IE_ENUM) {
3322 err = IE_ISDS;
3323 char *string_locale = _isds_utf82locale(string);
3324 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3325 string_locale);
3326 free(string_locale);
3328 goto leave;
3330 zfree(string);
3333 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3335 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3336 xpath_ctx);
3337 if (err) goto leave;
3339 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3341 (*db_owner_info)->birthInfo =
3342 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3343 if (!(*db_owner_info)->birthInfo) {
3344 err = IE_NOMEM;
3345 goto leave;
3347 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3348 xpath_ctx);
3349 if (err) goto leave;
3350 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3351 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3352 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3353 if (!(*db_owner_info)->birthInfo->biDate &&
3354 !(*db_owner_info)->birthInfo->biCity &&
3355 !(*db_owner_info)->birthInfo->biCounty &&
3356 !(*db_owner_info)->birthInfo->biState)
3357 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3359 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3360 if (err) goto leave;
3362 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3363 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3364 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3365 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3366 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3368 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3370 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3371 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3372 (*db_owner_info)->dbOpenAddressing);
3374 leave:
3375 if (err) isds_DbOwnerInfo_free(db_owner_info);
3376 free(string);
3377 xmlXPathFreeObject(result);
3378 return err;
3382 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3383 * @context is session context
3384 * @owner is libisds structure with box description.
3385 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3386 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3387 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3388 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3389 * ignored.
3390 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3391 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3392 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3393 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3394 * type. */
3395 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3396 const struct isds_DbOwnerInfo *owner, _Bool pfo_subtype,
3397 xmlNodePtr db_owner_info) {
3399 isds_error err = IE_SUCCESS;
3400 xmlNodePtr node;
3401 xmlChar *string = NULL;
3402 const xmlChar *type_string = NULL;
3404 if (!context) return IE_INVALID_CONTEXT;
3405 if (!owner || !db_owner_info) return IE_INVAL;
3408 /* XXX: All the elements except email and telNumber are mandatory. */
3409 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3410 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3412 if (pfo_subtype) {
3413 INSERT_BOOLEAN(db_owner_info, "aifoIsds", owner->aifoIsds);
3416 if (!pfo_subtype) {
3417 /* dbType */
3418 if (owner->dbType) {
3419 type_string = isds_DbType2string(*(owner->dbType));
3420 if (!type_string) {
3421 isds_printf_message(context, _("Invalid dbType value: %d"),
3422 *(owner->dbType));
3423 err = IE_ENUM;
3424 goto leave;
3427 INSERT_STRING(db_owner_info, "dbType", type_string);
3429 INSERT_STRING(db_owner_info, "ic", owner->ic);
3432 INSERT_STRING(db_owner_info, "pnFirstName",
3433 (NULL == owner->personName) ? NULL: owner->personName->pnFirstName);
3434 INSERT_STRING(db_owner_info, "pnMiddleName",
3435 (NULL == owner->personName) ? NULL: owner->personName->pnMiddleName);
3436 INSERT_STRING(db_owner_info, "pnLastName",
3437 (NULL == owner->personName) ? NULL: owner->personName->pnLastName);
3438 if (!pfo_subtype) {
3439 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3440 (NULL == owner->personName) ? NULL:
3441 owner->personName->pnLastNameAtBirth);
3443 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3446 if (NULL != owner->birthInfo && NULL != owner->birthInfo->biDate) {
3447 err = tm2datestring(owner->birthInfo->biDate, &string);
3448 if (err) goto leave;
3450 INSERT_STRING(db_owner_info, "biDate", string);
3451 zfree(string);
3453 INSERT_STRING(db_owner_info, "biCity",
3454 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCity);
3455 INSERT_STRING(db_owner_info, "biCounty",
3456 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCounty);
3457 INSERT_STRING(db_owner_info, "biState",
3458 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biState);
3460 if (pfo_subtype) {
3461 INSERT_LONGINT(db_owner_info, "adCode",
3462 (NULL == owner->address) ? NULL : owner->address->adCode,
3463 string);
3465 INSERT_STRING(db_owner_info, "adCity",
3466 (NULL == owner->address) ? NULL: owner->address->adCity);
3467 if (pfo_subtype) {
3468 INSERT_STRING(db_owner_info, "adDistrict",
3469 (NULL == owner->address) ? NULL: owner->address->adDistrict);
3471 INSERT_STRING(db_owner_info, "adStreet",
3472 (NULL == owner->address) ? NULL: owner->address->adStreet);
3473 INSERT_STRING(db_owner_info, "adNumberInStreet",
3474 (NULL == owner->address) ? NULL: owner->address->adNumberInStreet);
3475 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3476 (NULL == owner->address) ? NULL: owner->address->adNumberInMunicipality);
3477 INSERT_STRING(db_owner_info, "adZipCode",
3478 (NULL == owner->address) ? NULL: owner->address->adZipCode);
3479 INSERT_STRING(db_owner_info, "adState",
3480 (NULL == owner->address) ? NULL: owner->address->adState);
3482 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3484 if (!pfo_subtype) {
3485 INSERT_STRING(db_owner_info, "email", owner->email);
3486 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3488 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3489 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3491 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3492 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3494 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3496 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3497 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3498 owner->dbOpenAddressing);
3501 leave:
3502 free(string);
3503 return err;
3507 /* Convert XSD:tDbUserInfo XML tree into structure
3508 * @context is ISDS context
3509 * @db_user_info is automatically reallocated user info structure
3510 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3511 * In case of error @db_user_info will be freed. */
3512 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3513 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3514 isds_error err = IE_SUCCESS;
3515 xmlXPathObjectPtr result = NULL;
3516 char *string = NULL;
3518 if (!context) return IE_INVALID_CONTEXT;
3519 if (!db_user_info) return IE_INVAL;
3520 isds_DbUserInfo_free(db_user_info);
3521 if (!xpath_ctx) return IE_INVAL;
3524 *db_user_info = calloc(1, sizeof(**db_user_info));
3525 if (!*db_user_info) {
3526 err = IE_NOMEM;
3527 goto leave;
3530 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3532 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3534 EXTRACT_STRING("isds:userType", string);
3535 if (string) {
3536 (*db_user_info)->userType =
3537 calloc(1, sizeof(*((*db_user_info)->userType)));
3538 if (!(*db_user_info)->userType) {
3539 err = IE_NOMEM;
3540 goto leave;
3542 err = string2isds_UserType((xmlChar *)string,
3543 (*db_user_info)->userType);
3544 if (err) {
3545 zfree((*db_user_info)->userType);
3546 if (err == IE_ENUM) {
3547 err = IE_ISDS;
3548 char *string_locale = _isds_utf82locale(string);
3549 isds_printf_message(context,
3550 _("Unknown isds:userType value: %s"), string_locale);
3551 free(string_locale);
3553 goto leave;
3555 zfree(string);
3558 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3560 (*db_user_info)->personName =
3561 calloc(1, sizeof(*((*db_user_info)->personName)));
3562 if (!(*db_user_info)->personName) {
3563 err = IE_NOMEM;
3564 goto leave;
3567 err = extract_gPersonName(context, &(*db_user_info)->personName,
3568 xpath_ctx);
3569 if (err) goto leave;
3571 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3572 if (err) goto leave;
3574 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3575 if (err) goto leave;
3577 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3578 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3580 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3581 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3582 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3584 /* ???: Default value is "CZ" according specification. Should we provide
3585 * it? */
3586 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3588 leave:
3589 if (err) isds_DbUserInfo_free(db_user_info);
3590 free(string);
3591 xmlXPathFreeObject(result);
3592 return err;
3596 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3597 * @context is session context
3598 * @user is libisds structure with user description
3599 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3600 * @db_user_info is XML element of XSD:tDbUserInfo */
3601 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3602 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3603 xmlNodePtr db_user_info) {
3605 isds_error err = IE_SUCCESS;
3606 xmlNodePtr node;
3607 xmlAttrPtr attribute_node;
3608 xmlChar *string = NULL;
3610 if (!context) return IE_INVALID_CONTEXT;
3611 if (!user || !db_user_info) return IE_INVAL;
3613 /* Build XSD:tDbUserInfo */
3615 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3616 * allow it everywhere. */
3617 if (honor_aifo_ticket && user->aifo_ticket) {
3618 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3621 if (user->personName) {
3622 INSERT_STRING(db_user_info, "pnFirstName",
3623 user->personName->pnFirstName);
3624 INSERT_STRING(db_user_info, "pnMiddleName",
3625 user->personName->pnMiddleName);
3626 INSERT_STRING(db_user_info, "pnLastName",
3627 user->personName->pnLastName);
3628 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3629 user->personName->pnLastNameAtBirth);
3631 if (user->address) {
3632 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3633 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3634 INSERT_STRING(db_user_info, "adNumberInStreet",
3635 user->address->adNumberInStreet);
3636 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3637 user->address->adNumberInMunicipality);
3638 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3639 INSERT_STRING(db_user_info, "adState", user->address->adState);
3641 if (user->biDate) {
3642 if (!tm2datestring(user->biDate, &string))
3643 INSERT_STRING(db_user_info, "biDate", string);
3644 zfree(string);
3646 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3647 INSERT_STRING(db_user_info, "userID", user->userID);
3649 /* userType */
3650 if (user->userType) {
3651 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3652 if (!type_string) {
3653 isds_printf_message(context, _("Invalid userType value: %d"),
3654 *(user->userType));
3655 err = IE_ENUM;
3656 goto leave;
3658 INSERT_STRING(db_user_info, "userType", type_string);
3661 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3662 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3663 INSERT_STRING(db_user_info, "ic", user->ic);
3664 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3665 INSERT_STRING(db_user_info, "firmName", user->firmName);
3666 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3667 INSERT_STRING(db_user_info, "caCity", user->caCity);
3668 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3669 INSERT_STRING(db_user_info, "caState", user->caState);
3671 leave:
3672 free(string);
3673 return err;
3677 /* Convert XSD:tPDZRec XML tree into structure
3678 * @context is ISDS context
3679 * @permission is automatically reallocated commercial permission structure
3680 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3681 * In case of error @permission will be freed. */
3682 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3683 struct isds_commercial_permission **permission,
3684 xmlXPathContextPtr xpath_ctx) {
3685 isds_error err = IE_SUCCESS;
3686 xmlXPathObjectPtr result = NULL;
3687 char *string = NULL;
3689 if (!context) return IE_INVALID_CONTEXT;
3690 if (!permission) return IE_INVAL;
3691 isds_commercial_permission_free(permission);
3692 if (!xpath_ctx) return IE_INVAL;
3695 *permission = calloc(1, sizeof(**permission));
3696 if (!*permission) {
3697 err = IE_NOMEM;
3698 goto leave;
3701 EXTRACT_STRING("isds:PDZType", string);
3702 if (string) {
3703 err = string2isds_payment_type((xmlChar *)string,
3704 &(*permission)->type);
3705 if (err) {
3706 if (err == IE_ENUM) {
3707 err = IE_ISDS;
3708 char *string_locale = _isds_utf82locale(string);
3709 isds_printf_message(context,
3710 _("Unknown isds:PDZType value: %s"), string_locale);
3711 free(string_locale);
3713 goto leave;
3715 zfree(string);
3718 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3719 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3721 EXTRACT_STRING("isds:PDZExpire", string);
3722 if (string) {
3723 err = timestring2timeval((xmlChar *) string,
3724 &((*permission)->expiration));
3725 if (err) {
3726 char *string_locale = _isds_utf82locale(string);
3727 if (err == IE_DATE) err = IE_ISDS;
3728 isds_printf_message(context,
3729 _("Could not convert PDZExpire as ISO time: %s"),
3730 string_locale);
3731 free(string_locale);
3732 goto leave;
3734 zfree(string);
3737 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3738 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3740 leave:
3741 if (err) isds_commercial_permission_free(permission);
3742 free(string);
3743 xmlXPathFreeObject(result);
3744 return err;
3748 /* Convert XSD:tCiRecord XML tree into structure
3749 * @context is ISDS context
3750 * @event is automatically reallocated commercial credit event structure
3751 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3752 * In case of error @event will be freed. */
3753 static isds_error extract_CiRecord(struct isds_ctx *context,
3754 struct isds_credit_event **event,
3755 xmlXPathContextPtr xpath_ctx) {
3756 isds_error err = IE_SUCCESS;
3757 xmlXPathObjectPtr result = NULL;
3758 char *string = NULL;
3759 long int *number_ptr;
3761 if (!context) return IE_INVALID_CONTEXT;
3762 if (!event) return IE_INVAL;
3763 isds_credit_event_free(event);
3764 if (!xpath_ctx) return IE_INVAL;
3767 *event = calloc(1, sizeof(**event));
3768 if (!*event) {
3769 err = IE_NOMEM;
3770 goto leave;
3773 EXTRACT_STRING("isds:ciEventTime", string);
3774 if (string) {
3775 err = timestring2timeval((xmlChar *) string,
3776 &(*event)->time);
3777 if (err) {
3778 char *string_locale = _isds_utf82locale(string);
3779 if (err == IE_DATE) err = IE_ISDS;
3780 isds_printf_message(context,
3781 _("Could not convert ciEventTime as ISO time: %s"),
3782 string_locale);
3783 free(string_locale);
3784 goto leave;
3786 zfree(string);
3789 EXTRACT_STRING("isds:ciEventType", string);
3790 if (string) {
3791 err = string2isds_credit_event_type((xmlChar *)string,
3792 &(*event)->type);
3793 if (err) {
3794 if (err == IE_ENUM) {
3795 err = IE_ISDS;
3796 char *string_locale = _isds_utf82locale(string);
3797 isds_printf_message(context,
3798 _("Unknown isds:ciEventType value: %s"), string_locale);
3799 free(string_locale);
3801 goto leave;
3803 zfree(string);
3806 number_ptr = &((*event)->credit_change);
3807 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3808 number_ptr = &(*event)->new_credit;
3809 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3811 switch((*event)->type) {
3812 case ISDS_CREDIT_CHARGED:
3813 EXTRACT_STRING("isds:ciTransID",
3814 (*event)->details.charged.transaction);
3815 break;
3816 case ISDS_CREDIT_DISCHARGED:
3817 EXTRACT_STRING("isds:ciTransID",
3818 (*event)->details.discharged.transaction);
3819 break;
3820 case ISDS_CREDIT_MESSAGE_SENT:
3821 EXTRACT_STRING("isds:ciRecipientID",
3822 (*event)->details.message_sent.recipient);
3823 EXTRACT_STRING("isds:ciPDZID",
3824 (*event)->details.message_sent.message_id);
3825 break;
3826 case ISDS_CREDIT_STORAGE_SET:
3827 number_ptr = &((*event)->details.storage_set.new_capacity);
3828 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3829 EXTRACT_DATE("isds:ciNewFrom",
3830 (*event)->details.storage_set.new_valid_from);
3831 EXTRACT_DATE("isds:ciNewTo",
3832 (*event)->details.storage_set.new_valid_to);
3833 EXTRACT_LONGINT("isds:ciOldCapacity",
3834 (*event)->details.storage_set.old_capacity, 0);
3835 EXTRACT_DATE("isds:ciOldFrom",
3836 (*event)->details.storage_set.old_valid_from);
3837 EXTRACT_DATE("isds:ciOldTo",
3838 (*event)->details.storage_set.old_valid_to);
3839 EXTRACT_STRING("isds:ciDoneBy",
3840 (*event)->details.storage_set.initiator);
3841 break;
3842 case ISDS_CREDIT_EXPIRED:
3843 break;
3846 leave:
3847 if (err) isds_credit_event_free(event);
3848 free(string);
3849 xmlXPathFreeObject(result);
3850 return err;
3854 #endif /* HAVE_LIBCURL */
3857 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3858 * isds_envelope structure. The envelope is automatically allocated but not
3859 * reallocated. The date are just appended into envelope structure.
3860 * @context is ISDS context
3861 * @envelope is automatically allocated message envelope structure
3862 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3863 * In case of error @envelope will be freed. */
3864 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3865 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3866 isds_error err = IE_SUCCESS;
3867 xmlXPathObjectPtr result = NULL;
3869 if (!context) return IE_INVALID_CONTEXT;
3870 if (!envelope) return IE_INVAL;
3871 if (!xpath_ctx) return IE_INVAL;
3874 if (!*envelope) {
3875 /* Allocate envelope */
3876 *envelope = calloc(1, sizeof(**envelope));
3877 if (!*envelope) {
3878 err = IE_NOMEM;
3879 goto leave;
3881 } else {
3882 /* Else free former data */
3883 zfree((*envelope)->dmSenderOrgUnit);
3884 zfree((*envelope)->dmSenderOrgUnitNum);
3885 zfree((*envelope)->dbIDRecipient);
3886 zfree((*envelope)->dmRecipientOrgUnit);
3887 zfree((*envelope)->dmRecipientOrgUnitNum);
3888 zfree((*envelope)->dmToHands);
3889 zfree((*envelope)->dmAnnotation);
3890 zfree((*envelope)->dmRecipientRefNumber);
3891 zfree((*envelope)->dmSenderRefNumber);
3892 zfree((*envelope)->dmRecipientIdent);
3893 zfree((*envelope)->dmSenderIdent);
3894 zfree((*envelope)->dmLegalTitleLaw);
3895 zfree((*envelope)->dmLegalTitleYear);
3896 zfree((*envelope)->dmLegalTitleSect);
3897 zfree((*envelope)->dmLegalTitlePar);
3898 zfree((*envelope)->dmLegalTitlePoint);
3899 zfree((*envelope)->dmPersonalDelivery);
3900 zfree((*envelope)->dmAllowSubstDelivery);
3903 /* Extract envelope elements added by sender or ISDS
3904 * (XSD: gMessageEnvelopeSub type) */
3905 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3906 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3907 (*envelope)->dmSenderOrgUnitNum, 0);
3908 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3909 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3910 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3911 (*envelope)->dmRecipientOrgUnitNum, 0);
3912 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3913 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3914 EXTRACT_STRING("isds:dmRecipientRefNumber",
3915 (*envelope)->dmRecipientRefNumber);
3916 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3917 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3918 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3920 /* Extract envelope elements regarding law reference */
3921 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3922 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3923 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3924 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3925 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3927 /* Extract envelope other elements */
3928 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3929 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3930 (*envelope)->dmAllowSubstDelivery);
3932 leave:
3933 if (err) isds_envelope_free(envelope);
3934 xmlXPathFreeObject(result);
3935 return err;
3940 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3941 * isds_envelope structure. The envelope is automatically allocated but not
3942 * reallocated. The date are just appended into envelope structure.
3943 * @context is ISDS context
3944 * @envelope is automatically allocated message envelope structure
3945 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3946 * In case of error @envelope will be freed. */
3947 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3948 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3949 isds_error err = IE_SUCCESS;
3950 xmlXPathObjectPtr result = NULL;
3952 if (!context) return IE_INVALID_CONTEXT;
3953 if (!envelope) return IE_INVAL;
3954 if (!xpath_ctx) return IE_INVAL;
3957 if (!*envelope) {
3958 /* Allocate envelope */
3959 *envelope = calloc(1, sizeof(**envelope));
3960 if (!*envelope) {
3961 err = IE_NOMEM;
3962 goto leave;
3964 } else {
3965 /* Else free former data */
3966 zfree((*envelope)->dmID);
3967 zfree((*envelope)->dbIDSender);
3968 zfree((*envelope)->dmSender);
3969 zfree((*envelope)->dmSenderAddress);
3970 zfree((*envelope)->dmSenderType);
3971 zfree((*envelope)->dmRecipient);
3972 zfree((*envelope)->dmRecipientAddress);
3973 zfree((*envelope)->dmAmbiguousRecipient);
3976 /* Extract envelope elements added by ISDS
3977 * (XSD: gMessageEnvelope type) */
3978 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3979 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3980 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3981 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3982 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3983 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3984 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3985 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3986 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3987 (*envelope)->dmAmbiguousRecipient);
3989 /* Extract envelope elements added by sender and ISDS
3990 * (XSD: gMessageEnvelope type) */
3991 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3992 if (err) goto leave;
3994 leave:
3995 if (err) isds_envelope_free(envelope);
3996 xmlXPathFreeObject(result);
3997 return err;
4001 /* Convert other envelope elements from XML tree into isds_envelope structure:
4002 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
4003 * The envelope is automatically allocated but not reallocated.
4004 * The data are just appended into envelope structure.
4005 * @context is ISDS context
4006 * @envelope is automatically allocated message envelope structure
4007 * @xpath_ctx is XPath context with current node as parent desired elements
4008 * In case of error @envelope will be freed. */
4009 static isds_error append_status_size_times(struct isds_ctx *context,
4010 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4011 isds_error err = IE_SUCCESS;
4012 xmlXPathObjectPtr result = NULL;
4013 char *string = NULL;
4014 unsigned long int *unumber = NULL;
4016 if (!context) return IE_INVALID_CONTEXT;
4017 if (!envelope) return IE_INVAL;
4018 if (!xpath_ctx) return IE_INVAL;
4021 if (!*envelope) {
4022 /* Allocate new */
4023 *envelope = calloc(1, sizeof(**envelope));
4024 if (!*envelope) {
4025 err = IE_NOMEM;
4026 goto leave;
4028 } else {
4029 /* Free old data */
4030 zfree((*envelope)->dmMessageStatus);
4031 zfree((*envelope)->dmAttachmentSize);
4032 zfree((*envelope)->dmDeliveryTime);
4033 zfree((*envelope)->dmAcceptanceTime);
4037 /* dmMessageStatus element is mandatory */
4038 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
4039 if (!unumber) {
4040 isds_log_message(context,
4041 _("Missing mandatory sisds:dmMessageStatus integer"));
4042 err = IE_ISDS;
4043 goto leave;
4045 err = uint2isds_message_status(context, unumber,
4046 &((*envelope)->dmMessageStatus));
4047 if (err) {
4048 if (err == IE_ENUM) err = IE_ISDS;
4049 goto leave;
4051 free(unumber); unumber = NULL;
4053 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
4056 EXTRACT_STRING("sisds:dmDeliveryTime", string);
4057 if (string) {
4058 err = timestring2timeval((xmlChar *) string,
4059 &((*envelope)->dmDeliveryTime));
4060 if (err) {
4061 char *string_locale = _isds_utf82locale(string);
4062 if (err == IE_DATE) err = IE_ISDS;
4063 isds_printf_message(context,
4064 _("Could not convert dmDeliveryTime as ISO time: %s"),
4065 string_locale);
4066 free(string_locale);
4067 goto leave;
4069 zfree(string);
4072 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
4073 if (string) {
4074 err = timestring2timeval((xmlChar *) string,
4075 &((*envelope)->dmAcceptanceTime));
4076 if (err) {
4077 char *string_locale = _isds_utf82locale(string);
4078 if (err == IE_DATE) err = IE_ISDS;
4079 isds_printf_message(context,
4080 _("Could not convert dmAcceptanceTime as ISO time: %s"),
4081 string_locale);
4082 free(string_locale);
4083 goto leave;
4085 zfree(string);
4088 leave:
4089 if (err) isds_envelope_free(envelope);
4090 free(unumber);
4091 free(string);
4092 xmlXPathFreeObject(result);
4093 return err;
4097 /* Convert message type attribute of current element into isds_envelope
4098 * structure.
4099 * TODO: This function can be incorporated into append_status_size_times() as
4100 * they are called always together.
4101 * The envelope is automatically allocated but not reallocated.
4102 * The data are just appended into envelope structure.
4103 * @context is ISDS context
4104 * @envelope is automatically allocated message envelope structure
4105 * @xpath_ctx is XPath context with current node as parent of attribute
4106 * carrying message type
4107 * In case of error @envelope will be freed. */
4108 static isds_error append_message_type(struct isds_ctx *context,
4109 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4110 isds_error err = IE_SUCCESS;
4112 if (!context) return IE_INVALID_CONTEXT;
4113 if (!envelope) return IE_INVAL;
4114 if (!xpath_ctx) return IE_INVAL;
4117 if (!*envelope) {
4118 /* Allocate new */
4119 *envelope = calloc(1, sizeof(**envelope));
4120 if (!*envelope) {
4121 err = IE_NOMEM;
4122 goto leave;
4124 } else {
4125 /* Free old data */
4126 zfree((*envelope)->dmType);
4130 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
4132 if (!(*envelope)->dmType) {
4133 /* Use default value */
4134 (*envelope)->dmType = strdup("V");
4135 if (!(*envelope)->dmType) {
4136 err = IE_NOMEM;
4137 goto leave;
4139 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
4140 char *type_locale = _isds_utf82locale((*envelope)->dmType);
4141 isds_printf_message(context,
4142 _("Message type in dmType attribute is not 1 character long: "
4143 "%s"),
4144 type_locale);
4145 free(type_locale);
4146 err = IE_ISDS;
4147 goto leave;
4150 leave:
4151 if (err) isds_envelope_free(envelope);
4152 return err;
4156 #if HAVE_LIBCURL
4157 /* Convert dmType isds_envelope member into XML attribute and append it to
4158 * current node.
4159 * @context is ISDS context
4160 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
4161 * @dm_envelope is XML element the resulting attribute will be appended to.
4162 * @return error code, in case of error context' message is filled. */
4163 static isds_error insert_message_type(struct isds_ctx *context,
4164 const char *type, xmlNodePtr dm_envelope) {
4165 isds_error err = IE_SUCCESS;
4166 xmlAttrPtr attribute_node;
4168 if (!context) return IE_INVALID_CONTEXT;
4169 if (!dm_envelope) return IE_INVAL;
4171 /* Insert optional message type */
4172 if (type) {
4173 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
4174 char *type_locale = _isds_utf82locale(type);
4175 isds_printf_message(context,
4176 _("Message type in envelope is not 1 character long: %s"),
4177 type_locale);
4178 free(type_locale);
4179 err = IE_INVAL;
4180 goto leave;
4182 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
4185 leave:
4186 return err;
4188 #endif /* HAVE_LIBCURL */
4191 /* Extract message document into reallocated document structure
4192 * @context is ISDS context
4193 * @document is automatically reallocated message documents structure
4194 * @xpath_ctx is XPath context with current node as isds:dmFile
4195 * In case of error @document will be freed. */
4196 static isds_error extract_document(struct isds_ctx *context,
4197 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
4198 isds_error err = IE_SUCCESS;
4199 xmlXPathObjectPtr result = NULL;
4200 xmlNodePtr file_node;
4201 char *string = NULL;
4203 if (!context) return IE_INVALID_CONTEXT;
4204 if (!document) return IE_INVAL;
4205 isds_document_free(document);
4206 if (!xpath_ctx) return IE_INVAL;
4207 file_node = xpath_ctx->node;
4209 *document = calloc(1, sizeof(**document));
4210 if (!*document) {
4211 err = IE_NOMEM;
4212 goto leave;
4215 /* Extract document meta data */
4216 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
4217 if (context->normalize_mime_type) {
4218 const char *normalized_type =
4219 isds_normalize_mime_type((*document)->dmMimeType);
4220 if (NULL != normalized_type &&
4221 normalized_type != (*document)->dmMimeType) {
4222 char *new_type = strdup(normalized_type);
4223 if (NULL == new_type) {
4224 isds_printf_message(context,
4225 _("Not enough memory to normalize document MIME type"));
4226 err = IE_NOMEM;
4227 goto leave;
4229 free((*document)->dmMimeType);
4230 (*document)->dmMimeType = new_type;
4234 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
4235 err = string2isds_FileMetaType((xmlChar*)string,
4236 &((*document)->dmFileMetaType));
4237 if (err) {
4238 char *meta_type_locale = _isds_utf82locale(string);
4239 isds_printf_message(context,
4240 _("Document has invalid dmFileMetaType attribute value: %s"),
4241 meta_type_locale);
4242 free(meta_type_locale);
4243 err = IE_ISDS;
4244 goto leave;
4246 zfree(string);
4248 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
4249 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
4250 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
4251 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
4254 /* Extract document data.
4255 * Base64 encoded blob or XML subtree must be presented. */
4257 /* Check for dmEncodedContent */
4258 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
4259 xpath_ctx);
4260 if (!result) {
4261 err = IE_XML;
4262 goto leave;
4265 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4266 /* Here we have Base64 blob */
4267 (*document)->is_xml = 0;
4269 if (result->nodesetval->nodeNr > 1) {
4270 isds_printf_message(context,
4271 _("Document has more dmEncodedContent elements"));
4272 err = IE_ISDS;
4273 goto leave;
4276 xmlXPathFreeObject(result); result = NULL;
4277 EXTRACT_STRING("isds:dmEncodedContent", string);
4279 /* Decode non-empty document */
4280 if (string && string[0] != '\0') {
4281 (*document)->data_length =
4282 _isds_b64decode(string, &((*document)->data));
4283 if ((*document)->data_length == (size_t) -1) {
4284 isds_printf_message(context,
4285 _("Error while Base64-decoding document content"));
4286 err = IE_ERROR;
4287 goto leave;
4290 } else {
4291 /* No Base64 blob, try XML document */
4292 xmlXPathFreeObject(result); result = NULL;
4293 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4294 xpath_ctx);
4295 if (!result) {
4296 err = IE_XML;
4297 goto leave;
4300 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4301 /* Here we have XML document */
4302 (*document)->is_xml = 1;
4304 if (result->nodesetval->nodeNr > 1) {
4305 isds_printf_message(context,
4306 _("Document has more dmXMLContent elements"));
4307 err = IE_ISDS;
4308 goto leave;
4311 /* XXX: We cannot serialize the content simply because:
4312 * - XML document may point out of its scope (e.g. to message
4313 * envelope)
4314 * - isds:dmXMLContent can contain more elements, no element,
4315 * a text node only
4316 * - it's not the XML way
4317 * Thus we provide the only right solution: XML DOM. Let's
4318 * application to cope with this hot potato :) */
4319 (*document)->xml_node_list =
4320 result->nodesetval->nodeTab[0]->children;
4321 } else {
4322 /* No base64 blob, nor XML document */
4323 isds_printf_message(context,
4324 _("Document has no dmEncodedContent, nor dmXMLContent "
4325 "element"));
4326 err = IE_ISDS;
4327 goto leave;
4332 leave:
4333 if (err) isds_document_free(document);
4334 free(string);
4335 xmlXPathFreeObject(result);
4336 xpath_ctx->node = file_node;
4337 return err;
4342 /* Extract message documents into reallocated list of documents
4343 * @context is ISDS context
4344 * @documents is automatically reallocated message documents list structure
4345 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4346 * In case of error @documents will be freed. */
4347 static isds_error extract_documents(struct isds_ctx *context,
4348 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4349 isds_error err = IE_SUCCESS;
4350 xmlXPathObjectPtr result = NULL;
4351 xmlNodePtr files_node;
4352 struct isds_list *document, *prev_document = NULL;
4354 if (!context) return IE_INVALID_CONTEXT;
4355 if (!documents) return IE_INVAL;
4356 isds_list_free(documents);
4357 if (!xpath_ctx) return IE_INVAL;
4358 files_node = xpath_ctx->node;
4360 /* Find documents */
4361 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4362 if (!result) {
4363 err = IE_XML;
4364 goto leave;
4367 /* No match */
4368 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4369 isds_printf_message(context,
4370 _("Message does not contain any document"));
4371 err = IE_ISDS;
4372 goto leave;
4376 /* Iterate over documents */
4377 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4379 /* Allocate and append list item */
4380 document = calloc(1, sizeof(*document));
4381 if (!document) {
4382 err = IE_NOMEM;
4383 goto leave;
4385 document->destructor = (void (*)(void **))isds_document_free;
4386 if (i == 0) *documents = document;
4387 else prev_document->next = document;
4388 prev_document = document;
4390 /* Extract document */
4391 xpath_ctx->node = result->nodesetval->nodeTab[i];
4392 err = extract_document(context,
4393 (struct isds_document **) &(document->data), xpath_ctx);
4394 if (err) goto leave;
4398 leave:
4399 if (err) isds_list_free(documents);
4400 xmlXPathFreeObject(result);
4401 xpath_ctx->node = files_node;
4402 return err;
4406 #if HAVE_LIBCURL
4407 /* Convert isds:dmRecord XML tree into structure
4408 * @context is ISDS context
4409 * @envelope is automatically reallocated message envelope structure
4410 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4411 * In case of error @envelope will be freed. */
4412 static isds_error extract_DmRecord(struct isds_ctx *context,
4413 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4414 isds_error err = IE_SUCCESS;
4415 xmlXPathObjectPtr result = NULL;
4417 if (!context) return IE_INVALID_CONTEXT;
4418 if (!envelope) return IE_INVAL;
4419 isds_envelope_free(envelope);
4420 if (!xpath_ctx) return IE_INVAL;
4423 *envelope = calloc(1, sizeof(**envelope));
4424 if (!*envelope) {
4425 err = IE_NOMEM;
4426 goto leave;
4430 /* Extract tRecord data */
4431 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4433 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4434 * dmAcceptanceTime. */
4435 err = append_status_size_times(context, envelope, xpath_ctx);
4436 if (err) goto leave;
4438 /* Extract envelope elements added by sender and ISDS
4439 * (XSD: gMessageEnvelope type) */
4440 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4441 if (err) goto leave;
4443 /* Get message type */
4444 err = append_message_type(context, envelope, xpath_ctx);
4445 if (err) goto leave;
4448 leave:
4449 if (err) isds_envelope_free(envelope);
4450 xmlXPathFreeObject(result);
4451 return err;
4455 /* Convert XSD:tStateChangesRecord type XML tree into structure
4456 * @context is ISDS context
4457 * @changed_status is automatically reallocated message state change structure
4458 * @xpath_ctx is XPath context with current node as element of
4459 * XSD:tStateChangesRecord type
4460 * In case of error @changed_status will be freed. */
4461 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4462 struct isds_message_status_change **changed_status,
4463 xmlXPathContextPtr xpath_ctx) {
4464 isds_error err = IE_SUCCESS;
4465 xmlXPathObjectPtr result = NULL;
4466 unsigned long int *unumber = NULL;
4467 char *string = NULL;
4469 if (!context) return IE_INVALID_CONTEXT;
4470 if (!changed_status) return IE_INVAL;
4471 isds_message_status_change_free(changed_status);
4472 if (!xpath_ctx) return IE_INVAL;
4475 *changed_status = calloc(1, sizeof(**changed_status));
4476 if (!*changed_status) {
4477 err = IE_NOMEM;
4478 goto leave;
4482 /* Extract tGetStateChangesInput data */
4483 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4485 /* dmEventTime is mandatory */
4486 EXTRACT_STRING("isds:dmEventTime", string);
4487 if (string) {
4488 err = timestring2timeval((xmlChar *) string,
4489 &((*changed_status)->time));
4490 if (err) {
4491 char *string_locale = _isds_utf82locale(string);
4492 if (err == IE_DATE) err = IE_ISDS;
4493 isds_printf_message(context,
4494 _("Could not convert dmEventTime as ISO time: %s"),
4495 string_locale);
4496 free(string_locale);
4497 goto leave;
4499 zfree(string);
4502 /* dmMessageStatus element is mandatory */
4503 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4504 if (!unumber) {
4505 isds_log_message(context,
4506 _("Missing mandatory isds:dmMessageStatus integer"));
4507 err = IE_ISDS;
4508 goto leave;
4510 err = uint2isds_message_status(context, unumber,
4511 &((*changed_status)->dmMessageStatus));
4512 if (err) {
4513 if (err == IE_ENUM) err = IE_ISDS;
4514 goto leave;
4516 zfree(unumber);
4519 leave:
4520 free(unumber);
4521 free(string);
4522 if (err) isds_message_status_change_free(changed_status);
4523 xmlXPathFreeObject(result);
4524 return err;
4526 #endif /* HAVE_LIBCURL */
4529 /* Find and convert isds:dmHash XML tree into structure
4530 * @context is ISDS context
4531 * @envelope is automatically reallocated message hash structure
4532 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4533 * In case of error @hash will be freed. */
4534 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4535 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4536 isds_error err = IE_SUCCESS;
4537 xmlNodePtr old_ctx_node;
4538 xmlXPathObjectPtr result = NULL;
4539 char *string = NULL;
4541 if (!context) return IE_INVALID_CONTEXT;
4542 if (!hash) return IE_INVAL;
4543 isds_hash_free(hash);
4544 if (!xpath_ctx) return IE_INVAL;
4546 old_ctx_node = xpath_ctx->node;
4548 *hash = calloc(1, sizeof(**hash));
4549 if (!*hash) {
4550 err = IE_NOMEM;
4551 goto leave;
4554 /* Locate dmHash */
4555 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4556 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4557 err = IE_ISDS;
4558 goto leave;
4560 if (err) {
4561 err = IE_ERROR;
4562 goto leave;
4565 /* Get hash algorithm */
4566 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4567 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4568 if (err) {
4569 if (err == IE_ENUM) {
4570 char *string_locale = _isds_utf82locale(string);
4571 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4572 string_locale);
4573 free(string_locale);
4575 goto leave;
4577 zfree(string);
4579 /* Get hash value */
4580 EXTRACT_STRING(".", string);
4581 if (!string) {
4582 isds_printf_message(context,
4583 _("sisds:dmHash element is missing hash value"));
4584 err = IE_ISDS;
4585 goto leave;
4587 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4588 if ((*hash)->length == (size_t) -1) {
4589 isds_printf_message(context,
4590 _("Error while Base64-decoding hash value"));
4591 err = IE_ERROR;
4592 goto leave;
4595 leave:
4596 if (err) isds_hash_free(hash);
4597 free(string);
4598 xmlXPathFreeObject(result);
4599 xpath_ctx->node = old_ctx_node;
4600 return err;
4604 /* Find and append isds:dmQTimestamp XML tree into envelope.
4605 * Because one service is allowed to miss time-stamp content, and we think
4606 * other could too (flaw in specification), this function is deliberated and
4607 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4608 * @context is ISDS context
4609 * @envelope is automatically allocated envelope structure
4610 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4611 * child
4612 * In case of error @envelope will be freed. */
4613 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4614 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4615 isds_error err = IE_SUCCESS;
4616 xmlXPathObjectPtr result = NULL;
4617 char *string = NULL;
4619 if (!context) return IE_INVALID_CONTEXT;
4620 if (!envelope) return IE_INVAL;
4621 if (!xpath_ctx) {
4622 isds_envelope_free(envelope);
4623 return IE_INVAL;
4626 if (!*envelope) {
4627 *envelope = calloc(1, sizeof(**envelope));
4628 if (!*envelope) {
4629 err = IE_NOMEM;
4630 goto leave;
4632 } else {
4633 zfree((*envelope)->timestamp);
4634 (*envelope)->timestamp_length = 0;
4637 /* Get dmQTimestamp */
4638 EXTRACT_STRING("sisds:dmQTimestamp", string);
4639 if (!string) {
4640 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4641 goto leave;
4643 (*envelope)->timestamp_length =
4644 _isds_b64decode(string, &((*envelope)->timestamp));
4645 if ((*envelope)->timestamp_length == (size_t) -1) {
4646 isds_printf_message(context,
4647 _("Error while Base64-decoding time stamp value"));
4648 err = IE_ERROR;
4649 goto leave;
4652 leave:
4653 if (err) isds_envelope_free(envelope);
4654 free(string);
4655 xmlXPathFreeObject(result);
4656 return err;
4660 /* Convert XSD tReturnedMessage XML tree into message structure.
4661 * It does not store serialized XML tree into message->raw.
4662 * It does store (pointer to) parsed XML tree into message->xml if needed.
4663 * @context is ISDS context
4664 * @include_documents Use true if documents must be extracted
4665 * (tReturnedMessage XSD type), use false if documents shall be omitted
4666 * (tReturnedMessageEnvelope).
4667 * @message is automatically reallocated message structure
4668 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4669 * type
4670 * In case of error @message will be freed. */
4671 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4672 const _Bool include_documents, struct isds_message **message,
4673 xmlXPathContextPtr xpath_ctx) {
4674 isds_error err = IE_SUCCESS;
4675 xmlNodePtr message_node;
4677 if (!context) return IE_INVALID_CONTEXT;
4678 if (!message) return IE_INVAL;
4679 isds_message_free(message);
4680 if (!xpath_ctx) return IE_INVAL;
4683 *message = calloc(1, sizeof(**message));
4684 if (!*message) {
4685 err = IE_NOMEM;
4686 goto leave;
4689 /* Save message XPATH context node */
4690 message_node = xpath_ctx->node;
4693 /* Extract dmDM */
4694 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4695 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4696 if (err) { err = IE_ERROR; goto leave; }
4697 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4698 if (err) goto leave;
4700 if (include_documents) {
4701 struct isds_list *item;
4703 /* Extract dmFiles */
4704 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4705 xpath_ctx);
4706 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4707 err = IE_ISDS; goto leave;
4709 if (err) { err = IE_ERROR; goto leave; }
4710 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4711 if (err) goto leave;
4713 /* Store xmlDoc of this message if needed */
4714 /* Only if we got a XML document in all the documents. */
4715 for (item = (*message)->documents; item; item = item->next) {
4716 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4717 (*message)->xml = xpath_ctx->doc;
4718 break;
4724 /* Restore context to message */
4725 xpath_ctx->node = message_node;
4727 /* Extract dmHash */
4728 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4729 xpath_ctx);
4730 if (err) goto leave;
4732 /* Extract dmQTimestamp, */
4733 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4734 xpath_ctx);
4735 if (err) goto leave;
4737 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4738 * dmAcceptanceTime. */
4739 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4740 if (err) goto leave;
4742 /* Get message type */
4743 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4744 if (err) goto leave;
4746 leave:
4747 if (err) isds_message_free(message);
4748 return err;
4752 /* Extract message event into reallocated isds_event structure
4753 * @context is ISDS context
4754 * @event is automatically reallocated message event structure
4755 * @xpath_ctx is XPath context with current node as isds:dmEvent
4756 * In case of error @event will be freed. */
4757 static isds_error extract_event(struct isds_ctx *context,
4758 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4759 isds_error err = IE_SUCCESS;
4760 xmlXPathObjectPtr result = NULL;
4761 xmlNodePtr event_node;
4762 char *string = NULL;
4764 if (!context) return IE_INVALID_CONTEXT;
4765 if (!event) return IE_INVAL;
4766 isds_event_free(event);
4767 if (!xpath_ctx) return IE_INVAL;
4768 event_node = xpath_ctx->node;
4770 *event = calloc(1, sizeof(**event));
4771 if (!*event) {
4772 err = IE_NOMEM;
4773 goto leave;
4776 /* Extract event data.
4777 * All elements are optional according XSD. That's funny. */
4778 EXTRACT_STRING("sisds:dmEventTime", string);
4779 if (string) {
4780 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4781 if (err) {
4782 char *string_locale = _isds_utf82locale(string);
4783 if (err == IE_DATE) err = IE_ISDS;
4784 isds_printf_message(context,
4785 _("Could not convert dmEventTime as ISO time: %s"),
4786 string_locale);
4787 free(string_locale);
4788 goto leave;
4790 zfree(string);
4793 /* dmEventDescr element has prefix and the rest */
4794 EXTRACT_STRING("sisds:dmEventDescr", string);
4795 if (string) {
4796 err = eventstring2event((xmlChar *) string, *event);
4797 if (err) goto leave;
4798 zfree(string);
4801 leave:
4802 if (err) isds_event_free(event);
4803 free(string);
4804 xmlXPathFreeObject(result);
4805 xpath_ctx->node = event_node;
4806 return err;
4810 /* Convert element of XSD tEventsArray type from XML tree into
4811 * isds_list of isds_event's structure. The list is automatically reallocated.
4812 * @context is ISDS context
4813 * @events is automatically reallocated list of event structures
4814 * @xpath_ctx is XPath context with current node as tEventsArray
4815 * In case of error @events will be freed. */
4816 static isds_error extract_events(struct isds_ctx *context,
4817 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4818 isds_error err = IE_SUCCESS;
4819 xmlXPathObjectPtr result = NULL;
4820 xmlNodePtr events_node;
4821 struct isds_list *event, *prev_event = NULL;
4823 if (!context) return IE_INVALID_CONTEXT;
4824 if (!events) return IE_INVAL;
4825 if (!xpath_ctx) return IE_INVAL;
4826 events_node = xpath_ctx->node;
4828 /* Free old list */
4829 isds_list_free(events);
4831 /* Find events */
4832 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4833 if (!result) {
4834 err = IE_XML;
4835 goto leave;
4838 /* No match */
4839 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4840 isds_printf_message(context,
4841 _("Delivery info does not contain any event"));
4842 err = IE_ISDS;
4843 goto leave;
4847 /* Iterate over events */
4848 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4850 /* Allocate and append list item */
4851 event = calloc(1, sizeof(*event));
4852 if (!event) {
4853 err = IE_NOMEM;
4854 goto leave;
4856 event->destructor = (void (*)(void **))isds_event_free;
4857 if (i == 0) *events = event;
4858 else prev_event->next = event;
4859 prev_event = event;
4861 /* Extract event */
4862 xpath_ctx->node = result->nodesetval->nodeTab[i];
4863 err = extract_event(context,
4864 (struct isds_event **) &(event->data), xpath_ctx);
4865 if (err) goto leave;
4869 leave:
4870 if (err) isds_list_free(events);
4871 xmlXPathFreeObject(result);
4872 xpath_ctx->node = events_node;
4873 return err;
4877 #if HAVE_LIBCURL
4878 /* Insert Base64 encoded data as element with text child.
4879 * @context is session context
4880 * @parent is XML node to append @element with @data as child
4881 * @ns is XML namespace of @element, use NULL to inherit from @parent
4882 * @element is UTF-8 encoded name of new element
4883 * @data is bit stream to encode into @element
4884 * @length is size of @data in bytes
4885 * @return standard error code and fill long error message if needed */
4886 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4887 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4888 const void *data, size_t length) {
4889 isds_error err = IE_SUCCESS;
4890 xmlNodePtr node;
4892 if (!context) return IE_INVALID_CONTEXT;
4893 if (!data && length > 0) return IE_INVAL;
4894 if (!parent || !element) return IE_INVAL;
4896 xmlChar *base64data = NULL;
4897 base64data = (xmlChar *) _isds_b64encode(data, length);
4898 if (!base64data) {
4899 isds_printf_message(context,
4900 ngettext("Not enough memory to encode %zd byte into Base64",
4901 "Not enough memory to encode %zd bytes into Base64",
4902 length),
4903 length);
4904 err = IE_NOMEM;
4905 goto leave;
4907 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4909 leave:
4910 free(base64data);
4911 return err;
4915 /* Convert isds_document structure into XML tree and append to dmFiles node.
4916 * @context is session context
4917 * @document is ISDS document
4918 * @dm_files is XML element the resulting tree will be appended to as a child.
4919 * @return error code, in case of error context' message is filled. */
4920 static isds_error insert_document(struct isds_ctx *context,
4921 struct isds_document *document, xmlNodePtr dm_files) {
4922 isds_error err = IE_SUCCESS;
4923 xmlNodePtr new_file = NULL, file = NULL, node;
4924 xmlAttrPtr attribute_node;
4926 if (!context) return IE_INVALID_CONTEXT;
4927 if (!document || !dm_files) return IE_INVAL;
4929 /* Allocate new dmFile */
4930 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4931 if (!new_file) {
4932 isds_printf_message(context, _("Could not allocate main dmFile"));
4933 err = IE_ERROR;
4934 goto leave;
4936 /* Append the new dmFile.
4937 * XXX: Main document must go first */
4938 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4939 file = xmlAddPrevSibling(dm_files->children, new_file);
4940 else
4941 file = xmlAddChild(dm_files, new_file);
4943 if (!file) {
4944 xmlFreeNode(new_file); new_file = NULL;
4945 isds_printf_message(context, _("Could not add dmFile child to "
4946 "%s element"), dm_files->name);
4947 err = IE_ERROR;
4948 goto leave;
4951 /* @dmMimeType is required */
4952 if (!document->dmMimeType) {
4953 isds_log_message(context,
4954 _("Document is missing mandatory MIME type definition"));
4955 err = IE_INVAL;
4956 goto leave;
4958 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4960 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4961 if (!string) {
4962 isds_printf_message(context,
4963 _("Document has unknown dmFileMetaType: %ld"),
4964 document->dmFileMetaType);
4965 err = IE_ENUM;
4966 goto leave;
4968 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4970 if (document->dmFileGuid) {
4971 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4973 if (document->dmUpFileGuid) {
4974 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4977 /* @dmFileDescr is required */
4978 if (!document->dmFileDescr) {
4979 isds_log_message(context,
4980 _("Document is missing mandatory description (title)"));
4981 err = IE_INVAL;
4982 goto leave;
4984 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4986 if (document->dmFormat) {
4987 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4991 /* Insert content (body) of the document. */
4992 if (document->is_xml) {
4993 /* XML document requested */
4995 /* Allocate new dmXMLContent */
4996 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4997 if (!xmlcontent) {
4998 isds_printf_message(context,
4999 _("Could not allocate dmXMLContent element"));
5000 err = IE_ERROR;
5001 goto leave;
5003 /* Append it */
5004 node = xmlAddChild(file, xmlcontent);
5005 if (!node) {
5006 xmlFreeNode(xmlcontent); xmlcontent = NULL;
5007 isds_printf_message(context,
5008 _("Could not add dmXMLContent child to %s element"),
5009 file->name);
5010 err = IE_ERROR;
5011 goto leave;
5014 /* Copy non-empty node list */
5015 if (document->xml_node_list) {
5016 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
5017 document->xml_node_list);
5018 if (!content) {
5019 isds_printf_message(context,
5020 _("Not enough memory to copy XML document"));
5021 err = IE_NOMEM;
5022 goto leave;
5025 if (!xmlAddChildList(node, content)) {
5026 xmlFreeNodeList(content);
5027 isds_printf_message(context,
5028 _("Error while adding XML document into dmXMLContent"));
5029 err = IE_XML;
5030 goto leave;
5032 /* XXX: We cannot free the content here because it's part of node's
5033 * document since now. It will be freed with it automatically. */
5035 } else {
5036 /* Binary document requested */
5037 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
5038 document->data, document->data_length);
5039 if (err) goto leave;
5042 leave:
5043 return err;
5047 /* Append XSD tMStatus XML tree into isds_message_copy structure.
5048 * The copy must be preallocated, the date are just appended into structure.
5049 * @context is ISDS context
5050 * @copy is message copy structure
5051 * @xpath_ctx is XPath context with current node as tMStatus */
5052 static isds_error append_TMStatus(struct isds_ctx *context,
5053 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
5054 isds_error err = IE_SUCCESS;
5055 xmlXPathObjectPtr result = NULL;
5056 char *code = NULL, *message = NULL;
5058 if (!context) return IE_INVALID_CONTEXT;
5059 if (!copy || !xpath_ctx) return IE_INVAL;
5061 /* Free old values */
5062 zfree(copy->dmStatus);
5063 zfree(copy->dmID);
5065 /* Get error specific to this copy */
5066 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
5067 if (!code) {
5068 isds_log_message(context,
5069 _("Missing isds:dmStatusCode under "
5070 "XSD:tMStatus type element"));
5071 err = IE_ISDS;
5072 goto leave;
5075 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
5076 /* This copy failed */
5077 copy->error = IE_ISDS;
5078 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
5079 if (message) {
5080 copy->dmStatus = _isds_astrcat3(code, ": ", message);
5081 if (!copy->dmStatus) {
5082 copy->dmStatus = code;
5083 code = NULL;
5085 } else {
5086 copy->dmStatus = code;
5087 code = NULL;
5089 } else {
5090 /* This copy succeeded. In this case only, message ID is valid */
5091 copy->error = IE_SUCCESS;
5093 EXTRACT_STRING("isds:dmID", copy->dmID);
5094 if (!copy->dmID) {
5095 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
5096 "but did not returned assigned message ID\n"));
5097 err = IE_ISDS;
5101 leave:
5102 free(code);
5103 free(message);
5104 xmlXPathFreeObject(result);
5105 return err;
5109 /* Insert struct isds_approval data (box approval) into XML tree
5110 * @context is session context
5111 * @approval is libisds structure with approval description. NULL is
5112 * acceptable.
5113 * @parent is XML element to append @approval to */
5114 static isds_error insert_GExtApproval(struct isds_ctx *context,
5115 const struct isds_approval *approval, xmlNodePtr parent) {
5117 isds_error err = IE_SUCCESS;
5118 xmlNodePtr node;
5120 if (!context) return IE_INVALID_CONTEXT;
5121 if (!parent) return IE_INVAL;
5123 if (!approval) return IE_SUCCESS;
5125 /* Build XSD:gExtApproval */
5126 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
5127 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
5129 leave:
5130 return err;
5134 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
5135 * code
5136 * @context is session context
5137 * @service_name is name of SERVICE_DB_ACCESS
5138 * @response is reallocated server SOAP body response as XML document
5139 * @raw_response is reallocated bit stream with response body. Use
5140 * NULL if you don't care
5141 * @raw_response_length is size of @raw_response in bytes
5142 * @code is reallocated ISDS status code
5143 * @status_message is reallocated ISDS status message
5144 * @return error coded from lower layer, context message will be set up
5145 * appropriately. */
5146 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
5147 const xmlChar *service_name,
5148 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
5149 xmlChar **code, xmlChar **status_message) {
5151 isds_error err = IE_SUCCESS;
5152 char *service_name_locale = NULL;
5153 xmlNodePtr request = NULL, node;
5154 xmlNsPtr isds_ns = NULL;
5156 if (!context) return IE_INVALID_CONTEXT;
5157 if (!service_name) return IE_INVAL;
5158 if (!response || !code || !status_message) return IE_INVAL;
5159 if (!raw_response_length && raw_response) return IE_INVAL;
5161 /* Free output argument */
5162 xmlFreeDoc(*response); *response = NULL;
5163 if (raw_response) zfree(*raw_response);
5164 zfree(*code);
5165 zfree(*status_message);
5168 /* Check if connection is established
5169 * TODO: This check should be done downstairs. */
5170 if (!context->curl) return IE_CONNECTION_CLOSED;
5172 service_name_locale = _isds_utf82locale((char*)service_name);
5173 if (!service_name_locale)
5174 return IE_NOMEM;
5176 /* Build request */
5177 request = xmlNewNode(NULL, service_name);
5178 if (!request) {
5179 isds_printf_message(context,
5180 _("Could not build %s request"), service_name_locale);
5181 err = IE_ERROR;
5182 goto leave;
5184 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5185 if(!isds_ns) {
5186 isds_log_message(context, _("Could not create ISDS name space"));
5187 err = IE_ERROR;
5188 goto leave;
5190 xmlSetNs(request, isds_ns);
5193 /* Add XSD:tDummyInput child */
5194 INSERT_STRING(request, "dbDummy", NULL);
5197 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5198 service_name_locale);
5200 /* Send request */
5201 err = _isds(context, SERVICE_DB_ACCESS, request, response,
5202 raw_response, raw_response_length);
5203 xmlFreeNode(request); request = NULL;
5205 if (err) {
5206 isds_log(ILF_ISDS, ILL_DEBUG,
5207 _("Processing ISDS response on %s request failed\n"),
5208 service_name_locale);
5209 goto leave;
5212 /* Check for response status */
5213 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
5214 code, status_message, NULL);
5215 if (err) {
5216 isds_log(ILF_ISDS, ILL_DEBUG,
5217 _("ISDS response on %s request is missing status\n"),
5218 service_name_locale);
5219 goto leave;
5222 /* Request processed, but nothing found */
5223 if (xmlStrcmp(*code, BAD_CAST "0000")) {
5224 char *code_locale = _isds_utf82locale((char*) *code);
5225 char *status_message_locale =
5226 _isds_utf82locale((char*) *status_message);
5227 isds_log(ILF_ISDS, ILL_DEBUG,
5228 _("Server refused %s request (code=%s, message=%s)\n"),
5229 service_name_locale, code_locale, status_message_locale);
5230 isds_log_message(context, status_message_locale);
5231 free(code_locale);
5232 free(status_message_locale);
5233 err = IE_ISDS;
5234 goto leave;
5237 leave:
5238 free(service_name_locale);
5239 xmlFreeNode(request);
5240 return err;
5242 #endif
5245 /* Get data about logged in user and his box.
5246 * @context is session context
5247 * @db_owner_info is reallocated box owner description. It will be freed on
5248 * error.
5249 * @return error code from lower layer, context message will be set up
5250 * appropriately. */
5251 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
5252 struct isds_DbOwnerInfo **db_owner_info) {
5253 isds_error err = IE_SUCCESS;
5254 #if HAVE_LIBCURL
5255 xmlDocPtr response = NULL;
5256 xmlChar *code = NULL, *message = NULL;
5257 xmlXPathContextPtr xpath_ctx = NULL;
5258 xmlXPathObjectPtr result = NULL;
5259 char *string = NULL;
5260 #endif
5262 if (!context) return IE_INVALID_CONTEXT;
5263 zfree(context->long_message);
5264 if (!db_owner_info) return IE_INVAL;
5265 isds_DbOwnerInfo_free(db_owner_info);
5267 #if HAVE_LIBCURL
5268 /* Check if connection is established */
5269 if (!context->curl) return IE_CONNECTION_CLOSED;
5272 /* Do request and check for success */
5273 err = build_send_check_dbdummy_request(context,
5274 BAD_CAST "GetOwnerInfoFromLogin",
5275 &response, NULL, NULL, &code, &message);
5276 if (err) goto leave;
5279 /* Extract data */
5280 /* Prepare structure */
5281 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5282 if (!*db_owner_info) {
5283 err = IE_NOMEM;
5284 goto leave;
5286 xpath_ctx = xmlXPathNewContext(response);
5287 if (!xpath_ctx) {
5288 err = IE_ERROR;
5289 goto leave;
5291 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5292 err = IE_ERROR;
5293 goto leave;
5296 /* Set context node */
5297 result = xmlXPathEvalExpression(BAD_CAST
5298 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5299 if (!result) {
5300 err = IE_ERROR;
5301 goto leave;
5303 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5304 isds_log_message(context, _("Missing dbOwnerInfo element"));
5305 err = IE_ISDS;
5306 goto leave;
5308 if (result->nodesetval->nodeNr > 1) {
5309 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5310 err = IE_ISDS;
5311 goto leave;
5313 xpath_ctx->node = result->nodesetval->nodeTab[0];
5314 xmlXPathFreeObject(result); result = NULL;
5316 /* Extract it */
5317 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5320 leave:
5321 if (err) {
5322 isds_DbOwnerInfo_free(db_owner_info);
5325 free(string);
5326 xmlXPathFreeObject(result);
5327 xmlXPathFreeContext(xpath_ctx);
5329 free(code);
5330 free(message);
5331 xmlFreeDoc(response);
5333 if (!err)
5334 isds_log(ILF_ISDS, ILL_DEBUG,
5335 _("GetOwnerInfoFromLogin request processed by server "
5336 "successfully.\n"));
5337 #else /* not HAVE_LIBCURL */
5338 err = IE_NOTSUP;
5339 #endif
5341 return err;
5345 /* Get data about logged in user. */
5346 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5347 struct isds_DbUserInfo **db_user_info) {
5348 isds_error err = IE_SUCCESS;
5349 #if HAVE_LIBCURL
5350 xmlDocPtr response = NULL;
5351 xmlChar *code = NULL, *message = NULL;
5352 xmlXPathContextPtr xpath_ctx = NULL;
5353 xmlXPathObjectPtr result = NULL;
5354 #endif
5356 if (!context) return IE_INVALID_CONTEXT;
5357 zfree(context->long_message);
5358 if (!db_user_info) return IE_INVAL;
5359 isds_DbUserInfo_free(db_user_info);
5361 #if HAVE_LIBCURL
5362 /* Check if connection is established */
5363 if (!context->curl) return IE_CONNECTION_CLOSED;
5366 /* Do request and check for success */
5367 err = build_send_check_dbdummy_request(context,
5368 BAD_CAST "GetUserInfoFromLogin",
5369 &response, NULL, NULL, &code, &message);
5370 if (err) goto leave;
5373 /* Extract data */
5374 /* Prepare structure */
5375 *db_user_info = calloc(1, sizeof(**db_user_info));
5376 if (!*db_user_info) {
5377 err = IE_NOMEM;
5378 goto leave;
5380 xpath_ctx = xmlXPathNewContext(response);
5381 if (!xpath_ctx) {
5382 err = IE_ERROR;
5383 goto leave;
5385 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5386 err = IE_ERROR;
5387 goto leave;
5390 /* Set context node */
5391 result = xmlXPathEvalExpression(BAD_CAST
5392 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5393 if (!result) {
5394 err = IE_ERROR;
5395 goto leave;
5397 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5398 isds_log_message(context, _("Missing dbUserInfo element"));
5399 err = IE_ISDS;
5400 goto leave;
5402 if (result->nodesetval->nodeNr > 1) {
5403 isds_log_message(context, _("Multiple dbUserInfo element"));
5404 err = IE_ISDS;
5405 goto leave;
5407 xpath_ctx->node = result->nodesetval->nodeTab[0];
5408 xmlXPathFreeObject(result); result = NULL;
5410 /* Extract it */
5411 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5413 leave:
5414 if (err) {
5415 isds_DbUserInfo_free(db_user_info);
5418 xmlXPathFreeObject(result);
5419 xmlXPathFreeContext(xpath_ctx);
5421 free(code);
5422 free(message);
5423 xmlFreeDoc(response);
5425 if (!err)
5426 isds_log(ILF_ISDS, ILL_DEBUG,
5427 _("GetUserInfoFromLogin request processed by server "
5428 "successfully.\n"));
5429 #else /* not HAVE_LIBCURL */
5430 err = IE_NOTSUP;
5431 #endif
5433 return err;
5437 /* Get expiration time of current password
5438 * @context is session context
5439 * @expiration is automatically reallocated time when password expires. If
5440 * password expiration is disabled, NULL will be returned. In case of error
5441 * it will be nulled too. */
5442 isds_error isds_get_password_expiration(struct isds_ctx *context,
5443 struct timeval **expiration) {
5444 isds_error err = IE_SUCCESS;
5445 #if HAVE_LIBCURL
5446 xmlDocPtr response = NULL;
5447 xmlChar *code = NULL, *message = NULL;
5448 xmlXPathContextPtr xpath_ctx = NULL;
5449 xmlXPathObjectPtr result = NULL;
5450 char *string = NULL;
5451 #endif
5453 if (!context) return IE_INVALID_CONTEXT;
5454 zfree(context->long_message);
5455 if (!expiration) return IE_INVAL;
5456 zfree(*expiration);
5458 #if HAVE_LIBCURL
5459 /* Check if connection is established */
5460 if (!context->curl) return IE_CONNECTION_CLOSED;
5463 /* Do request and check for success */
5464 err = build_send_check_dbdummy_request(context,
5465 BAD_CAST "GetPasswordInfo",
5466 &response, NULL, NULL, &code, &message);
5467 if (err) goto leave;
5470 /* Extract data */
5471 xpath_ctx = xmlXPathNewContext(response);
5472 if (!xpath_ctx) {
5473 err = IE_ERROR;
5474 goto leave;
5476 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5477 err = IE_ERROR;
5478 goto leave;
5481 /* Set context node */
5482 result = xmlXPathEvalExpression(BAD_CAST
5483 "/isds:GetPasswordInfoResponse", xpath_ctx);
5484 if (!result) {
5485 err = IE_ERROR;
5486 goto leave;
5488 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5489 isds_log_message(context,
5490 _("Missing GetPasswordInfoResponse element"));
5491 err = IE_ISDS;
5492 goto leave;
5494 if (result->nodesetval->nodeNr > 1) {
5495 isds_log_message(context,
5496 _("Multiple GetPasswordInfoResponse element"));
5497 err = IE_ISDS;
5498 goto leave;
5500 xpath_ctx->node = result->nodesetval->nodeTab[0];
5501 xmlXPathFreeObject(result); result = NULL;
5503 /* Extract expiration date */
5504 EXTRACT_STRING("isds:pswExpDate", string);
5505 if (string) {
5506 /* And convert it if any returned. Otherwise expiration is disabled. */
5507 err = timestring2timeval((xmlChar *) string, expiration);
5508 if (err) {
5509 char *string_locale = _isds_utf82locale(string);
5510 if (err == IE_DATE) err = IE_ISDS;
5511 isds_printf_message(context,
5512 _("Could not convert pswExpDate as ISO time: %s"),
5513 string_locale);
5514 free(string_locale);
5515 goto leave;
5519 leave:
5520 if (err) {
5521 if (*expiration) {
5522 zfree(*expiration);
5526 free(string);
5527 xmlXPathFreeObject(result);
5528 xmlXPathFreeContext(xpath_ctx);
5530 free(code);
5531 free(message);
5532 xmlFreeDoc(response);
5534 if (!err)
5535 isds_log(ILF_ISDS, ILL_DEBUG,
5536 _("GetPasswordInfo request processed by server "
5537 "successfully.\n"));
5538 #else /* not HAVE_LIBCURL */
5539 err = IE_NOTSUP;
5540 #endif
5542 return err;
5546 #if HAVE_LIBCURL
5547 /* Request delivering new TOTP code from ISDS through side channel before
5548 * changing password.
5549 * @context is session context
5550 * @password is current password.
5551 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5552 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5553 * function for more details.
5554 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5555 * NULL, if you don't care.
5556 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5557 * error code. */
5558 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5559 const char *password, struct isds_otp *otp, char **refnumber) {
5560 isds_error err = IE_SUCCESS;
5561 char *saved_url = NULL; /* No copy */
5562 #if HAVE_CURL_REAUTHORIZATION_BUG
5563 CURL *saved_curl = NULL; /* No copy */
5564 #endif
5565 xmlNsPtr isds_ns = NULL;
5566 xmlNodePtr request = NULL;
5567 xmlDocPtr response = NULL;
5568 xmlChar *code = NULL, *message = NULL;
5569 const xmlChar *codes[] = {
5570 BAD_CAST "2300",
5571 BAD_CAST "2301",
5572 BAD_CAST "2302"
5574 const char *meanings[] = {
5575 N_("Unexpected error"),
5576 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5577 N_("One-time code could not been sent. Try later again.")
5579 const isds_otp_resolution resolutions[] = {
5580 OTP_RESOLUTION_UNKNOWN,
5581 OTP_RESOLUTION_TO_FAST,
5582 OTP_RESOLUTION_TOTP_NOT_SENT
5585 if (NULL == context) return IE_INVALID_CONTEXT;
5586 zfree(context->long_message);
5587 if (NULL == password) {
5588 isds_log_message(context,
5589 _("Second argument (password) of isds_change_password() "
5590 "is NULL"));
5591 return IE_INVAL;
5594 /* Check if connection is established
5595 * TODO: This check should be done downstairs. */
5596 if (!context->curl) return IE_CONNECTION_CLOSED;
5598 if (!context->otp) {
5599 isds_log_message(context, _("This function requires OTP-authenticated "
5600 "context"));
5601 return IE_INVALID_CONTEXT;
5603 if (NULL == otp) {
5604 isds_log_message(context, _("If one-time password authentication "
5605 "method is in use, requesting new OTP code requires "
5606 "one-time credentials argument either"));
5607 return IE_INVAL;
5609 if (otp->method != OTP_TIME) {
5610 isds_log_message(context, _("Requesting new time-based OTP code from "
5611 "server requires one-time password authentication "
5612 "method"));
5613 return IE_INVAL;
5615 if (otp->otp_code != NULL) {
5616 isds_log_message(context, _("Requesting new time-based OTP code from "
5617 "server requires undefined OTP code member in "
5618 "one-time credentials argument"));
5619 return IE_INVAL;
5623 /* Build request */
5624 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5625 if (!request) {
5626 isds_log_message(context, _("Could not build SendSMSCode request"));
5627 return IE_ERROR;
5629 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5630 if(!isds_ns) {
5631 isds_log_message(context, _("Could not create ISDS name space"));
5632 xmlFreeNode(request);
5633 return IE_ERROR;
5635 xmlSetNs(request, isds_ns);
5637 /* Change URL temporarily for sending this request only */
5639 char *new_url = NULL;
5640 if ((err = _isds_build_url_from_context(context,
5641 "%.*sasws/changePassword", &new_url))) {
5642 goto leave;
5644 saved_url = context->url;
5645 context->url = new_url;
5648 /* Store credentials for sending this request only */
5649 context->otp_credentials = otp;
5650 _isds_discard_credentials(context, 0);
5651 if ((err = _isds_store_credentials(context, context->saved_username,
5652 password, NULL))) {
5653 _isds_discard_credentials(context, 0);
5654 goto leave;
5656 #if HAVE_CURL_REAUTHORIZATION_BUG
5657 saved_curl = context->curl;
5658 context->curl = curl_easy_init();
5659 if (NULL == context->curl) {
5660 err = IE_ERROR;
5661 goto leave;
5663 if (context->timeout) {
5664 err = isds_set_timeout(context, context->timeout);
5665 if (err) goto leave;
5667 #endif
5669 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5671 /* Sent request */
5672 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5674 /* Remove temporal credentials */
5675 _isds_discard_credentials(context, 0);
5676 /* Detach pointer to OTP credentials from context */
5677 context->otp_credentials = NULL;
5678 /* Keep context->otp true to keep signaling this is OTP session */
5680 /* Destroy request */
5681 xmlFreeNode(request); request = NULL;
5683 if (err) {
5684 isds_log(ILF_ISDS, ILL_DEBUG,
5685 _("Processing ISDS response on SendSMSCode request failed\n"));
5686 goto leave;
5689 /* Check for response status */
5690 err = isds_response_status(context, SERVICE_ASWS, response,
5691 &code, &message, (xmlChar **)refnumber);
5692 if (err) {
5693 isds_log(ILF_ISDS, ILL_DEBUG,
5694 _("ISDS response on SendSMSCode request is missing "
5695 "status\n"));
5696 goto leave;
5699 /* Check for error */
5700 if (xmlStrcmp(code, BAD_CAST "0000")) {
5701 char *code_locale = _isds_utf82locale((char*)code);
5702 char *message_locale = _isds_utf82locale((char*)message);
5703 size_t i;
5704 isds_log(ILF_ISDS, ILL_DEBUG,
5705 _("Server refused to send new code on SendSMSCode "
5706 "request (code=%s, message=%s)\n"),
5707 code_locale, message_locale);
5709 /* Check for known error codes */
5710 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5711 if (!xmlStrcmp(code, codes[i])) break;
5713 if (i < sizeof(codes)/sizeof(*codes)) {
5714 isds_log_message(context, _(meanings[i]));
5715 /* Mimic otp->resolution according to the code, specification does
5716 * prescribe OTP header to be available. */
5717 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5718 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5719 otp->resolution = resolutions[i];
5720 } else
5721 isds_log_message(context, message_locale);
5723 free(code_locale);
5724 free(message_locale);
5726 err = IE_ISDS;
5727 goto leave;
5730 /* Otherwise new code sent successfully */
5731 /* Mimic otp->resolution according to the code, specification does
5732 * prescribe OTP header to be available. */
5733 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5734 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5736 leave:
5737 if (NULL != saved_url) {
5738 /* Revert URL to original one */
5739 zfree(context->url);
5740 context->url = saved_url;
5742 #if HAVE_CURL_REAUTHORIZATION_BUG
5743 if (NULL != saved_curl) {
5744 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5745 context->curl = saved_curl;
5747 #endif
5749 free(code);
5750 free(message);
5751 xmlFreeDoc(response);
5752 xmlFreeNode(request);
5754 if (!err)
5755 isds_log(ILF_ISDS, ILL_DEBUG,
5756 _("New OTP code has been sent successfully on SendSMSCode "
5757 "request.\n"));
5758 return err;
5762 /* Convert response status code to isds_error code and set long message
5763 * @context is context to save long message to
5764 * @map is mapping from codes to errors and messages. Pass NULL for generic
5765 * handling.
5766 * @code is status code to translate
5767 * @message is non-localized status message to put into long message in case
5768 * of uknown error. It can be NULL if server did not provide any.
5769 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5770 * invalid invocation. */
5771 static isds_error statuscode2isds_error(struct isds_ctx *context,
5772 const struct code_map_isds_error *map,
5773 const xmlChar *code, const xmlChar *message) {
5774 if (NULL == code) {
5775 isds_log_message(context,
5776 _("NULL status code passed to statuscode2isds_error()"));
5777 return IE_INVAL;
5780 if (NULL != map) {
5781 /* Check for known error codes */
5782 for (int i=0; map->codes[i] != NULL; i++) {
5783 if (!xmlStrcmp(code, map->codes[i])) {
5784 isds_log_message(context, _(map->meanings[i]));
5785 return map->errors[i];
5790 /* Other error */
5791 if (xmlStrcmp(code, BAD_CAST "0000")) {
5792 char *message_locale = _isds_utf82locale((char*)message);
5793 if (NULL == message_locale)
5794 isds_log_message(context, _("ISDS server returned unknown error"));
5795 else
5796 isds_log_message(context, message_locale);
5797 free(message_locale);
5798 return IE_ISDS;
5801 return IE_SUCCESS;
5803 #endif
5806 /* Change user password in ISDS.
5807 * User must supply old password, new password will takes effect after some
5808 * time, current session can continue. Password must fulfill some constraints.
5809 * @context is session context
5810 * @old_password is current password.
5811 * @new_password is requested new password
5812 * @otp auxiliary data required if one-time password authentication is in use,
5813 * defines OTP code (if known) and returns fine grade resolution of OTP
5814 * procedure. Pass NULL, if one-time password authentication is not needed.
5815 * Please note the @otp argument must match OTP method used at log-in time. See
5816 * isds_login() function for more details.
5817 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5818 * NULL, if you don't care.
5819 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5820 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5821 * awaiting OTP code that has been delivered by side channel to the user. */
5822 isds_error isds_change_password(struct isds_ctx *context,
5823 const char *old_password, const char *new_password,
5824 struct isds_otp *otp, char **refnumber) {
5825 isds_error err = IE_SUCCESS;
5826 #if HAVE_LIBCURL
5827 char *saved_url = NULL; /* No copy */
5828 #if HAVE_CURL_REAUTHORIZATION_BUG
5829 CURL *saved_curl = NULL; /* No copy */
5830 #endif
5831 xmlNsPtr isds_ns = NULL;
5832 xmlNodePtr request = NULL, node;
5833 xmlDocPtr response = NULL;
5834 xmlChar *code = NULL, *message = NULL;
5835 const xmlChar *codes[] = {
5836 BAD_CAST "1066",
5837 BAD_CAST "1067",
5838 BAD_CAST "1079",
5839 BAD_CAST "1080",
5840 BAD_CAST "1081",
5841 BAD_CAST "1082",
5842 BAD_CAST "1083",
5843 BAD_CAST "1090",
5844 BAD_CAST "1091",
5845 BAD_CAST "2300",
5846 BAD_CAST "9204"
5848 const char *meanings[] = {
5849 N_("Password length must be between 8 and 32 characters"),
5850 N_("Password cannot be reused"), /* Server does not distinguish 1067
5851 and 1091 on ChangePasswordOTP */
5852 N_("Password contains forbidden character"),
5853 N_("Password must contain at least one upper-case letter, "
5854 "one lower-case, and one digit"),
5855 N_("Password cannot contain sequence of three identical characters"),
5856 N_("Password cannot contain user identifier"),
5857 N_("Password is too simmple"),
5858 N_("Old password is not valid"),
5859 N_("Password cannot be reused"),
5860 N_("Unexpected error"),
5861 N_("LDAP update error")
5863 #endif
5865 if (!context) return IE_INVALID_CONTEXT;
5866 zfree(context->long_message);
5867 if (NULL != refnumber)
5868 zfree(*refnumber);
5869 if (NULL == old_password) {
5870 isds_log_message(context,
5871 _("Second argument (old password) of isds_change_password() "
5872 "is NULL"));
5873 return IE_INVAL;
5875 if (NULL == otp && NULL == new_password) {
5876 isds_log_message(context,
5877 _("Third argument (new password) of isds_change_password() "
5878 "is NULL"));
5879 return IE_INVAL;
5882 #if HAVE_LIBCURL
5883 /* Check if connection is established
5884 * TODO: This check should be done downstairs. */
5885 if (!context->curl) return IE_CONNECTION_CLOSED;
5887 if (context->otp && NULL == otp) {
5888 isds_log_message(context, _("If one-time password authentication "
5889 "method is in use, changing password requires one-time "
5890 "credentials either"));
5891 return IE_INVAL;
5894 /* Build ChangeISDSPassword request */
5895 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5896 BAD_CAST "ChangePasswordOTP");
5897 if (!request) {
5898 isds_log_message(context, (NULL == otp) ?
5899 _("Could not build ChangeISDSPassword request") :
5900 _("Could not build ChangePasswordOTP request"));
5901 return IE_ERROR;
5903 isds_ns = xmlNewNs(request,
5904 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5905 NULL);
5906 if(!isds_ns) {
5907 isds_log_message(context, _("Could not create ISDS name space"));
5908 xmlFreeNode(request);
5909 return IE_ERROR;
5911 xmlSetNs(request, isds_ns);
5913 INSERT_STRING(request, "dbOldPassword", old_password);
5914 INSERT_STRING(request, "dbNewPassword", new_password);
5916 if (NULL != otp) {
5917 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5918 switch (otp->method) {
5919 case OTP_HMAC:
5920 isds_log(ILF_SEC, ILL_INFO,
5921 _("Selected authentication method: "
5922 "HMAC-based one-time password\n"));
5923 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5924 break;
5925 case OTP_TIME:
5926 isds_log(ILF_SEC, ILL_INFO,
5927 _("Selected authentication method: "
5928 "Time-based one-time password\n"));
5929 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5930 if (otp->otp_code == NULL) {
5931 isds_log(ILF_SEC, ILL_INFO,
5932 _("OTP code has not been provided by "
5933 "application, requesting server for "
5934 "new one.\n"));
5935 err = _isds_request_totp_code(context, old_password, otp,
5936 refnumber);
5937 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5938 goto leave;
5940 } else {
5941 isds_log(ILF_SEC, ILL_INFO,
5942 _("OTP code has been provided by "
5943 "application, not requesting server "
5944 "for new one.\n"));
5946 break;
5947 default:
5948 isds_log_message(context,
5949 _("Unknown one-time password authentication "
5950 "method requested by application"));
5951 err = IE_ENUM;
5952 goto leave;
5955 /* Change URL temporarily for sending this request only */
5957 char *new_url = NULL;
5958 if ((err = _isds_build_url_from_context(context,
5959 "%.*sasws/changePassword", &new_url))) {
5960 goto leave;
5962 saved_url = context->url;
5963 context->url = new_url;
5966 /* Store credentials for sending this request only */
5967 context->otp_credentials = otp;
5968 _isds_discard_credentials(context, 0);
5969 if ((err = _isds_store_credentials(context, context->saved_username,
5970 old_password, NULL))) {
5971 _isds_discard_credentials(context, 0);
5972 goto leave;
5974 #if HAVE_CURL_REAUTHORIZATION_BUG
5975 saved_curl = context->curl;
5976 context->curl = curl_easy_init();
5977 if (NULL == context->curl) {
5978 err = IE_ERROR;
5979 goto leave;
5981 if (context->timeout) {
5982 err = isds_set_timeout(context, context->timeout);
5983 if (err) goto leave;
5985 #endif
5988 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5989 _("Sending ChangeISDSPassword request to ISDS\n") :
5990 _("Sending ChangePasswordOTP request to ISDS\n"));
5992 /* Sent request */
5993 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5994 request, &response, NULL, NULL);
5996 if (otp) {
5997 /* Remove temporal credentials */
5998 _isds_discard_credentials(context, 0);
5999 /* Detach pointer to OTP credentials from context */
6000 context->otp_credentials = NULL;
6001 /* Keep context->otp true to keep signaling this is OTP session */
6004 /* Destroy request */
6005 xmlFreeNode(request); request = NULL;
6007 if (err) {
6008 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6009 _("Processing ISDS response on ChangeISDSPassword "
6010 "request failed\n") :
6011 _("Processing ISDS response on ChangePasswordOTP "
6012 "request failed\n"));
6013 goto leave;
6016 /* Check for response status */
6017 err = isds_response_status(context,
6018 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
6019 &code, &message, (xmlChar **)refnumber);
6020 if (err) {
6021 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6022 _("ISDS response on ChangeISDSPassword request is missing "
6023 "status\n") :
6024 _("ISDS response on ChangePasswordOTP request is missing "
6025 "status\n"));
6026 goto leave;
6029 /* Check for known error codes */
6030 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
6031 if (!xmlStrcmp(code, codes[i])) {
6032 char *code_locale = _isds_utf82locale((char*)code);
6033 char *message_locale = _isds_utf82locale((char*)message);
6034 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6035 _("Server refused to change password on ChangeISDSPassword "
6036 "request (code=%s, message=%s)\n") :
6037 _("Server refused to change password on ChangePasswordOTP "
6038 "request (code=%s, message=%s)\n"),
6039 code_locale, message_locale);
6040 free(code_locale);
6041 free(message_locale);
6042 isds_log_message(context, _(meanings[i]));
6043 err = IE_INVAL;
6044 goto leave;
6048 /* Other error */
6049 if (xmlStrcmp(code, BAD_CAST "0000")) {
6050 char *code_locale = _isds_utf82locale((char*)code);
6051 char *message_locale = _isds_utf82locale((char*)message);
6052 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6053 _("Server refused to change password on ChangeISDSPassword "
6054 "request (code=%s, message=%s)\n") :
6055 _("Server refused to change password on ChangePasswordOTP "
6056 "request (code=%s, message=%s)\n"),
6057 code_locale, message_locale);
6058 isds_log_message(context, message_locale);
6059 free(code_locale);
6060 free(message_locale);
6061 err = IE_ISDS;
6062 goto leave;
6065 /* Otherwise password changed successfully */
6067 leave:
6068 if (NULL != saved_url) {
6069 /* Revert URL to original one */
6070 zfree(context->url);
6071 context->url = saved_url;
6073 #if HAVE_CURL_REAUTHORIZATION_BUG
6074 if (NULL != saved_curl) {
6075 if (context->curl != NULL) curl_easy_cleanup(context->curl);
6076 context->curl = saved_curl;
6078 #endif
6080 free(code);
6081 free(message);
6082 xmlFreeDoc(response);
6083 xmlFreeNode(request);
6085 if (!err)
6086 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6087 _("Password changed successfully on ChangeISDSPassword "
6088 "request.\n") :
6089 _("Password changed successfully on ChangePasswordOTP "
6090 "request.\n"));
6091 #else /* not HAVE_LIBCURL */
6092 err = IE_NOTSUP;
6093 #endif
6095 return err;
6099 #if HAVE_LIBCURL
6100 /* Generic middle part with request sending and response check.
6101 * It sends prepared request and checks for error code.
6102 * @context is ISDS session context.
6103 * @service is ISDS service handler
6104 * @service_name is name in scope of given @service
6105 * @request is XML tree with request. Will be freed to save memory.
6106 * @response is XML document outputting ISDS response.
6107 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6108 * @map is mapping from status code to library error. Pass NULL if no special
6109 * handling is requested.
6110 * NULL, if you don't care. */
6111 static isds_error send_destroy_request_check_response(
6112 struct isds_ctx *context,
6113 const isds_service service, const xmlChar *service_name,
6114 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
6115 const struct code_map_isds_error *map) {
6116 isds_error err = IE_SUCCESS;
6117 char *service_name_locale = NULL;
6118 xmlChar *code = NULL, *message = NULL;
6121 if (!context) return IE_INVALID_CONTEXT;
6122 if (!service_name || *service_name == '\0' || !request || !*request ||
6123 !response)
6124 return IE_INVAL;
6126 /* Check if connection is established
6127 * TODO: This check should be done downstairs. */
6128 if (!context->curl) return IE_CONNECTION_CLOSED;
6130 service_name_locale = _isds_utf82locale((char*) service_name);
6131 if (!service_name_locale) {
6132 err = IE_NOMEM;
6133 goto leave;
6136 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
6137 service_name_locale);
6139 /* Send request */
6140 err = _isds(context, service, *request, response, NULL, NULL);
6141 xmlFreeNode(*request); *request = NULL;
6143 if (err) {
6144 isds_log(ILF_ISDS, ILL_DEBUG,
6145 _("Processing ISDS response on %s request failed\n"),
6146 service_name_locale);
6147 goto leave;
6150 /* Check for response status */
6151 err = isds_response_status(context, service, *response,
6152 &code, &message, refnumber);
6153 if (err) {
6154 isds_log(ILF_ISDS, ILL_DEBUG,
6155 _("ISDS response on %s request is missing status\n"),
6156 service_name_locale);
6157 goto leave;
6160 err = statuscode2isds_error(context, map, code, message);
6162 /* Request processed, but server failed */
6163 if (xmlStrcmp(code, BAD_CAST "0000")) {
6164 char *code_locale = _isds_utf82locale((char*) code);
6165 char *message_locale = _isds_utf82locale((char*) message);
6166 isds_log(ILF_ISDS, ILL_DEBUG,
6167 _("Server refused %s request (code=%s, message=%s)\n"),
6168 service_name_locale, code_locale, message_locale);
6169 free(code_locale);
6170 free(message_locale);
6171 goto leave;
6175 leave:
6176 free(code);
6177 free(message);
6178 if (err && *response) {
6179 xmlFreeDoc(*response);
6180 *response = NULL;
6182 if (*request) {
6183 xmlFreeNode(*request);
6184 *request = NULL;
6186 free(service_name_locale);
6188 return err;
6192 /* Generic bottom half with request sending.
6193 * It sends prepared request, checks for error code, destroys response and
6194 * request and log success or failure.
6195 * @context is ISDS session context.
6196 * @service is ISDS service handler
6197 * @service_name is name in scope of given @service
6198 * @request is XML tree with request. Will be freed to save memory.
6199 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6200 * NULL, if you don't care. */
6201 static isds_error send_request_check_drop_response(
6202 struct isds_ctx *context,
6203 const isds_service service, const xmlChar *service_name,
6204 xmlNodePtr *request, xmlChar **refnumber) {
6205 isds_error err = IE_SUCCESS;
6206 xmlDocPtr response = NULL;
6209 if (!context) return IE_INVALID_CONTEXT;
6210 if (!service_name || *service_name == '\0' || !request || !*request)
6211 return IE_INVAL;
6213 /* Send request and check response*/
6214 err = send_destroy_request_check_response(context,
6215 service, service_name, request, &response, refnumber, NULL);
6217 xmlFreeDoc(response);
6219 if (*request) {
6220 xmlFreeNode(*request);
6221 *request = NULL;
6224 if (!err) {
6225 char *service_name_locale = _isds_utf82locale((char *) service_name);
6226 isds_log(ILF_ISDS, ILL_DEBUG,
6227 _("%s request processed by server successfully.\n"),
6228 service_name_locale);
6229 free(service_name_locale);
6232 return err;
6236 /* Insert isds_credentials_delivery structure into XML request if not NULL
6237 * @context is session context
6238 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6239 * credentials delivery. The email field is passed.
6240 * @parent is XML element where to insert */
6241 static isds_error insert_credentials_delivery(struct isds_ctx *context,
6242 const struct isds_credentials_delivery *credentials_delivery,
6243 xmlNodePtr parent) {
6244 isds_error err = IE_SUCCESS;
6245 xmlNodePtr node;
6247 if (!context) return IE_INVALID_CONTEXT;
6248 if (!parent) return IE_INVAL;
6250 if (credentials_delivery) {
6251 /* Following elements are valid only for services:
6252 * NewAccessData, AddDataBoxUser, CreateDataBox */
6253 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
6254 INSERT_STRING(parent, "email", credentials_delivery->email);
6257 leave:
6258 return err;
6262 /* Extract credentials delivery from ISDS response.
6263 * @context is session context
6264 * @credentials_delivery is pointer to valid structure to fill in returned
6265 * user's password (and new log-in name). If NULL, do not extract the data.
6266 * @response is pointer to XML document with ISDS response
6267 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6268 * @return IE_SUCCESS even if new user name has not been found because it's not
6269 * clear whether it's returned always. */
6270 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6271 struct isds_credentials_delivery *credentials_delivery,
6272 xmlDocPtr response, const char *request_name) {
6273 isds_error err = IE_SUCCESS;
6274 xmlXPathContextPtr xpath_ctx = NULL;
6275 xmlXPathObjectPtr result = NULL;
6276 char *xpath_query = NULL;
6278 if (!context) return IE_INVALID_CONTEXT;
6279 if (credentials_delivery) {
6280 zfree(credentials_delivery->token);
6281 zfree(credentials_delivery->new_user_name);
6283 if (!response || !request_name || !*request_name) return IE_INVAL;
6286 /* Extract optional token */
6287 if (credentials_delivery) {
6288 xpath_ctx = xmlXPathNewContext(response);
6289 if (!xpath_ctx) {
6290 err = IE_ERROR;
6291 goto leave;
6293 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6294 err = IE_ERROR;
6295 goto leave;
6298 /* Verify root element */
6299 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6300 request_name)) {
6301 err = IE_NOMEM;
6302 goto leave;
6304 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6305 if (!result) {
6306 err = IE_ERROR;
6307 goto leave;
6309 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6310 char *request_name_locale = _isds_utf82locale(request_name);
6311 isds_log(ILF_ISDS, ILL_WARNING,
6312 _("Wrong element in ISDS response for %s request "
6313 "while extracting credentials delivery details\n"),
6314 request_name_locale);
6315 free(request_name_locale);
6316 err = IE_ERROR;
6317 goto leave;
6319 xpath_ctx->node = result->nodesetval->nodeTab[0];
6322 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6323 * optional. */
6324 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6326 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6327 if (!credentials_delivery->token) {
6328 char *request_name_locale = _isds_utf82locale(request_name);
6329 isds_log(ILF_ISDS, ILL_ERR,
6330 _("ISDS did not return token on %s request "
6331 "even if requested\n"), request_name_locale);
6332 free(request_name_locale);
6333 err = IE_ERROR;
6337 leave:
6338 free(xpath_query);
6339 xmlXPathFreeObject(result);
6340 xmlXPathFreeContext(xpath_ctx);
6342 return err;
6346 /* Build XSD:tCreateDBInput request type for box creating.
6347 * @context is session context
6348 * @request outputs built XML tree
6349 * @service_name is request name of SERVICE_DB_MANIPULATION service
6350 * @box is box description to create including single primary user (in case of
6351 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6352 * ignored.
6353 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6354 * box, or contact address of PFO box owner)
6355 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6356 * @upper_box_id is optional ID of supper box if currently created box is
6357 * subordinated.
6358 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6359 * don't care.
6360 * @credentials_delivery is valid pointer if ISDS should return token that box
6361 * owner can use to obtain his new credentials in on-line way. Then valid email
6362 * member value should be supplied.
6363 * @approval is optional external approval of box manipulation */
6364 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6365 xmlNodePtr *request, const xmlChar *service_name,
6366 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6367 const xmlChar *former_names, const xmlChar *upper_box_id,
6368 const xmlChar *ceo_label,
6369 const struct isds_credentials_delivery *credentials_delivery,
6370 const struct isds_approval *approval) {
6371 isds_error err = IE_SUCCESS;
6372 xmlNsPtr isds_ns = NULL;
6373 xmlNodePtr node, dbPrimaryUsers;
6374 xmlChar *string = NULL;
6375 const struct isds_list *item;
6378 if (!context) return IE_INVALID_CONTEXT;
6379 if (!request || !service_name || service_name[0] == '\0' || !box)
6380 return IE_INVAL;
6383 /* Build CreateDataBox-similar request */
6384 *request = xmlNewNode(NULL, service_name);
6385 if (!*request) {
6386 char *service_name_locale = _isds_utf82locale((char*) service_name);
6387 isds_printf_message(context, _("Could build %s request"),
6388 service_name_locale);
6389 free(service_name_locale);
6390 return IE_ERROR;
6392 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6393 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6394 if (!isds_ns) {
6395 isds_log_message(context, _("Could not create ISDS1 name space"));
6396 xmlFreeNode(*request);
6397 return IE_ERROR;
6399 } else {
6400 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6401 if (!isds_ns) {
6402 isds_log_message(context, _("Could not create ISDS name space"));
6403 xmlFreeNode(*request);
6404 return IE_ERROR;
6407 xmlSetNs(*request, isds_ns);
6409 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6410 err = insert_DbOwnerInfo(context, box, 0, node);
6411 if (err) goto leave;
6413 /* Insert users */
6414 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6415 * verbose documentation allows none dbUserInfo */
6416 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6417 for (item = users; item; item = item->next) {
6418 if (item->data) {
6419 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6420 err = insert_DbUserInfo(context,
6421 (struct isds_DbUserInfo *) item->data, 1, node);
6422 if (err) goto leave;
6426 INSERT_STRING(*request, "dbFormerNames", former_names);
6427 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6428 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6430 err = insert_credentials_delivery(context, credentials_delivery, *request);
6431 if (err) goto leave;
6433 err = insert_GExtApproval(context, approval, *request);
6434 if (err) goto leave;
6436 leave:
6437 if (err) {
6438 xmlFreeNode(*request);
6439 *request = NULL;
6441 free(string);
6442 return err;
6444 #endif /* HAVE_LIBCURL */
6447 /* Create new box.
6448 * @context is session context
6449 * @box is box description to create including single primary user (in case of
6450 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6451 * ignored. It outputs box ID assigned by ISDS in dbID element.
6452 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6453 * box, or contact address of PFO box owner)
6454 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6455 * @upper_box_id is optional ID of supper box if currently created box is
6456 * subordinated.
6457 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6458 * @credentials_delivery is NULL if new password should be delivered off-line
6459 * to box owner. It is valid pointer if owner should obtain new password on-line
6460 * on dedicated web server. Then input @credentials_delivery.email value is
6461 * his e-mail address he must provide to dedicated web server together
6462 * with output reallocated @credentials_delivery.token member. Output
6463 * member @credentials_delivery.new_user_name is unused up on this call.
6464 * @approval is optional external approval of box manipulation
6465 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6466 * NULL, if you don't care.*/
6467 isds_error isds_add_box(struct isds_ctx *context,
6468 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6469 const char *former_names, const char *upper_box_id,
6470 const char *ceo_label,
6471 struct isds_credentials_delivery *credentials_delivery,
6472 const struct isds_approval *approval, char **refnumber) {
6473 isds_error err = IE_SUCCESS;
6474 #if HAVE_LIBCURL
6475 xmlNodePtr request = NULL;
6476 xmlDocPtr response = NULL;
6477 xmlXPathContextPtr xpath_ctx = NULL;
6478 xmlXPathObjectPtr result = NULL;
6479 #endif
6482 if (!context) return IE_INVALID_CONTEXT;
6483 zfree(context->long_message);
6484 if (credentials_delivery) {
6485 zfree(credentials_delivery->token);
6486 zfree(credentials_delivery->new_user_name);
6488 if (!box) return IE_INVAL;
6490 #if HAVE_LIBCURL
6491 /* Scratch box ID */
6492 zfree(box->dbID);
6494 /* Build CreateDataBox request */
6495 err = build_CreateDBInput_request(context,
6496 &request, BAD_CAST "CreateDataBox",
6497 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6498 (xmlChar *) ceo_label, credentials_delivery, approval);
6499 if (err) goto leave;
6501 /* Send it to server and process response */
6502 err = send_destroy_request_check_response(context,
6503 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6504 &response, (xmlChar **) refnumber, NULL);
6506 /* Extract box ID */
6507 xpath_ctx = xmlXPathNewContext(response);
6508 if (!xpath_ctx) {
6509 err = IE_ERROR;
6510 goto leave;
6512 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6513 err = IE_ERROR;
6514 goto leave;
6516 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6518 /* Extract optional token */
6519 err = extract_credentials_delivery(context, credentials_delivery, response,
6520 "CreateDataBox");
6522 leave:
6523 xmlXPathFreeObject(result);
6524 xmlXPathFreeContext(xpath_ctx);
6525 xmlFreeDoc(response);
6526 xmlFreeNode(request);
6528 if (!err) {
6529 isds_log(ILF_ISDS, ILL_DEBUG,
6530 _("CreateDataBox request processed by server successfully.\n"));
6532 #else /* not HAVE_LIBCURL */
6533 err = IE_NOTSUP;
6534 #endif
6536 return err;
6540 /* Notify ISDS about new PFO entity.
6541 * This function has no real effect.
6542 * @context is session context
6543 * @box is PFO description including single primary user. aifoIsds,
6544 * address->adCode, address->adDistrict members are ignored.
6545 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6546 * @former_names is optional undocumented string. Pass NULL if you don't care.
6547 * @upper_box_id is optional ID of supper box if currently created box is
6548 * subordinated.
6549 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6550 * @approval is optional external approval of box manipulation
6551 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6552 * NULL, if you don't care.*/
6553 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6554 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6555 const char *former_names, const char *upper_box_id,
6556 const char *ceo_label, const struct isds_approval *approval,
6557 char **refnumber) {
6558 isds_error err = IE_SUCCESS;
6559 #if HAVE_LIBCURL
6560 xmlNodePtr request = NULL;
6561 #endif
6563 if (!context) return IE_INVALID_CONTEXT;
6564 zfree(context->long_message);
6565 if (!box) return IE_INVAL;
6567 #if HAVE_LIBCURL
6568 /* Build CreateDataBoxPFOInfo request */
6569 err = build_CreateDBInput_request(context,
6570 &request, BAD_CAST "CreateDataBoxPFOInfo",
6571 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6572 (xmlChar *) ceo_label, NULL, approval);
6573 if (err) goto leave;
6575 /* Send it to server and process response */
6576 err = send_request_check_drop_response(context,
6577 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6578 (xmlChar **) refnumber);
6579 /* XXX: XML Schema names output dbID element but textual documentation
6580 * states no box identifier is returned. */
6581 leave:
6582 xmlFreeNode(request);
6583 #else /* not HAVE_LIBCURL */
6584 err = IE_NOTSUP;
6585 #endif
6586 return err;
6590 /* Common implementation for removing given box.
6591 * @context is session context
6592 * @service_name is UTF-8 encoded name fo ISDS service
6593 * @box is box description to delete. aifoIsds, address->adCode,
6594 * address->adDistrict members are ignored.
6595 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6596 * carry sane value. If NULL, do not inject this information into request.
6597 * @approval is optional external approval of box manipulation
6598 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6599 * NULL, if you don't care.*/
6600 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6601 const xmlChar *service_name,
6602 const struct isds_DbOwnerInfo *box, const struct tm *since,
6603 const struct isds_approval *approval, char **refnumber) {
6604 isds_error err = IE_SUCCESS;
6605 #if HAVE_LIBCURL
6606 xmlNsPtr isds_ns = NULL;
6607 xmlNodePtr request = NULL;
6608 xmlNodePtr node;
6609 xmlChar *string = NULL;
6610 #endif
6613 if (!context) return IE_INVALID_CONTEXT;
6614 zfree(context->long_message);
6615 if (!service_name || !*service_name || !box) return IE_INVAL;
6618 #if HAVE_LIBCURL
6619 /* Build DeleteDataBox(Promptly) request */
6620 request = xmlNewNode(NULL, service_name);
6621 if (!request) {
6622 char *service_name_locale = _isds_utf82locale((char*)service_name);
6623 isds_printf_message(context,
6624 _("Could build %s request"), service_name_locale);
6625 free(service_name_locale);
6626 return IE_ERROR;
6628 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6629 if(!isds_ns) {
6630 isds_log_message(context, _("Could not create ISDS name space"));
6631 xmlFreeNode(request);
6632 return IE_ERROR;
6634 xmlSetNs(request, isds_ns);
6636 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6637 err = insert_DbOwnerInfo(context, box, 0, node);
6638 if (err) goto leave;
6640 if (since) {
6641 err = tm2datestring(since, &string);
6642 if (err) {
6643 isds_log_message(context,
6644 _("Could not convert `since' argument to ISO date string"));
6645 goto leave;
6647 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6648 zfree(string);
6651 err = insert_GExtApproval(context, approval, request);
6652 if (err) goto leave;
6655 /* Send it to server and process response */
6656 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6657 service_name, &request, (xmlChar **) refnumber);
6659 leave:
6660 xmlFreeNode(request);
6661 free(string);
6662 #else /* not HAVE_LIBCURL */
6663 err = IE_NOTSUP;
6664 #endif
6665 return err;
6669 /* Remove given box permanently.
6670 * @context is session context
6671 * @box is box description to delete. aifoIsds, address->adCode,
6672 * address->adDistrict members are ignored.
6673 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6674 * carry sane value.
6675 * @approval is optional external approval of box manipulation
6676 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6677 * NULL, if you don't care.*/
6678 isds_error isds_delete_box(struct isds_ctx *context,
6679 const struct isds_DbOwnerInfo *box, const struct tm *since,
6680 const struct isds_approval *approval, char **refnumber) {
6681 if (!context) return IE_INVALID_CONTEXT;
6682 zfree(context->long_message);
6683 if (!box || !since) return IE_INVAL;
6685 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6686 box, since, approval, refnumber);
6690 /* Undocumented function.
6691 * @context is session context
6692 * @box is box description to delete. aifoIsds, address->adCode,
6693 * address->adDistrict members are ignored.
6694 * @approval is optional external approval of box manipulation
6695 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6696 * NULL, if you don't care.*/
6697 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6698 const struct isds_DbOwnerInfo *box,
6699 const struct isds_approval *approval, char **refnumber) {
6700 if (!context) return IE_INVALID_CONTEXT;
6701 zfree(context->long_message);
6702 if (!box) return IE_INVAL;
6704 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6705 box, NULL, approval, refnumber);
6709 /* Update data about given box.
6710 * @context is session context
6711 * @old_box current box description. aifoIsds, address->adCode,
6712 * address->adDistrict members are ignored.
6713 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6714 * address->adDistrict members are ignored.
6715 * @approval is optional external approval of box manipulation
6716 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6717 * NULL, if you don't care.*/
6718 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6719 const struct isds_DbOwnerInfo *old_box,
6720 const struct isds_DbOwnerInfo *new_box,
6721 const struct isds_approval *approval, char **refnumber) {
6722 isds_error err = IE_SUCCESS;
6723 #if HAVE_LIBCURL
6724 xmlNsPtr isds_ns = NULL;
6725 xmlNodePtr request = NULL;
6726 xmlNodePtr node;
6727 #endif
6730 if (!context) return IE_INVALID_CONTEXT;
6731 zfree(context->long_message);
6732 if (!old_box || !new_box) return IE_INVAL;
6735 #if HAVE_LIBCURL
6736 /* Build UpdateDataBoxDescr request */
6737 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6738 if (!request) {
6739 isds_log_message(context,
6740 _("Could build UpdateDataBoxDescr request"));
6741 return IE_ERROR;
6743 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6744 if(!isds_ns) {
6745 isds_log_message(context, _("Could not create ISDS name space"));
6746 xmlFreeNode(request);
6747 return IE_ERROR;
6749 xmlSetNs(request, isds_ns);
6751 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6752 err = insert_DbOwnerInfo(context, old_box, 0, node);
6753 if (err) goto leave;
6755 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6756 err = insert_DbOwnerInfo(context, new_box, 0, node);
6757 if (err) goto leave;
6759 err = insert_GExtApproval(context, approval, request);
6760 if (err) goto leave;
6763 /* Send it to server and process response */
6764 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6765 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6767 leave:
6768 xmlFreeNode(request);
6769 #else /* not HAVE_LIBCURL */
6770 err = IE_NOTSUP;
6771 #endif
6773 return err;
6777 #if HAVE_LIBCURL
6778 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6779 * code
6780 * @context is session context
6781 * @service is SOAP service
6782 * @service_name is name of request in @service
6783 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6784 * @box_id is box ID of interest
6785 * @approval is optional external approval of box manipulation
6786 * @response is server SOAP body response as XML document
6787 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6788 * NULL, if you don't care.
6789 * @return error coded from lower layer, context message will be set up
6790 * appropriately. */
6791 static isds_error build_send_dbid_request_check_response(
6792 struct isds_ctx *context, const isds_service service,
6793 const xmlChar *service_name, const xmlChar *box_id_element,
6794 const xmlChar *box_id, const struct isds_approval *approval,
6795 xmlDocPtr *response, xmlChar **refnumber) {
6797 isds_error err = IE_SUCCESS;
6798 char *service_name_locale = NULL, *box_id_locale = NULL;
6799 xmlNodePtr request = NULL, node;
6800 xmlNsPtr isds_ns = NULL;
6802 if (!context) return IE_INVALID_CONTEXT;
6803 if (!service_name || !box_id) return IE_INVAL;
6804 if (!response) return IE_INVAL;
6806 /* Free output argument */
6807 xmlFreeDoc(*response); *response = NULL;
6809 /* Prepare strings */
6810 service_name_locale = _isds_utf82locale((char*)service_name);
6811 if (!service_name_locale) {
6812 err = IE_NOMEM;
6813 goto leave;
6815 box_id_locale = _isds_utf82locale((char*)box_id);
6816 if (!box_id_locale) {
6817 err = IE_NOMEM;
6818 goto leave;
6821 /* Build request */
6822 request = xmlNewNode(NULL, service_name);
6823 if (!request) {
6824 isds_printf_message(context,
6825 _("Could not build %s request for %s box"), service_name_locale,
6826 box_id_locale);
6827 err = IE_ERROR;
6828 goto leave;
6830 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6831 if(!isds_ns) {
6832 isds_log_message(context, _("Could not create ISDS name space"));
6833 err = IE_ERROR;
6834 goto leave;
6836 xmlSetNs(request, isds_ns);
6838 /* Add XSD:tIdDbInput children */
6839 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6840 INSERT_STRING(request, box_id_element, box_id);
6841 err = insert_GExtApproval(context, approval, request);
6842 if (err) goto leave;
6844 /* Send request and check response*/
6845 err = send_destroy_request_check_response(context,
6846 service, service_name, &request, response, refnumber, NULL);
6848 leave:
6849 free(service_name_locale);
6850 free(box_id_locale);
6851 xmlFreeNode(request);
6852 return err;
6854 #endif /* HAVE_LIBCURL */
6857 /* Get data about all users assigned to given box.
6858 * @context is session context
6859 * @box_id is box ID
6860 * @users is automatically reallocated list of struct isds_DbUserInfo */
6861 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6862 struct isds_list **users) {
6863 isds_error err = IE_SUCCESS;
6864 #if HAVE_LIBCURL
6865 xmlDocPtr response = NULL;
6866 xmlXPathContextPtr xpath_ctx = NULL;
6867 xmlXPathObjectPtr result = NULL;
6868 int i;
6869 struct isds_list *item, *prev_item = NULL;
6870 #endif
6872 if (!context) return IE_INVALID_CONTEXT;
6873 zfree(context->long_message);
6874 if (!users || !box_id) return IE_INVAL;
6875 isds_list_free(users);
6878 #if HAVE_LIBCURL
6879 /* Do request and check for success */
6880 err = build_send_dbid_request_check_response(context,
6881 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6882 BAD_CAST box_id, NULL, &response, NULL);
6883 if (err) goto leave;
6886 /* Extract data */
6887 /* Prepare structure */
6888 xpath_ctx = xmlXPathNewContext(response);
6889 if (!xpath_ctx) {
6890 err = IE_ERROR;
6891 goto leave;
6893 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6894 err = IE_ERROR;
6895 goto leave;
6898 /* Set context node */
6899 result = xmlXPathEvalExpression(BAD_CAST
6900 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6901 xpath_ctx);
6902 if (!result) {
6903 err = IE_ERROR;
6904 goto leave;
6906 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6907 /* Iterate over all users */
6908 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6910 /* Prepare structure */
6911 item = calloc(1, sizeof(*item));
6912 if (!item) {
6913 err = IE_NOMEM;
6914 goto leave;
6916 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6917 if (i == 0) *users = item;
6918 else prev_item->next = item;
6919 prev_item = item;
6921 /* Extract it */
6922 xpath_ctx->node = result->nodesetval->nodeTab[i];
6923 err = extract_DbUserInfo(context,
6924 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6925 if (err) goto leave;
6929 leave:
6930 if (err) {
6931 isds_list_free(users);
6934 xmlXPathFreeObject(result);
6935 xmlXPathFreeContext(xpath_ctx);
6936 xmlFreeDoc(response);
6938 if (!err)
6939 isds_log(ILF_ISDS, ILL_DEBUG,
6940 _("GetDataBoxUsers request processed by server "
6941 "successfully.\n"));
6942 #else /* not HAVE_LIBCURL */
6943 err = IE_NOTSUP;
6944 #endif
6946 return err;
6950 /* Update data about user assigned to given box.
6951 * @context is session context
6952 * @box is box identification. aifoIsds, address->adCode,
6953 * address->adDistrict members are ignored.
6954 * @old_user identifies user to update, aifo_ticket member is ignored
6955 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6956 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6957 * NULL, if you don't care.*/
6958 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6959 const struct isds_DbOwnerInfo *box,
6960 const struct isds_DbUserInfo *old_user,
6961 const struct isds_DbUserInfo *new_user,
6962 char **refnumber) {
6963 isds_error err = IE_SUCCESS;
6964 #if HAVE_LIBCURL
6965 xmlNsPtr isds_ns = NULL;
6966 xmlNodePtr request = NULL;
6967 xmlNodePtr node;
6968 #endif
6971 if (!context) return IE_INVALID_CONTEXT;
6972 zfree(context->long_message);
6973 if (!box || !old_user || !new_user) return IE_INVAL;
6976 #if HAVE_LIBCURL
6977 /* Build UpdateDataBoxUser request */
6978 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6979 if (!request) {
6980 isds_log_message(context,
6981 _("Could build UpdateDataBoxUser request"));
6982 return IE_ERROR;
6984 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6985 if(!isds_ns) {
6986 isds_log_message(context, _("Could not create ISDS name space"));
6987 xmlFreeNode(request);
6988 return IE_ERROR;
6990 xmlSetNs(request, isds_ns);
6992 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6993 err = insert_DbOwnerInfo(context, box, 0, node);
6994 if (err) goto leave;
6996 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6997 err = insert_DbUserInfo(context, old_user, 0, node);
6998 if (err) goto leave;
7000 INSERT_ELEMENT(node, request, "dbNewUserInfo");
7001 err = insert_DbUserInfo(context, new_user, 0, node);
7002 if (err) goto leave;
7004 /* Send it to server and process response */
7005 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7006 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
7008 leave:
7009 xmlFreeNode(request);
7010 #else /* not HAVE_LIBCURL */
7011 err = IE_NOTSUP;
7012 #endif
7014 return err;
7018 /* Undocumented function.
7019 * @context is session context
7020 * @box_id is UTF-8 encoded box identifier
7021 * @token is UTF-8 encoded temporary password
7022 * @user_id outputs UTF-8 encoded reallocated user identifier
7023 * @password outpus UTF-8 encoded reallocated user password
7024 * Output arguments will be nulled in case of error */
7025 isds_error isds_activate(struct isds_ctx *context,
7026 const char *box_id, const char *token,
7027 char **user_id, char **password) {
7028 isds_error err = IE_SUCCESS;
7029 #if HAVE_LIBCURL
7030 xmlNsPtr isds_ns = NULL;
7031 xmlNodePtr request = NULL, node;
7032 xmlDocPtr response = NULL;
7033 xmlXPathContextPtr xpath_ctx = NULL;
7034 xmlXPathObjectPtr result = NULL;
7035 #endif
7038 if (!context) return IE_INVALID_CONTEXT;
7039 zfree(context->long_message);
7041 if (user_id) zfree(*user_id);
7042 if (password) zfree(*password);
7044 if (!box_id || !token || !user_id || !password) return IE_INVAL;
7047 #if HAVE_LIBCURL
7048 /* Build Activate request */
7049 request = xmlNewNode(NULL, BAD_CAST "Activate");
7050 if (!request) {
7051 isds_log_message(context, _("Could build Activate request"));
7052 return IE_ERROR;
7054 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7055 if(!isds_ns) {
7056 isds_log_message(context, _("Could not create ISDS name space"));
7057 xmlFreeNode(request);
7058 return IE_ERROR;
7060 xmlSetNs(request, isds_ns);
7062 INSERT_STRING(request, "dbAccessDataId", token);
7063 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
7064 INSERT_STRING(request, "dbID", box_id);
7067 /* Send request and check response*/
7068 err = send_destroy_request_check_response(context,
7069 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
7070 &response, NULL, NULL);
7071 if (err) goto leave;
7074 /* Extract data */
7075 xpath_ctx = xmlXPathNewContext(response);
7076 if (!xpath_ctx) {
7077 err = IE_ERROR;
7078 goto leave;
7080 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7081 err = IE_ERROR;
7082 goto leave;
7084 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
7085 xpath_ctx);
7086 if (!result) {
7087 err = IE_ERROR;
7088 goto leave;
7090 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7091 isds_log_message(context, _("Missing ActivateResponse element"));
7092 err = IE_ISDS;
7093 goto leave;
7095 if (result->nodesetval->nodeNr > 1) {
7096 isds_log_message(context, _("Multiple ActivateResponse element"));
7097 err = IE_ISDS;
7098 goto leave;
7100 xpath_ctx->node = result->nodesetval->nodeTab[0];
7101 xmlXPathFreeObject(result); result = NULL;
7103 EXTRACT_STRING("isds:userId", *user_id);
7104 if (!*user_id)
7105 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
7106 "but did not return `userId' element.\n"));
7108 EXTRACT_STRING("isds:password", *password);
7109 if (!*password)
7110 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
7111 "but did not return `password' element.\n"));
7113 leave:
7114 xmlXPathFreeObject(result);
7115 xmlXPathFreeContext(xpath_ctx);
7116 xmlFreeDoc(response);
7117 xmlFreeNode(request);
7119 if (!err)
7120 isds_log(ILF_ISDS, ILL_DEBUG,
7121 _("Activate request processed by server successfully.\n"));
7122 #else /* not HAVE_LIBCURL */
7123 err = IE_NOTSUP;
7124 #endif
7126 return err;
7130 /* Reset credentials of user assigned to given box.
7131 * @context is session context
7132 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7133 * members are ignored.
7134 * @user identifies user to reset password, aifo_ticket member is ignored
7135 * @fee_paid is true if fee has been paid, false otherwise
7136 * @approval is optional external approval of box manipulation
7137 * @credentials_delivery is NULL if new password should be delivered off-line
7138 * to the user. It is valid pointer if user should obtain new password on-line
7139 * on dedicated web server. Then input @credentials_delivery.email value is
7140 * user's e-mail address user must provide to dedicated web server together
7141 * with @credentials_delivery.token. The output reallocated token user needs
7142 * to use to authorize on the web server to view his new password. Output
7143 * reallocated @credentials_delivery.new_user_name is user's log-in name that
7144 * ISDS changed up on this call. (No reason why server could change the name
7145 * is known now.)
7146 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7147 * NULL, if you don't care.*/
7148 isds_error isds_reset_password(struct isds_ctx *context,
7149 const struct isds_DbOwnerInfo *box,
7150 const struct isds_DbUserInfo *user,
7151 const _Bool fee_paid, const struct isds_approval *approval,
7152 struct isds_credentials_delivery *credentials_delivery,
7153 char **refnumber) {
7154 isds_error err = IE_SUCCESS;
7155 #if HAVE_LIBCURL
7156 xmlNsPtr isds_ns = NULL;
7157 xmlNodePtr request = NULL, node;
7158 xmlDocPtr response = NULL;
7159 #endif
7162 if (!context) return IE_INVALID_CONTEXT;
7163 zfree(context->long_message);
7165 if (credentials_delivery) {
7166 zfree(credentials_delivery->token);
7167 zfree(credentials_delivery->new_user_name);
7169 if (!box || !user) return IE_INVAL;
7172 #if HAVE_LIBCURL
7173 /* Build NewAccessData request */
7174 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
7175 if (!request) {
7176 isds_log_message(context,
7177 _("Could build NewAccessData request"));
7178 return IE_ERROR;
7180 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7181 if(!isds_ns) {
7182 isds_log_message(context, _("Could not create ISDS name space"));
7183 xmlFreeNode(request);
7184 return IE_ERROR;
7186 xmlSetNs(request, isds_ns);
7188 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7189 err = insert_DbOwnerInfo(context, box, 0, node);
7190 if (err) goto leave;
7192 INSERT_ELEMENT(node, request, "dbUserInfo");
7193 err = insert_DbUserInfo(context, user, 0, node);
7194 if (err) goto leave;
7196 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
7198 err = insert_credentials_delivery(context, credentials_delivery, request);
7199 if (err) goto leave;
7201 err = insert_GExtApproval(context, approval, request);
7202 if (err) goto leave;
7204 /* Send request and check response*/
7205 err = send_destroy_request_check_response(context,
7206 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
7207 &response, (xmlChar **) refnumber, NULL);
7208 if (err) goto leave;
7211 /* Extract optional token */
7212 err = extract_credentials_delivery(context, credentials_delivery,
7213 response, "NewAccessData");
7215 leave:
7216 xmlFreeDoc(response);
7217 xmlFreeNode(request);
7219 if (!err)
7220 isds_log(ILF_ISDS, ILL_DEBUG,
7221 _("NewAccessData request processed by server "
7222 "successfully.\n"));
7223 #else /* not HAVE_LIBCURL */
7224 err = IE_NOTSUP;
7225 #endif
7227 return err;
7231 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7232 * code, destroy response and log success.
7233 * @context is ISDS session context.
7234 * @service_name is name of SERVICE_DB_MANIPULATION service
7235 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7236 * members are ignored.
7237 * @user identifies user to remove
7238 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7239 * @credentials_delivery is NULL if new user's password should be delivered
7240 * off-line to the user. It is valid pointer if user should obtain new
7241 * password on-line on dedicated web server. Then input
7242 * @credentials_delivery.email value is user's e-mail address user must
7243 * provide to dedicated web server together with @credentials_delivery.token.
7244 * The output reallocated token user needs to use to authorize on the web
7245 * server to view his new password. Output reallocated
7246 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7247 * assingned or changed up on this call.
7248 * @approval is optional external approval of box manipulation
7249 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7250 * NULL, if you don't care. */
7251 static isds_error build_send_manipulationboxuser_request_check_drop_response(
7252 struct isds_ctx *context, const xmlChar *service_name,
7253 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7254 _Bool honor_aifo_ticket,
7255 struct isds_credentials_delivery *credentials_delivery,
7256 const struct isds_approval *approval, xmlChar **refnumber) {
7257 isds_error err = IE_SUCCESS;
7258 #if HAVE_LIBCURL
7259 xmlNsPtr isds_ns = NULL;
7260 xmlNodePtr request = NULL, node;
7261 xmlDocPtr response = NULL;
7262 #endif
7265 if (!context) return IE_INVALID_CONTEXT;
7266 zfree(context->long_message);
7267 if (credentials_delivery) {
7268 zfree(credentials_delivery->token);
7269 zfree(credentials_delivery->new_user_name);
7271 if (!service_name || service_name[0] == '\0' || !box || !user)
7272 return IE_INVAL;
7275 #if HAVE_LIBCURL
7276 /* Build NewAccessData or similar request */
7277 request = xmlNewNode(NULL, service_name);
7278 if (!request) {
7279 char *service_name_locale = _isds_utf82locale((char *) service_name);
7280 isds_printf_message(context, _("Could not build %s request"),
7281 service_name_locale);
7282 free(service_name_locale);
7283 return IE_ERROR;
7285 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7286 if(!isds_ns) {
7287 isds_log_message(context, _("Could not create ISDS name space"));
7288 xmlFreeNode(request);
7289 return IE_ERROR;
7291 xmlSetNs(request, isds_ns);
7293 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7294 err = insert_DbOwnerInfo(context, box, 0, node);
7295 if (err) goto leave;
7297 INSERT_ELEMENT(node, request, "dbUserInfo");
7298 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7299 if (err) goto leave;
7301 err = insert_credentials_delivery(context, credentials_delivery, request);
7302 if (err) goto leave;
7304 err = insert_GExtApproval(context, approval, request);
7305 if (err) goto leave;
7308 /* Send request and check response*/
7309 err = send_destroy_request_check_response(context,
7310 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7311 refnumber, NULL);
7313 xmlFreeNode(request);
7314 request = NULL;
7316 /* Pick up credentials_delivery if requested */
7317 err = extract_credentials_delivery(context, credentials_delivery, response,
7318 (char *)service_name);
7320 leave:
7321 xmlFreeDoc(response);
7322 if (request) xmlFreeNode(request);
7324 if (!err) {
7325 char *service_name_locale = _isds_utf82locale((char *) service_name);
7326 isds_log(ILF_ISDS, ILL_DEBUG,
7327 _("%s request processed by server successfully.\n"),
7328 service_name_locale);
7329 free(service_name_locale);
7331 #else /* not HAVE_LIBCURL */
7332 err = IE_NOTSUP;
7333 #endif
7335 return err;
7339 /* Assign new user to given box.
7340 * @context is session context
7341 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7342 * members are ignored.
7343 * @user defines new user to add
7344 * @credentials_delivery is NULL if new user's password should be delivered
7345 * off-line to the user. It is valid pointer if user should obtain new
7346 * password on-line on dedicated web server. Then input
7347 * @credentials_delivery.email value is user's e-mail address user must
7348 * provide to dedicated web server together with @credentials_delivery.token.
7349 * The output reallocated token user needs to use to authorize on the web
7350 * server to view his new password. Output reallocated
7351 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7352 * assingned up on this call.
7353 * @approval is optional external approval of box manipulation
7354 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7355 * NULL, if you don't care.*/
7356 isds_error isds_add_user(struct isds_ctx *context,
7357 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7358 struct isds_credentials_delivery *credentials_delivery,
7359 const struct isds_approval *approval, char **refnumber) {
7360 return build_send_manipulationboxuser_request_check_drop_response(context,
7361 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7362 approval, (xmlChar **) refnumber);
7366 /* Remove user assigned to given box.
7367 * @context is session context
7368 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7369 * members are ignored.
7370 * @user identifies user to remove, aifo_ticket member is ignored
7371 * @approval is optional external approval of box manipulation
7372 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7373 * NULL, if you don't care.*/
7374 isds_error isds_delete_user(struct isds_ctx *context,
7375 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7376 const struct isds_approval *approval, char **refnumber) {
7377 return build_send_manipulationboxuser_request_check_drop_response(context,
7378 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7379 (xmlChar **) refnumber);
7383 /* Get list of boxes in ZIP archive.
7384 * @context is session context
7385 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7386 * System recognizes following values currently: ALL (all boxes), UPG
7387 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7388 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7389 * commercial messages). This argument is a string because specification
7390 * states new values can appear in the future. Not all list types are
7391 * available to all users.
7392 * @buffer is automatically reallocated memory to store the list of boxes. The
7393 * list is zipped CSV file.
7394 * @buffer_length is size of @buffer data in bytes.
7395 * In case of error @buffer will be freed and @buffer_length will be
7396 * undefined.*/
7397 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7398 const char *list_identifier, void **buffer, size_t *buffer_length) {
7399 isds_error err = IE_SUCCESS;
7400 #if HAVE_LIBCURL
7401 xmlNsPtr isds_ns = NULL;
7402 xmlNodePtr request = NULL, node;
7403 xmlDocPtr response = NULL;
7404 xmlXPathContextPtr xpath_ctx = NULL;
7405 xmlXPathObjectPtr result = NULL;
7406 char *string = NULL;
7407 #endif
7410 if (!context) return IE_INVALID_CONTEXT;
7411 zfree(context->long_message);
7412 if (buffer) zfree(*buffer);
7413 if (!buffer || !buffer_length) return IE_INVAL;
7416 #if HAVE_LIBCURL
7417 /* Check if connection is established
7418 * TODO: This check should be done downstairs. */
7419 if (!context->curl) return IE_CONNECTION_CLOSED;
7422 /* Build AuthenticateMessage request */
7423 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7424 if (!request) {
7425 isds_log_message(context,
7426 _("Could not build GetDataBoxList request"));
7427 return IE_ERROR;
7429 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7430 if(!isds_ns) {
7431 isds_log_message(context, _("Could not create ISDS name space"));
7432 xmlFreeNode(request);
7433 return IE_ERROR;
7435 xmlSetNs(request, isds_ns);
7436 INSERT_STRING(request, "dblType", list_identifier);
7438 /* Send request to server and process response */
7439 err = send_destroy_request_check_response(context,
7440 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7441 &response, NULL, NULL);
7442 if (err) goto leave;
7445 /* Extract Base-64 encoded ZIP file */
7446 xpath_ctx = xmlXPathNewContext(response);
7447 if (!xpath_ctx) {
7448 err = IE_ERROR;
7449 goto leave;
7451 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7452 err = IE_ERROR;
7453 goto leave;
7455 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7457 /* Decode non-empty archive */
7458 if (string && string[0] != '\0') {
7459 *buffer_length = _isds_b64decode(string, buffer);
7460 if (*buffer_length == (size_t) -1) {
7461 isds_printf_message(context,
7462 _("Error while Base64-decoding box list archive"));
7463 err = IE_ERROR;
7464 goto leave;
7469 leave:
7470 free(string);
7471 xmlXPathFreeObject(result);
7472 xmlXPathFreeContext(xpath_ctx);
7473 xmlFreeDoc(response);
7474 xmlFreeNode(request);
7476 if (!err) {
7477 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7478 "processed by server successfully.\n"));
7480 #else /* not HAVE_LIBCURL */
7481 err = IE_NOTSUP;
7482 #endif
7484 return err;
7488 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7489 * send it, check for error code, extract list of results, destroy response
7490 * and log success.
7491 * @context is ISDS session context.
7492 * @service_name is name of SERVICE_DB_SEARCH service
7493 * @pfo_service is false if tDbOwnerInfo request should be built from
7494 * @criteria and corresponding result extracted. It is true if
7495 * tDbPersonalOwnerInfoRequest request should be built. The request and
7496 * response differ subset of significant isds_DbOwnerInfo structure members.
7497 * @criteria is filter. You should fill in at least some members.
7498 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7499 * members will be ignored. If @pfo_service is true, dbType, ic,
7500 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7501 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7502 * ignored.
7503 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7504 * possibly empty. Input NULL or valid old structure. The same memebers as
7505 * in described for @criteria argument will be NULL according to @pfo_service
7506 * switch.
7507 * @return:
7508 * IE_SUCCESS if search succeeded, @boxes contains useful data
7509 * IE_NOEXIST if no such box exists, @boxes will be NULL
7510 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7511 * contains still valid data
7512 * other code if something bad happens. @boxes will be NULL. */
7513 static isds_error build_send_findbox_request_check_parse_drop_response(
7514 struct isds_ctx *context, const xmlChar *service_name,
7515 _Bool pfo_service, const struct isds_DbOwnerInfo *criteria,
7516 struct isds_list **boxes) {
7517 isds_error err = IE_SUCCESS;
7518 #if HAVE_LIBCURL
7519 char *service_name_locale = NULL;
7520 _Bool truncated = 0;
7521 xmlNsPtr isds_ns = NULL;
7522 xmlNodePtr request = NULL;
7523 xmlDocPtr response = NULL;
7524 xmlChar *code = NULL, *message = NULL;
7525 xmlNodePtr db_owner_info;
7526 xmlXPathContextPtr xpath_ctx = NULL;
7527 xmlXPathObjectPtr result = NULL;
7528 xmlChar *string = NULL;
7529 #endif
7532 if (!context) return IE_INVALID_CONTEXT;
7533 zfree(context->long_message);
7534 if (!boxes) return IE_INVAL;
7535 isds_list_free(boxes);
7537 if (!criteria) {
7538 return IE_INVAL;
7541 #if HAVE_LIBCURL
7542 /* Check if connection is established
7543 * TODO: This check should be done downstairs. */
7544 if (!context->curl) return IE_CONNECTION_CLOSED;
7545 service_name_locale = _isds_utf82locale((char *) service_name);
7547 /* Build request */
7548 request = xmlNewNode(NULL, service_name);
7549 if (!request) {
7550 isds_printf_message(context, _("Could not build %s request"),
7551 service_name_locale);
7552 free(service_name_locale);
7553 return IE_ERROR;
7555 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7556 if(!isds_ns) {
7557 isds_log_message(context, _("Could not create ISDS name space"));
7558 free(service_name_locale);
7559 xmlFreeNode(request);
7560 return IE_ERROR;
7562 xmlSetNs(request, isds_ns);
7563 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7564 if (!db_owner_info) {
7565 isds_printf_message(context,
7566 _("Could not add dbOwnerInfo child to %s element"),
7567 service_name_locale);
7568 free(service_name_locale);
7569 xmlFreeNode(request);
7570 return IE_ERROR;
7573 err = insert_DbOwnerInfo(context, criteria, pfo_service, db_owner_info);
7574 if (err) goto leave;
7577 /* Send request */
7578 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
7579 service_name_locale);
7580 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7582 /* Destroy request */
7583 xmlFreeNode(request); request = NULL;
7585 if (err) {
7586 isds_log(ILF_ISDS, ILL_DEBUG,
7587 _("Processing ISDS response on %s request failed\n"),
7588 service_name_locale);
7589 goto leave;
7592 /* Check for response status */
7593 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7594 &code, &message, NULL);
7595 if (err) {
7596 isds_log(ILF_ISDS, ILL_DEBUG,
7597 _("ISDS response on %s request is missing status\n"),
7598 service_name_locale);
7599 goto leave;
7602 /* Request processed, but nothing found */
7603 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7604 !xmlStrcmp(code, BAD_CAST "5001")) {
7605 char *code_locale = _isds_utf82locale((char*)code);
7606 char *message_locale = _isds_utf82locale((char*)message);
7607 isds_log(ILF_ISDS, ILL_DEBUG,
7608 _("Server did not find any box on %s request "
7609 "(code=%s, message=%s)\n"), service_name_locale,
7610 code_locale, message_locale);
7611 isds_log_message(context, message_locale);
7612 free(code_locale);
7613 free(message_locale);
7614 err = IE_NOEXIST;
7615 goto leave;
7618 /* Warning, not an error */
7619 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7620 char *code_locale = _isds_utf82locale((char*)code);
7621 char *message_locale = _isds_utf82locale((char*)message);
7622 isds_log(ILF_ISDS, ILL_DEBUG,
7623 _("Server truncated response on %s request "
7624 "(code=%s, message=%s)\n"), service_name_locale,
7625 code_locale, message_locale);
7626 isds_log_message(context, message_locale);
7627 free(code_locale);
7628 free(message_locale);
7629 truncated = 1;
7632 /* Other error */
7633 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7634 char *code_locale = _isds_utf82locale((char*)code);
7635 char *message_locale = _isds_utf82locale((char*)message);
7636 isds_log(ILF_ISDS, ILL_DEBUG,
7637 _("Server refused %s request (code=%s, message=%s)\n"),
7638 service_name_locale, code_locale, message_locale);
7639 isds_log_message(context, message_locale);
7640 free(code_locale);
7641 free(message_locale);
7642 err = IE_ISDS;
7643 goto leave;
7646 xpath_ctx = xmlXPathNewContext(response);
7647 if (!xpath_ctx) {
7648 err = IE_ERROR;
7649 goto leave;
7651 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7652 err = IE_ERROR;
7653 goto leave;
7656 /* Extract boxes if they present */
7657 if (-1 == isds_asprintf((char **)&string,
7658 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7659 service_name)) {
7660 err = IE_NOMEM;
7661 goto leave;
7663 result = xmlXPathEvalExpression(string, xpath_ctx);
7664 zfree(string);
7665 if (!result) {
7666 err = IE_ERROR;
7667 goto leave;
7669 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7670 struct isds_list *item, *prev_item = NULL;
7671 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7672 item = calloc(1, sizeof(*item));
7673 if (!item) {
7674 err = IE_NOMEM;
7675 goto leave;
7678 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7679 if (i == 0) *boxes = item;
7680 else prev_item->next = item;
7681 prev_item = item;
7683 xpath_ctx->node = result->nodesetval->nodeTab[i];
7684 err = extract_DbOwnerInfo(context,
7685 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7686 if (err) goto leave;
7690 leave:
7691 if (err) {
7692 isds_list_free(boxes);
7693 } else {
7694 if (truncated) err = IE_2BIG;
7697 free(string);
7698 xmlFreeNode(request);
7699 xmlXPathFreeObject(result);
7700 xmlXPathFreeContext(xpath_ctx);
7702 free(code);
7703 free(message);
7704 xmlFreeDoc(response);
7706 if (!err)
7707 isds_log(ILF_ISDS, ILL_DEBUG,
7708 _("%s request processed by server successfully.\n"),
7709 service_name_locale);
7710 free(service_name_locale);
7711 #else /* not HAVE_LIBCURL */
7712 err = IE_NOTSUP;
7713 #endif
7715 return err;
7719 /* Find boxes suiting given criteria.
7720 * @criteria is filter. You should fill in at least some members. aifoIsds,
7721 * address->adCode, address->adDistrict members are ignored.
7722 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7723 * possibly empty. Input NULL or valid old structure.
7724 * @return:
7725 * IE_SUCCESS if search succeeded, @boxes contains useful data
7726 * IE_NOEXIST if no such box exists, @boxes will be NULL
7727 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7728 * contains still valid data
7729 * other code if something bad happens. @boxes will be NULL. */
7730 isds_error isds_FindDataBox(struct isds_ctx *context,
7731 const struct isds_DbOwnerInfo *criteria,
7732 struct isds_list **boxes) {
7733 return build_send_findbox_request_check_parse_drop_response(context,
7734 BAD_CAST "FindDataBox", 0, criteria, boxes);
7738 /* Find accessible FO-type boxes suiting given criteria.
7739 * @criteria is filter. You should fill in at least some members. dbType, ic,
7740 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7741 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7742 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7743 * possibly empty. Input NULL or valid old structure.
7744 * @return:
7745 * IE_SUCCESS if search succeeded, @boxes contains useful data
7746 * IE_NOEXIST if no such box exists, @boxes will be NULL
7747 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7748 * contains still valid data
7749 * other code if something bad happens. @boxes will be NULL. */
7750 isds_error isds_FindPersonalDataBox(struct isds_ctx *context,
7751 const struct isds_DbOwnerInfo *criteria,
7752 struct isds_list **boxes) {
7753 return build_send_findbox_request_check_parse_drop_response(context,
7754 BAD_CAST "FindPersonalDataBox", 1, criteria, boxes);
7758 #if HAVE_LIBCURL
7759 /* Convert a string with match markers into a plain string with list of
7760 * pointers to the matches
7761 * @string is an UTF-8 encoded non-constant string with match markers
7762 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7763 * The markers will be removed from the string.
7764 * @starts is a reallocated list of static pointers into the @string pointing
7765 * to places where match start markers occured.
7766 * @ends is a reallocated list of static pointers into the @string pointing
7767 * to places where match end markers occured.
7768 * @return IE_SUCCESS in case of no failure. */
7769 static isds_error interpret_matches(xmlChar *string,
7770 struct isds_list **starts, struct isds_list **ends) {
7771 isds_error err = IE_SUCCESS;
7772 xmlChar *pointer, *destination, *source;
7773 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7775 isds_list_free(starts);
7776 isds_list_free(ends);
7777 if (NULL == starts || NULL == ends) return IE_INVAL;
7778 if (NULL == string) return IE_SUCCESS;
7780 for (pointer = string; *pointer != '\0';) {
7781 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7782 /* Remove the start marker */
7783 for (source = pointer + 14, destination = pointer;
7784 *source != '\0'; source++, destination++) {
7785 *destination = *source;
7787 *destination = '\0';
7788 /* Append the pointer into the list */
7789 item = calloc(1, sizeof(*item));
7790 if (!item) {
7791 err = IE_NOMEM;
7792 goto leave;
7794 item->destructor = (void (*)(void **))NULL;
7795 item->data = pointer;
7796 if (NULL == prev_start) *starts = item;
7797 else prev_start->next = item;
7798 prev_start = item;
7799 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7800 /* Remove the end marker */
7801 for (source = pointer + 12, destination = pointer;
7802 *source != '\0'; source++, destination++) {
7803 *destination = *source;
7805 *destination = '\0';
7806 /* Append the pointer into the list */
7807 item = calloc(1, sizeof(*item));
7808 if (!item) {
7809 err = IE_NOMEM;
7810 goto leave;
7812 item->destructor = (void (*)(void **))NULL;
7813 item->data = pointer;
7814 if (NULL == prev_end) *ends = item;
7815 else prev_end->next = item;
7816 prev_end = item;
7817 } else {
7818 pointer++;
7822 leave:
7823 if (err) {
7824 isds_list_free(starts);
7825 isds_list_free(ends);
7827 return err;
7831 /* Convert isds:dbResult XML tree into structure
7832 * @context is ISDS context.
7833 * @fulltext_result is automatically reallocated found box structure.
7834 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7835 * @collect_matches is true to interpret match markers.
7836 * In case of error @result will be freed. */
7837 static isds_error extract_dbResult(struct isds_ctx *context,
7838 struct isds_fulltext_result **fulltext_result,
7839 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7840 isds_error err = IE_SUCCESS;
7841 xmlXPathObjectPtr result = NULL;
7842 char *string = NULL;
7844 if (NULL == context) return IE_INVALID_CONTEXT;
7845 if (NULL == fulltext_result) return IE_INVAL;
7846 isds_fulltext_result_free(fulltext_result);
7847 if (!xpath_ctx) return IE_INVAL;
7850 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7851 if (NULL == *fulltext_result) {
7852 err = IE_NOMEM;
7853 goto leave;
7856 /* Extract data */
7857 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7859 EXTRACT_STRING("isds:dbType", string);
7860 if (NULL == string) {
7861 err = IE_ISDS;
7862 isds_log_message(context, _("Empty isds:dbType element"));
7863 goto leave;
7865 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7866 if (err) {
7867 if (err == IE_ENUM) {
7868 err = IE_ISDS;
7869 char *string_locale = _isds_utf82locale(string);
7870 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7871 string_locale);
7872 free(string_locale);
7874 goto leave;
7876 zfree(string);
7878 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7879 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7881 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7882 if (err) goto leave;
7884 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7885 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7886 (*fulltext_result)->dbEffectiveOVM);
7888 EXTRACT_STRING("isds:dbSendOptions", string);
7889 if (NULL == string) {
7890 err = IE_ISDS;
7891 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7892 goto leave;
7894 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7895 (*fulltext_result)->active = 1;
7896 (*fulltext_result)->public_sending = 1;
7897 (*fulltext_result)->commercial_sending = 0;
7898 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7899 (*fulltext_result)->active = 1;
7900 (*fulltext_result)->public_sending = 1;
7901 (*fulltext_result)->commercial_sending = 1;
7902 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7903 (*fulltext_result)->active = 1;
7904 (*fulltext_result)->public_sending = 0;
7905 (*fulltext_result)->commercial_sending = 1;
7906 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7907 (*fulltext_result)->active = 1;
7908 (*fulltext_result)->public_sending = 0;
7909 (*fulltext_result)->commercial_sending = 0;
7910 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7911 (*fulltext_result)->active = 0;
7912 (*fulltext_result)->public_sending = 0;
7913 (*fulltext_result)->commercial_sending = 0;
7914 } else {
7915 err = IE_ISDS;
7916 char *string_locale = _isds_utf82locale(string);
7917 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7918 string_locale);
7919 free(string_locale);
7920 goto leave;
7922 zfree(string);
7924 /* Interpret match marks */
7925 if (collect_matches) {
7926 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7927 &((*fulltext_result)->name_match_start),
7928 &((*fulltext_result)->name_match_end));
7929 if (err) goto leave;
7930 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7931 &((*fulltext_result)->address_match_start),
7932 &((*fulltext_result)->address_match_end));
7933 if (err) goto leave;
7936 leave:
7937 if (err) isds_fulltext_result_free(fulltext_result);
7938 free(string);
7939 xmlXPathFreeObject(result);
7940 return err;
7942 #endif /* HAVE_LIBCURL */
7945 /* Find boxes matching a given full-text criteria.
7946 * @context is a session context
7947 * @query is a non-empty string which consists of words to search
7948 * @target selects box attributes to search for @query words. Pass NULL if you
7949 * don't care.
7950 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7951 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7952 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7953 * which is DBTYPE_SYSTEM.
7954 * @page_size defines count of boxes to constitute a response page. It counts
7955 * from zero. Pass NULL to let server to use a default value (50 now).
7956 * @page_number defines ordinar number of the response page to return. It
7957 * counts from zero. Pass NULL to let server to use a default value (0 now).
7958 * @track_matches points to true for marking @query words found in the box
7959 * attributes. It points to false for not marking. Pass NULL to let the server
7960 * to use default value (false now).
7961 * @total_matching_boxes outputs reallocated number of all boxes matching the
7962 * query. Will be pointer to NULL if server did not provide the value.
7963 * Pass NULL if you don't care.
7964 * @current_page_beginning outputs reallocated ordinar number of the first box
7965 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7966 * server did not provide the value. Pass NULL if you don't care.
7967 * @current_page_size outputs reallocated count of boxes in the this @boxes
7968 * page. It will be pointer to NULL if the server did not provide the value.
7969 * Pass NULL if you don't care.
7970 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7971 * is the last one, false if more boxes match, NULL if the server did not
7972 * provude the value. Pass NULL if you don't care.
7973 * @boxes outputs reallocated list of isds_fulltext_result structures,
7974 * possibly empty.
7975 * @return:
7976 * IE_SUCCESS if search succeeded
7977 * IE_2BIG if @page_size is too large
7978 * other code if something bad happens; output arguments will be NULL. */
7979 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7980 const char *query,
7981 const isds_fulltext_target *target,
7982 const isds_DbType *box_type,
7983 const unsigned long int *page_size,
7984 const unsigned long int *page_number,
7985 const _Bool *track_matches,
7986 unsigned long int **total_matching_boxes,
7987 unsigned long int **current_page_beginning,
7988 unsigned long int **current_page_size,
7989 _Bool **last_page,
7990 struct isds_list **boxes) {
7991 isds_error err = IE_SUCCESS;
7992 #if HAVE_LIBCURL
7993 xmlNsPtr isds_ns = NULL;
7994 xmlNodePtr request = NULL;
7995 xmlDocPtr response = NULL;
7996 xmlNodePtr node;
7997 xmlXPathContextPtr xpath_ctx = NULL;
7998 xmlXPathObjectPtr result = NULL;
7999 const xmlChar *static_string = NULL;
8000 xmlChar *string = NULL;
8002 const xmlChar *codes[] = {
8003 BAD_CAST "1004",
8004 BAD_CAST "1152",
8005 BAD_CAST "1153",
8006 BAD_CAST "1154",
8007 BAD_CAST "1155",
8008 BAD_CAST "1156",
8009 BAD_CAST "9002",
8010 NULL
8012 const char *meanings[] = {
8013 N_("You are not allowed to perform the search"),
8014 N_("The query string is empty"),
8015 N_("Searched box ID is malformed"),
8016 N_("Searched organization ID is malformed"),
8017 N_("Invalid input"),
8018 N_("Requested page size is too large"),
8019 N_("Search engine internal error")
8021 const isds_error errors[] = {
8022 IE_ISDS,
8023 IE_INVAL,
8024 IE_INVAL,
8025 IE_INVAL,
8026 IE_INVAL,
8027 IE_2BIG,
8028 IE_ISDS
8030 struct code_map_isds_error map = {
8031 .codes = codes,
8032 .meanings = meanings,
8033 .errors = errors
8035 #endif
8038 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
8039 if (NULL != current_page_beginning) zfree(*current_page_beginning);
8040 if (NULL != current_page_size) zfree(*current_page_size);
8041 if (NULL != last_page) zfree(*last_page);
8042 isds_list_free(boxes);
8044 if (NULL == context) return IE_INVALID_CONTEXT;
8045 zfree(context->long_message);
8047 if (NULL == boxes) return IE_INVAL;
8049 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
8050 isds_log_message(context, _("Query string must be non-empty"));
8051 return IE_INVAL;
8054 #if HAVE_LIBCURL
8055 /* Check if connection is established
8056 * TODO: This check should be done downstairs. */
8057 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8059 /* Build FindDataBox request */
8060 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
8061 if (NULL == request) {
8062 isds_log_message(context,
8063 _("Could not build ISDSSearch2 request"));
8064 return IE_ERROR;
8066 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8067 if(NULL == isds_ns) {
8068 isds_log_message(context, _("Could not create ISDS name space"));
8069 xmlFreeNode(request);
8070 return IE_ERROR;
8072 xmlSetNs(request, isds_ns);
8074 INSERT_STRING(request, "searchText", query);
8076 if (NULL != target) {
8077 static_string = isds_fulltext_target2string(*(target));
8078 if (NULL == static_string) {
8079 isds_printf_message(context, _("Invalid target value: %d"),
8080 *(target));
8081 err = IE_ENUM;
8082 goto leave;
8085 INSERT_STRING(request, "searchType", static_string);
8086 static_string = NULL;
8088 if (NULL != box_type) {
8089 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
8090 if (DBTYPE_SYSTEM == *box_type) {
8091 static_string = BAD_CAST "ALL";
8092 } else if (DBTYPE_OVM_MAIN == *box_type) {
8093 static_string = BAD_CAST "OVM_MAIN";
8094 } else {
8095 static_string = isds_DbType2string(*(box_type));
8096 if (NULL == static_string) {
8097 isds_printf_message(context, _("Invalid box type value: %d"),
8098 *(box_type));
8099 err = IE_ENUM;
8100 goto leave;
8104 INSERT_STRING(request, "searchScope", static_string);
8105 static_string = NULL;
8107 INSERT_ULONGINT(request, "page", page_number, string);
8108 INSERT_ULONGINT(request, "pageSize", page_size, string);
8109 INSERT_BOOLEAN(request, "highlighting", track_matches);
8111 /* Send request and check response */
8112 err = send_destroy_request_check_response(context,
8113 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
8114 &request, &response, NULL, &map);
8115 if (err) goto leave;
8117 /* Parse response */
8118 xpath_ctx = xmlXPathNewContext(response);
8119 if (NULL == xpath_ctx) {
8120 err = IE_ERROR;
8121 goto leave;
8123 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8124 err = IE_ERROR;
8125 goto leave;
8127 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
8128 xpath_ctx);
8129 if (!result) {
8130 err = IE_ERROR;
8131 goto leave;
8133 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8134 isds_log_message(context, _("Missing ISDSSearch2 element"));
8135 err = IE_ISDS;
8136 goto leave;
8138 if (result->nodesetval->nodeNr > 1) {
8139 isds_log_message(context, _("Multiple ISDSSearch2 element"));
8140 err = IE_ISDS;
8141 goto leave;
8143 xpath_ctx->node = result->nodesetval->nodeTab[0];
8144 xmlXPathFreeObject(result); result = NULL;
8147 /* Extract counters */
8148 if (NULL != total_matching_boxes) {
8149 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
8151 if (NULL != current_page_size) {
8152 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
8154 if (NULL != current_page_beginning) {
8155 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
8157 if (NULL != last_page) {
8158 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
8160 xmlXPathFreeObject(result); result = NULL;
8162 /* Extract boxes if they present */
8163 result = xmlXPathEvalExpression(BAD_CAST
8164 "isds:dbResults/isds:dbResult", xpath_ctx);
8165 if (NULL == result) {
8166 err = IE_ERROR;
8167 goto leave;
8169 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8170 struct isds_list *item, *prev_item = NULL;
8171 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8172 item = calloc(1, sizeof(*item));
8173 if (!item) {
8174 err = IE_NOMEM;
8175 goto leave;
8178 item->destructor = (void (*)(void **))isds_fulltext_result_free;
8179 if (i == 0) *boxes = item;
8180 else prev_item->next = item;
8181 prev_item = item;
8183 xpath_ctx->node = result->nodesetval->nodeTab[i];
8184 err = extract_dbResult(context,
8185 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
8186 (NULL == track_matches) ? 0 : *track_matches);
8187 if (err) goto leave;
8191 leave:
8192 if (err) {
8193 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
8194 if (NULL != current_page_beginning) zfree(*current_page_beginning);
8195 if (NULL != current_page_size) zfree(*current_page_size);
8196 if (NULL != last_page) zfree(*last_page);
8197 isds_list_free(boxes);
8200 free(string);
8201 xmlFreeNode(request);
8202 xmlXPathFreeObject(result);
8203 xmlXPathFreeContext(xpath_ctx);
8204 xmlFreeDoc(response);
8206 if (!err)
8207 isds_log(ILF_ISDS, ILL_DEBUG,
8208 _("ISDSSearch2 request processed by server successfully.\n"));
8209 #else /* not HAVE_LIBCURL */
8210 err = IE_NOTSUP;
8211 #endif
8213 return err;
8217 /* Get status of a box.
8218 * @context is ISDS session context.
8219 * @box_id is UTF-8 encoded box identifier as zero terminated string
8220 * @box_status is return value of box status.
8221 * @return:
8222 * IE_SUCCESS if box has been found and its status retrieved
8223 * IE_NOEXIST if box is not known to ISDS server
8224 * or other appropriate error.
8225 * You can use isds_DbState to enumerate box status. However out of enum
8226 * range value can be returned too. This is feature because ISDS
8227 * specification leaves the set of values open.
8228 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8229 * the box has been deleted, but ISDS still lists its former existence. */
8230 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
8231 long int *box_status) {
8232 isds_error err = IE_SUCCESS;
8233 #if HAVE_LIBCURL
8234 xmlNsPtr isds_ns = NULL;
8235 xmlNodePtr request = NULL, db_id;
8236 xmlDocPtr response = NULL;
8237 xmlXPathContextPtr xpath_ctx = NULL;
8238 xmlXPathObjectPtr result = NULL;
8239 xmlChar *string = NULL;
8241 const xmlChar *codes[] = {
8242 BAD_CAST "5001",
8243 BAD_CAST "1007",
8244 BAD_CAST "2011",
8245 NULL
8247 const char *meanings[] = {
8248 "The box does not exist",
8249 "Box ID is malformed",
8250 "Box ID malformed",
8252 const isds_error errors[] = {
8253 IE_NOEXIST,
8254 IE_INVAL,
8255 IE_INVAL,
8257 struct code_map_isds_error map = {
8258 .codes = codes,
8259 .meanings = meanings,
8260 .errors = errors
8262 #endif
8264 if (!context) return IE_INVALID_CONTEXT;
8265 zfree(context->long_message);
8266 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
8268 #if HAVE_LIBCURL
8269 /* Check if connection is established
8270 * TODO: This check should be done downstairs. */
8271 if (!context->curl) return IE_CONNECTION_CLOSED;
8274 /* Build CheckDataBox request */
8275 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
8276 if (!request) {
8277 isds_log_message(context,
8278 _("Could build CheckDataBox request"));
8279 return IE_ERROR;
8281 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8282 if(!isds_ns) {
8283 isds_log_message(context, _("Could not create ISDS name space"));
8284 xmlFreeNode(request);
8285 return IE_ERROR;
8287 xmlSetNs(request, isds_ns);
8288 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
8289 if (!db_id) {
8290 isds_log_message(context, _("Could not add dbID child to "
8291 "CheckDataBox element"));
8292 xmlFreeNode(request);
8293 return IE_ERROR;
8297 /* Send request and check response*/
8298 err = send_destroy_request_check_response(context,
8299 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
8300 &request, &response, NULL, &map);
8301 if (err) goto leave;
8304 /* Extract data */
8305 xpath_ctx = xmlXPathNewContext(response);
8306 if (!xpath_ctx) {
8307 err = IE_ERROR;
8308 goto leave;
8310 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8311 err = IE_ERROR;
8312 goto leave;
8314 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
8315 xpath_ctx);
8316 if (!result) {
8317 err = IE_ERROR;
8318 goto leave;
8320 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8321 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
8322 err = IE_ISDS;
8323 goto leave;
8325 if (result->nodesetval->nodeNr > 1) {
8326 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
8327 err = IE_ISDS;
8328 goto leave;
8330 xpath_ctx->node = result->nodesetval->nodeTab[0];
8331 xmlXPathFreeObject(result); result = NULL;
8333 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8336 leave:
8337 free(string);
8338 xmlXPathFreeObject(result);
8339 xmlXPathFreeContext(xpath_ctx);
8341 xmlFreeDoc(response);
8343 if (!err)
8344 isds_log(ILF_ISDS, ILL_DEBUG,
8345 _("CheckDataBox request processed by server successfully.\n"));
8346 #else /* not HAVE_LIBCURL */
8347 err = IE_NOTSUP;
8348 #endif
8350 return err;
8354 #if HAVE_LIBCURL
8355 /* Convert XSD:tdbPeriod XML tree into structure
8356 * @context is ISDS context.
8357 * @period is automatically reallocated found box status period structure.
8358 * @xpath_ctx is XPath context with current node as element of
8359 * XSD:tDbPeriod type.
8360 * In case of error @period will be freed. */
8361 static isds_error extract_Period(struct isds_ctx *context,
8362 struct isds_box_state_period **period, xmlXPathContextPtr xpath_ctx) {
8363 isds_error err = IE_SUCCESS;
8364 xmlXPathObjectPtr result = NULL;
8365 char *string = NULL;
8366 long int *dbState_ptr;
8368 if (NULL == context) return IE_INVALID_CONTEXT;
8369 if (NULL == period) return IE_INVAL;
8370 isds_box_state_period_free(period);
8371 if (!xpath_ctx) return IE_INVAL;
8374 *period = calloc(1, sizeof(**period));
8375 if (NULL == *period) {
8376 err = IE_NOMEM;
8377 goto leave;
8380 /* Extract data */
8381 EXTRACT_STRING("isds:PeriodFrom", string);
8382 if (NULL == string) {
8383 err = IE_XML;
8384 isds_log_message(context,
8385 _("Could not find PeriodFrom element value"));
8386 goto leave;
8388 err = timestring2static_timeval((xmlChar *) string,
8389 &((*period)->from));
8390 if (err) {
8391 char *string_locale = _isds_utf82locale(string);
8392 if (err == IE_DATE) err = IE_ISDS;
8393 isds_printf_message(context,
8394 _("Could not convert PeriodFrom as ISO time: %s"),
8395 string_locale);
8396 free(string_locale);
8397 goto leave;
8399 zfree(string);
8401 EXTRACT_STRING("isds:PeriodTo", string);
8402 if (NULL == string) {
8403 err = IE_XML;
8404 isds_log_message(context,
8405 _("Could not find PeriodTo element value"));
8406 goto leave;
8408 err = timestring2static_timeval((xmlChar *) string,
8409 &((*period)->to));
8410 if (err) {
8411 char *string_locale = _isds_utf82locale(string);
8412 if (err == IE_DATE) err = IE_ISDS;
8413 isds_printf_message(context,
8414 _("Could not convert PeriodTo as ISO time: %s"),
8415 string_locale);
8416 free(string_locale);
8417 goto leave;
8419 zfree(string);
8421 dbState_ptr = &((*period)->dbState);
8422 EXTRACT_LONGINT("isds:DbState", dbState_ptr, 1);
8424 leave:
8425 if (err) isds_box_state_period_free(period);
8426 free(string);
8427 xmlXPathFreeObject(result);
8428 return err;
8430 #endif /* HAVE_LIBCURL */
8433 /* Get history of box state changes.
8434 * @context is ISDS session context.
8435 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8436 * @from_time is first second of history to return in @history. Server ignores
8437 * subseconds. NULL means time of creating the box.
8438 * @to_time is last second of history to return in @history. Server ignores
8439 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8440 * interval is closed from both ends. NULL means now.
8441 * @history outputs auto-reallocated list of pointers to struct
8442 * isds_box_state_period. Each item describes a continues time when the box
8443 * was in one state. The state is 1 for accessible box. Otherwise the box
8444 * is inaccessible (priviledged users will get exact box state as enumerated
8445 * in isds_DbState, other users 0).
8446 * @return:
8447 * IE_SUCCESS if the history has been obtained correctly,
8448 * or other appropriate error. Please note that server allows to retrieve
8449 * the history only to some users. */
8450 isds_error isds_get_box_state_history(struct isds_ctx *context,
8451 const char *box_id,
8452 const struct timeval *from_time, const struct timeval *to_time,
8453 struct isds_list **history) {
8454 isds_error err = IE_SUCCESS;
8455 #if HAVE_LIBCURL
8456 char *box_id_locale = NULL;
8457 xmlNodePtr request = NULL, node;
8458 xmlNsPtr isds_ns = NULL;
8459 xmlChar *string = NULL;
8461 xmlDocPtr response = NULL;
8462 xmlXPathContextPtr xpath_ctx = NULL;
8463 xmlXPathObjectPtr result = NULL;
8464 #endif
8466 if (!context) return IE_INVALID_CONTEXT;
8467 zfree(context->long_message);
8469 /* Free output argument */
8470 isds_list_free(history);
8472 #if HAVE_LIBCURL
8473 /* Check if connection is established */
8474 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8476 /* ??? XML schema allows empty box ID, textual documentation
8477 * requries the value. */
8478 /* Allow undefined box_id */
8479 if (NULL != box_id) {
8480 box_id_locale = _isds_utf82locale((char*)box_id);
8481 if (NULL == box_id_locale) {
8482 err = IE_NOMEM;
8483 goto leave;
8487 /* Build request */
8488 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxActivityStatus");
8489 if (NULL == request) {
8490 isds_printf_message(context,
8491 _("Could not build GetDataBoxActivityStatus request "
8492 "for %s box"),
8493 box_id_locale);
8494 err = IE_ERROR;
8495 goto leave;
8497 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8498 if(!isds_ns) {
8499 isds_log_message(context, _("Could not create ISDS name space"));
8500 err = IE_ERROR;
8501 goto leave;
8503 xmlSetNs(request, isds_ns);
8505 /* Add mandatory XSD:tIdDbInput child */
8506 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8507 /* Add times elements only when defined */
8508 /* ???: XML schema requires the values, textual documentation does not. */
8509 if (from_time) {
8510 err = timeval2timestring(from_time, &string);
8511 if (err) {
8512 isds_log_message(context,
8513 _("Could not convert `from_time' argument to ISO time "
8514 "string"));
8515 goto leave;
8517 INSERT_STRING(request, "baFrom", string);
8518 zfree(string);
8520 if (to_time) {
8521 err = timeval2timestring(to_time, &string);
8522 if (err) {
8523 isds_log_message(context,
8524 _("Could not convert `to_time' argument to ISO time "
8525 "string"));
8526 goto leave;
8528 INSERT_STRING(request, "baTo", string);
8529 zfree(string);
8532 /* Send request and check response*/
8533 err = send_destroy_request_check_response(context,
8534 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxActivityStatus",
8535 &request, &response, NULL, NULL);
8536 if (err) goto leave;
8539 /* Extract data */
8540 /* Set context to the root */
8541 xpath_ctx = xmlXPathNewContext(response);
8542 if (!xpath_ctx) {
8543 err = IE_ERROR;
8544 goto leave;
8546 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8547 err = IE_ERROR;
8548 goto leave;
8550 result = xmlXPathEvalExpression(BAD_CAST "/isds:GetDataBoxActivityStatusResponse",
8551 xpath_ctx);
8552 if (!result) {
8553 err = IE_ERROR;
8554 goto leave;
8556 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8557 isds_log_message(context, _("Missing GetDataBoxActivityStatusResponse element"));
8558 err = IE_ISDS;
8559 goto leave;
8561 if (result->nodesetval->nodeNr > 1) {
8562 isds_log_message(context, _("Multiple GetDataBoxActivityStatusResponse element"));
8563 err = IE_ISDS;
8564 goto leave;
8566 xpath_ctx->node = result->nodesetval->nodeTab[0];
8567 xmlXPathFreeObject(result); result = NULL;
8569 /* Ignore dbID, it's the same as the input argument. */
8571 /* Extract records */
8572 if (NULL == history) goto leave;
8573 result = xmlXPathEvalExpression(BAD_CAST "isds:Periods/isds:Period",
8574 xpath_ctx);
8575 if (!result) {
8576 err = IE_ERROR;
8577 goto leave;
8579 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8580 struct isds_list *prev_item = NULL;
8582 /* Iterate over all records */
8583 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8584 struct isds_list *item;
8586 /* Prepare structure */
8587 item = calloc(1, sizeof(*item));
8588 if (!item) {
8589 err = IE_NOMEM;
8590 goto leave;
8592 item->destructor = (void(*)(void**))isds_box_state_period_free;
8593 if (i == 0) *history = item;
8594 else prev_item->next = item;
8595 prev_item = item;
8597 /* Extract it */
8598 xpath_ctx->node = result->nodesetval->nodeTab[i];
8599 err = extract_Period(context,
8600 (struct isds_box_state_period **) (&item->data),
8601 xpath_ctx);
8602 if (err) goto leave;
8606 leave:
8607 if (!err) {
8608 isds_log(ILF_ISDS, ILL_DEBUG,
8609 _("GetDataBoxActivityStatus request for %s box "
8610 "processed by server successfully.\n"), box_id_locale);
8612 if (err) {
8613 isds_list_free(history);
8616 free(box_id_locale);
8617 xmlXPathFreeObject(result);
8618 xmlXPathFreeContext(xpath_ctx);
8619 xmlFreeDoc(response);
8621 #else /* not HAVE_LIBCURL */
8622 err = IE_NOTSUP;
8623 #endif
8625 return err;
8629 /* Get list of permissions to send commercial messages.
8630 * @context is ISDS session context.
8631 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8632 * @permissions is a reallocated list of permissions (struct
8633 * isds_commercial_permission*) to send commercial messages from @box_id. The
8634 * order of permissions is significant as the server applies the permissions
8635 * and associated pre-paid credits in the order. Empty list means no
8636 * permission.
8637 * @return:
8638 * IE_SUCCESS if the list has been obtained correctly,
8639 * or other appropriate error. */
8640 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8641 const char *box_id, struct isds_list **permissions) {
8642 isds_error err = IE_SUCCESS;
8643 #if HAVE_LIBCURL
8644 xmlDocPtr response = NULL;
8645 xmlXPathContextPtr xpath_ctx = NULL;
8646 xmlXPathObjectPtr result = NULL;
8647 #endif
8649 if (!context) return IE_INVALID_CONTEXT;
8650 zfree(context->long_message);
8651 if (NULL == permissions) return IE_INVAL;
8652 isds_list_free(permissions);
8653 if (NULL == box_id) return IE_INVAL;
8655 #if HAVE_LIBCURL
8656 /* Check if connection is established */
8657 if (!context->curl) return IE_CONNECTION_CLOSED;
8659 /* Do request and check for success */
8660 err = build_send_dbid_request_check_response(context,
8661 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8662 BAD_CAST box_id, NULL, &response, NULL);
8663 if (!err) {
8664 isds_log(ILF_ISDS, ILL_DEBUG,
8665 _("PDZInfo request processed by server successfully.\n"));
8668 /* Extract data */
8669 /* Prepare structure */
8670 xpath_ctx = xmlXPathNewContext(response);
8671 if (!xpath_ctx) {
8672 err = IE_ERROR;
8673 goto leave;
8675 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8676 err = IE_ERROR;
8677 goto leave;
8680 /* Set context node */
8681 result = xmlXPathEvalExpression(BAD_CAST
8682 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8683 xpath_ctx);
8684 if (!result) {
8685 err = IE_ERROR;
8686 goto leave;
8688 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8689 struct isds_list *prev_item = NULL;
8691 /* Iterate over all permission records */
8692 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8693 struct isds_list *item;
8695 /* Prepare structure */
8696 item = calloc(1, sizeof(*item));
8697 if (!item) {
8698 err = IE_NOMEM;
8699 goto leave;
8701 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8702 if (i == 0) *permissions = item;
8703 else prev_item->next = item;
8704 prev_item = item;
8706 /* Extract it */
8707 xpath_ctx->node = result->nodesetval->nodeTab[i];
8708 err = extract_DbPDZRecord(context,
8709 (struct isds_commercial_permission **) (&item->data),
8710 xpath_ctx);
8711 if (err) goto leave;
8715 leave:
8716 if (err) {
8717 isds_list_free(permissions);
8720 xmlXPathFreeObject(result);
8721 xmlXPathFreeContext(xpath_ctx);
8722 xmlFreeDoc(response);
8724 #else /* not HAVE_LIBCURL */
8725 err = IE_NOTSUP;
8726 #endif
8728 return err;
8732 /* Get details about credit for sending pre-paid commercial messages.
8733 * @context is ISDS session context.
8734 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8735 * @from_date is first day of credit history to return in @history. Only
8736 * tm_year, tm_mon and tm_mday carry sane value.
8737 * @to_date is last day of credit history to return in @history. Only
8738 * tm_year, tm_mon and tm_mday carry sane value.
8739 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8740 * if you don't care. This and all other credit values are integers in
8741 * hundredths of Czech Crowns.
8742 * @email outputs notification e-mail address where notifications about credit
8743 * are sent. This is automatically reallocated string. Pass NULL if you don't
8744 * care. It can return NULL if no address is defined.
8745 * @history outputs auto-reallocated list of pointers to struct
8746 * isds_credit_event. Events in closed interval @from_time to @to_time are
8747 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8748 * are sorted by time.
8749 * @return:
8750 * IE_SUCCESS if the credit details have been obtained correctly,
8751 * or other appropriate error. Please note that server allows to retrieve
8752 * only limited history of events. */
8753 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8754 const char *box_id,
8755 const struct tm *from_date, const struct tm *to_date,
8756 long int *credit, char **email, struct isds_list **history) {
8757 isds_error err = IE_SUCCESS;
8758 #if HAVE_LIBCURL
8759 char *box_id_locale = NULL;
8760 xmlNodePtr request = NULL, node;
8761 xmlNsPtr isds_ns = NULL;
8762 xmlChar *string = NULL;
8764 xmlDocPtr response = NULL;
8765 xmlXPathContextPtr xpath_ctx = NULL;
8766 xmlXPathObjectPtr result = NULL;
8768 const xmlChar *codes[] = {
8769 BAD_CAST "1004",
8770 BAD_CAST "2011",
8771 BAD_CAST "1093",
8772 BAD_CAST "1137",
8773 BAD_CAST "1058",
8774 NULL
8776 const char *meanings[] = {
8777 "Insufficient priviledges for the box",
8778 "The box does not exist",
8779 "Date is too long (history is not available after 15 months)",
8780 "Interval is too long (limit is 3 months)",
8781 "Invalid date"
8783 const isds_error errors[] = {
8784 IE_ISDS,
8785 IE_NOEXIST,
8786 IE_DATE,
8787 IE_DATE,
8788 IE_DATE,
8790 struct code_map_isds_error map = {
8791 .codes = codes,
8792 .meanings = meanings,
8793 .errors = errors
8795 #endif
8797 if (!context) return IE_INVALID_CONTEXT;
8798 zfree(context->long_message);
8800 /* Free output argument */
8801 if (NULL != credit) *credit = 0;
8802 if (NULL != email) zfree(*email);
8803 isds_list_free(history);
8805 if (NULL == box_id) return IE_INVAL;
8807 #if HAVE_LIBCURL
8808 /* Check if connection is established */
8809 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8811 box_id_locale = _isds_utf82locale((char*)box_id);
8812 if (NULL == box_id_locale) {
8813 err = IE_NOMEM;
8814 goto leave;
8817 /* Build request */
8818 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8819 if (NULL == request) {
8820 isds_printf_message(context,
8821 _("Could not build DataBoxCreditInfo request for %s box"),
8822 box_id_locale);
8823 err = IE_ERROR;
8824 goto leave;
8826 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8827 if(!isds_ns) {
8828 isds_log_message(context, _("Could not create ISDS name space"));
8829 err = IE_ERROR;
8830 goto leave;
8832 xmlSetNs(request, isds_ns);
8834 /* Add mandatory XSD:tIdDbInput child */
8835 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8836 /* Add mandatory dates elements with optional values */
8837 if (from_date) {
8838 err = tm2datestring(from_date, &string);
8839 if (err) {
8840 isds_log_message(context,
8841 _("Could not convert `from_date' argument to ISO date "
8842 "string"));
8843 goto leave;
8845 INSERT_STRING(request, "ciFromDate", string);
8846 zfree(string);
8847 } else {
8848 INSERT_STRING(request, "ciFromDate", NULL);
8850 if (to_date) {
8851 err = tm2datestring(to_date, &string);
8852 if (err) {
8853 isds_log_message(context,
8854 _("Could not convert `to_date' argument to ISO date "
8855 "string"));
8856 goto leave;
8858 INSERT_STRING(request, "ciTodate", string);
8859 zfree(string);
8860 } else {
8861 INSERT_STRING(request, "ciTodate", NULL);
8864 /* Send request and check response*/
8865 err = send_destroy_request_check_response(context,
8866 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8867 &request, &response, NULL, &map);
8868 if (err) goto leave;
8871 /* Extract data */
8872 /* Set context to the root */
8873 xpath_ctx = xmlXPathNewContext(response);
8874 if (!xpath_ctx) {
8875 err = IE_ERROR;
8876 goto leave;
8878 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8879 err = IE_ERROR;
8880 goto leave;
8882 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8883 xpath_ctx);
8884 if (!result) {
8885 err = IE_ERROR;
8886 goto leave;
8888 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8889 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8890 err = IE_ISDS;
8891 goto leave;
8893 if (result->nodesetval->nodeNr > 1) {
8894 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8895 err = IE_ISDS;
8896 goto leave;
8898 xpath_ctx->node = result->nodesetval->nodeTab[0];
8899 xmlXPathFreeObject(result); result = NULL;
8901 /* Extract common data */
8902 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8903 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8905 /* result gets overwritten in next step */
8906 xmlXPathFreeObject(result); result = NULL;
8908 /* Extract records */
8909 if (NULL == history) goto leave;
8910 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8911 xpath_ctx);
8912 if (!result) {
8913 err = IE_ERROR;
8914 goto leave;
8916 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8917 struct isds_list *prev_item = NULL;
8919 /* Iterate over all records */
8920 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8921 struct isds_list *item;
8923 /* Prepare structure */
8924 item = calloc(1, sizeof(*item));
8925 if (!item) {
8926 err = IE_NOMEM;
8927 goto leave;
8929 item->destructor = (void(*)(void**))isds_credit_event_free;
8930 if (i == 0) *history = item;
8931 else prev_item->next = item;
8932 prev_item = item;
8934 /* Extract it */
8935 xpath_ctx->node = result->nodesetval->nodeTab[i];
8936 err = extract_CiRecord(context,
8937 (struct isds_credit_event **) (&item->data),
8938 xpath_ctx);
8939 if (err) goto leave;
8943 leave:
8944 if (!err) {
8945 isds_log(ILF_ISDS, ILL_DEBUG,
8946 _("DataBoxCreditInfo request processed by server successfully.\n"));
8948 if (err) {
8949 isds_list_free(history);
8950 if (NULL != email) zfree(*email)
8953 free(box_id_locale);
8954 xmlXPathFreeObject(result);
8955 xmlXPathFreeContext(xpath_ctx);
8956 xmlFreeDoc(response);
8958 #else /* not HAVE_LIBCURL */
8959 err = IE_NOTSUP;
8960 #endif
8962 return err;
8966 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8967 * code, destroy response and log success.
8968 * @context is ISDS session context.
8969 * @service_name is name of SERVICE_DB_MANIPULATION service
8970 * @box_id is UTF-8 encoded box identifier as zero terminated string
8971 * @approval is optional external approval of box manipulation
8972 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8973 * NULL, if you don't care. */
8974 static isds_error build_send_manipulationdbid_request_check_drop_response(
8975 struct isds_ctx *context, const xmlChar *service_name,
8976 const xmlChar *box_id, const struct isds_approval *approval,
8977 xmlChar **refnumber) {
8978 isds_error err = IE_SUCCESS;
8979 #if HAVE_LIBCURL
8980 xmlDocPtr response = NULL;
8981 #endif
8983 if (!context) return IE_INVALID_CONTEXT;
8984 zfree(context->long_message);
8985 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8987 #if HAVE_LIBCURL
8988 /* Check if connection is established */
8989 if (!context->curl) return IE_CONNECTION_CLOSED;
8991 /* Do request and check for success */
8992 err = build_send_dbid_request_check_response(context,
8993 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8994 &response, refnumber);
8995 xmlFreeDoc(response);
8997 if (!err) {
8998 char *service_name_locale = _isds_utf82locale((char *) service_name);
8999 isds_log(ILF_ISDS, ILL_DEBUG,
9000 _("%s request processed by server successfully.\n"),
9001 service_name_locale);
9002 free(service_name_locale);
9004 #else /* not HAVE_LIBCURL */
9005 err = IE_NOTSUP;
9006 #endif
9008 return err;
9012 /* Switch box into state where box can receive commercial messages (off by
9013 * default)
9014 * @context is ISDS session context.
9015 * @box_id is UTF-8 encoded box identifier as zero terminated string
9016 * @allow is true for enable, false for disable commercial messages income
9017 * @approval is optional external approval of box manipulation
9018 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9019 * NULL, if you don't care. */
9020 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
9021 const char *box_id, const _Bool allow,
9022 const struct isds_approval *approval, char **refnumber) {
9023 return build_send_manipulationdbid_request_check_drop_response(context,
9024 (allow) ? BAD_CAST "SetOpenAddressing" :
9025 BAD_CAST "ClearOpenAddressing",
9026 BAD_CAST box_id, approval, (xmlChar **) refnumber);
9030 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
9031 * message acceptance). This is just a box permission. Sender must apply
9032 * such role by sending each message.
9033 * @context is ISDS session context.
9034 * @box_id is UTF-8 encoded box identifier as zero terminated string
9035 * @allow is true for enable, false for disable OVM role permission
9036 * @approval is optional external approval of box manipulation
9037 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9038 * NULL, if you don't care. */
9039 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
9040 const char *box_id, const _Bool allow,
9041 const struct isds_approval *approval, char **refnumber) {
9042 return build_send_manipulationdbid_request_check_drop_response(context,
9043 (allow) ? BAD_CAST "SetEffectiveOVM" :
9044 BAD_CAST "ClearEffectiveOVM",
9045 BAD_CAST box_id, approval, (xmlChar **) refnumber);
9049 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
9050 * code, destroy response and log success.
9051 * @context is ISDS session context.
9052 * @service_name is name of SERVICE_DB_MANIPULATION service
9053 * @owner is structure describing box. aifoIsds, address->adCode,
9054 * address->adDistrict members are ignored.
9055 * @approval is optional external approval of box manipulation
9056 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9057 * NULL, if you don't care. */
9058 static isds_error build_send_manipulationdbowner_request_check_drop_response(
9059 struct isds_ctx *context, const xmlChar *service_name,
9060 const struct isds_DbOwnerInfo *owner,
9061 const struct isds_approval *approval, xmlChar **refnumber) {
9062 isds_error err = IE_SUCCESS;
9063 #if HAVE_LIBCURL
9064 char *service_name_locale = NULL;
9065 xmlNodePtr request = NULL, db_owner_info;
9066 xmlNsPtr isds_ns = NULL;
9067 #endif
9070 if (!context) return IE_INVALID_CONTEXT;
9071 zfree(context->long_message);
9072 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
9074 #if HAVE_LIBCURL
9075 service_name_locale = _isds_utf82locale((char*)service_name);
9076 if (!service_name_locale) {
9077 err = IE_NOMEM;
9078 goto leave;
9081 /* Build request */
9082 request = xmlNewNode(NULL, service_name);
9083 if (!request) {
9084 isds_printf_message(context,
9085 _("Could not build %s request"), service_name_locale);
9086 err = IE_ERROR;
9087 goto leave;
9089 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9090 if(!isds_ns) {
9091 isds_log_message(context, _("Could not create ISDS name space"));
9092 err = IE_ERROR;
9093 goto leave;
9095 xmlSetNs(request, isds_ns);
9098 /* Add XSD:tOwnerInfoInput child*/
9099 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
9100 err = insert_DbOwnerInfo(context, owner, 0, db_owner_info);
9101 if (err) goto leave;
9103 /* Add XSD:gExtApproval*/
9104 err = insert_GExtApproval(context, approval, request);
9105 if (err) goto leave;
9107 /* Send it to server and process response */
9108 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
9109 service_name, &request, refnumber);
9111 leave:
9112 xmlFreeNode(request);
9113 free(service_name_locale);
9114 #else /* not HAVE_LIBCURL */
9115 err = IE_NOTSUP;
9116 #endif
9118 return err;
9122 /* Switch box accessibility state on request of box owner.
9123 * Despite the name, owner must do the request off-line. This function is
9124 * designed for such off-line meeting points (e.g. Czech POINT).
9125 * @context is ISDS session context.
9126 * @box identifies box to switch accessibility state. aifoIsds,
9127 * address->adCode, address->adDistrict members are ignored.
9128 * @allow is true for making accessible, false to disallow access.
9129 * @approval is optional external approval of box manipulation
9130 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9131 * NULL, if you don't care. */
9132 isds_error isds_switch_box_accessibility_on_owner_request(
9133 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
9134 const _Bool allow, const struct isds_approval *approval,
9135 char **refnumber) {
9136 return build_send_manipulationdbowner_request_check_drop_response(context,
9137 (allow) ? BAD_CAST "EnableOwnDataBox" :
9138 BAD_CAST "DisableOwnDataBox",
9139 box, approval, (xmlChar **) refnumber);
9143 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
9144 * date.
9145 * @context is ISDS session context.
9146 * @box identifies box to switch accessibility state. aifoIsds,
9147 * address->adCode, address->adDistrict members are ignored.
9148 * @since is date since accessibility has been denied. This can be past too.
9149 * Only tm_year, tm_mon and tm_mday carry sane value.
9150 * @approval is optional external approval of box manipulation
9151 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9152 * NULL, if you don't care. */
9153 isds_error isds_disable_box_accessibility_externaly(
9154 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
9155 const struct tm *since, const struct isds_approval *approval,
9156 char **refnumber) {
9157 isds_error err = IE_SUCCESS;
9158 #if HAVE_LIBCURL
9159 char *service_name_locale = NULL;
9160 xmlNodePtr request = NULL, node;
9161 xmlNsPtr isds_ns = NULL;
9162 xmlChar *string = NULL;
9163 #endif
9166 if (!context) return IE_INVALID_CONTEXT;
9167 zfree(context->long_message);
9168 if (!box || !since) return IE_INVAL;
9170 #if HAVE_LIBCURL
9171 /* Build request */
9172 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
9173 if (!request) {
9174 isds_printf_message(context,
9175 _("Could not build %s request"), "DisableDataBoxExternally");
9176 err = IE_ERROR;
9177 goto leave;
9179 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9180 if(!isds_ns) {
9181 isds_log_message(context, _("Could not create ISDS name space"));
9182 err = IE_ERROR;
9183 goto leave;
9185 xmlSetNs(request, isds_ns);
9188 /* Add @box identification */
9189 INSERT_ELEMENT(node, request, "dbOwnerInfo");
9190 err = insert_DbOwnerInfo(context, box, 0, node);
9191 if (err) goto leave;
9193 /* Add @since date */
9194 err = tm2datestring(since, &string);
9195 if(err) {
9196 isds_log_message(context,
9197 _("Could not convert `since' argument to ISO date string"));
9198 goto leave;
9200 INSERT_STRING(request, "dbOwnerDisableDate", string);
9201 zfree(string);
9203 /* Add @approval */
9204 err = insert_GExtApproval(context, approval, request);
9205 if (err) goto leave;
9207 /* Send it to server and process response */
9208 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
9209 BAD_CAST "DisableDataBoxExternally", &request,
9210 (xmlChar **) refnumber);
9212 leave:
9213 free(string);
9214 xmlFreeNode(request);
9215 free(service_name_locale);
9216 #else /* not HAVE_LIBCURL */
9217 err = IE_NOTSUP;
9218 #endif
9220 return err;
9224 #if HAVE_LIBCURL
9225 /* Insert struct isds_message data (envelope (recipient data optional) and
9226 * documents into XML tree
9227 * @context is session context
9228 * @outgoing_message is libisds structure with message data
9229 * @create_message is XML CreateMessage or CreateMultipleMessage element
9230 * @process_recipient true for recipient data serialization, false for no
9231 * serialization */
9232 static isds_error insert_envelope_files(struct isds_ctx *context,
9233 const struct isds_message *outgoing_message, xmlNodePtr create_message,
9234 const _Bool process_recipient) {
9236 isds_error err = IE_SUCCESS;
9237 xmlNodePtr envelope, dm_files, node;
9238 xmlChar *string = NULL;
9240 if (!context) return IE_INVALID_CONTEXT;
9241 if (!outgoing_message || !create_message) return IE_INVAL;
9244 /* Build envelope */
9245 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
9246 if (!envelope) {
9247 isds_printf_message(context, _("Could not add dmEnvelope child to "
9248 "%s element"), create_message->name);
9249 return IE_ERROR;
9252 if (!outgoing_message->envelope) {
9253 isds_log_message(context, _("Outgoing message is missing envelope"));
9254 err = IE_INVAL;
9255 goto leave;
9258 /* Insert optional message type */
9259 err = insert_message_type(context, outgoing_message->envelope->dmType,
9260 envelope);
9261 if (err) goto leave;
9263 INSERT_STRING(envelope, "dmSenderOrgUnit",
9264 outgoing_message->envelope->dmSenderOrgUnit);
9265 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
9266 outgoing_message->envelope->dmSenderOrgUnitNum, string);
9268 if (process_recipient) {
9269 if (!outgoing_message->envelope->dbIDRecipient) {
9270 isds_log_message(context,
9271 _("Outgoing message is missing recipient box identifier"));
9272 err = IE_INVAL;
9273 goto leave;
9275 INSERT_STRING(envelope, "dbIDRecipient",
9276 outgoing_message->envelope->dbIDRecipient);
9278 INSERT_STRING(envelope, "dmRecipientOrgUnit",
9279 outgoing_message->envelope->dmRecipientOrgUnit);
9280 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
9281 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
9282 INSERT_STRING(envelope, "dmToHands",
9283 outgoing_message->envelope->dmToHands);
9286 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
9287 "dmAnnotation");
9288 INSERT_STRING(envelope, "dmAnnotation",
9289 outgoing_message->envelope->dmAnnotation);
9291 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
9292 0, 50, "dmRecipientRefNumber");
9293 INSERT_STRING(envelope, "dmRecipientRefNumber",
9294 outgoing_message->envelope->dmRecipientRefNumber);
9296 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
9297 0, 50, "dmSenderRefNumber");
9298 INSERT_STRING(envelope, "dmSenderRefNumber",
9299 outgoing_message->envelope->dmSenderRefNumber);
9301 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
9302 0, 50, "dmRecipientIdent");
9303 INSERT_STRING(envelope, "dmRecipientIdent",
9304 outgoing_message->envelope->dmRecipientIdent);
9306 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
9307 0, 50, "dmSenderIdent");
9308 INSERT_STRING(envelope, "dmSenderIdent",
9309 outgoing_message->envelope->dmSenderIdent);
9311 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
9312 outgoing_message->envelope->dmLegalTitleLaw, string);
9313 INSERT_LONGINT(envelope, "dmLegalTitleYear",
9314 outgoing_message->envelope->dmLegalTitleYear, string);
9315 INSERT_STRING(envelope, "dmLegalTitleSect",
9316 outgoing_message->envelope->dmLegalTitleSect);
9317 INSERT_STRING(envelope, "dmLegalTitlePar",
9318 outgoing_message->envelope->dmLegalTitlePar);
9319 INSERT_STRING(envelope, "dmLegalTitlePoint",
9320 outgoing_message->envelope->dmLegalTitlePoint);
9322 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
9323 outgoing_message->envelope->dmPersonalDelivery);
9324 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
9325 outgoing_message->envelope->dmAllowSubstDelivery);
9327 /* ???: Should we require value for dbEffectiveOVM sender?
9328 * ISDS has default as true */
9329 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
9330 INSERT_BOOLEAN(envelope, "dmPublishOwnID",
9331 outgoing_message->envelope->dmPublishOwnID);
9334 /* Append dmFiles */
9335 if (!outgoing_message->documents) {
9336 isds_log_message(context,
9337 _("Outgoing message is missing list of documents"));
9338 err = IE_INVAL;
9339 goto leave;
9341 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
9342 if (!dm_files) {
9343 isds_printf_message(context, _("Could not add dmFiles child to "
9344 "%s element"), create_message->name);
9345 err = IE_ERROR;
9346 goto leave;
9349 /* Check for document hierarchy */
9350 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
9351 if (err) goto leave;
9353 /* Process each document */
9354 for (struct isds_list *item =
9355 (struct isds_list *) outgoing_message->documents;
9356 item; item = item->next) {
9357 if (!item->data) {
9358 isds_log_message(context,
9359 _("List of documents contains empty item"));
9360 err = IE_INVAL;
9361 goto leave;
9363 /* FIXME: Check for dmFileMetaType and for document references.
9364 * Only first document can be of MAIN type */
9365 err = insert_document(context, (struct isds_document*) item->data,
9366 dm_files);
9368 if (err) goto leave;
9371 leave:
9372 free(string);
9373 return err;
9375 #endif /* HAVE_LIBCURL */
9378 /* Send a message via ISDS to a recipient
9379 * @context is session context
9380 * @outgoing_message is message to send; Some members are mandatory (like
9381 * dbIDRecipient), some are optional and some are irrelevant (especially data
9382 * about sender). Included pointer to isds_list documents must contain at
9383 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9384 * members will be filled with valid data from ISDS. Exact list of write
9385 * members is subject to change. Currently dmID is changed.
9386 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9387 isds_error isds_send_message(struct isds_ctx *context,
9388 struct isds_message *outgoing_message) {
9390 isds_error err = IE_SUCCESS;
9391 #if HAVE_LIBCURL
9392 xmlNsPtr isds_ns = NULL;
9393 xmlNodePtr request = NULL;
9394 xmlDocPtr response = NULL;
9395 xmlChar *code = NULL, *message = NULL;
9396 xmlXPathContextPtr xpath_ctx = NULL;
9397 xmlXPathObjectPtr result = NULL;
9398 /*_Bool message_is_complete = 0;*/
9399 #endif
9401 if (!context) return IE_INVALID_CONTEXT;
9402 zfree(context->long_message);
9403 if (!outgoing_message) return IE_INVAL;
9405 #if HAVE_LIBCURL
9406 /* Check if connection is established
9407 * TODO: This check should be done downstairs. */
9408 if (!context->curl) return IE_CONNECTION_CLOSED;
9411 /* Build CreateMessage request */
9412 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
9413 if (!request) {
9414 isds_log_message(context,
9415 _("Could not build CreateMessage request"));
9416 return IE_ERROR;
9418 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9419 if(!isds_ns) {
9420 isds_log_message(context, _("Could not create ISDS name space"));
9421 xmlFreeNode(request);
9422 return IE_ERROR;
9424 xmlSetNs(request, isds_ns);
9426 /* Append envelope and files */
9427 err = insert_envelope_files(context, outgoing_message, request, 1);
9428 if (err) goto leave;
9431 /* Signal we can serialize message since now */
9432 /*message_is_complete = 1;*/
9435 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
9437 /* Sent request */
9438 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9440 /* Don't' destroy request, we want to provide it to application later */
9442 if (err) {
9443 isds_log(ILF_ISDS, ILL_DEBUG,
9444 _("Processing ISDS response on CreateMessage "
9445 "request failed\n"));
9446 goto leave;
9449 /* Check for response status */
9450 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9451 &code, &message, NULL);
9452 if (err) {
9453 isds_log(ILF_ISDS, ILL_DEBUG,
9454 _("ISDS response on CreateMessage request "
9455 "is missing status\n"));
9456 goto leave;
9459 /* Request processed, but refused by server or server failed */
9460 if (xmlStrcmp(code, BAD_CAST "0000")) {
9461 char *box_id_locale =
9462 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9463 char *code_locale = _isds_utf82locale((char*)code);
9464 char *message_locale = _isds_utf82locale((char*)message);
9465 isds_log(ILF_ISDS, ILL_DEBUG,
9466 _("Server did not accept message for %s on CreateMessage "
9467 "request (code=%s, message=%s)\n"),
9468 box_id_locale, code_locale, message_locale);
9469 isds_log_message(context, message_locale);
9470 free(box_id_locale);
9471 free(code_locale);
9472 free(message_locale);
9473 err = IE_ISDS;
9474 goto leave;
9478 /* Extract data */
9479 xpath_ctx = xmlXPathNewContext(response);
9480 if (!xpath_ctx) {
9481 err = IE_ERROR;
9482 goto leave;
9484 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9485 err = IE_ERROR;
9486 goto leave;
9488 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
9489 xpath_ctx);
9490 if (!result) {
9491 err = IE_ERROR;
9492 goto leave;
9494 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9495 isds_log_message(context, _("Missing CreateMessageResponse element"));
9496 err = IE_ISDS;
9497 goto leave;
9499 if (result->nodesetval->nodeNr > 1) {
9500 isds_log_message(context, _("Multiple CreateMessageResponse element"));
9501 err = IE_ISDS;
9502 goto leave;
9504 xpath_ctx->node = result->nodesetval->nodeTab[0];
9505 xmlXPathFreeObject(result); result = NULL;
9507 if (outgoing_message->envelope->dmID) {
9508 free(outgoing_message->envelope->dmID);
9509 outgoing_message->envelope->dmID = NULL;
9511 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
9512 if (!outgoing_message->envelope->dmID) {
9513 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
9514 "but did not return assigned message ID\n"));
9517 leave:
9518 /* TODO: Serialize message into structure member raw */
9519 /* XXX: Each web service transport message in different format.
9520 * Therefore it's not possible to save them directly.
9521 * To save them, one must figure out common format.
9522 * We can leave it on application, or we can implement the ESS format. */
9523 /*if (message_is_complete) {
9524 if (outgoing_message->envelope->dmID) {
9526 /* Add assigned message ID as first child*/
9527 /*xmlNodePtr dmid_text = xmlNewText(
9528 (xmlChar *) outgoing_message->envelope->dmID);
9529 if (!dmid_text) goto serialization_failed;
9531 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9532 BAD_CAST "dmID");
9533 if (!dmid_element) {
9534 xmlFreeNode(dmid_text);
9535 goto serialization_failed;
9538 xmlNodePtr dmid_element_with_text =
9539 xmlAddChild(dmid_element, dmid_text);
9540 if (!dmid_element_with_text) {
9541 xmlFreeNode(dmid_element);
9542 xmlFreeNode(dmid_text);
9543 goto serialization_failed;
9546 node = xmlAddPrevSibling(envelope->childern,
9547 dmid_element_with_text);
9548 if (!node) {
9549 xmlFreeNodeList(dmid_element_with_text);
9550 goto serialization_failed;
9554 /* Serialize message with ID into raw */
9555 /*buffer = serialize_element(envelope)*/
9556 /* }
9558 serialization_failed:
9562 /* Clean up */
9563 xmlXPathFreeObject(result);
9564 xmlXPathFreeContext(xpath_ctx);
9566 free(code);
9567 free(message);
9568 xmlFreeDoc(response);
9569 xmlFreeNode(request);
9571 if (!err)
9572 isds_log(ILF_ISDS, ILL_DEBUG,
9573 _("CreateMessage request processed by server "
9574 "successfully.\n"));
9575 #else /* not HAVE_LIBCURL */
9576 err = IE_NOTSUP;
9577 #endif
9579 return err;
9583 /* Send a message via ISDS to a multiple recipients
9584 * @context is session context
9585 * @outgoing_message is message to send; Some members are mandatory,
9586 * some are optional and some are irrelevant (especially data
9587 * about sender). Data about recipient will be substituted by ISDS from
9588 * @copies. Included pointer to isds_list documents must
9589 * contain at least one document of FILEMETATYPE_MAIN.
9590 * @copies is list of isds_message_copy structures addressing all desired
9591 * recipients. This is read-write structure, some members will be filled with
9592 * valid data from ISDS (message IDs, error codes, error descriptions).
9593 * @return
9594 * ISDS_SUCCESS if all messages have been sent
9595 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9596 * succeeded messages can be identified by copies->data->error),
9597 * or other error code if something other goes wrong. */
9598 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
9599 const struct isds_message *outgoing_message,
9600 struct isds_list *copies) {
9602 isds_error err = IE_SUCCESS;
9603 #if HAVE_LIBCURL
9604 isds_error append_err;
9605 xmlNsPtr isds_ns = NULL;
9606 xmlNodePtr request = NULL, recipients, recipient, node;
9607 struct isds_list *item;
9608 struct isds_message_copy *copy;
9609 xmlDocPtr response = NULL;
9610 xmlChar *code = NULL, *message = NULL;
9611 xmlXPathContextPtr xpath_ctx = NULL;
9612 xmlXPathObjectPtr result = NULL;
9613 xmlChar *string = NULL;
9614 int i;
9615 #endif
9617 if (!context) return IE_INVALID_CONTEXT;
9618 zfree(context->long_message);
9619 if (!outgoing_message || !copies) return IE_INVAL;
9621 #if HAVE_LIBCURL
9622 /* Check if connection is established
9623 * TODO: This check should be done downstairs. */
9624 if (!context->curl) return IE_CONNECTION_CLOSED;
9627 /* Build CreateMultipleMessage request */
9628 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9629 if (!request) {
9630 isds_log_message(context,
9631 _("Could not build CreateMultipleMessage request"));
9632 return IE_ERROR;
9634 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9635 if(!isds_ns) {
9636 isds_log_message(context, _("Could not create ISDS name space"));
9637 xmlFreeNode(request);
9638 return IE_ERROR;
9640 xmlSetNs(request, isds_ns);
9643 /* Build recipients */
9644 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9645 if (!recipients) {
9646 isds_log_message(context, _("Could not add dmRecipients child to "
9647 "CreateMultipleMessage element"));
9648 xmlFreeNode(request);
9649 return IE_ERROR;
9652 /* Insert each recipient */
9653 for (item = copies; item; item = item->next) {
9654 copy = (struct isds_message_copy *) item->data;
9655 if (!copy) {
9656 isds_log_message(context,
9657 _("`copies' list item contains empty data"));
9658 err = IE_INVAL;
9659 goto leave;
9662 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9663 if (!recipient) {
9664 isds_log_message(context, _("Could not add dmRecipient child to "
9665 "dmRecipients element"));
9666 err = IE_ERROR;
9667 goto leave;
9670 if (!copy->dbIDRecipient) {
9671 isds_log_message(context,
9672 _("Message copy is missing recipient box identifier"));
9673 err = IE_INVAL;
9674 goto leave;
9676 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9677 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9678 copy->dmRecipientOrgUnit);
9679 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9680 copy->dmRecipientOrgUnitNum, string);
9681 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9684 /* Append envelope and files */
9685 err = insert_envelope_files(context, outgoing_message, request, 0);
9686 if (err) goto leave;
9689 isds_log(ILF_ISDS, ILL_DEBUG,
9690 _("Sending CreateMultipleMessage request to ISDS\n"));
9692 /* Sent request */
9693 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9694 if (err) {
9695 isds_log(ILF_ISDS, ILL_DEBUG,
9696 _("Processing ISDS response on CreateMultipleMessage "
9697 "request failed\n"));
9698 goto leave;
9701 /* Check for response status */
9702 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9703 &code, &message, NULL);
9704 if (err) {
9705 isds_log(ILF_ISDS, ILL_DEBUG,
9706 _("ISDS response on CreateMultipleMessage request "
9707 "is missing status\n"));
9708 goto leave;
9711 /* Request processed, but some copies failed */
9712 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9713 char *box_id_locale =
9714 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9715 char *code_locale = _isds_utf82locale((char*)code);
9716 char *message_locale = _isds_utf82locale((char*)message);
9717 isds_log(ILF_ISDS, ILL_DEBUG,
9718 _("Server did accept message for multiple recipients "
9719 "on CreateMultipleMessage request but delivery to "
9720 "some of them failed (code=%s, message=%s)\n"),
9721 box_id_locale, code_locale, message_locale);
9722 isds_log_message(context, message_locale);
9723 free(box_id_locale);
9724 free(code_locale);
9725 free(message_locale);
9726 err = IE_PARTIAL_SUCCESS;
9729 /* Request refused by server as whole */
9730 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9731 char *box_id_locale =
9732 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9733 char *code_locale = _isds_utf82locale((char*)code);
9734 char *message_locale = _isds_utf82locale((char*)message);
9735 isds_log(ILF_ISDS, ILL_DEBUG,
9736 _("Server did not accept message for multiple recipients "
9737 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9738 box_id_locale, code_locale, message_locale);
9739 isds_log_message(context, message_locale);
9740 free(box_id_locale);
9741 free(code_locale);
9742 free(message_locale);
9743 err = IE_ISDS;
9744 goto leave;
9748 /* Extract data */
9749 xpath_ctx = xmlXPathNewContext(response);
9750 if (!xpath_ctx) {
9751 err = IE_ERROR;
9752 goto leave;
9754 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9755 err = IE_ERROR;
9756 goto leave;
9758 result = xmlXPathEvalExpression(
9759 BAD_CAST "/isds:CreateMultipleMessageResponse"
9760 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9761 xpath_ctx);
9762 if (!result) {
9763 err = IE_ERROR;
9764 goto leave;
9766 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9767 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9768 err = IE_ISDS;
9769 goto leave;
9772 /* Extract message ID and delivery status for each copy */
9773 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9774 item = item->next, i++) {
9775 copy = (struct isds_message_copy *) item->data;
9776 xpath_ctx->node = result->nodesetval->nodeTab[i];
9778 append_err = append_TMStatus(context, copy, xpath_ctx);
9779 if (append_err) {
9780 err = append_err;
9781 goto leave;
9784 if (item || i < result->nodesetval->nodeNr) {
9785 isds_printf_message(context, _("ISDS returned unexpected number of "
9786 "message copy delivery states: %d"),
9787 result->nodesetval->nodeNr);
9788 err = IE_ISDS;
9789 goto leave;
9793 leave:
9794 /* Clean up */
9795 free(string);
9796 xmlXPathFreeObject(result);
9797 xmlXPathFreeContext(xpath_ctx);
9799 free(code);
9800 free(message);
9801 xmlFreeDoc(response);
9802 xmlFreeNode(request);
9804 if (!err)
9805 isds_log(ILF_ISDS, ILL_DEBUG,
9806 _("CreateMultipleMessageResponse request processed by server "
9807 "successfully.\n"));
9808 #else /* not HAVE_LIBCURL */
9809 err = IE_NOTSUP;
9810 #endif
9812 return err;
9816 /* Get list of messages. This is common core for getting sent or received
9817 * messages.
9818 * Any criterion argument can be NULL, if you don't care about it.
9819 * @context is session context. Must not be NULL.
9820 * @outgoing_direction is true if you want list of outgoing messages,
9821 * it's false if you want incoming messages.
9822 * @from_time is minimal time and date of message sending inclusive.
9823 * @to_time is maximal time and date of message sending inclusive
9824 * @organization_unit_number is number of sender/recipient respectively.
9825 * @status_filter is bit field of isds_message_status values. Use special
9826 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9827 * all values, you can use bit-wise arithmetic if you want.)
9828 * @offset is index of first message we are interested in. First message is 1.
9829 * Set to 0 (or 1) if you don't care.
9830 * @number is maximal length of list you want to get as input value, outputs
9831 * number of messages matching these criteria. Can be NULL if you don't care
9832 * (applies to output value either).
9833 * @messages is automatically reallocated list of isds_message's. Be ware that
9834 * it returns only brief overview (envelope and some other fields) about each
9835 * message, not the complete message. FIXME: Specify exact fields.
9836 * The list is sorted by delivery time in ascending order.
9837 * Use NULL if you don't care about don't need the data (useful if you want to
9838 * know only the @number). If you provide &NULL, list will be allocated on
9839 * heap, if you provide pointer to non-NULL, list will be freed automatically
9840 * at first. Also in case of error the list will be NULLed.
9841 * @return IE_SUCCESS or appropriate error code. */
9842 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9843 _Bool outgoing_direction,
9844 const struct timeval *from_time, const struct timeval *to_time,
9845 const long int *organization_unit_number,
9846 const unsigned int status_filter,
9847 const unsigned long int offset, unsigned long int *number,
9848 struct isds_list **messages) {
9850 isds_error err = IE_SUCCESS;
9851 #if HAVE_LIBCURL
9852 xmlNsPtr isds_ns = NULL;
9853 xmlNodePtr request = NULL, node;
9854 xmlDocPtr response = NULL;
9855 xmlChar *code = NULL, *message = NULL;
9856 xmlXPathContextPtr xpath_ctx = NULL;
9857 xmlXPathObjectPtr result = NULL;
9858 xmlChar *string = NULL;
9859 int count = 0;
9860 #endif
9862 if (!context) return IE_INVALID_CONTEXT;
9863 zfree(context->long_message);
9865 /* Free former message list if any */
9866 if (messages) isds_list_free(messages);
9868 #if HAVE_LIBCURL
9869 /* Check if connection is established
9870 * TODO: This check should be done downstairs. */
9871 if (!context->curl) return IE_CONNECTION_CLOSED;
9873 /* Build GetListOf*Messages request */
9874 request = xmlNewNode(NULL,
9875 (outgoing_direction) ?
9876 BAD_CAST "GetListOfSentMessages" :
9877 BAD_CAST "GetListOfReceivedMessages"
9879 if (!request) {
9880 isds_log_message(context,
9881 (outgoing_direction) ?
9882 _("Could not build GetListOfSentMessages request") :
9883 _("Could not build GetListOfReceivedMessages request")
9885 return IE_ERROR;
9887 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9888 if(!isds_ns) {
9889 isds_log_message(context, _("Could not create ISDS name space"));
9890 xmlFreeNode(request);
9891 return IE_ERROR;
9893 xmlSetNs(request, isds_ns);
9896 if (from_time) {
9897 err = timeval2timestring(from_time, &string);
9898 if (err) goto leave;
9900 INSERT_STRING(request, "dmFromTime", string);
9901 free(string); string = NULL;
9903 if (to_time) {
9904 err = timeval2timestring(to_time, &string);
9905 if (err) goto leave;
9907 INSERT_STRING(request, "dmToTime", string);
9908 free(string); string = NULL;
9910 if (outgoing_direction) {
9911 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9912 organization_unit_number, string);
9913 } else {
9914 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9915 organization_unit_number, string);
9918 if (status_filter > MESSAGESTATE_ANY) {
9919 isds_printf_message(context,
9920 _("Invalid message state filter value: %ld"), status_filter);
9921 err = IE_INVAL;
9922 goto leave;
9924 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9926 if (offset > 0 ) {
9927 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9928 } else {
9929 INSERT_STRING(request, "dmOffset", "1");
9932 /* number 0 means no limit */
9933 if (number && *number == 0) {
9934 INSERT_STRING(request, "dmLimit", NULL);
9935 } else {
9936 INSERT_ULONGINT(request, "dmLimit", number, string);
9940 isds_log(ILF_ISDS, ILL_DEBUG,
9941 (outgoing_direction) ?
9942 _("Sending GetListOfSentMessages request to ISDS\n") :
9943 _("Sending GetListOfReceivedMessages request to ISDS\n")
9946 /* Sent request */
9947 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9948 xmlFreeNode(request); request = NULL;
9950 if (err) {
9951 isds_log(ILF_ISDS, ILL_DEBUG,
9952 (outgoing_direction) ?
9953 _("Processing ISDS response on GetListOfSentMessages "
9954 "request failed\n") :
9955 _("Processing ISDS response on GetListOfReceivedMessages "
9956 "request failed\n")
9958 goto leave;
9961 /* Check for response status */
9962 err = isds_response_status(context, SERVICE_DM_INFO, response,
9963 &code, &message, NULL);
9964 if (err) {
9965 isds_log(ILF_ISDS, ILL_DEBUG,
9966 (outgoing_direction) ?
9967 _("ISDS response on GetListOfSentMessages request "
9968 "is missing status\n") :
9969 _("ISDS response on GetListOfReceivedMessages request "
9970 "is missing status\n")
9972 goto leave;
9975 /* Request processed, but nothing found */
9976 if (xmlStrcmp(code, BAD_CAST "0000")) {
9977 char *code_locale = _isds_utf82locale((char*)code);
9978 char *message_locale = _isds_utf82locale((char*)message);
9979 isds_log(ILF_ISDS, ILL_DEBUG,
9980 (outgoing_direction) ?
9981 _("Server refused GetListOfSentMessages request "
9982 "(code=%s, message=%s)\n") :
9983 _("Server refused GetListOfReceivedMessages request "
9984 "(code=%s, message=%s)\n"),
9985 code_locale, message_locale);
9986 isds_log_message(context, message_locale);
9987 free(code_locale);
9988 free(message_locale);
9989 err = IE_ISDS;
9990 goto leave;
9994 /* Extract data */
9995 xpath_ctx = xmlXPathNewContext(response);
9996 if (!xpath_ctx) {
9997 err = IE_ERROR;
9998 goto leave;
10000 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10001 err = IE_ERROR;
10002 goto leave;
10004 result = xmlXPathEvalExpression(
10005 (outgoing_direction) ?
10006 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
10007 "isds:dmRecords/isds:dmRecord" :
10008 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
10009 "isds:dmRecords/isds:dmRecord",
10010 xpath_ctx);
10011 if (!result) {
10012 err = IE_ERROR;
10013 goto leave;
10016 /* Fill output arguments in */
10017 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10018 struct isds_envelope *envelope;
10019 struct isds_list *item = NULL, *last_item = NULL;
10021 for (count = 0; count < result->nodesetval->nodeNr; count++) {
10022 /* Create new message */
10023 item = calloc(1, sizeof(*item));
10024 if (!item) {
10025 err = IE_NOMEM;
10026 goto leave;
10028 item->destructor = (void(*)(void**)) &isds_message_free;
10029 item->data = calloc(1, sizeof(struct isds_message));
10030 if (!item->data) {
10031 isds_list_free(&item);
10032 err = IE_NOMEM;
10033 goto leave;
10036 /* Extract envelope data */
10037 xpath_ctx->node = result->nodesetval->nodeTab[count];
10038 envelope = NULL;
10039 err = extract_DmRecord(context, &envelope, xpath_ctx);
10040 if (err) {
10041 isds_list_free(&item);
10042 goto leave;
10045 /* Attach extracted envelope */
10046 ((struct isds_message *) item->data)->envelope = envelope;
10048 /* Append new message into the list */
10049 if (!*messages) {
10050 *messages = last_item = item;
10051 } else {
10052 last_item->next = item;
10053 last_item = item;
10057 if (number) *number = count;
10059 leave:
10060 if (err) {
10061 isds_list_free(messages);
10064 free(string);
10065 xmlXPathFreeObject(result);
10066 xmlXPathFreeContext(xpath_ctx);
10068 free(code);
10069 free(message);
10070 xmlFreeDoc(response);
10071 xmlFreeNode(request);
10073 if (!err)
10074 isds_log(ILF_ISDS, ILL_DEBUG,
10075 (outgoing_direction) ?
10076 _("GetListOfSentMessages request processed by server "
10077 "successfully.\n") :
10078 _("GetListOfReceivedMessages request processed by server "
10079 "successfully.\n")
10081 #else /* not HAVE_LIBCURL */
10082 err = IE_NOTSUP;
10083 #endif
10084 return err;
10088 /* Get list of outgoing (already sent) messages.
10089 * Any criterion argument can be NULL, if you don't care about it.
10090 * @context is session context. Must not be NULL.
10091 * @from_time is minimal time and date of message sending inclusive.
10092 * @to_time is maximal time and date of message sending inclusive
10093 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
10094 * @status_filter is bit field of isds_message_status values. Use special
10095 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10096 * all values, you can use bit-wise arithmetic if you want.)
10097 * @offset is index of first message we are interested in. First message is 1.
10098 * Set to 0 (or 1) if you don't care.
10099 * @number is maximal length of list you want to get as input value, outputs
10100 * number of messages matching these criteria. Can be NULL if you don't care
10101 * (applies to output value either).
10102 * @messages is automatically reallocated list of isds_message's. Be ware that
10103 * it returns only brief overview (envelope and some other fields) about each
10104 * message, not the complete message. FIXME: Specify exact fields.
10105 * The list is sorted by delivery time in ascending order.
10106 * Use NULL if you don't care about the meta data (useful if you want to know
10107 * only the @number). If you provide &NULL, list will be allocated on heap,
10108 * if you provide pointer to non-NULL, list will be freed automatically at
10109 * first. Also in case of error the list will be NULLed.
10110 * @return IE_SUCCESS or appropriate error code. */
10111 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
10112 const struct timeval *from_time, const struct timeval *to_time,
10113 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
10114 const unsigned long int offset, unsigned long int *number,
10115 struct isds_list **messages) {
10117 return isds_get_list_of_messages(
10118 context, 1,
10119 from_time, to_time, dmSenderOrgUnitNum, status_filter,
10120 offset, number,
10121 messages);
10125 /* Get list of incoming (addressed to you) messages.
10126 * Any criterion argument can be NULL, if you don't care about it.
10127 * @context is session context. Must not be NULL.
10128 * @from_time is minimal time and date of message sending inclusive.
10129 * @to_time is maximal time and date of message sending inclusive
10130 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
10131 * @status_filter is bit field of isds_message_status values. Use special
10132 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10133 * all values, you can use bit-wise arithmetic if you want.)
10134 * @offset is index of first message we are interested in. First message is 1.
10135 * Set to 0 (or 1) if you don't care.
10136 * @number is maximal length of list you want to get as input value, outputs
10137 * number of messages matching these criteria. Can be NULL if you don't care
10138 * (applies to output value either).
10139 * @messages is automatically reallocated list of isds_message's. Be ware that
10140 * it returns only brief overview (envelope and some other fields) about each
10141 * message, not the complete message. FIXME: Specify exact fields.
10142 * Use NULL if you don't care about the meta data (useful if you want to know
10143 * only the @number). If you provide &NULL, list will be allocated on heap,
10144 * if you provide pointer to non-NULL, list will be freed automatically at
10145 * first. Also in case of error the list will be NULLed.
10146 * @return IE_SUCCESS or appropriate error code. */
10147 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
10148 const struct timeval *from_time, const struct timeval *to_time,
10149 const long int *dmRecipientOrgUnitNum,
10150 const unsigned int status_filter,
10151 const unsigned long int offset, unsigned long int *number,
10152 struct isds_list **messages) {
10154 return isds_get_list_of_messages(
10155 context, 0,
10156 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
10157 offset, number,
10158 messages);
10162 /* Get list of sent message state changes.
10163 * Any criterion argument can be NULL, if you don't care about it.
10164 * @context is session context. Must not be NULL.
10165 * @from_time is minimal time and date of status changes inclusive
10166 * @to_time is maximal time and date of status changes inclusive
10167 * @changed_states is automatically reallocated list of
10168 * isds_message_status_change's. If you provide &NULL, list will be allocated
10169 * on heap, if you provide pointer to non-NULL, list will be freed
10170 * automatically at first. Also in case of error the list will be NULLed.
10171 * XXX: The list item ordering is not specified.
10172 * XXX: Server provides only `recent' changes.
10173 * @return IE_SUCCESS or appropriate error code. */
10174 isds_error isds_get_list_of_sent_message_state_changes(
10175 struct isds_ctx *context,
10176 const struct timeval *from_time, const struct timeval *to_time,
10177 struct isds_list **changed_states) {
10179 isds_error err = IE_SUCCESS;
10180 #if HAVE_LIBCURL
10181 xmlNsPtr isds_ns = NULL;
10182 xmlNodePtr request = NULL, node;
10183 xmlDocPtr response = NULL;
10184 xmlXPathContextPtr xpath_ctx = NULL;
10185 xmlXPathObjectPtr result = NULL;
10186 xmlChar *string = NULL;
10187 int count = 0;
10188 #endif
10190 if (!context) return IE_INVALID_CONTEXT;
10191 zfree(context->long_message);
10193 /* Free former message list if any */
10194 isds_list_free(changed_states);
10196 #if HAVE_LIBCURL
10197 /* Check if connection is established
10198 * TODO: This check should be done downstairs. */
10199 if (!context->curl) return IE_CONNECTION_CLOSED;
10201 /* Build GetMessageStateChanges request */
10202 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
10203 if (!request) {
10204 isds_log_message(context,
10205 _("Could not build GetMessageStateChanges request"));
10206 return IE_ERROR;
10208 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10209 if(!isds_ns) {
10210 isds_log_message(context, _("Could not create ISDS name space"));
10211 xmlFreeNode(request);
10212 return IE_ERROR;
10214 xmlSetNs(request, isds_ns);
10217 if (from_time) {
10218 err = timeval2timestring(from_time, &string);
10219 if (err) goto leave;
10221 INSERT_STRING(request, "dmFromTime", string);
10222 zfree(string);
10224 if (to_time) {
10225 err = timeval2timestring(to_time, &string);
10226 if (err) goto leave;
10228 INSERT_STRING(request, "dmToTime", string);
10229 zfree(string);
10232 /* Sent request */
10233 err = send_destroy_request_check_response(context,
10234 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
10235 &response, NULL, NULL);
10236 if (err) goto leave;
10239 /* Extract data */
10240 xpath_ctx = xmlXPathNewContext(response);
10241 if (!xpath_ctx) {
10242 err = IE_ERROR;
10243 goto leave;
10245 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10246 err = IE_ERROR;
10247 goto leave;
10249 result = xmlXPathEvalExpression(
10250 BAD_CAST "/isds:GetMessageStateChangesResponse/"
10251 "isds:dmRecords/isds:dmRecord", xpath_ctx);
10252 if (!result) {
10253 err = IE_ERROR;
10254 goto leave;
10257 /* Fill output arguments in */
10258 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10259 struct isds_list *item = NULL, *last_item = NULL;
10261 for (count = 0; count < result->nodesetval->nodeNr; count++) {
10262 /* Create new status change */
10263 item = calloc(1, sizeof(*item));
10264 if (!item) {
10265 err = IE_NOMEM;
10266 goto leave;
10268 item->destructor =
10269 (void(*)(void**)) &isds_message_status_change_free;
10271 /* Extract message status change */
10272 xpath_ctx->node = result->nodesetval->nodeTab[count];
10273 err = extract_StateChangesRecord(context,
10274 (struct isds_message_status_change **) &item->data,
10275 xpath_ctx);
10276 if (err) {
10277 isds_list_free(&item);
10278 goto leave;
10281 /* Append new message status change into the list */
10282 if (!*changed_states) {
10283 *changed_states = last_item = item;
10284 } else {
10285 last_item->next = item;
10286 last_item = item;
10291 leave:
10292 if (err) {
10293 isds_list_free(changed_states);
10296 free(string);
10297 xmlXPathFreeObject(result);
10298 xmlXPathFreeContext(xpath_ctx);
10299 xmlFreeDoc(response);
10300 xmlFreeNode(request);
10302 if (!err)
10303 isds_log(ILF_ISDS, ILL_DEBUG,
10304 _("GetMessageStateChanges request processed by server "
10305 "successfully.\n"));
10306 #else /* not HAVE_LIBCURL */
10307 err = IE_NOTSUP;
10308 #endif
10309 return err;
10313 #if HAVE_LIBCURL
10314 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10315 * code
10316 * @context is session context
10317 * @service is ISDS WS service handler
10318 * @service_name is name of SERVICE_DM_OPERATIONS
10319 * @message_id is message ID to send as service argument to ISDS
10320 * @response is reallocated server SOAP body response as XML document
10321 * @raw_response is reallocated bit stream with response body. Use
10322 * NULL if you don't care
10323 * @raw_response_length is size of @raw_response in bytes
10324 * @code is reallocated ISDS status code
10325 * @status_message is reallocated ISDS status message
10326 * @return error coded from lower layer, context message will be set up
10327 * appropriately. */
10328 static isds_error build_send_check_message_request(struct isds_ctx *context,
10329 const isds_service service, const xmlChar *service_name,
10330 const char *message_id,
10331 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
10332 xmlChar **code, xmlChar **status_message) {
10334 isds_error err = IE_SUCCESS;
10335 char *service_name_locale = NULL, *message_id_locale = NULL;
10336 xmlNodePtr request = NULL, node;
10337 xmlNsPtr isds_ns = NULL;
10339 if (!context) return IE_INVALID_CONTEXT;
10340 if (!service_name || !message_id) return IE_INVAL;
10341 if (!response || !code || !status_message) return IE_INVAL;
10342 if (!raw_response_length && raw_response) return IE_INVAL;
10344 /* Free output argument */
10345 xmlFreeDoc(*response); *response = NULL;
10346 if (raw_response) zfree(*raw_response);
10347 zfree(*code);
10348 zfree(*status_message);
10351 /* Check if connection is established
10352 * TODO: This check should be done downstairs. */
10353 if (!context->curl) return IE_CONNECTION_CLOSED;
10355 service_name_locale = _isds_utf82locale((char*)service_name);
10356 message_id_locale = _isds_utf82locale(message_id);
10357 if (!service_name_locale || !message_id_locale) {
10358 err = IE_NOMEM;
10359 goto leave;
10362 /* Build request */
10363 request = xmlNewNode(NULL, service_name);
10364 if (!request) {
10365 isds_printf_message(context,
10366 _("Could not build %s request for %s message ID"),
10367 service_name_locale, message_id_locale);
10368 err = IE_ERROR;
10369 goto leave;
10371 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10372 if(!isds_ns) {
10373 isds_log_message(context, _("Could not create ISDS name space"));
10374 err = IE_ERROR;
10375 goto leave;
10377 xmlSetNs(request, isds_ns);
10380 /* Add requested ID */
10381 err = validate_message_id_length(context, (xmlChar *) message_id);
10382 if (err) goto leave;
10383 INSERT_STRING(request, "dmID", message_id);
10386 isds_log(ILF_ISDS, ILL_DEBUG,
10387 _("Sending %s request for %s message ID to ISDS\n"),
10388 service_name_locale, message_id_locale);
10390 /* Send request */
10391 err = _isds(context, service, request, response,
10392 raw_response, raw_response_length);
10393 xmlFreeNode(request); request = NULL;
10395 if (err) {
10396 isds_log(ILF_ISDS, ILL_DEBUG,
10397 _("Processing ISDS response on %s request failed\n"),
10398 service_name_locale);
10399 goto leave;
10402 /* Check for response status */
10403 err = isds_response_status(context, service, *response,
10404 code, status_message, NULL);
10405 if (err) {
10406 isds_log(ILF_ISDS, ILL_DEBUG,
10407 _("ISDS response on %s request is missing status\n"),
10408 service_name_locale);
10409 goto leave;
10412 /* Request processed, but nothing found */
10413 if (xmlStrcmp(*code, BAD_CAST "0000")) {
10414 char *code_locale = _isds_utf82locale((char*) *code);
10415 char *status_message_locale = _isds_utf82locale((char*) *status_message);
10416 isds_log(ILF_ISDS, ILL_DEBUG,
10417 _("Server refused %s request for %s message ID "
10418 "(code=%s, message=%s)\n"),
10419 service_name_locale, message_id_locale,
10420 code_locale, status_message_locale);
10421 isds_log_message(context, status_message_locale);
10422 free(code_locale);
10423 free(status_message_locale);
10424 err = IE_ISDS;
10425 goto leave;
10428 leave:
10429 free(message_id_locale);
10430 free(service_name_locale);
10431 xmlFreeNode(request);
10432 return err;
10436 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10437 * signed data and free ISDS response.
10438 * @context is session context
10439 * @message_id is UTF-8 encoded message ID for logging purpose
10440 * @response is parsed XML document. It will be freed and NULLed in the middle
10441 * of function run to save memory. This is not guaranteed in case of error.
10442 * @request_name is name of ISDS request used to construct response root
10443 * element name and for logging purpose.
10444 * @raw is reallocated output buffer with DER encoded CMS data
10445 * @raw_length is size of @raw buffer in bytes
10446 * @returns standard error codes, in case of error, @raw will be freed and
10447 * NULLed, @response sometimes. */
10448 static isds_error find_extract_signed_data_free_response(
10449 struct isds_ctx *context, const xmlChar *message_id,
10450 xmlDocPtr *response, const xmlChar *request_name,
10451 void **raw, size_t *raw_length) {
10453 isds_error err = IE_SUCCESS;
10454 char *xpath_expression = NULL;
10455 xmlXPathContextPtr xpath_ctx = NULL;
10456 xmlXPathObjectPtr result = NULL;
10457 char *encoded_structure = NULL;
10459 if (!context) return IE_INVALID_CONTEXT;
10460 if (!raw) return IE_INVAL;
10461 zfree(*raw);
10462 if (!message_id || !response || !*response || !request_name || !raw_length)
10463 return IE_INVAL;
10465 /* Build XPath expression */
10466 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
10467 "Response/isds:dmSignature");
10468 if (!xpath_expression) return IE_NOMEM;
10470 /* Extract data */
10471 xpath_ctx = xmlXPathNewContext(*response);
10472 if (!xpath_ctx) {
10473 err = IE_ERROR;
10474 goto leave;
10476 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10477 err = IE_ERROR;
10478 goto leave;
10480 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
10481 if (!result) {
10482 err = IE_ERROR;
10483 goto leave;
10485 /* Empty response */
10486 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10487 char *message_id_locale = _isds_utf82locale((char*) message_id);
10488 isds_printf_message(context,
10489 _("Server did not return any signed data for message ID `%s' "
10490 "on %s request"),
10491 message_id_locale, request_name);
10492 free(message_id_locale);
10493 err = IE_ISDS;
10494 goto leave;
10496 /* More responses */
10497 if (result->nodesetval->nodeNr > 1) {
10498 char *message_id_locale = _isds_utf82locale((char*) message_id);
10499 isds_printf_message(context,
10500 _("Server did return more signed data for message ID `%s' "
10501 "on %s request"),
10502 message_id_locale, request_name);
10503 free(message_id_locale);
10504 err = IE_ISDS;
10505 goto leave;
10507 /* One response */
10508 xpath_ctx->node = result->nodesetval->nodeTab[0];
10510 /* Extract PKCS#7 structure */
10511 EXTRACT_STRING(".", encoded_structure);
10512 if (!encoded_structure) {
10513 isds_log_message(context, _("dmSignature element is empty"));
10516 /* Here we have delivery info as standalone CMS in encoded_structure.
10517 * We don't need any other data, free them: */
10518 xmlXPathFreeObject(result); result = NULL;
10519 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
10520 xmlFreeDoc(*response); *response = NULL;
10523 /* Decode PKCS#7 to DER format */
10524 *raw_length = _isds_b64decode(encoded_structure, raw);
10525 if (*raw_length == (size_t) -1) {
10526 isds_log_message(context,
10527 _("Error while Base64-decoding PKCS#7 structure"));
10528 err = IE_ERROR;
10529 goto leave;
10532 leave:
10533 if (err) {
10534 zfree(*raw);
10535 raw_length = 0;
10538 free(encoded_structure);
10539 xmlXPathFreeObject(result);
10540 xmlXPathFreeContext(xpath_ctx);
10541 free(xpath_expression);
10543 return err;
10545 #endif /* HAVE_LIBCURL */
10548 /* Download incoming message envelope identified by ID.
10549 * @context is session context
10550 * @message_id is message identifier (you can get them from
10551 * isds_get_list_of_received_messages())
10552 * @message is automatically reallocated message retrieved from ISDS.
10553 * It will miss documents per se. Use isds_get_received_message(), if you are
10554 * interested in documents (content) too.
10555 * Returned hash and timestamp require documents to be verifiable. */
10556 isds_error isds_get_received_envelope(struct isds_ctx *context,
10557 const char *message_id, struct isds_message **message) {
10559 isds_error err = IE_SUCCESS;
10560 #if HAVE_LIBCURL
10561 xmlDocPtr response = NULL;
10562 xmlChar *code = NULL, *status_message = NULL;
10563 xmlXPathContextPtr xpath_ctx = NULL;
10564 xmlXPathObjectPtr result = NULL;
10565 #endif
10567 if (!context) return IE_INVALID_CONTEXT;
10568 zfree(context->long_message);
10570 /* Free former message if any */
10571 if (!message) return IE_INVAL;
10572 isds_message_free(message);
10574 #if HAVE_LIBCURL
10575 /* Do request and check for success */
10576 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10577 BAD_CAST "MessageEnvelopeDownload", message_id,
10578 &response, NULL, NULL, &code, &status_message);
10579 if (err) goto leave;
10581 /* Extract data */
10582 xpath_ctx = xmlXPathNewContext(response);
10583 if (!xpath_ctx) {
10584 err = IE_ERROR;
10585 goto leave;
10587 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10588 err = IE_ERROR;
10589 goto leave;
10591 result = xmlXPathEvalExpression(
10592 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
10593 "isds:dmReturnedMessageEnvelope",
10594 xpath_ctx);
10595 if (!result) {
10596 err = IE_ERROR;
10597 goto leave;
10599 /* Empty response */
10600 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10601 char *message_id_locale = _isds_utf82locale((char*) message_id);
10602 isds_printf_message(context,
10603 _("Server did not return any envelope for ID `%s' "
10604 "on MessageEnvelopeDownload request"), message_id_locale);
10605 free(message_id_locale);
10606 err = IE_ISDS;
10607 goto leave;
10609 /* More envelops */
10610 if (result->nodesetval->nodeNr > 1) {
10611 char *message_id_locale = _isds_utf82locale((char*) message_id);
10612 isds_printf_message(context,
10613 _("Server did return more envelopes for ID `%s' "
10614 "on MessageEnvelopeDownload request"), message_id_locale);
10615 free(message_id_locale);
10616 err = IE_ISDS;
10617 goto leave;
10619 /* One message */
10620 xpath_ctx->node = result->nodesetval->nodeTab[0];
10622 /* Extract the envelope (= message without documents, hence 0) */
10623 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10624 if (err) goto leave;
10626 /* Save XML blob */
10627 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10628 &(*message)->raw_length);
10630 leave:
10631 if (err) {
10632 isds_message_free(message);
10635 xmlXPathFreeObject(result);
10636 xmlXPathFreeContext(xpath_ctx);
10638 free(code);
10639 free(status_message);
10640 if (!*message || !(*message)->xml) {
10641 xmlFreeDoc(response);
10644 if (!err)
10645 isds_log(ILF_ISDS, ILL_DEBUG,
10646 _("MessageEnvelopeDownload request processed by server "
10647 "successfully.\n")
10649 #else /* not HAVE_LIBCURL */
10650 err = IE_NOTSUP;
10651 #endif
10652 return err;
10656 /* Load delivery info of any format from buffer.
10657 * @context is session context
10658 * @raw_type advertises format of @buffer content. Only delivery info types
10659 * are accepted.
10660 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10661 * retrieve such data from message->raw after calling
10662 * isds_get_signed_delivery_info().
10663 * @length is length of buffer in bytes.
10664 * @message is automatically reallocated message parsed from @buffer.
10665 * @strategy selects how buffer will be attached into raw isds_message member.
10666 * */
10667 isds_error isds_load_delivery_info(struct isds_ctx *context,
10668 const isds_raw_type raw_type,
10669 const void *buffer, const size_t length,
10670 struct isds_message **message, const isds_buffer_strategy strategy) {
10672 isds_error err = IE_SUCCESS;
10673 message_ns_type message_ns;
10674 xmlDocPtr message_doc = NULL;
10675 xmlXPathContextPtr xpath_ctx = NULL;
10676 xmlXPathObjectPtr result = NULL;
10677 void *xml_stream = NULL;
10678 size_t xml_stream_length = 0;
10680 if (!context) return IE_INVALID_CONTEXT;
10681 zfree(context->long_message);
10682 if (!message) return IE_INVAL;
10683 isds_message_free(message);
10684 if (!buffer) return IE_INVAL;
10687 /* Select buffer format and extract XML from CMS*/
10688 switch (raw_type) {
10689 case RAWTYPE_DELIVERYINFO:
10690 message_ns = MESSAGE_NS_UNSIGNED;
10691 xml_stream = (void *) buffer;
10692 xml_stream_length = length;
10693 break;
10695 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10696 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10697 xml_stream = (void *) buffer;
10698 xml_stream_length = length;
10699 break;
10701 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10702 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10703 err = _isds_extract_cms_data(context, buffer, length,
10704 &xml_stream, &xml_stream_length);
10705 if (err) goto leave;
10706 break;
10708 default:
10709 isds_log_message(context, _("Bad raw delivery representation type"));
10710 return IE_INVAL;
10711 break;
10714 if (_isds_sizet2int(xml_stream_length) >= 0) {
10715 isds_log(ILF_ISDS, ILL_DEBUG,
10716 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10717 _isds_sizet2int(xml_stream_length), xml_stream);
10720 /* Convert delivery info XML stream into XPath context */
10721 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10722 if (!message_doc) {
10723 err = IE_XML;
10724 goto leave;
10726 xpath_ctx = xmlXPathNewContext(message_doc);
10727 if (!xpath_ctx) {
10728 err = IE_ERROR;
10729 goto leave;
10731 /* XXX: Name spaces mangled for signed delivery info:
10732 * http://isds.czechpoint.cz/v20/delivery:
10734 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10735 * <q:dmDelivery>
10736 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10737 * <p:dmID>170272</p:dmID>
10738 * ...
10739 * </p:dmDm>
10740 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10741 * ...
10742 * </q:dmEvents>...</q:dmEvents>
10743 * </q:dmDelivery>
10744 * </q:GetDeliveryInfoResponse>
10745 * */
10746 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10747 err = IE_ERROR;
10748 goto leave;
10750 result = xmlXPathEvalExpression(
10751 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10752 xpath_ctx);
10753 if (!result) {
10754 err = IE_ERROR;
10755 goto leave;
10757 /* Empty delivery info */
10758 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10759 isds_printf_message(context,
10760 _("XML document is not sisds:dmDelivery document"));
10761 err = IE_ISDS;
10762 goto leave;
10764 /* More delivery info's */
10765 if (result->nodesetval->nodeNr > 1) {
10766 isds_printf_message(context,
10767 _("XML document has more sisds:dmDelivery elements"));
10768 err = IE_ISDS;
10769 goto leave;
10771 /* One delivery info */
10772 xpath_ctx->node = result->nodesetval->nodeTab[0];
10774 /* Extract the envelope (= message without documents, hence 0).
10775 * XXX: extract_TReturnedMessage() can obtain attachments size,
10776 * but delivery info carries none. It's coded as option elements,
10777 * so it should work. */
10778 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10779 if (err) goto leave;
10781 /* Extract events */
10782 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10783 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10784 if (err) { err = IE_ERROR; goto leave; }
10785 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10786 if (err) goto leave;
10788 /* Append raw CMS structure into message */
10789 (*message)->raw_type = raw_type;
10790 switch (strategy) {
10791 case BUFFER_DONT_STORE:
10792 break;
10793 case BUFFER_COPY:
10794 (*message)->raw = malloc(length);
10795 if (!(*message)->raw) {
10796 err = IE_NOMEM;
10797 goto leave;
10799 memcpy((*message)->raw, buffer, length);
10800 (*message)->raw_length = length;
10801 break;
10802 case BUFFER_MOVE:
10803 (*message)->raw = (void *) buffer;
10804 (*message)->raw_length = length;
10805 break;
10806 default:
10807 err = IE_ENUM;
10808 goto leave;
10811 leave:
10812 if (err) {
10813 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10814 isds_message_free(message);
10817 xmlXPathFreeObject(result);
10818 xmlXPathFreeContext(xpath_ctx);
10819 if (!*message || !(*message)->xml) {
10820 xmlFreeDoc(message_doc);
10822 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10824 if (!err)
10825 isds_log(ILF_ISDS, ILL_DEBUG,
10826 _("Delivery info loaded successfully.\n"));
10827 return err;
10831 /* Download signed delivery info-sheet of given message identified by ID.
10832 * @context is session context
10833 * @message_id is message identifier (you can get them from
10834 * isds_get_list_of_{sent,received}_messages())
10835 * @message is automatically reallocated message retrieved from ISDS.
10836 * It will miss documents per se. Use isds_get_signed_received_message(),
10837 * if you are interested in documents (content). OTOH, only this function
10838 * can get list events message has gone through. */
10839 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10840 const char *message_id, struct isds_message **message) {
10842 isds_error err = IE_SUCCESS;
10843 #if HAVE_LIBCURL
10844 xmlDocPtr response = NULL;
10845 xmlChar *code = NULL, *status_message = NULL;
10846 void *raw = NULL;
10847 size_t raw_length = 0;
10848 #endif
10850 if (!context) return IE_INVALID_CONTEXT;
10851 zfree(context->long_message);
10853 /* Free former message if any */
10854 if (!message) return IE_INVAL;
10855 isds_message_free(message);
10857 #if HAVE_LIBCURL
10858 /* Do request and check for success */
10859 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10860 BAD_CAST "GetSignedDeliveryInfo", message_id,
10861 &response, NULL, NULL, &code, &status_message);
10862 if (err) goto leave;
10864 /* Find signed delivery info, extract it into raw and maybe free
10865 * response */
10866 err = find_extract_signed_data_free_response(context,
10867 (xmlChar *)message_id, &response,
10868 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10869 if (err) goto leave;
10871 /* Parse delivery info */
10872 err = isds_load_delivery_info(context,
10873 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10874 message, BUFFER_MOVE);
10875 if (err) goto leave;
10877 raw = NULL;
10879 leave:
10880 if (err) {
10881 isds_message_free(message);
10884 free(raw);
10885 free(code);
10886 free(status_message);
10887 xmlFreeDoc(response);
10889 if (!err)
10890 isds_log(ILF_ISDS, ILL_DEBUG,
10891 _("GetSignedDeliveryInfo request processed by server "
10892 "successfully.\n")
10894 #else /* not HAVE_LIBCURL */
10895 err = IE_NOTSUP;
10896 #endif
10897 return err;
10901 /* Download delivery info-sheet of given message identified by ID.
10902 * @context is session context
10903 * @message_id is message identifier (you can get them from
10904 * isds_get_list_of_{sent,received}_messages())
10905 * @message is automatically reallocated message retrieved from ISDS.
10906 * It will miss documents per se. Use isds_get_received_message(), if you are
10907 * interested in documents (content). OTOH, only this function can get list
10908 * of events message has gone through. */
10909 isds_error isds_get_delivery_info(struct isds_ctx *context,
10910 const char *message_id, struct isds_message **message) {
10912 isds_error err = IE_SUCCESS;
10913 #if HAVE_LIBCURL
10914 xmlDocPtr response = NULL;
10915 xmlChar *code = NULL, *status_message = NULL;
10916 xmlNodePtr delivery_node = NULL;
10917 void *raw = NULL;
10918 size_t raw_length = 0;
10919 #endif
10921 if (!context) return IE_INVALID_CONTEXT;
10922 zfree(context->long_message);
10924 /* Free former message if any */
10925 if (!message) return IE_INVAL;
10926 isds_message_free(message);
10928 #if HAVE_LIBCURL
10929 /* Do request and check for success */
10930 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10931 BAD_CAST "GetDeliveryInfo", message_id,
10932 &response, NULL, NULL, &code, &status_message);
10933 if (err) goto leave;
10936 /* Serialize delivery info */
10937 delivery_node = xmlDocGetRootElement(response);
10938 if (!delivery_node) {
10939 char *message_id_locale = _isds_utf82locale((char*) message_id);
10940 isds_printf_message(context,
10941 _("Server did not return any delivery info for ID `%s' "
10942 "on GetDeliveryInfo request"), message_id_locale);
10943 free(message_id_locale);
10944 err = IE_ISDS;
10945 goto leave;
10947 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10948 if (err) goto leave;
10950 /* Parse delivery info */
10951 /* TODO: Here we parse the response second time. We could single delivery
10952 * parser from isds_load_delivery_info() to make things faster. */
10953 err = isds_load_delivery_info(context,
10954 RAWTYPE_DELIVERYINFO, raw, raw_length,
10955 message, BUFFER_MOVE);
10956 if (err) goto leave;
10958 raw = NULL;
10961 leave:
10962 if (err) {
10963 isds_message_free(message);
10966 free(raw);
10967 free(code);
10968 free(status_message);
10969 xmlFreeDoc(response);
10971 if (!err)
10972 isds_log(ILF_ISDS, ILL_DEBUG,
10973 _("GetDeliveryInfo request processed by server "
10974 "successfully.\n")
10976 #else /* not HAVE_LIBCURL */
10977 err = IE_NOTSUP;
10978 #endif
10979 return err;
10983 /* Download incoming message identified by ID.
10984 * @context is session context
10985 * @message_id is message identifier (you can get them from
10986 * isds_get_list_of_received_messages())
10987 * @message is automatically reallocated message retrieved from ISDS */
10988 isds_error isds_get_received_message(struct isds_ctx *context,
10989 const char *message_id, struct isds_message **message) {
10991 isds_error err = IE_SUCCESS;
10992 #if HAVE_LIBCURL
10993 xmlDocPtr response = NULL;
10994 void *xml_stream = NULL;
10995 size_t xml_stream_length;
10996 xmlChar *code = NULL, *status_message = NULL;
10997 xmlXPathContextPtr xpath_ctx = NULL;
10998 xmlXPathObjectPtr result = NULL;
10999 char *phys_path = NULL;
11000 size_t phys_start, phys_end;
11001 #endif
11003 if (!context) return IE_INVALID_CONTEXT;
11004 zfree(context->long_message);
11006 /* Free former message if any */
11007 if (NULL == message) return IE_INVAL;
11008 if (message) isds_message_free(message);
11010 #if HAVE_LIBCURL
11011 /* Do request and check for success */
11012 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
11013 BAD_CAST "MessageDownload", message_id,
11014 &response, &xml_stream, &xml_stream_length,
11015 &code, &status_message);
11016 if (err) goto leave;
11018 /* Extract data */
11019 xpath_ctx = xmlXPathNewContext(response);
11020 if (!xpath_ctx) {
11021 err = IE_ERROR;
11022 goto leave;
11024 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11025 err = IE_ERROR;
11026 goto leave;
11028 result = xmlXPathEvalExpression(
11029 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
11030 xpath_ctx);
11031 if (!result) {
11032 err = IE_ERROR;
11033 goto leave;
11035 /* Empty response */
11036 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11037 char *message_id_locale = _isds_utf82locale((char*) message_id);
11038 isds_printf_message(context,
11039 _("Server did not return any message for ID `%s' "
11040 "on MessageDownload request"), message_id_locale);
11041 free(message_id_locale);
11042 err = IE_ISDS;
11043 goto leave;
11045 /* More messages */
11046 if (result->nodesetval->nodeNr > 1) {
11047 char *message_id_locale = _isds_utf82locale((char*) message_id);
11048 isds_printf_message(context,
11049 _("Server did return more messages for ID `%s' "
11050 "on MessageDownload request"), message_id_locale);
11051 free(message_id_locale);
11052 err = IE_ISDS;
11053 goto leave;
11055 /* One message */
11056 xpath_ctx->node = result->nodesetval->nodeTab[0];
11058 /* Extract the message */
11059 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
11060 if (err) goto leave;
11062 /* Locate raw XML blob */
11063 phys_path = strdup(
11064 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
11065 PHYSXML_ELEMENT_SEPARATOR
11066 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
11067 PHYSXML_ELEMENT_SEPARATOR
11068 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11070 if (!phys_path) {
11071 err = IE_NOMEM;
11072 goto leave;
11074 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11075 phys_path, &phys_start, &phys_end);
11076 zfree(phys_path);
11077 if (err) {
11078 isds_log_message(context,
11079 _("Substring with isds:MessageDownloadResponse element "
11080 "could not be located in raw SOAP message"));
11081 goto leave;
11083 /* Save XML blob */
11084 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
11085 &(*message)->raw_length);*/
11086 /* TODO: Store name space declarations from ancestors */
11087 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
11088 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
11089 (*message)->raw_length = phys_end - phys_start + 1;
11090 (*message)->raw = malloc((*message)->raw_length);
11091 if (!(*message)->raw) {
11092 err = IE_NOMEM;
11093 goto leave;
11095 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
11098 leave:
11099 if (err) {
11100 isds_message_free(message);
11103 free(phys_path);
11105 xmlXPathFreeObject(result);
11106 xmlXPathFreeContext(xpath_ctx);
11108 free(code);
11109 free(status_message);
11110 free(xml_stream);
11111 if (!*message || !(*message)->xml) {
11112 xmlFreeDoc(response);
11115 if (!err)
11116 isds_log(ILF_ISDS, ILL_DEBUG,
11117 _("MessageDownload request processed by server "
11118 "successfully.\n")
11120 #else /* not HAVE_LIBCURL */
11121 err = IE_NOTSUP;
11122 #endif
11123 return err;
11127 /* Load message of any type from buffer.
11128 * @context is session context
11129 * @raw_type defines content type of @buffer. Only message types are allowed.
11130 * @buffer is message raw representation. Format (CMS, plain signed,
11131 * message direction) is defined in @raw_type. You can retrieve such data
11132 * from message->raw after calling isds_get_[signed]{received,sent}_message().
11133 * @length is length of buffer in bytes.
11134 * @message is automatically reallocated message parsed from @buffer.
11135 * @strategy selects how buffer will be attached into raw isds_message member.
11136 * */
11137 isds_error isds_load_message(struct isds_ctx *context,
11138 const isds_raw_type raw_type, const void *buffer, const size_t length,
11139 struct isds_message **message, const isds_buffer_strategy strategy) {
11141 isds_error err = IE_SUCCESS;
11142 void *xml_stream = NULL;
11143 size_t xml_stream_length = 0;
11144 message_ns_type message_ns;
11145 xmlDocPtr message_doc = NULL;
11146 xmlXPathContextPtr xpath_ctx = NULL;
11147 xmlXPathObjectPtr result = NULL;
11149 if (!context) return IE_INVALID_CONTEXT;
11150 zfree(context->long_message);
11151 if (!message) return IE_INVAL;
11152 isds_message_free(message);
11153 if (!buffer) return IE_INVAL;
11156 /* Select buffer format and extract XML from CMS*/
11157 switch (raw_type) {
11158 case RAWTYPE_INCOMING_MESSAGE:
11159 message_ns = MESSAGE_NS_UNSIGNED;
11160 xml_stream = (void *) buffer;
11161 xml_stream_length = length;
11162 break;
11164 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11165 message_ns = MESSAGE_NS_SIGNED_INCOMING;
11166 xml_stream = (void *) buffer;
11167 xml_stream_length = length;
11168 break;
11170 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11171 message_ns = MESSAGE_NS_SIGNED_INCOMING;
11172 err = _isds_extract_cms_data(context, buffer, length,
11173 &xml_stream, &xml_stream_length);
11174 if (err) goto leave;
11175 break;
11177 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11178 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11179 xml_stream = (void *) buffer;
11180 xml_stream_length = length;
11181 break;
11183 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11184 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11185 err = _isds_extract_cms_data(context, buffer, length,
11186 &xml_stream, &xml_stream_length);
11187 if (err) goto leave;
11188 break;
11190 default:
11191 isds_log_message(context, _("Bad raw message representation type"));
11192 return IE_INVAL;
11193 break;
11196 if (_isds_sizet2int(xml_stream_length) >= 0) {
11197 isds_log(ILF_ISDS, ILL_DEBUG,
11198 _("Loading message:\n%.*s\nEnd of message\n"),
11199 _isds_sizet2int(xml_stream_length), xml_stream);
11202 /* Convert messages XML stream into XPath context */
11203 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
11204 if (!message_doc) {
11205 err = IE_XML;
11206 goto leave;
11208 xpath_ctx = xmlXPathNewContext(message_doc);
11209 if (!xpath_ctx) {
11210 err = IE_ERROR;
11211 goto leave;
11213 /* XXX: Standard name space for unsigned incoming direction:
11214 * http://isds.czechpoint.cz/v20/
11216 * XXX: Name spaces mangled for signed outgoing direction:
11217 * http://isds.czechpoint.cz/v20/SentMessage:
11219 * <q:MessageDownloadResponse
11220 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11221 * <q:dmReturnedMessage>
11222 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11223 * <p:dmID>151916</p:dmID>
11224 * ...
11225 * </p:dmDm>
11226 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11227 * ...
11228 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11229 * </q:dmReturnedMessage>
11230 * </q:MessageDownloadResponse>
11232 * XXX: Name spaces mangled for signed incoming direction:
11233 * http://isds.czechpoint.cz/v20/message:
11235 * <q:MessageDownloadResponse
11236 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11237 * <q:dmReturnedMessage>
11238 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11239 * <p:dmID>151916</p:dmID>
11240 * ...
11241 * </p:dmDm>
11242 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11243 * ...
11244 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11245 * </q:dmReturnedMessage>
11246 * </q:MessageDownloadResponse>
11248 * Stupidity of ISDS developers is unlimited */
11249 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
11250 err = IE_ERROR;
11251 goto leave;
11253 result = xmlXPathEvalExpression(
11254 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11255 xpath_ctx);
11256 if (!result) {
11257 err = IE_ERROR;
11258 goto leave;
11260 /* Empty message */
11261 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11262 isds_printf_message(context,
11263 _("XML document does not contain "
11264 "sisds:dmReturnedMessage element"));
11265 err = IE_ISDS;
11266 goto leave;
11268 /* More messages */
11269 if (result->nodesetval->nodeNr > 1) {
11270 isds_printf_message(context,
11271 _("XML document has more sisds:dmReturnedMessage elements"));
11272 err = IE_ISDS;
11273 goto leave;
11275 /* One message */
11276 xpath_ctx->node = result->nodesetval->nodeTab[0];
11278 /* Extract the message */
11279 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
11280 if (err) goto leave;
11282 /* Append raw buffer into message */
11283 (*message)->raw_type = raw_type;
11284 switch (strategy) {
11285 case BUFFER_DONT_STORE:
11286 break;
11287 case BUFFER_COPY:
11288 (*message)->raw = malloc(length);
11289 if (!(*message)->raw) {
11290 err = IE_NOMEM;
11291 goto leave;
11293 memcpy((*message)->raw, buffer, length);
11294 (*message)->raw_length = length;
11295 break;
11296 case BUFFER_MOVE:
11297 (*message)->raw = (void *) buffer;
11298 (*message)->raw_length = length;
11299 break;
11300 default:
11301 err = IE_ENUM;
11302 goto leave;
11306 leave:
11307 if (err) {
11308 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
11309 isds_message_free(message);
11312 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11313 xmlXPathFreeObject(result);
11314 xmlXPathFreeContext(xpath_ctx);
11315 if (!*message || !(*message)->xml) {
11316 xmlFreeDoc(message_doc);
11319 if (!err)
11320 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
11321 return err;
11325 /* Determine type of raw message or delivery info according some heuristics.
11326 * It does not validate the raw blob.
11327 * @context is session context
11328 * @raw_type returns content type of @buffer. Valid only if exit code of this
11329 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11330 * reallocated memory.
11331 * @buffer is message raw representation.
11332 * @length is length of buffer in bytes. */
11333 isds_error isds_guess_raw_type(struct isds_ctx *context,
11334 isds_raw_type *raw_type, const void *buffer, const size_t length) {
11335 isds_error err;
11336 void *xml_stream = NULL;
11337 size_t xml_stream_length = 0;
11338 xmlDocPtr document = NULL;
11339 xmlNodePtr root = NULL;
11341 if (!context) return IE_INVALID_CONTEXT;
11342 zfree(context->long_message);
11343 if (length == 0 || !buffer) return IE_INVAL;
11344 if (!raw_type) return IE_INVAL;
11346 /* Try CMS */
11347 err = _isds_extract_cms_data(context, buffer, length,
11348 &xml_stream, &xml_stream_length);
11349 if (err) {
11350 xml_stream = (void *) buffer;
11351 xml_stream_length = (size_t) length;
11352 err = IE_SUCCESS;
11355 /* Try XML */
11356 document = xmlParseMemory(xml_stream, xml_stream_length);
11357 if (!document) {
11358 isds_printf_message(context,
11359 _("Could not parse data as XML document"));
11360 err = IE_NOTSUP;
11361 goto leave;
11364 /* Get root element */
11365 root = xmlDocGetRootElement(document);
11366 if (!root) {
11367 isds_printf_message(context,
11368 _("XML document is missing root element"));
11369 err = IE_XML;
11370 goto leave;
11373 if (!root->ns || !root->ns->href) {
11374 isds_printf_message(context,
11375 _("Root element does not belong to any name space"));
11376 err = IE_NOTSUP;
11377 goto leave;
11380 /* Test name space */
11381 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
11382 if (xml_stream == buffer)
11383 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
11384 else
11385 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
11386 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
11387 if (xml_stream == buffer)
11388 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
11389 else
11390 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
11391 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
11392 if (xml_stream == buffer)
11393 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
11394 else
11395 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
11396 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
11397 if (xml_stream != buffer) {
11398 isds_printf_message(context,
11399 _("Document in ISDS name space is encapsulated into CMS" ));
11400 err = IE_NOTSUP;
11401 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
11402 *raw_type = RAWTYPE_INCOMING_MESSAGE;
11403 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
11404 *raw_type = RAWTYPE_DELIVERYINFO;
11405 else {
11406 isds_printf_message(context,
11407 _("Unknown root element in ISDS name space"));
11408 err = IE_NOTSUP;
11410 } else {
11411 isds_printf_message(context,
11412 _("Unknown name space"));
11413 err = IE_NOTSUP;
11416 leave:
11417 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11418 xmlFreeDoc(document);
11419 return err;
11423 /* Download signed incoming/outgoing message identified by ID.
11424 * @context is session context
11425 * @output is true for outgoing message, false for incoming message
11426 * @message_id is message identifier (you can get them from
11427 * isds_get_list_of_{sent,received}_messages())
11428 * @message is automatically reallocated message retrieved from ISDS. The raw
11429 * member will be filled with PKCS#7 structure in DER format. */
11430 static isds_error isds_get_signed_message(struct isds_ctx *context,
11431 const _Bool outgoing, const char *message_id,
11432 struct isds_message **message) {
11434 isds_error err = IE_SUCCESS;
11435 #if HAVE_LIBCURL
11436 xmlDocPtr response = NULL;
11437 xmlChar *code = NULL, *status_message = NULL;
11438 xmlXPathContextPtr xpath_ctx = NULL;
11439 xmlXPathObjectPtr result = NULL;
11440 char *encoded_structure = NULL;
11441 void *raw = NULL;
11442 size_t raw_length = 0;
11443 #endif
11445 if (!context) return IE_INVALID_CONTEXT;
11446 zfree(context->long_message);
11447 if (!message) return IE_INVAL;
11448 isds_message_free(message);
11450 #if HAVE_LIBCURL
11451 /* Do request and check for success */
11452 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
11453 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11454 BAD_CAST "SignedMessageDownload",
11455 message_id, &response, NULL, NULL, &code, &status_message);
11456 if (err) goto leave;
11458 /* Find signed message, extract it into raw and maybe free
11459 * response */
11460 err = find_extract_signed_data_free_response(context,
11461 (xmlChar *)message_id, &response,
11462 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11463 BAD_CAST "SignedMessageDownload",
11464 &raw, &raw_length);
11465 if (err) goto leave;
11467 /* Parse message */
11468 err = isds_load_message(context,
11469 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
11470 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
11471 raw, raw_length, message, BUFFER_MOVE);
11472 if (err) goto leave;
11474 raw = NULL;
11476 leave:
11477 if (err) {
11478 isds_message_free(message);
11481 free(encoded_structure);
11482 xmlXPathFreeObject(result);
11483 xmlXPathFreeContext(xpath_ctx);
11484 free(raw);
11486 free(code);
11487 free(status_message);
11488 xmlFreeDoc(response);
11490 if (!err)
11491 isds_log(ILF_ISDS, ILL_DEBUG,
11492 (outgoing) ?
11493 _("SignedSentMessageDownload request processed by server "
11494 "successfully.\n") :
11495 _("SignedMessageDownload request processed by server "
11496 "successfully.\n")
11498 #else /* not HAVE_LIBCURL */
11499 err = IE_NOTSUP;
11500 #endif
11501 return err;
11505 /* Download signed incoming message identified by ID.
11506 * @context is session context
11507 * @message_id is message identifier (you can get them from
11508 * isds_get_list_of_received_messages())
11509 * @message is automatically reallocated message retrieved from ISDS. The raw
11510 * member will be filled with PKCS#7 structure in DER format. */
11511 isds_error isds_get_signed_received_message(struct isds_ctx *context,
11512 const char *message_id, struct isds_message **message) {
11513 return isds_get_signed_message(context, 0, message_id, message);
11517 /* Download signed outgoing message identified by ID.
11518 * @context is session context
11519 * @message_id is message identifier (you can get them from
11520 * isds_get_list_of_sent_messages())
11521 * @message is automatically reallocated message retrieved from ISDS. The raw
11522 * member will be filled with PKCS#7 structure in DER format. */
11523 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
11524 const char *message_id, struct isds_message **message) {
11525 return isds_get_signed_message(context, 1, message_id, message);
11529 /* Get type and name of user who sent a message identified by ID.
11530 * @context is session context
11531 * @message_id is message identifier
11532 * @sender_type is pointer to automatically allocated type of sender detected
11533 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11534 * library or to the server, NULL will be returned. Pass NULL if you don't
11535 * care about it.
11536 * @raw_sender_type is automatically reallocated UTF-8 string describing
11537 * sender type or NULL if not known to server. Pass NULL if you don't care.
11538 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11539 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11540 isds_error isds_get_message_sender(struct isds_ctx *context,
11541 const char *message_id, isds_sender_type **sender_type,
11542 char **raw_sender_type, char **sender_name) {
11543 isds_error err = IE_SUCCESS;
11544 #if HAVE_LIBCURL
11545 xmlDocPtr response = NULL;
11546 xmlChar *code = NULL, *status_message = NULL;
11547 xmlXPathContextPtr xpath_ctx = NULL;
11548 xmlXPathObjectPtr result = NULL;
11549 char *type_string = NULL;
11550 #endif
11552 if (!context) return IE_INVALID_CONTEXT;
11553 zfree(context->long_message);
11554 if (sender_type) zfree(*sender_type);
11555 if (raw_sender_type) zfree(*raw_sender_type);
11556 if (sender_name) zfree(*sender_name);
11557 if (!message_id) return IE_INVAL;
11559 #if HAVE_LIBCURL
11560 /* Do request and check for success */
11561 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11562 BAD_CAST "GetMessageAuthor",
11563 message_id, &response, NULL, NULL, &code, &status_message);
11564 if (err) goto leave;
11566 /* Extract data */
11567 xpath_ctx = xmlXPathNewContext(response);
11568 if (!xpath_ctx) {
11569 err = IE_ERROR;
11570 goto leave;
11572 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11573 err = IE_ERROR;
11574 goto leave;
11576 result = xmlXPathEvalExpression(
11577 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
11578 if (!result) {
11579 err = IE_ERROR;
11580 goto leave;
11582 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11583 isds_log_message(context,
11584 _("Missing GetMessageAuthorResponse element"));
11585 err = IE_ISDS;
11586 goto leave;
11588 if (result->nodesetval->nodeNr > 1) {
11589 isds_log_message(context,
11590 _("Multiple GetMessageAuthorResponse element"));
11591 err = IE_ISDS;
11592 goto leave;
11594 xpath_ctx->node = result->nodesetval->nodeTab[0];
11595 xmlXPathFreeObject(result); result = NULL;
11597 /* Fill output arguments in */
11598 EXTRACT_STRING("isds:userType", type_string);
11599 if (NULL != type_string) {
11600 if (NULL != sender_type) {
11601 *sender_type = calloc(1, sizeof(**sender_type));
11602 if (NULL == *sender_type) {
11603 err = IE_NOMEM;
11604 goto leave;
11607 err = string2isds_sender_type((xmlChar *)type_string,
11608 *sender_type);
11609 if (err) {
11610 zfree(*sender_type);
11611 if (err == IE_ENUM) {
11612 err = IE_SUCCESS;
11613 char *type_string_locale = _isds_utf82locale(type_string);
11614 isds_log(ILF_ISDS, ILL_WARNING,
11615 _("Unknown isds:userType value: %s"),
11616 type_string_locale);
11617 free(type_string_locale);
11622 if (NULL == raw_sender_type)
11623 zfree(type_string);
11624 if (NULL != sender_name)
11625 EXTRACT_STRING("isds:authorName", *sender_name);
11627 leave:
11628 if (err) {
11629 if (NULL != sender_type) zfree(*sender_type);
11630 zfree(type_string);
11631 if (NULL != sender_name) zfree(*sender_name);
11633 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11635 xmlXPathFreeObject(result);
11636 xmlXPathFreeContext(xpath_ctx);
11638 free(code);
11639 free(status_message);
11640 xmlFreeDoc(response);
11642 if (!err)
11643 isds_log(ILF_ISDS, ILL_DEBUG,
11644 _("GetMessageAuthor request processed by server "
11645 "successfully.\n"));
11646 #else /* not HAVE_LIBCURL */
11647 err = IE_NOTSUP;
11648 #endif
11649 return err;
11653 /* Retrieve hash of message identified by ID stored in ISDS.
11654 * @context is session context
11655 * @message_id is message identifier
11656 * @hash is automatically reallocated message hash downloaded from ISDS.
11657 * Message must exist in system and must not be deleted. */
11658 isds_error isds_download_message_hash(struct isds_ctx *context,
11659 const char *message_id, struct isds_hash **hash) {
11661 isds_error err = IE_SUCCESS;
11662 #if HAVE_LIBCURL
11663 xmlDocPtr response = NULL;
11664 xmlChar *code = NULL, *status_message = NULL;
11665 xmlXPathContextPtr xpath_ctx = NULL;
11666 xmlXPathObjectPtr result = NULL;
11667 #endif
11669 if (!context) return IE_INVALID_CONTEXT;
11670 zfree(context->long_message);
11672 isds_hash_free(hash);
11674 #if HAVE_LIBCURL
11675 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11676 BAD_CAST "VerifyMessage", message_id,
11677 &response, NULL, NULL, &code, &status_message);
11678 if (err) goto leave;
11681 /* Extract data */
11682 xpath_ctx = xmlXPathNewContext(response);
11683 if (!xpath_ctx) {
11684 err = IE_ERROR;
11685 goto leave;
11687 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11688 err = IE_ERROR;
11689 goto leave;
11691 result = xmlXPathEvalExpression(
11692 BAD_CAST "/isds:VerifyMessageResponse",
11693 xpath_ctx);
11694 if (!result) {
11695 err = IE_ERROR;
11696 goto leave;
11698 /* Empty response */
11699 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11700 char *message_id_locale = _isds_utf82locale((char*) message_id);
11701 isds_printf_message(context,
11702 _("Server did not return any response for ID `%s' "
11703 "on VerifyMessage request"), message_id_locale);
11704 free(message_id_locale);
11705 err = IE_ISDS;
11706 goto leave;
11708 /* More responses */
11709 if (result->nodesetval->nodeNr > 1) {
11710 char *message_id_locale = _isds_utf82locale((char*) message_id);
11711 isds_printf_message(context,
11712 _("Server did return more responses for ID `%s' "
11713 "on VerifyMessage request"), message_id_locale);
11714 free(message_id_locale);
11715 err = IE_ISDS;
11716 goto leave;
11718 /* One response */
11719 xpath_ctx->node = result->nodesetval->nodeTab[0];
11721 /* Extract the hash */
11722 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11724 leave:
11725 if (err) {
11726 isds_hash_free(hash);
11729 xmlXPathFreeObject(result);
11730 xmlXPathFreeContext(xpath_ctx);
11732 free(code);
11733 free(status_message);
11734 xmlFreeDoc(response);
11736 if (!err)
11737 isds_log(ILF_ISDS, ILL_DEBUG,
11738 _("VerifyMessage request processed by server "
11739 "successfully.\n")
11741 #else /* not HAVE_LIBCURL */
11742 err = IE_NOTSUP;
11743 #endif
11744 return err;
11748 /* Erase message specified by @message_id from long term storage. Other
11749 * message cannot be erased on user request.
11750 * @context is session context
11751 * @message_id is message identifier.
11752 * @incoming is true for incoming message, false for outgoing message.
11753 * @return
11754 * IE_SUCCESS if message has ben removed
11755 * IE_INVAL if message does not exist in long term storage or message
11756 * belongs to different box
11757 * TODO: IE_NOEPRM if user has no permission to erase a message */
11758 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11759 const char *message_id, _Bool incoming) {
11760 isds_error err = IE_SUCCESS;
11761 #if HAVE_LIBCURL
11762 xmlNodePtr request = NULL, node;
11763 xmlNsPtr isds_ns = NULL;
11764 xmlDocPtr response = NULL;
11765 xmlChar *code = NULL, *status_message = NULL;
11766 #endif
11768 if (!context) return IE_INVALID_CONTEXT;
11769 zfree(context->long_message);
11770 if (NULL == message_id) return IE_INVAL;
11772 #if HAVE_LIBCURL
11773 /* Check if connection is established
11774 * TODO: This check should be done downstairs. */
11775 if (!context->curl) return IE_CONNECTION_CLOSED;
11777 /* Build request */
11778 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11779 if (!request) {
11780 isds_log_message(context,
11781 _("Could build EraseMessage request"));
11782 return IE_ERROR;
11784 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11785 if(!isds_ns) {
11786 isds_log_message(context, _("Could not create ISDS name space"));
11787 xmlFreeNode(request);
11788 return IE_ERROR;
11790 xmlSetNs(request, isds_ns);
11792 err = validate_message_id_length(context, (xmlChar *) message_id);
11793 if (err) goto leave;
11794 INSERT_STRING(request, "dmID", message_id);
11796 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11799 /* Send request */
11800 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11801 "message ID %s to ISDS\n"), message_id);
11802 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11803 xmlFreeNode(request); request = NULL;
11805 if (err) {
11806 isds_log(ILF_ISDS, ILL_DEBUG,
11807 _("Processing ISDS response on EraseMessage request "
11808 "failed\n"));
11809 goto leave;
11812 /* Check for response status */
11813 err = isds_response_status(context, SERVICE_DM_INFO, response,
11814 &code, &status_message, NULL);
11815 if (err) {
11816 isds_log(ILF_ISDS, ILL_DEBUG,
11817 _("ISDS response on EraseMessage request is missing "
11818 "status\n"));
11819 goto leave;
11822 /* Check server status code */
11823 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11824 isds_log_message(context, _("Message to erase belongs to other box"));
11825 err = IE_INVAL;
11826 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11827 isds_log_message(context, _("Message to erase is not saved in "
11828 "long term storage or the direction does not match"));
11829 err = IE_INVAL;
11830 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11831 char *code_locale = _isds_utf82locale((char*) code);
11832 char *message_locale = _isds_utf82locale((char*) status_message);
11833 isds_log(ILF_ISDS, ILL_DEBUG,
11834 _("Server refused EraseMessage request "
11835 "(code=%s, message=%s)\n"),
11836 code_locale, message_locale);
11837 isds_log_message(context, message_locale);
11838 free(code_locale);
11839 free(message_locale);
11840 err = IE_ISDS;
11841 goto leave;
11844 leave:
11845 free(code);
11846 free(status_message);
11847 xmlFreeDoc(response);
11848 xmlFreeNode(request);
11850 if (!err)
11851 isds_log(ILF_ISDS, ILL_DEBUG,
11852 _("EraseMessage request processed by server "
11853 "successfully.\n")
11855 #else /* not HAVE_LIBCURL */
11856 err = IE_NOTSUP;
11857 #endif
11858 return err;
11862 /* Mark message as read. This is a transactional commit function to acknowledge
11863 * to ISDS the message has been downloaded and processed by client properly.
11864 * @context is session context
11865 * @message_id is message identifier. */
11866 isds_error isds_mark_message_read(struct isds_ctx *context,
11867 const char *message_id) {
11869 isds_error err = IE_SUCCESS;
11870 #if HAVE_LIBCURL
11871 xmlDocPtr response = NULL;
11872 xmlChar *code = NULL, *status_message = NULL;
11873 #endif
11875 if (!context) return IE_INVALID_CONTEXT;
11876 zfree(context->long_message);
11878 #if HAVE_LIBCURL
11879 /* Do request and check for success */
11880 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11881 BAD_CAST "MarkMessageAsDownloaded", message_id,
11882 &response, NULL, NULL, &code, &status_message);
11884 free(code);
11885 free(status_message);
11886 xmlFreeDoc(response);
11888 if (!err)
11889 isds_log(ILF_ISDS, ILL_DEBUG,
11890 _("MarkMessageAsDownloaded request processed by server "
11891 "successfully.\n")
11893 #else /* not HAVE_LIBCURL */
11894 err = IE_NOTSUP;
11895 #endif
11896 return err;
11900 /* Mark message as received by recipient. This is applicable only to
11901 * commercial message. Use envelope->dmType message member to distinguish
11902 * commercial message from government message. Government message is
11903 * received automatically (by law), commercial message on recipient request.
11904 * @context is session context
11905 * @message_id is message identifier. */
11906 isds_error isds_mark_message_received(struct isds_ctx *context,
11907 const char *message_id) {
11909 isds_error err = IE_SUCCESS;
11910 #if HAVE_LIBCURL
11911 xmlDocPtr response = NULL;
11912 xmlChar *code = NULL, *status_message = NULL;
11913 #endif
11915 if (!context) return IE_INVALID_CONTEXT;
11916 zfree(context->long_message);
11918 #if HAVE_LIBCURL
11919 /* Do request and check for success */
11920 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11921 BAD_CAST "ConfirmDelivery", message_id,
11922 &response, NULL, NULL, &code, &status_message);
11924 free(code);
11925 free(status_message);
11926 xmlFreeDoc(response);
11928 if (!err)
11929 isds_log(ILF_ISDS, ILL_DEBUG,
11930 _("ConfirmDelivery request processed by server "
11931 "successfully.\n")
11933 #else /* not HAVE_LIBCURL */
11934 err = IE_NOTSUP;
11935 #endif
11936 return err;
11940 /* Send document for authorized conversion into Czech POINT system.
11941 * This is public anonymous service, no log-in necessary. Special context is
11942 * used to reuse keep-a-live HTTPS connection.
11943 * @context is Czech POINT session context. DO NOT use context connected to
11944 * ISDS server. Use new context or context used by this function previously.
11945 * @document is document to convert. Only data, data_length, dmFileDescr and
11946 * is_xml members are significant. Be ware that not all document formats can be
11947 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11948 * @id is reallocated identifier assigned by Czech POINT system to
11949 * your document on submit. Use is to tell it to Czech POINT officer.
11950 * @date is reallocated document submit date (submitted documents
11951 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11952 * value. */
11953 isds_error czp_convert_document(struct isds_ctx *context,
11954 const struct isds_document *document,
11955 char **id, struct tm **date) {
11956 isds_error err = IE_SUCCESS;
11957 #if HAVE_LIBCURL
11958 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11959 xmlNodePtr request = NULL, node;
11960 xmlDocPtr response = NULL;
11962 xmlXPathContextPtr xpath_ctx = NULL;
11963 xmlXPathObjectPtr result = NULL;
11964 long int status = -1;
11965 long int *status_ptr = &status;
11966 char *string = NULL;
11967 #endif
11970 if (!context) return IE_INVALID_CONTEXT;
11971 zfree(context->long_message);
11972 if (!document || !id || !date) return IE_INVAL;
11974 if (document->is_xml) {
11975 isds_log_message(context,
11976 _("XML documents cannot be submitted to conversion"));
11977 return IE_NOTSUP;
11980 /* Free output arguments */
11981 zfree(*id);
11982 zfree(*date);
11984 #if HAVE_LIBCURL
11985 /* Store configuration */
11986 context->type = CTX_TYPE_CZP;
11987 free(context->url);
11988 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11989 if (!(context->url))
11990 return IE_NOMEM;
11992 /* Prepare CURL handle if not yet connected */
11993 if (!context->curl) {
11994 context->curl = curl_easy_init();
11995 if (!(context->curl))
11996 return IE_ERROR;
11999 /* Build conversion request */
12000 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
12001 if (!request) {
12002 isds_log_message(context,
12003 _("Could not build Czech POINT conversion request"));
12004 return IE_ERROR;
12006 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
12007 if(!deposit_ns) {
12008 isds_log_message(context,
12009 _("Could not create Czech POINT deposit name space"));
12010 xmlFreeNode(request);
12011 return IE_ERROR;
12013 xmlSetNs(request, deposit_ns);
12015 /* Insert children. They are in empty namespace! */
12016 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
12017 if(!empty_ns) {
12018 isds_log_message(context, _("Could not create empty name space"));
12019 err = IE_ERROR;
12020 goto leave;
12022 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
12023 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
12024 document->dmFileDescr);
12026 /* Document encoded in Base64 */
12027 err = insert_base64_encoded_string(context, request, empty_ns, "document",
12028 document->data, document->data_length);
12029 if (err) goto leave;
12031 isds_log(ILF_ISDS, ILL_DEBUG,
12032 _("Submitting document for conversion into Czech POINT deposit"));
12034 /* Send conversion request */
12035 err = _czp_czpdeposit(context, request, &response);
12036 xmlFreeNode(request); request = NULL;
12038 if (err) {
12039 czp_do_close_connection(context);
12040 goto leave;
12044 /* Extract response */
12045 xpath_ctx = xmlXPathNewContext(response);
12046 if (!xpath_ctx) {
12047 err = IE_ERROR;
12048 goto leave;
12050 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12051 err = IE_ERROR;
12052 goto leave;
12054 result = xmlXPathEvalExpression(
12055 BAD_CAST "/deposit:saveDocumentResponse/return",
12056 xpath_ctx);
12057 if (!result) {
12058 err = IE_ERROR;
12059 goto leave;
12061 /* Empty response */
12062 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
12063 isds_printf_message(context,
12064 _("Missing `return' element in Czech POINT deposit response"));
12065 err = IE_ISDS;
12066 goto leave;
12068 /* More responses */
12069 if (result->nodesetval->nodeNr > 1) {
12070 isds_printf_message(context,
12071 _("Multiple `return' element in Czech POINT deposit response"));
12072 err = IE_ISDS;
12073 goto leave;
12075 /* One response */
12076 xpath_ctx->node = result->nodesetval->nodeTab[0];
12078 /* Get status */
12079 EXTRACT_LONGINT("status", status_ptr, 1);
12080 if (status) {
12081 EXTRACT_STRING("statusMsg", string);
12082 char *string_locale = _isds_utf82locale(string);
12083 isds_printf_message(context,
12084 _("Czech POINT deposit refused document for conversion "
12085 "(code=%ld, message=%s)"),
12086 status, string_locale);
12087 free(string_locale);
12088 err = IE_ISDS;
12089 goto leave;
12092 /* Get document ID */
12093 EXTRACT_STRING("documentID", *id);
12095 /* Get submit date */
12096 EXTRACT_STRING("dateInserted", string);
12097 if (string) {
12098 *date = calloc(1, sizeof(**date));
12099 if (!*date) {
12100 err = IE_NOMEM;
12101 goto leave;
12103 err = _isds_datestring2tm((xmlChar *)string, *date);
12104 if (err) {
12105 if (err == IE_NOTSUP) {
12106 err = IE_ISDS;
12107 char *string_locale = _isds_utf82locale(string);
12108 isds_printf_message(context,
12109 _("Invalid dateInserted value: %s"), string_locale);
12110 free(string_locale);
12112 goto leave;
12116 leave:
12117 free(string);
12118 xmlXPathFreeObject(result);
12119 xmlXPathFreeContext(xpath_ctx);
12121 xmlFreeDoc(response);
12122 xmlFreeNode(request);
12124 if (!err) {
12125 char *id_locale = _isds_utf82locale((char *) *id);
12126 isds_log(ILF_ISDS, ILL_DEBUG,
12127 _("Document %s has been submitted for conversion "
12128 "to server successfully\n"), id_locale);
12129 free(id_locale);
12131 #else /* not HAVE_LIBCURL */
12132 err = IE_NOTSUP;
12133 #endif
12134 return err;
12138 /* Close possibly opened connection to Czech POINT document deposit.
12139 * @context is Czech POINT session context. */
12140 isds_error czp_close_connection(struct isds_ctx *context) {
12141 if (!context) return IE_INVALID_CONTEXT;
12142 zfree(context->long_message);
12143 #if HAVE_LIBCURL
12144 return czp_do_close_connection(context);
12145 #else
12146 return IE_NOTSUP;
12147 #endif
12151 /* Send request for new box creation in testing ISDS instance.
12152 * It's not possible to request for a production box currently, as it
12153 * communicates via e-mail.
12154 * XXX: This function does not work either. Server complains about invalid
12155 * e-mail address.
12156 * XXX: Remove context->type hacks in isds.c and validator.c when removing
12157 * this function
12158 * @context is special session context for box creation request. DO NOT use
12159 * standard context as it could reveal your password. Use fresh new context or
12160 * context previously used by this function.
12161 * @box is box description to create including single primary user (in case of
12162 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
12163 * ignored. It outputs box ID assigned by ISDS in dbID element.
12164 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
12165 * box, or contact address of PFO box owner). The email member is mandatory as
12166 * it will be used to deliver credentials.
12167 * @former_names is former name of box owner. Pass NULL if you don't care.
12168 * @approval is optional external approval of box manipulation
12169 * @refnumber is reallocated serial number of request assigned by ISDS. Use
12170 * NULL, if you don't care.*/
12171 isds_error isds_request_new_testing_box(struct isds_ctx *context,
12172 struct isds_DbOwnerInfo *box, const struct isds_list *users,
12173 const char *former_names, const struct isds_approval *approval,
12174 char **refnumber) {
12175 isds_error err = IE_SUCCESS;
12176 #if HAVE_LIBCURL
12177 xmlNodePtr request = NULL;
12178 xmlDocPtr response = NULL;
12179 xmlXPathContextPtr xpath_ctx = NULL;
12180 xmlXPathObjectPtr result = NULL;
12181 #endif
12184 if (!context) return IE_INVALID_CONTEXT;
12185 zfree(context->long_message);
12186 if (!box) return IE_INVAL;
12188 #if HAVE_LIBCURL
12189 if (!box->email || box->email[0] == '\0') {
12190 isds_log_message(context, _("E-mail field is mandatory"));
12191 return IE_INVAL;
12194 /* Scratch box ID */
12195 zfree(box->dbID);
12197 /* Store configuration */
12198 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
12199 free(context->url);
12200 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
12201 if (!(context->url))
12202 return IE_NOMEM;
12204 /* Prepare CURL handle if not yet connected */
12205 if (!context->curl) {
12206 context->curl = curl_easy_init();
12207 if (!(context->curl))
12208 return IE_ERROR;
12211 /* Build CreateDataBox request */
12212 err = build_CreateDBInput_request(context,
12213 &request, BAD_CAST "CreateDataBox",
12214 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
12215 if (err) goto leave;
12217 /* Send it to server and process response */
12218 err = send_destroy_request_check_response(context,
12219 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
12220 &response, (xmlChar **) refnumber, NULL);
12221 if (err) goto leave;
12223 /* Extract box ID */
12224 xpath_ctx = xmlXPathNewContext(response);
12225 if (!xpath_ctx) {
12226 err = IE_ERROR;
12227 goto leave;
12229 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12230 err = IE_ERROR;
12231 goto leave;
12233 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
12235 leave:
12236 xmlXPathFreeObject(result);
12237 xmlXPathFreeContext(xpath_ctx);
12238 xmlFreeDoc(response);
12239 xmlFreeNode(request);
12241 if (!err) {
12242 isds_log(ILF_ISDS, ILL_DEBUG,
12243 _("CreateDataBox request processed by server successfully.\n"));
12245 #else /* not HAVE_LIBCURL */
12246 err = IE_NOTSUP;
12247 #endif
12249 return err;
12253 /* Submit CMS signed message to ISDS to verify its originality. This is
12254 * stronger form of isds_verify_message_hash() because ISDS does more checks
12255 * than simple one (potentialy old weak) hash comparison.
12256 * @context is session context
12257 * @message is memory with raw CMS signed message bit stream
12258 * @length is @message size in bytes
12259 * @return
12260 * IE_SUCCESS if message originates in ISDS
12261 * IE_NOTEQUAL if message is unknown to ISDS
12262 * other code for other errors */
12263 isds_error isds_authenticate_message(struct isds_ctx *context,
12264 const void *message, size_t length) {
12265 isds_error err = IE_SUCCESS;
12266 #if HAVE_LIBCURL
12267 xmlNsPtr isds_ns = NULL;
12268 xmlNodePtr request = NULL;
12269 xmlDocPtr response = NULL;
12270 xmlXPathContextPtr xpath_ctx = NULL;
12271 xmlXPathObjectPtr result = NULL;
12272 _Bool *authentic = NULL;
12273 #endif
12275 if (!context) return IE_INVALID_CONTEXT;
12276 zfree(context->long_message);
12277 if (!message || length == 0) return IE_INVAL;
12279 #if HAVE_LIBCURL
12280 /* Check if connection is established
12281 * TODO: This check should be done downstairs. */
12282 if (!context->curl) return IE_CONNECTION_CLOSED;
12285 /* Build AuthenticateMessage request */
12286 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
12287 if (!request) {
12288 isds_log_message(context,
12289 _("Could not build AuthenticateMessage request"));
12290 return IE_ERROR;
12292 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12293 if(!isds_ns) {
12294 isds_log_message(context, _("Could not create ISDS name space"));
12295 xmlFreeNode(request);
12296 return IE_ERROR;
12298 xmlSetNs(request, isds_ns);
12300 /* Insert Base64 encoded message */
12301 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
12302 message, length);
12303 if (err) goto leave;
12305 /* Send request to server and process response */
12306 err = send_destroy_request_check_response(context,
12307 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
12308 &response, NULL, NULL);
12309 if (err) goto leave;
12312 /* ISDS has decided */
12313 xpath_ctx = xmlXPathNewContext(response);
12314 if (!xpath_ctx) {
12315 err = IE_ERROR;
12316 goto leave;
12318 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12319 err = IE_ERROR;
12320 goto leave;
12323 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
12325 if (!authentic) {
12326 isds_log_message(context,
12327 _("Server did not return any response on "
12328 "AuthenticateMessage request"));
12329 err = IE_ISDS;
12330 goto leave;
12332 if (*authentic) {
12333 isds_log(ILF_ISDS, ILL_DEBUG,
12334 _("ISDS authenticated the message successfully\n"));
12335 } else {
12336 isds_log_message(context, _("ISDS does not know the message"));
12337 err = IE_NOTEQUAL;
12341 leave:
12342 free(authentic);
12343 xmlXPathFreeObject(result);
12344 xmlXPathFreeContext(xpath_ctx);
12346 xmlFreeDoc(response);
12347 xmlFreeNode(request);
12348 #else /* not HAVE_LIBCURL */
12349 err = IE_NOTSUP;
12350 #endif
12352 return err;
12356 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12357 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12358 * be re-signed.
12359 * @context is session context
12360 * @input_data is memory with raw CMS signed message or delivery info bit
12361 * stream to re-sign
12362 * @input_length is @input_data size in bytes
12363 * @output_data is pointer to auto-allocated memory where to store re-signed
12364 * input data blob. Caller must free it.
12365 * @output_data is pointer where to store @output_data size in bytes
12366 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12367 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12368 * @return
12369 * IE_SUCCESS if CMS blob has been re-signed successfully
12370 * other code for other errors */
12371 isds_error isds_resign_message(struct isds_ctx *context,
12372 const void *input_data, size_t input_length,
12373 void **output_data, size_t *output_length, struct tm **valid_to) {
12374 isds_error err = IE_SUCCESS;
12375 #if HAVE_LIBCURL
12376 xmlNsPtr isds_ns = NULL;
12377 xmlNodePtr request = NULL;
12378 xmlDocPtr response = NULL;
12379 xmlXPathContextPtr xpath_ctx = NULL;
12380 xmlXPathObjectPtr result = NULL;
12381 char *string = NULL;
12382 const xmlChar *codes[] = {
12383 BAD_CAST "2200",
12384 BAD_CAST "2201",
12385 BAD_CAST "2204",
12386 BAD_CAST "2207",
12387 NULL
12389 const char *meanings[] = {
12390 "Message is bad",
12391 "Message is not original",
12392 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12393 "Time stamp could not been generated in time"
12395 const isds_error errors[] = {
12396 IE_INVAL,
12397 IE_NOTUNIQ,
12398 IE_INVAL,
12399 IE_ISDS,
12401 struct code_map_isds_error map = {
12402 .codes = codes,
12403 .meanings = meanings,
12404 .errors = errors
12406 #endif
12408 if (NULL != output_data) *output_data = NULL;
12409 if (NULL != output_length) *output_length = 0;
12410 if (NULL != valid_to) *valid_to = NULL;
12412 if (NULL == context) return IE_INVALID_CONTEXT;
12413 zfree(context->long_message);
12414 if (NULL == input_data || 0 == input_length) {
12415 isds_log_message(context, _("Empty CMS blob on input"));
12416 return IE_INVAL;
12418 if (NULL == output_data || NULL == output_length) {
12419 isds_log_message(context,
12420 _("NULL pointer provided for output CMS blob"));
12421 return IE_INVAL;
12424 #if HAVE_LIBCURL
12425 /* Check if connection is established
12426 * TODO: This check should be done downstairs. */
12427 if (!context->curl) return IE_CONNECTION_CLOSED;
12430 /* Build Re-signISDSDocument request */
12431 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
12432 if (!request) {
12433 isds_log_message(context,
12434 _("Could not build Re-signISDSDocument request"));
12435 return IE_ERROR;
12437 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12438 if(!isds_ns) {
12439 isds_log_message(context, _("Could not create ISDS name space"));
12440 xmlFreeNode(request);
12441 return IE_ERROR;
12443 xmlSetNs(request, isds_ns);
12445 /* Insert Base64 encoded CMS blob */
12446 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
12447 input_data, input_length);
12448 if (err) goto leave;
12450 /* Send request to server and process response */
12451 err = send_destroy_request_check_response(context,
12452 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
12453 &response, NULL, &map);
12454 if (err) goto leave;
12457 /* Extract re-signed data */
12458 xpath_ctx = xmlXPathNewContext(response);
12459 if (!xpath_ctx) {
12460 err = IE_ERROR;
12461 goto leave;
12463 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12464 err = IE_ERROR;
12465 goto leave;
12467 result = xmlXPathEvalExpression(
12468 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
12469 if (!result) {
12470 err = IE_ERROR;
12471 goto leave;
12473 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
12474 isds_log_message(context,
12475 _("Missing Re-signISDSDocumentResponse element"));
12476 err = IE_ISDS;
12477 goto leave;
12479 if (result->nodesetval->nodeNr > 1) {
12480 isds_log_message(context,
12481 _("Multiple Re-signISDSDocumentResponse element"));
12482 err = IE_ISDS;
12483 goto leave;
12485 xpath_ctx->node = result->nodesetval->nodeTab[0];
12486 xmlXPathFreeObject(result); result = NULL;
12488 EXTRACT_STRING("isds:dmResultDoc", string);
12489 /* Decode non-empty data */
12490 if (NULL != string && string[0] != '\0') {
12491 *output_length = _isds_b64decode(string, output_data);
12492 if (*output_length == (size_t) -1) {
12493 isds_log_message(context,
12494 _("Error while Base64-decoding re-signed data"));
12495 err = IE_ERROR;
12496 goto leave;
12498 } else {
12499 isds_log_message(context, _("Server did not send re-signed data"));
12500 err = IE_ISDS;
12501 goto leave;
12503 zfree(string);
12505 if (NULL != valid_to) {
12506 /* Get time stamp expiration date */
12507 EXTRACT_STRING("isds:dmValidTo", string);
12508 if (NULL != string) {
12509 *valid_to = calloc(1, sizeof(**valid_to));
12510 if (!*valid_to) {
12511 err = IE_NOMEM;
12512 goto leave;
12514 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
12515 if (err) {
12516 if (err == IE_NOTSUP) {
12517 err = IE_ISDS;
12518 char *string_locale = _isds_utf82locale(string);
12519 isds_printf_message(context,
12520 _("Invalid dmValidTo value: %s"), string_locale);
12521 free(string_locale);
12523 goto leave;
12528 leave:
12529 free(string);
12531 xmlXPathFreeObject(result);
12532 xmlXPathFreeContext(xpath_ctx);
12534 xmlFreeDoc(response);
12535 xmlFreeNode(request);
12536 #else /* not HAVE_LIBCURL */
12537 err = IE_NOTSUP;
12538 #endif
12540 return err;
12543 #undef INSERT_ELEMENT
12544 #undef CHECK_FOR_STRING_LENGTH
12545 #undef INSERT_STRING_ATTRIBUTE
12546 #undef INSERT_ULONGINTNOPTR
12547 #undef INSERT_ULONGINT
12548 #undef INSERT_LONGINT
12549 #undef INSERT_BOOLEAN
12550 #undef INSERT_SCALAR_BOOLEAN
12551 #undef INSERT_STRING
12552 #undef INSERT_STRING_WITH_NS
12553 #undef EXTRACT_STRING_ATTRIBUTE
12554 #undef EXTRACT_ULONGINT
12555 #undef EXTRACT_LONGINT
12556 #undef EXTRACT_BOOLEAN
12557 #undef EXTRACT_STRING
12560 /* Compute hash of message from raw representation and store it into envelope.
12561 * Original hash structure will be destroyed in envelope.
12562 * @context is session context
12563 * @message is message carrying raw XML message blob
12564 * @algorithm is desired hash algorithm to use */
12565 isds_error isds_compute_message_hash(struct isds_ctx *context,
12566 struct isds_message *message, const isds_hash_algorithm algorithm) {
12567 isds_error err = IE_SUCCESS;
12568 const char *nsuri;
12569 void *xml_stream = NULL;
12570 size_t xml_stream_length;
12571 size_t phys_start, phys_end;
12572 char *phys_path = NULL;
12573 struct isds_hash *new_hash = NULL;
12576 if (!context) return IE_INVALID_CONTEXT;
12577 zfree(context->long_message);
12578 if (!message) return IE_INVAL;
12580 if (!message->raw) {
12581 isds_log_message(context,
12582 _("Message does not carry raw representation"));
12583 return IE_INVAL;
12586 switch (message->raw_type) {
12587 case RAWTYPE_INCOMING_MESSAGE:
12588 nsuri = ISDS_NS;
12589 xml_stream = message->raw;
12590 xml_stream_length = message->raw_length;
12591 break;
12593 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
12594 nsuri = SISDS_INCOMING_NS;
12595 xml_stream = message->raw;
12596 xml_stream_length = message->raw_length;
12597 break;
12599 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
12600 nsuri = SISDS_INCOMING_NS;
12601 err = _isds_extract_cms_data(context,
12602 message->raw, message->raw_length,
12603 &xml_stream, &xml_stream_length);
12604 if (err) goto leave;
12605 break;
12607 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
12608 nsuri = SISDS_OUTGOING_NS;
12609 xml_stream = message->raw;
12610 xml_stream_length = message->raw_length;
12611 break;
12613 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12614 nsuri = SISDS_OUTGOING_NS;
12615 err = _isds_extract_cms_data(context,
12616 message->raw, message->raw_length,
12617 &xml_stream, &xml_stream_length);
12618 if (err) goto leave;
12619 break;
12621 default:
12622 isds_log_message(context, _("Bad raw representation type"));
12623 return IE_INVAL;
12624 break;
12628 /* XXX: Hash is computed from original string representing isds:dmDm
12629 * subtree. That means no encoding, white space, xmlns attributes changes.
12630 * In other words, input for hash can be invalid XML stream. */
12631 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12632 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12633 PHYSXML_ELEMENT_SEPARATOR,
12634 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12635 PHYSXML_ELEMENT_SEPARATOR
12636 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12637 err = IE_NOMEM;
12638 goto leave;
12640 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12641 phys_path, &phys_start, &phys_end);
12642 zfree(phys_path);
12643 if (err) {
12644 isds_log_message(context,
12645 _("Substring with isds:dmDM element could not be located "
12646 "in raw message"));
12647 goto leave;
12651 /* Compute hash */
12652 new_hash = calloc(1, sizeof(*new_hash));
12653 if (!new_hash) {
12654 err = IE_NOMEM;
12655 goto leave;
12657 new_hash->algorithm = algorithm;
12658 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12659 new_hash);
12660 if (err) {
12661 isds_log_message(context, _("Could not compute message hash"));
12662 goto leave;
12665 /* Save computed hash */
12666 if (!message->envelope) {
12667 message->envelope = calloc(1, sizeof(*message->envelope));
12668 if (!message->envelope) {
12669 err = IE_NOMEM;
12670 goto leave;
12673 isds_hash_free(&message->envelope->hash);
12674 message->envelope->hash = new_hash;
12676 leave:
12677 if (err) {
12678 isds_hash_free(&new_hash);
12681 free(phys_path);
12682 if (xml_stream != message->raw) free(xml_stream);
12683 return err;
12687 /* Compare two hashes.
12688 * @h1 is first hash
12689 * @h2 is another hash
12690 * @return
12691 * IE_SUCCESS if hashes equal
12692 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12693 * IE_ENUM if not comparable, but both structures defined
12694 * IE_INVAL if some of the structures are undefined (NULL)
12695 * IE_ERROR if internal error occurs */
12696 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12697 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12698 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12699 if (h1->length != h2->length) return IE_ERROR;
12700 if (h1->length > 0 && !h1->value) return IE_ERROR;
12701 if (h2->length > 0 && !h2->value) return IE_ERROR;
12703 for (size_t i = 0; i < h1->length; i++) {
12704 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12705 return IE_NOTEQUAL;
12707 return IE_SUCCESS;
12711 /* Check message has gone through ISDS by comparing message hash stored in
12712 * ISDS and locally computed hash. You must provide message with valid raw
12713 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12714 * This is convenient wrapper for isds_download_message_hash(),
12715 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12716 * @context is session context
12717 * @message is message with valid raw and envelope member; envelope->hash
12718 * member will be changed during function run. Use envelope on heap only.
12719 * @return
12720 * IE_SUCCESS if message originates in ISDS
12721 * IE_NOTEQUAL if message is unknown to ISDS
12722 * other code for other errors */
12723 isds_error isds_verify_message_hash(struct isds_ctx *context,
12724 struct isds_message *message) {
12725 isds_error err = IE_SUCCESS;
12726 struct isds_hash *downloaded_hash = NULL;
12728 if (!context) return IE_INVALID_CONTEXT;
12729 zfree(context->long_message);
12730 if (!message) return IE_INVAL;
12732 if (!message->envelope) {
12733 isds_log_message(context,
12734 _("Given message structure is missing envelope"));
12735 return IE_INVAL;
12737 if (!message->raw) {
12738 isds_log_message(context,
12739 _("Given message structure is missing raw representation"));
12740 return IE_INVAL;
12743 err = isds_download_message_hash(context, message->envelope->dmID,
12744 &downloaded_hash);
12745 if (err) goto leave;
12747 err = isds_compute_message_hash(context, message,
12748 downloaded_hash->algorithm);
12749 if (err) goto leave;
12751 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12753 leave:
12754 isds_hash_free(&downloaded_hash);
12755 return err;
12759 /* Search for document by document ID in list of documents. IDs are compared
12760 * as UTF-8 string.
12761 * @documents is list of isds_documents
12762 * @id is document identifier
12763 * @return first matching document or NULL. */
12764 const struct isds_document *isds_find_document_by_id(
12765 const struct isds_list *documents, const char *id) {
12766 const struct isds_list *item;
12767 const struct isds_document *document;
12769 for (item = documents; item; item = item->next) {
12770 document = (struct isds_document *) item->data;
12771 if (!document) continue;
12773 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12774 return document;
12777 return NULL;
12781 /* Normalize @mime_type to be proper MIME type.
12782 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12783 * guess regular MIME type (e.g. "application/pdf").
12784 * @mime_type is UTF-8 encoded MIME type to fix
12785 * @return original @mime_type if no better interpretation exists, or
12786 * constant static UTF-8 encoded string with proper MIME type. */
12787 const char *isds_normalize_mime_type(const char *mime_type) {
12788 if (!mime_type) return NULL;
12790 for (size_t offset = 0;
12791 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12792 offset += 2) {
12793 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12794 extension_map_mime[offset]))
12795 return (const char *) extension_map_mime[offset + 1];
12798 return mime_type;
12802 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12803 struct isds_message **message);
12804 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12805 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12806 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12807 struct isds_address **address);
12809 int isds_message_free(struct isds_message **message);
12810 int isds_address_free(struct isds_address **address);
12814 /* Makes known all relevant namespaces to given XPath context
12815 * @xpath_ctx is XPath context
12816 * @message_ns selects proper message name space. Unsigned and signed
12817 * messages and delivery info's differ in prefix and URI. */
12818 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12819 const message_ns_type message_ns) {
12820 const xmlChar *message_namespace = NULL;
12822 if (!xpath_ctx) return IE_ERROR;
12824 switch(message_ns) {
12825 case MESSAGE_NS_1:
12826 message_namespace = BAD_CAST ISDS1_NS; break;
12827 case MESSAGE_NS_UNSIGNED:
12828 message_namespace = BAD_CAST ISDS_NS; break;
12829 case MESSAGE_NS_SIGNED_INCOMING:
12830 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12831 case MESSAGE_NS_SIGNED_OUTGOING:
12832 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12833 case MESSAGE_NS_SIGNED_DELIVERY:
12834 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12835 default:
12836 return IE_ENUM;
12839 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12840 return IE_ERROR;
12841 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12842 return IE_ERROR;
12843 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12844 return IE_ERROR;
12845 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12846 return IE_ERROR;
12847 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12848 return IE_ERROR;
12849 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12850 return IE_ERROR;
12851 return IE_SUCCESS;