Various corrections for the MEP login feature
[libisds.git] / src / isds.c
blobfe19c85475f2ec8343960594f4ce9b873e6bdd46
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 err = IE_NOMEM;
5175 goto leave;
5178 /* Build request */
5179 request = xmlNewNode(NULL, service_name);
5180 if (!request) {
5181 isds_printf_message(context,
5182 _("Could not build %s request"), service_name_locale);
5183 err = IE_ERROR;
5184 goto leave;
5186 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5187 if(!isds_ns) {
5188 isds_log_message(context, _("Could not create ISDS name space"));
5189 err = IE_ERROR;
5190 goto leave;
5192 xmlSetNs(request, isds_ns);
5195 /* Add XSD:tDummyInput child */
5196 INSERT_STRING(request, "dbDummy", NULL);
5199 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5200 service_name_locale);
5202 /* Send request */
5203 err = _isds(context, SERVICE_DB_ACCESS, request, response,
5204 raw_response, raw_response_length);
5205 xmlFreeNode(request); request = NULL;
5207 if (err) {
5208 isds_log(ILF_ISDS, ILL_DEBUG,
5209 _("Processing ISDS response on %s request failed\n"),
5210 service_name_locale);
5211 goto leave;
5214 /* Check for response status */
5215 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
5216 code, status_message, NULL);
5217 if (err) {
5218 isds_log(ILF_ISDS, ILL_DEBUG,
5219 _("ISDS response on %s request is missing status\n"),
5220 service_name_locale);
5221 goto leave;
5224 /* Request processed, but nothing found */
5225 if (xmlStrcmp(*code, BAD_CAST "0000")) {
5226 char *code_locale = _isds_utf82locale((char*) *code);
5227 char *status_message_locale =
5228 _isds_utf82locale((char*) *status_message);
5229 isds_log(ILF_ISDS, ILL_DEBUG,
5230 _("Server refused %s request (code=%s, message=%s)\n"),
5231 service_name_locale, code_locale, status_message_locale);
5232 isds_log_message(context, status_message_locale);
5233 free(code_locale);
5234 free(status_message_locale);
5235 err = IE_ISDS;
5236 goto leave;
5239 leave:
5240 free(service_name_locale);
5241 xmlFreeNode(request);
5242 return err;
5244 #endif
5247 /* Get data about logged in user and his box.
5248 * @context is session context
5249 * @db_owner_info is reallocated box owner description. It will be freed on
5250 * error.
5251 * @return error code from lower layer, context message will be set up
5252 * appropriately. */
5253 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
5254 struct isds_DbOwnerInfo **db_owner_info) {
5255 isds_error err = IE_SUCCESS;
5256 #if HAVE_LIBCURL
5257 xmlDocPtr response = NULL;
5258 xmlChar *code = NULL, *message = NULL;
5259 xmlXPathContextPtr xpath_ctx = NULL;
5260 xmlXPathObjectPtr result = NULL;
5261 char *string = NULL;
5262 #endif
5264 if (!context) return IE_INVALID_CONTEXT;
5265 zfree(context->long_message);
5266 if (!db_owner_info) return IE_INVAL;
5267 isds_DbOwnerInfo_free(db_owner_info);
5269 #if HAVE_LIBCURL
5270 /* Check if connection is established */
5271 if (!context->curl) return IE_CONNECTION_CLOSED;
5274 /* Do request and check for success */
5275 err = build_send_check_dbdummy_request(context,
5276 BAD_CAST "GetOwnerInfoFromLogin",
5277 &response, NULL, NULL, &code, &message);
5278 if (err) goto leave;
5281 /* Extract data */
5282 /* Prepare structure */
5283 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5284 if (!*db_owner_info) {
5285 err = IE_NOMEM;
5286 goto leave;
5288 xpath_ctx = xmlXPathNewContext(response);
5289 if (!xpath_ctx) {
5290 err = IE_ERROR;
5291 goto leave;
5293 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5294 err = IE_ERROR;
5295 goto leave;
5298 /* Set context node */
5299 result = xmlXPathEvalExpression(BAD_CAST
5300 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5301 if (!result) {
5302 err = IE_ERROR;
5303 goto leave;
5305 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5306 isds_log_message(context, _("Missing dbOwnerInfo element"));
5307 err = IE_ISDS;
5308 goto leave;
5310 if (result->nodesetval->nodeNr > 1) {
5311 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5312 err = IE_ISDS;
5313 goto leave;
5315 xpath_ctx->node = result->nodesetval->nodeTab[0];
5316 xmlXPathFreeObject(result); result = NULL;
5318 /* Extract it */
5319 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5322 leave:
5323 if (err) {
5324 isds_DbOwnerInfo_free(db_owner_info);
5327 free(string);
5328 xmlXPathFreeObject(result);
5329 xmlXPathFreeContext(xpath_ctx);
5331 free(code);
5332 free(message);
5333 xmlFreeDoc(response);
5335 if (!err)
5336 isds_log(ILF_ISDS, ILL_DEBUG,
5337 _("GetOwnerInfoFromLogin request processed by server "
5338 "successfully.\n"));
5339 #else /* not HAVE_LIBCURL */
5340 err = IE_NOTSUP;
5341 #endif
5343 return err;
5347 /* Get data about logged in user. */
5348 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5349 struct isds_DbUserInfo **db_user_info) {
5350 isds_error err = IE_SUCCESS;
5351 #if HAVE_LIBCURL
5352 xmlDocPtr response = NULL;
5353 xmlChar *code = NULL, *message = NULL;
5354 xmlXPathContextPtr xpath_ctx = NULL;
5355 xmlXPathObjectPtr result = NULL;
5356 #endif
5358 if (!context) return IE_INVALID_CONTEXT;
5359 zfree(context->long_message);
5360 if (!db_user_info) return IE_INVAL;
5361 isds_DbUserInfo_free(db_user_info);
5363 #if HAVE_LIBCURL
5364 /* Check if connection is established */
5365 if (!context->curl) return IE_CONNECTION_CLOSED;
5368 /* Do request and check for success */
5369 err = build_send_check_dbdummy_request(context,
5370 BAD_CAST "GetUserInfoFromLogin",
5371 &response, NULL, NULL, &code, &message);
5372 if (err) goto leave;
5375 /* Extract data */
5376 /* Prepare structure */
5377 *db_user_info = calloc(1, sizeof(**db_user_info));
5378 if (!*db_user_info) {
5379 err = IE_NOMEM;
5380 goto leave;
5382 xpath_ctx = xmlXPathNewContext(response);
5383 if (!xpath_ctx) {
5384 err = IE_ERROR;
5385 goto leave;
5387 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5388 err = IE_ERROR;
5389 goto leave;
5392 /* Set context node */
5393 result = xmlXPathEvalExpression(BAD_CAST
5394 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5395 if (!result) {
5396 err = IE_ERROR;
5397 goto leave;
5399 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5400 isds_log_message(context, _("Missing dbUserInfo element"));
5401 err = IE_ISDS;
5402 goto leave;
5404 if (result->nodesetval->nodeNr > 1) {
5405 isds_log_message(context, _("Multiple dbUserInfo element"));
5406 err = IE_ISDS;
5407 goto leave;
5409 xpath_ctx->node = result->nodesetval->nodeTab[0];
5410 xmlXPathFreeObject(result); result = NULL;
5412 /* Extract it */
5413 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5415 leave:
5416 if (err) {
5417 isds_DbUserInfo_free(db_user_info);
5420 xmlXPathFreeObject(result);
5421 xmlXPathFreeContext(xpath_ctx);
5423 free(code);
5424 free(message);
5425 xmlFreeDoc(response);
5427 if (!err)
5428 isds_log(ILF_ISDS, ILL_DEBUG,
5429 _("GetUserInfoFromLogin request processed by server "
5430 "successfully.\n"));
5431 #else /* not HAVE_LIBCURL */
5432 err = IE_NOTSUP;
5433 #endif
5435 return err;
5439 /* Get expiration time of current password
5440 * @context is session context
5441 * @expiration is automatically reallocated time when password expires. If
5442 * password expiration is disabled, NULL will be returned. In case of error
5443 * it will be nulled too. */
5444 isds_error isds_get_password_expiration(struct isds_ctx *context,
5445 struct timeval **expiration) {
5446 isds_error err = IE_SUCCESS;
5447 #if HAVE_LIBCURL
5448 xmlDocPtr response = NULL;
5449 xmlChar *code = NULL, *message = NULL;
5450 xmlXPathContextPtr xpath_ctx = NULL;
5451 xmlXPathObjectPtr result = NULL;
5452 char *string = NULL;
5453 #endif
5455 if (!context) return IE_INVALID_CONTEXT;
5456 zfree(context->long_message);
5457 if (!expiration) return IE_INVAL;
5458 zfree(*expiration);
5460 #if HAVE_LIBCURL
5461 /* Check if connection is established */
5462 if (!context->curl) return IE_CONNECTION_CLOSED;
5465 /* Do request and check for success */
5466 err = build_send_check_dbdummy_request(context,
5467 BAD_CAST "GetPasswordInfo",
5468 &response, NULL, NULL, &code, &message);
5469 if (err) goto leave;
5472 /* Extract data */
5473 xpath_ctx = xmlXPathNewContext(response);
5474 if (!xpath_ctx) {
5475 err = IE_ERROR;
5476 goto leave;
5478 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5479 err = IE_ERROR;
5480 goto leave;
5483 /* Set context node */
5484 result = xmlXPathEvalExpression(BAD_CAST
5485 "/isds:GetPasswordInfoResponse", xpath_ctx);
5486 if (!result) {
5487 err = IE_ERROR;
5488 goto leave;
5490 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5491 isds_log_message(context,
5492 _("Missing GetPasswordInfoResponse element"));
5493 err = IE_ISDS;
5494 goto leave;
5496 if (result->nodesetval->nodeNr > 1) {
5497 isds_log_message(context,
5498 _("Multiple GetPasswordInfoResponse element"));
5499 err = IE_ISDS;
5500 goto leave;
5502 xpath_ctx->node = result->nodesetval->nodeTab[0];
5503 xmlXPathFreeObject(result); result = NULL;
5505 /* Extract expiration date */
5506 EXTRACT_STRING("isds:pswExpDate", string);
5507 if (string) {
5508 /* And convert it if any returned. Otherwise expiration is disabled. */
5509 err = timestring2timeval((xmlChar *) string, expiration);
5510 if (err) {
5511 char *string_locale = _isds_utf82locale(string);
5512 if (err == IE_DATE) err = IE_ISDS;
5513 isds_printf_message(context,
5514 _("Could not convert pswExpDate as ISO time: %s"),
5515 string_locale);
5516 free(string_locale);
5517 goto leave;
5521 leave:
5522 if (err) {
5523 if (*expiration) {
5524 zfree(*expiration);
5528 free(string);
5529 xmlXPathFreeObject(result);
5530 xmlXPathFreeContext(xpath_ctx);
5532 free(code);
5533 free(message);
5534 xmlFreeDoc(response);
5536 if (!err)
5537 isds_log(ILF_ISDS, ILL_DEBUG,
5538 _("GetPasswordInfo request processed by server "
5539 "successfully.\n"));
5540 #else /* not HAVE_LIBCURL */
5541 err = IE_NOTSUP;
5542 #endif
5544 return err;
5548 #if HAVE_LIBCURL
5549 /* Request delivering new TOTP code from ISDS through side channel before
5550 * changing password.
5551 * @context is session context
5552 * @password is current password.
5553 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5554 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5555 * function for more details.
5556 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5557 * NULL, if you don't care.
5558 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5559 * error code. */
5560 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5561 const char *password, struct isds_otp *otp, char **refnumber) {
5562 isds_error err = IE_SUCCESS;
5563 char *saved_url = NULL; /* No copy */
5564 #if HAVE_CURL_REAUTHORIZATION_BUG
5565 CURL *saved_curl = NULL; /* No copy */
5566 #endif
5567 xmlNsPtr isds_ns = NULL;
5568 xmlNodePtr request = NULL;
5569 xmlDocPtr response = NULL;
5570 xmlChar *code = NULL, *message = NULL;
5571 const xmlChar *codes[] = {
5572 BAD_CAST "2300",
5573 BAD_CAST "2301",
5574 BAD_CAST "2302"
5576 const char *meanings[] = {
5577 N_("Unexpected error"),
5578 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5579 N_("One-time code could not been sent. Try later again.")
5581 const isds_otp_resolution resolutions[] = {
5582 OTP_RESOLUTION_UNKNOWN,
5583 OTP_RESOLUTION_TO_FAST,
5584 OTP_RESOLUTION_TOTP_NOT_SENT
5587 if (NULL == context) return IE_INVALID_CONTEXT;
5588 zfree(context->long_message);
5589 if (NULL == password) {
5590 isds_log_message(context,
5591 _("Second argument (password) of isds_change_password() "
5592 "is NULL"));
5593 return IE_INVAL;
5596 /* Check if connection is established
5597 * TODO: This check should be done downstairs. */
5598 if (!context->curl) return IE_CONNECTION_CLOSED;
5600 if (!context->otp) {
5601 isds_log_message(context, _("This function requires OTP-authenticated "
5602 "context"));
5603 return IE_INVALID_CONTEXT;
5605 if (NULL == otp) {
5606 isds_log_message(context, _("If one-time password authentication "
5607 "method is in use, requesting new OTP code requires "
5608 "one-time credentials argument either"));
5609 return IE_INVAL;
5611 if (otp->method != OTP_TIME) {
5612 isds_log_message(context, _("Requesting new time-based OTP code from "
5613 "server requires one-time password authentication "
5614 "method"));
5615 return IE_INVAL;
5617 if (otp->otp_code != NULL) {
5618 isds_log_message(context, _("Requesting new time-based OTP code from "
5619 "server requires undefined OTP code member in "
5620 "one-time credentials argument"));
5621 return IE_INVAL;
5625 /* Build request */
5626 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5627 if (!request) {
5628 isds_log_message(context, _("Could not build SendSMSCode request"));
5629 return IE_ERROR;
5631 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5632 if(!isds_ns) {
5633 isds_log_message(context, _("Could not create ISDS name space"));
5634 xmlFreeNode(request);
5635 return IE_ERROR;
5637 xmlSetNs(request, isds_ns);
5639 /* Change URL temporarily for sending this request only */
5641 char *new_url = NULL;
5642 if ((err = _isds_build_url_from_context(context,
5643 "%.*sasws/changePassword", &new_url))) {
5644 goto leave;
5646 saved_url = context->url;
5647 context->url = new_url;
5650 /* Store credentials for sending this request only */
5651 context->otp_credentials = otp;
5652 _isds_discard_credentials(context, 0);
5653 if ((err = _isds_store_credentials(context, context->saved_username,
5654 password, NULL))) {
5655 _isds_discard_credentials(context, 0);
5656 goto leave;
5658 #if HAVE_CURL_REAUTHORIZATION_BUG
5659 saved_curl = context->curl;
5660 context->curl = curl_easy_init();
5661 if (NULL == context->curl) {
5662 err = IE_ERROR;
5663 goto leave;
5665 if (context->timeout) {
5666 err = isds_set_timeout(context, context->timeout);
5667 if (err) goto leave;
5669 #endif
5671 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5673 /* Sent request */
5674 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5676 /* Remove temporal credentials */
5677 _isds_discard_credentials(context, 0);
5678 /* Detach pointer to OTP credentials from context */
5679 context->otp_credentials = NULL;
5680 /* Keep context->otp true to keep signaling this is OTP session */
5682 /* Destroy request */
5683 xmlFreeNode(request); request = NULL;
5685 if (err) {
5686 isds_log(ILF_ISDS, ILL_DEBUG,
5687 _("Processing ISDS response on SendSMSCode request failed\n"));
5688 goto leave;
5691 /* Check for response status */
5692 err = isds_response_status(context, SERVICE_ASWS, response,
5693 &code, &message, (xmlChar **)refnumber);
5694 if (err) {
5695 isds_log(ILF_ISDS, ILL_DEBUG,
5696 _("ISDS response on SendSMSCode request is missing "
5697 "status\n"));
5698 goto leave;
5701 /* Check for error */
5702 if (xmlStrcmp(code, BAD_CAST "0000")) {
5703 char *code_locale = _isds_utf82locale((char*)code);
5704 char *message_locale = _isds_utf82locale((char*)message);
5705 size_t i;
5706 isds_log(ILF_ISDS, ILL_DEBUG,
5707 _("Server refused to send new code on SendSMSCode "
5708 "request (code=%s, message=%s)\n"),
5709 code_locale, message_locale);
5711 /* Check for known error codes */
5712 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5713 if (!xmlStrcmp(code, codes[i])) break;
5715 if (i < sizeof(codes)/sizeof(*codes)) {
5716 isds_log_message(context, _(meanings[i]));
5717 /* Mimic otp->resolution according to the code, specification does
5718 * prescribe OTP header to be available. */
5719 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5720 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5721 otp->resolution = resolutions[i];
5722 } else
5723 isds_log_message(context, message_locale);
5725 free(code_locale);
5726 free(message_locale);
5728 err = IE_ISDS;
5729 goto leave;
5732 /* Otherwise new code sent successfully */
5733 /* Mimic otp->resolution according to the code, specification does
5734 * prescribe OTP header to be available. */
5735 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5736 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5738 leave:
5739 if (NULL != saved_url) {
5740 /* Revert URL to original one */
5741 zfree(context->url);
5742 context->url = saved_url;
5744 #if HAVE_CURL_REAUTHORIZATION_BUG
5745 if (NULL != saved_curl) {
5746 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5747 context->curl = saved_curl;
5749 #endif
5751 free(code);
5752 free(message);
5753 xmlFreeDoc(response);
5754 xmlFreeNode(request);
5756 if (!err)
5757 isds_log(ILF_ISDS, ILL_DEBUG,
5758 _("New OTP code has been sent successfully on SendSMSCode "
5759 "request.\n"));
5760 return err;
5764 /* Convert response status code to isds_error code and set long message
5765 * @context is context to save long message to
5766 * @map is mapping from codes to errors and messages. Pass NULL for generic
5767 * handling.
5768 * @code is status code to translate
5769 * @message is non-localized status message to put into long message in case
5770 * of uknown error. It can be NULL if server did not provide any.
5771 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5772 * invalid invocation. */
5773 static isds_error statuscode2isds_error(struct isds_ctx *context,
5774 const struct code_map_isds_error *map,
5775 const xmlChar *code, const xmlChar *message) {
5776 if (NULL == code) {
5777 isds_log_message(context,
5778 _("NULL status code passed to statuscode2isds_error()"));
5779 return IE_INVAL;
5782 if (NULL != map) {
5783 /* Check for known error codes */
5784 for (int i=0; map->codes[i] != NULL; i++) {
5785 if (!xmlStrcmp(code, map->codes[i])) {
5786 isds_log_message(context, _(map->meanings[i]));
5787 return map->errors[i];
5792 /* Other error */
5793 if (xmlStrcmp(code, BAD_CAST "0000")) {
5794 char *message_locale = _isds_utf82locale((char*)message);
5795 if (NULL == message_locale)
5796 isds_log_message(context, _("ISDS server returned unknown error"));
5797 else
5798 isds_log_message(context, message_locale);
5799 free(message_locale);
5800 return IE_ISDS;
5803 return IE_SUCCESS;
5805 #endif
5808 /* Change user password in ISDS.
5809 * User must supply old password, new password will takes effect after some
5810 * time, current session can continue. Password must fulfill some constraints.
5811 * @context is session context
5812 * @old_password is current password.
5813 * @new_password is requested new password
5814 * @otp auxiliary data required if one-time password authentication is in use,
5815 * defines OTP code (if known) and returns fine grade resolution of OTP
5816 * procedure. Pass NULL, if one-time password authentication is not needed.
5817 * Please note the @otp argument must match OTP method used at log-in time. See
5818 * isds_login() function for more details.
5819 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5820 * NULL, if you don't care.
5821 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5822 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5823 * awaiting OTP code that has been delivered by side channel to the user. */
5824 isds_error isds_change_password(struct isds_ctx *context,
5825 const char *old_password, const char *new_password,
5826 struct isds_otp *otp, char **refnumber) {
5827 isds_error err = IE_SUCCESS;
5828 #if HAVE_LIBCURL
5829 char *saved_url = NULL; /* No copy */
5830 #if HAVE_CURL_REAUTHORIZATION_BUG
5831 CURL *saved_curl = NULL; /* No copy */
5832 #endif
5833 xmlNsPtr isds_ns = NULL;
5834 xmlNodePtr request = NULL, node;
5835 xmlDocPtr response = NULL;
5836 xmlChar *code = NULL, *message = NULL;
5837 const xmlChar *codes[] = {
5838 BAD_CAST "1066",
5839 BAD_CAST "1067",
5840 BAD_CAST "1079",
5841 BAD_CAST "1080",
5842 BAD_CAST "1081",
5843 BAD_CAST "1082",
5844 BAD_CAST "1083",
5845 BAD_CAST "1090",
5846 BAD_CAST "1091",
5847 BAD_CAST "2300",
5848 BAD_CAST "9204"
5850 const char *meanings[] = {
5851 N_("Password length must be between 8 and 32 characters"),
5852 N_("Password cannot be reused"), /* Server does not distinguish 1067
5853 and 1091 on ChangePasswordOTP */
5854 N_("Password contains forbidden character"),
5855 N_("Password must contain at least one upper-case letter, "
5856 "one lower-case, and one digit"),
5857 N_("Password cannot contain sequence of three identical characters"),
5858 N_("Password cannot contain user identifier"),
5859 N_("Password is too simmple"),
5860 N_("Old password is not valid"),
5861 N_("Password cannot be reused"),
5862 N_("Unexpected error"),
5863 N_("LDAP update error")
5865 #endif
5867 if (!context) return IE_INVALID_CONTEXT;
5868 zfree(context->long_message);
5869 if (NULL != refnumber)
5870 zfree(*refnumber);
5871 if (NULL == old_password) {
5872 isds_log_message(context,
5873 _("Second argument (old password) of isds_change_password() "
5874 "is NULL"));
5875 return IE_INVAL;
5877 if (NULL == otp && NULL == new_password) {
5878 isds_log_message(context,
5879 _("Third argument (new password) of isds_change_password() "
5880 "is NULL"));
5881 return IE_INVAL;
5884 #if HAVE_LIBCURL
5885 /* Check if connection is established
5886 * TODO: This check should be done downstairs. */
5887 if (!context->curl) return IE_CONNECTION_CLOSED;
5889 if (context->otp && NULL == otp) {
5890 isds_log_message(context, _("If one-time password authentication "
5891 "method is in use, changing password requires one-time "
5892 "credentials either"));
5893 return IE_INVAL;
5896 /* Build ChangeISDSPassword request */
5897 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5898 BAD_CAST "ChangePasswordOTP");
5899 if (!request) {
5900 isds_log_message(context, (NULL == otp) ?
5901 _("Could not build ChangeISDSPassword request") :
5902 _("Could not build ChangePasswordOTP request"));
5903 return IE_ERROR;
5905 isds_ns = xmlNewNs(request,
5906 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5907 NULL);
5908 if(!isds_ns) {
5909 isds_log_message(context, _("Could not create ISDS name space"));
5910 xmlFreeNode(request);
5911 return IE_ERROR;
5913 xmlSetNs(request, isds_ns);
5915 INSERT_STRING(request, "dbOldPassword", old_password);
5916 INSERT_STRING(request, "dbNewPassword", new_password);
5918 if (NULL != otp) {
5919 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5920 switch (otp->method) {
5921 case OTP_HMAC:
5922 isds_log(ILF_SEC, ILL_INFO,
5923 _("Selected authentication method: "
5924 "HMAC-based one-time password\n"));
5925 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5926 break;
5927 case OTP_TIME:
5928 isds_log(ILF_SEC, ILL_INFO,
5929 _("Selected authentication method: "
5930 "Time-based one-time password\n"));
5931 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5932 if (otp->otp_code == NULL) {
5933 isds_log(ILF_SEC, ILL_INFO,
5934 _("OTP code has not been provided by "
5935 "application, requesting server for "
5936 "new one.\n"));
5937 err = _isds_request_totp_code(context, old_password, otp,
5938 refnumber);
5939 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5940 goto leave;
5942 } else {
5943 isds_log(ILF_SEC, ILL_INFO,
5944 _("OTP code has been provided by "
5945 "application, not requesting server "
5946 "for new one.\n"));
5948 break;
5949 default:
5950 isds_log_message(context,
5951 _("Unknown one-time password authentication "
5952 "method requested by application"));
5953 err = IE_ENUM;
5954 goto leave;
5957 /* Change URL temporarily for sending this request only */
5959 char *new_url = NULL;
5960 if ((err = _isds_build_url_from_context(context,
5961 "%.*sasws/changePassword", &new_url))) {
5962 goto leave;
5964 saved_url = context->url;
5965 context->url = new_url;
5968 /* Store credentials for sending this request only */
5969 context->otp_credentials = otp;
5970 _isds_discard_credentials(context, 0);
5971 if ((err = _isds_store_credentials(context, context->saved_username,
5972 old_password, NULL))) {
5973 _isds_discard_credentials(context, 0);
5974 goto leave;
5976 #if HAVE_CURL_REAUTHORIZATION_BUG
5977 saved_curl = context->curl;
5978 context->curl = curl_easy_init();
5979 if (NULL == context->curl) {
5980 err = IE_ERROR;
5981 goto leave;
5983 if (context->timeout) {
5984 err = isds_set_timeout(context, context->timeout);
5985 if (err) goto leave;
5987 #endif
5990 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5991 _("Sending ChangeISDSPassword request to ISDS\n") :
5992 _("Sending ChangePasswordOTP request to ISDS\n"));
5994 /* Sent request */
5995 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5996 request, &response, NULL, NULL);
5998 if (otp) {
5999 /* Remove temporal credentials */
6000 _isds_discard_credentials(context, 0);
6001 /* Detach pointer to OTP credentials from context */
6002 context->otp_credentials = NULL;
6003 /* Keep context->otp true to keep signaling this is OTP session */
6006 /* Destroy request */
6007 xmlFreeNode(request); request = NULL;
6009 if (err) {
6010 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6011 _("Processing ISDS response on ChangeISDSPassword "
6012 "request failed\n") :
6013 _("Processing ISDS response on ChangePasswordOTP "
6014 "request failed\n"));
6015 goto leave;
6018 /* Check for response status */
6019 err = isds_response_status(context,
6020 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
6021 &code, &message, (xmlChar **)refnumber);
6022 if (err) {
6023 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6024 _("ISDS response on ChangeISDSPassword request is missing "
6025 "status\n") :
6026 _("ISDS response on ChangePasswordOTP request is missing "
6027 "status\n"));
6028 goto leave;
6031 /* Check for known error codes */
6032 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
6033 if (!xmlStrcmp(code, codes[i])) {
6034 char *code_locale = _isds_utf82locale((char*)code);
6035 char *message_locale = _isds_utf82locale((char*)message);
6036 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6037 _("Server refused to change password on ChangeISDSPassword "
6038 "request (code=%s, message=%s)\n") :
6039 _("Server refused to change password on ChangePasswordOTP "
6040 "request (code=%s, message=%s)\n"),
6041 code_locale, message_locale);
6042 free(code_locale);
6043 free(message_locale);
6044 isds_log_message(context, _(meanings[i]));
6045 err = IE_INVAL;
6046 goto leave;
6050 /* Other error */
6051 if (xmlStrcmp(code, BAD_CAST "0000")) {
6052 char *code_locale = _isds_utf82locale((char*)code);
6053 char *message_locale = _isds_utf82locale((char*)message);
6054 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6055 _("Server refused to change password on ChangeISDSPassword "
6056 "request (code=%s, message=%s)\n") :
6057 _("Server refused to change password on ChangePasswordOTP "
6058 "request (code=%s, message=%s)\n"),
6059 code_locale, message_locale);
6060 isds_log_message(context, message_locale);
6061 free(code_locale);
6062 free(message_locale);
6063 err = IE_ISDS;
6064 goto leave;
6067 /* Otherwise password changed successfully */
6069 leave:
6070 if (NULL != saved_url) {
6071 /* Revert URL to original one */
6072 zfree(context->url);
6073 context->url = saved_url;
6075 #if HAVE_CURL_REAUTHORIZATION_BUG
6076 if (NULL != saved_curl) {
6077 if (context->curl != NULL) curl_easy_cleanup(context->curl);
6078 context->curl = saved_curl;
6080 #endif
6082 free(code);
6083 free(message);
6084 xmlFreeDoc(response);
6085 xmlFreeNode(request);
6087 if (!err)
6088 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
6089 _("Password changed successfully on ChangeISDSPassword "
6090 "request.\n") :
6091 _("Password changed successfully on ChangePasswordOTP "
6092 "request.\n"));
6093 #else /* not HAVE_LIBCURL */
6094 err = IE_NOTSUP;
6095 #endif
6097 return err;
6101 #if HAVE_LIBCURL
6102 /* Generic middle part with request sending and response check.
6103 * It sends prepared request and checks for error code.
6104 * @context is ISDS session context.
6105 * @service is ISDS service handler
6106 * @service_name is name in scope of given @service
6107 * @request is XML tree with request. Will be freed to save memory.
6108 * @response is XML document outputting ISDS response.
6109 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6110 * @map is mapping from status code to library error. Pass NULL if no special
6111 * handling is requested.
6112 * NULL, if you don't care. */
6113 static isds_error send_destroy_request_check_response(
6114 struct isds_ctx *context,
6115 const isds_service service, const xmlChar *service_name,
6116 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
6117 const struct code_map_isds_error *map) {
6118 isds_error err = IE_SUCCESS;
6119 char *service_name_locale = NULL;
6120 xmlChar *code = NULL, *message = NULL;
6123 if (!context) return IE_INVALID_CONTEXT;
6124 if (!service_name || *service_name == '\0' || !request || !*request ||
6125 !response)
6126 return IE_INVAL;
6128 /* Check if connection is established
6129 * TODO: This check should be done downstairs. */
6130 if (!context->curl) return IE_CONNECTION_CLOSED;
6132 service_name_locale = _isds_utf82locale((char*) service_name);
6133 if (!service_name_locale) {
6134 err = IE_NOMEM;
6135 goto leave;
6138 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
6139 service_name_locale);
6141 /* Send request */
6142 err = _isds(context, service, *request, response, NULL, NULL);
6143 xmlFreeNode(*request); *request = NULL;
6145 if (err) {
6146 isds_log(ILF_ISDS, ILL_DEBUG,
6147 _("Processing ISDS response on %s request failed\n"),
6148 service_name_locale);
6149 goto leave;
6152 /* Check for response status */
6153 err = isds_response_status(context, service, *response,
6154 &code, &message, refnumber);
6155 if (err) {
6156 isds_log(ILF_ISDS, ILL_DEBUG,
6157 _("ISDS response on %s request is missing status\n"),
6158 service_name_locale);
6159 goto leave;
6162 err = statuscode2isds_error(context, map, code, message);
6164 /* Request processed, but server failed */
6165 if (xmlStrcmp(code, BAD_CAST "0000")) {
6166 char *code_locale = _isds_utf82locale((char*) code);
6167 char *message_locale = _isds_utf82locale((char*) message);
6168 isds_log(ILF_ISDS, ILL_DEBUG,
6169 _("Server refused %s request (code=%s, message=%s)\n"),
6170 service_name_locale, code_locale, message_locale);
6171 free(code_locale);
6172 free(message_locale);
6173 goto leave;
6177 leave:
6178 free(code);
6179 free(message);
6180 if (err && *response) {
6181 xmlFreeDoc(*response);
6182 *response = NULL;
6184 if (*request) {
6185 xmlFreeNode(*request);
6186 *request = NULL;
6188 free(service_name_locale);
6190 return err;
6194 /* Generic bottom half with request sending.
6195 * It sends prepared request, checks for error code, destroys response and
6196 * request and log success or failure.
6197 * @context is ISDS session context.
6198 * @service is ISDS service handler
6199 * @service_name is name in scope of given @service
6200 * @request is XML tree with request. Will be freed to save memory.
6201 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6202 * NULL, if you don't care. */
6203 static isds_error send_request_check_drop_response(
6204 struct isds_ctx *context,
6205 const isds_service service, const xmlChar *service_name,
6206 xmlNodePtr *request, xmlChar **refnumber) {
6207 isds_error err = IE_SUCCESS;
6208 xmlDocPtr response = NULL;
6211 if (!context) return IE_INVALID_CONTEXT;
6212 if (!service_name || *service_name == '\0' || !request || !*request)
6213 return IE_INVAL;
6215 /* Send request and check response*/
6216 err = send_destroy_request_check_response(context,
6217 service, service_name, request, &response, refnumber, NULL);
6219 xmlFreeDoc(response);
6221 if (*request) {
6222 xmlFreeNode(*request);
6223 *request = NULL;
6226 if (!err) {
6227 char *service_name_locale = _isds_utf82locale((char *) service_name);
6228 isds_log(ILF_ISDS, ILL_DEBUG,
6229 _("%s request processed by server successfully.\n"),
6230 service_name_locale);
6231 free(service_name_locale);
6234 return err;
6238 /* Insert isds_credentials_delivery structure into XML request if not NULL
6239 * @context is session context
6240 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6241 * credentials delivery. The email field is passed.
6242 * @parent is XML element where to insert */
6243 static isds_error insert_credentials_delivery(struct isds_ctx *context,
6244 const struct isds_credentials_delivery *credentials_delivery,
6245 xmlNodePtr parent) {
6246 isds_error err = IE_SUCCESS;
6247 xmlNodePtr node;
6249 if (!context) return IE_INVALID_CONTEXT;
6250 if (!parent) return IE_INVAL;
6252 if (credentials_delivery) {
6253 /* Following elements are valid only for services:
6254 * NewAccessData, AddDataBoxUser, CreateDataBox */
6255 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
6256 INSERT_STRING(parent, "email", credentials_delivery->email);
6259 leave:
6260 return err;
6264 /* Extract credentials delivery from ISDS response.
6265 * @context is session context
6266 * @credentials_delivery is pointer to valid structure to fill in returned
6267 * user's password (and new log-in name). If NULL, do not extract the data.
6268 * @response is pointer to XML document with ISDS response
6269 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6270 * @return IE_SUCCESS even if new user name has not been found because it's not
6271 * clear whether it's returned always. */
6272 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6273 struct isds_credentials_delivery *credentials_delivery,
6274 xmlDocPtr response, const char *request_name) {
6275 isds_error err = IE_SUCCESS;
6276 xmlXPathContextPtr xpath_ctx = NULL;
6277 xmlXPathObjectPtr result = NULL;
6278 char *xpath_query = NULL;
6280 if (!context) return IE_INVALID_CONTEXT;
6281 if (credentials_delivery) {
6282 zfree(credentials_delivery->token);
6283 zfree(credentials_delivery->new_user_name);
6285 if (!response || !request_name || !*request_name) return IE_INVAL;
6288 /* Extract optional token */
6289 if (credentials_delivery) {
6290 xpath_ctx = xmlXPathNewContext(response);
6291 if (!xpath_ctx) {
6292 err = IE_ERROR;
6293 goto leave;
6295 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6296 err = IE_ERROR;
6297 goto leave;
6300 /* Verify root element */
6301 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6302 request_name)) {
6303 err = IE_NOMEM;
6304 goto leave;
6306 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6307 if (!result) {
6308 err = IE_ERROR;
6309 goto leave;
6311 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6312 char *request_name_locale = _isds_utf82locale(request_name);
6313 isds_log(ILF_ISDS, ILL_WARNING,
6314 _("Wrong element in ISDS response for %s request "
6315 "while extracting credentials delivery details\n"),
6316 request_name_locale);
6317 free(request_name_locale);
6318 err = IE_ERROR;
6319 goto leave;
6321 xpath_ctx->node = result->nodesetval->nodeTab[0];
6324 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6325 * optional. */
6326 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6328 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6329 if (!credentials_delivery->token) {
6330 char *request_name_locale = _isds_utf82locale(request_name);
6331 isds_log(ILF_ISDS, ILL_ERR,
6332 _("ISDS did not return token on %s request "
6333 "even if requested\n"), request_name_locale);
6334 free(request_name_locale);
6335 err = IE_ERROR;
6339 leave:
6340 free(xpath_query);
6341 xmlXPathFreeObject(result);
6342 xmlXPathFreeContext(xpath_ctx);
6344 return err;
6348 /* Build XSD:tCreateDBInput request type for box creating.
6349 * @context is session context
6350 * @request outputs built XML tree
6351 * @service_name is request name of SERVICE_DB_MANIPULATION service
6352 * @box is box description to create including single primary user (in case of
6353 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6354 * ignored.
6355 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6356 * box, or contact address of PFO box owner)
6357 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6358 * @upper_box_id is optional ID of supper box if currently created box is
6359 * subordinated.
6360 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6361 * don't care.
6362 * @credentials_delivery is valid pointer if ISDS should return token that box
6363 * owner can use to obtain his new credentials in on-line way. Then valid email
6364 * member value should be supplied.
6365 * @approval is optional external approval of box manipulation */
6366 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6367 xmlNodePtr *request, const xmlChar *service_name,
6368 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6369 const xmlChar *former_names, const xmlChar *upper_box_id,
6370 const xmlChar *ceo_label,
6371 const struct isds_credentials_delivery *credentials_delivery,
6372 const struct isds_approval *approval) {
6373 isds_error err = IE_SUCCESS;
6374 xmlNsPtr isds_ns = NULL;
6375 xmlNodePtr node, dbPrimaryUsers;
6376 xmlChar *string = NULL;
6377 const struct isds_list *item;
6380 if (!context) return IE_INVALID_CONTEXT;
6381 if (!request || !service_name || service_name[0] == '\0' || !box)
6382 return IE_INVAL;
6385 /* Build CreateDataBox-similar request */
6386 *request = xmlNewNode(NULL, service_name);
6387 if (!*request) {
6388 char *service_name_locale = _isds_utf82locale((char*) service_name);
6389 isds_printf_message(context, _("Could build %s request"),
6390 service_name_locale);
6391 free(service_name_locale);
6392 return IE_ERROR;
6394 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6395 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6396 if (!isds_ns) {
6397 isds_log_message(context, _("Could not create ISDS1 name space"));
6398 xmlFreeNode(*request);
6399 return IE_ERROR;
6401 } else {
6402 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6403 if (!isds_ns) {
6404 isds_log_message(context, _("Could not create ISDS name space"));
6405 xmlFreeNode(*request);
6406 return IE_ERROR;
6409 xmlSetNs(*request, isds_ns);
6411 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6412 err = insert_DbOwnerInfo(context, box, 0, node);
6413 if (err) goto leave;
6415 /* Insert users */
6416 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6417 * verbose documentation allows none dbUserInfo */
6418 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6419 for (item = users; item; item = item->next) {
6420 if (item->data) {
6421 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6422 err = insert_DbUserInfo(context,
6423 (struct isds_DbUserInfo *) item->data, 1, node);
6424 if (err) goto leave;
6428 INSERT_STRING(*request, "dbFormerNames", former_names);
6429 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6430 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6432 err = insert_credentials_delivery(context, credentials_delivery, *request);
6433 if (err) goto leave;
6435 err = insert_GExtApproval(context, approval, *request);
6436 if (err) goto leave;
6438 leave:
6439 if (err) {
6440 xmlFreeNode(*request);
6441 *request = NULL;
6443 free(string);
6444 return err;
6446 #endif /* HAVE_LIBCURL */
6449 /* Create new box.
6450 * @context is session context
6451 * @box is box description to create including single primary user (in case of
6452 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6453 * ignored. It outputs box ID assigned by ISDS in dbID element.
6454 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6455 * box, or contact address of PFO box owner)
6456 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6457 * @upper_box_id is optional ID of supper box if currently created box is
6458 * subordinated.
6459 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6460 * @credentials_delivery is NULL if new password should be delivered off-line
6461 * to box owner. It is valid pointer if owner should obtain new password on-line
6462 * on dedicated web server. Then input @credentials_delivery.email value is
6463 * his e-mail address he must provide to dedicated web server together
6464 * with output reallocated @credentials_delivery.token member. Output
6465 * member @credentials_delivery.new_user_name is unused up on this call.
6466 * @approval is optional external approval of box manipulation
6467 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6468 * NULL, if you don't care.*/
6469 isds_error isds_add_box(struct isds_ctx *context,
6470 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6471 const char *former_names, const char *upper_box_id,
6472 const char *ceo_label,
6473 struct isds_credentials_delivery *credentials_delivery,
6474 const struct isds_approval *approval, char **refnumber) {
6475 isds_error err = IE_SUCCESS;
6476 #if HAVE_LIBCURL
6477 xmlNodePtr request = NULL;
6478 xmlDocPtr response = NULL;
6479 xmlXPathContextPtr xpath_ctx = NULL;
6480 xmlXPathObjectPtr result = NULL;
6481 #endif
6484 if (!context) return IE_INVALID_CONTEXT;
6485 zfree(context->long_message);
6486 if (credentials_delivery) {
6487 zfree(credentials_delivery->token);
6488 zfree(credentials_delivery->new_user_name);
6490 if (!box) return IE_INVAL;
6492 #if HAVE_LIBCURL
6493 /* Scratch box ID */
6494 zfree(box->dbID);
6496 /* Build CreateDataBox request */
6497 err = build_CreateDBInput_request(context,
6498 &request, BAD_CAST "CreateDataBox",
6499 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6500 (xmlChar *) ceo_label, credentials_delivery, approval);
6501 if (err) goto leave;
6503 /* Send it to server and process response */
6504 err = send_destroy_request_check_response(context,
6505 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6506 &response, (xmlChar **) refnumber, NULL);
6508 /* Extract box ID */
6509 xpath_ctx = xmlXPathNewContext(response);
6510 if (!xpath_ctx) {
6511 err = IE_ERROR;
6512 goto leave;
6514 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6515 err = IE_ERROR;
6516 goto leave;
6518 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6520 /* Extract optional token */
6521 err = extract_credentials_delivery(context, credentials_delivery, response,
6522 "CreateDataBox");
6524 leave:
6525 xmlXPathFreeObject(result);
6526 xmlXPathFreeContext(xpath_ctx);
6527 xmlFreeDoc(response);
6528 xmlFreeNode(request);
6530 if (!err) {
6531 isds_log(ILF_ISDS, ILL_DEBUG,
6532 _("CreateDataBox request processed by server successfully.\n"));
6534 #else /* not HAVE_LIBCURL */
6535 err = IE_NOTSUP;
6536 #endif
6538 return err;
6542 /* Notify ISDS about new PFO entity.
6543 * This function has no real effect.
6544 * @context is session context
6545 * @box is PFO description including single primary user. aifoIsds,
6546 * address->adCode, address->adDistrict members are ignored.
6547 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6548 * @former_names is optional undocumented string. Pass NULL if you don't care.
6549 * @upper_box_id is optional ID of supper box if currently created box is
6550 * subordinated.
6551 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6552 * @approval is optional external approval of box manipulation
6553 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6554 * NULL, if you don't care.*/
6555 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6556 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6557 const char *former_names, const char *upper_box_id,
6558 const char *ceo_label, const struct isds_approval *approval,
6559 char **refnumber) {
6560 isds_error err = IE_SUCCESS;
6561 #if HAVE_LIBCURL
6562 xmlNodePtr request = NULL;
6563 #endif
6565 if (!context) return IE_INVALID_CONTEXT;
6566 zfree(context->long_message);
6567 if (!box) return IE_INVAL;
6569 #if HAVE_LIBCURL
6570 /* Build CreateDataBoxPFOInfo request */
6571 err = build_CreateDBInput_request(context,
6572 &request, BAD_CAST "CreateDataBoxPFOInfo",
6573 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6574 (xmlChar *) ceo_label, NULL, approval);
6575 if (err) goto leave;
6577 /* Send it to server and process response */
6578 err = send_request_check_drop_response(context,
6579 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6580 (xmlChar **) refnumber);
6581 /* XXX: XML Schema names output dbID element but textual documentation
6582 * states no box identifier is returned. */
6583 leave:
6584 xmlFreeNode(request);
6585 #else /* not HAVE_LIBCURL */
6586 err = IE_NOTSUP;
6587 #endif
6588 return err;
6592 /* Common implementation for removing given box.
6593 * @context is session context
6594 * @service_name is UTF-8 encoded name fo ISDS service
6595 * @box is box description to delete. aifoIsds, address->adCode,
6596 * address->adDistrict members are ignored.
6597 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6598 * carry sane value. If NULL, do not inject this information into request.
6599 * @approval is optional external approval of box manipulation
6600 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6601 * NULL, if you don't care.*/
6602 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6603 const xmlChar *service_name,
6604 const struct isds_DbOwnerInfo *box, const struct tm *since,
6605 const struct isds_approval *approval, char **refnumber) {
6606 isds_error err = IE_SUCCESS;
6607 #if HAVE_LIBCURL
6608 xmlNsPtr isds_ns = NULL;
6609 xmlNodePtr request = NULL;
6610 xmlNodePtr node;
6611 xmlChar *string = NULL;
6612 #endif
6615 if (!context) return IE_INVALID_CONTEXT;
6616 zfree(context->long_message);
6617 if (!service_name || !*service_name || !box) return IE_INVAL;
6620 #if HAVE_LIBCURL
6621 /* Build DeleteDataBox(Promptly) request */
6622 request = xmlNewNode(NULL, service_name);
6623 if (!request) {
6624 char *service_name_locale = _isds_utf82locale((char*)service_name);
6625 isds_printf_message(context,
6626 _("Could build %s request"), service_name_locale);
6627 free(service_name_locale);
6628 return IE_ERROR;
6630 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6631 if(!isds_ns) {
6632 isds_log_message(context, _("Could not create ISDS name space"));
6633 xmlFreeNode(request);
6634 return IE_ERROR;
6636 xmlSetNs(request, isds_ns);
6638 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6639 err = insert_DbOwnerInfo(context, box, 0, node);
6640 if (err) goto leave;
6642 if (since) {
6643 err = tm2datestring(since, &string);
6644 if (err) {
6645 isds_log_message(context,
6646 _("Could not convert `since' argument to ISO date string"));
6647 goto leave;
6649 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6650 zfree(string);
6653 err = insert_GExtApproval(context, approval, request);
6654 if (err) goto leave;
6657 /* Send it to server and process response */
6658 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6659 service_name, &request, (xmlChar **) refnumber);
6661 leave:
6662 xmlFreeNode(request);
6663 free(string);
6664 #else /* not HAVE_LIBCURL */
6665 err = IE_NOTSUP;
6666 #endif
6667 return err;
6671 /* Remove given box permanently.
6672 * @context is session context
6673 * @box is box description to delete. aifoIsds, address->adCode,
6674 * address->adDistrict members are ignored.
6675 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6676 * carry sane value.
6677 * @approval is optional external approval of box manipulation
6678 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6679 * NULL, if you don't care.*/
6680 isds_error isds_delete_box(struct isds_ctx *context,
6681 const struct isds_DbOwnerInfo *box, const struct tm *since,
6682 const struct isds_approval *approval, char **refnumber) {
6683 if (!context) return IE_INVALID_CONTEXT;
6684 zfree(context->long_message);
6685 if (!box || !since) return IE_INVAL;
6687 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6688 box, since, approval, refnumber);
6692 /* Undocumented function.
6693 * @context is session context
6694 * @box is box description to delete. aifoIsds, address->adCode,
6695 * address->adDistrict members are ignored.
6696 * @approval is optional external approval of box manipulation
6697 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6698 * NULL, if you don't care.*/
6699 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6700 const struct isds_DbOwnerInfo *box,
6701 const struct isds_approval *approval, char **refnumber) {
6702 if (!context) return IE_INVALID_CONTEXT;
6703 zfree(context->long_message);
6704 if (!box) return IE_INVAL;
6706 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6707 box, NULL, approval, refnumber);
6711 /* Update data about given box.
6712 * @context is session context
6713 * @old_box current box description. aifoIsds, address->adCode,
6714 * address->adDistrict members are ignored.
6715 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6716 * address->adDistrict members are ignored.
6717 * @approval is optional external approval of box manipulation
6718 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6719 * NULL, if you don't care.*/
6720 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6721 const struct isds_DbOwnerInfo *old_box,
6722 const struct isds_DbOwnerInfo *new_box,
6723 const struct isds_approval *approval, char **refnumber) {
6724 isds_error err = IE_SUCCESS;
6725 #if HAVE_LIBCURL
6726 xmlNsPtr isds_ns = NULL;
6727 xmlNodePtr request = NULL;
6728 xmlNodePtr node;
6729 #endif
6732 if (!context) return IE_INVALID_CONTEXT;
6733 zfree(context->long_message);
6734 if (!old_box || !new_box) return IE_INVAL;
6737 #if HAVE_LIBCURL
6738 /* Build UpdateDataBoxDescr request */
6739 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6740 if (!request) {
6741 isds_log_message(context,
6742 _("Could build UpdateDataBoxDescr request"));
6743 return IE_ERROR;
6745 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6746 if(!isds_ns) {
6747 isds_log_message(context, _("Could not create ISDS name space"));
6748 xmlFreeNode(request);
6749 return IE_ERROR;
6751 xmlSetNs(request, isds_ns);
6753 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6754 err = insert_DbOwnerInfo(context, old_box, 0, node);
6755 if (err) goto leave;
6757 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6758 err = insert_DbOwnerInfo(context, new_box, 0, node);
6759 if (err) goto leave;
6761 err = insert_GExtApproval(context, approval, request);
6762 if (err) goto leave;
6765 /* Send it to server and process response */
6766 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6767 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6769 leave:
6770 xmlFreeNode(request);
6771 #else /* not HAVE_LIBCURL */
6772 err = IE_NOTSUP;
6773 #endif
6775 return err;
6779 #if HAVE_LIBCURL
6780 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6781 * code
6782 * @context is session context
6783 * @service is SOAP service
6784 * @service_name is name of request in @service
6785 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6786 * @box_id is box ID of interest
6787 * @approval is optional external approval of box manipulation
6788 * @response is server SOAP body response as XML document
6789 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6790 * NULL, if you don't care.
6791 * @return error coded from lower layer, context message will be set up
6792 * appropriately. */
6793 static isds_error build_send_dbid_request_check_response(
6794 struct isds_ctx *context, const isds_service service,
6795 const xmlChar *service_name, const xmlChar *box_id_element,
6796 const xmlChar *box_id, const struct isds_approval *approval,
6797 xmlDocPtr *response, xmlChar **refnumber) {
6799 isds_error err = IE_SUCCESS;
6800 char *service_name_locale = NULL, *box_id_locale = NULL;
6801 xmlNodePtr request = NULL, node;
6802 xmlNsPtr isds_ns = NULL;
6804 if (!context) return IE_INVALID_CONTEXT;
6805 if (!service_name || !box_id) return IE_INVAL;
6806 if (!response) return IE_INVAL;
6808 /* Free output argument */
6809 xmlFreeDoc(*response); *response = NULL;
6811 /* Prepare strings */
6812 service_name_locale = _isds_utf82locale((char*)service_name);
6813 if (!service_name_locale) {
6814 err = IE_NOMEM;
6815 goto leave;
6817 box_id_locale = _isds_utf82locale((char*)box_id);
6818 if (!box_id_locale) {
6819 err = IE_NOMEM;
6820 goto leave;
6823 /* Build request */
6824 request = xmlNewNode(NULL, service_name);
6825 if (!request) {
6826 isds_printf_message(context,
6827 _("Could not build %s request for %s box"), service_name_locale,
6828 box_id_locale);
6829 err = IE_ERROR;
6830 goto leave;
6832 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6833 if(!isds_ns) {
6834 isds_log_message(context, _("Could not create ISDS name space"));
6835 err = IE_ERROR;
6836 goto leave;
6838 xmlSetNs(request, isds_ns);
6840 /* Add XSD:tIdDbInput children */
6841 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6842 INSERT_STRING(request, box_id_element, box_id);
6843 err = insert_GExtApproval(context, approval, request);
6844 if (err) goto leave;
6846 /* Send request and check response*/
6847 err = send_destroy_request_check_response(context,
6848 service, service_name, &request, response, refnumber, NULL);
6850 leave:
6851 free(service_name_locale);
6852 free(box_id_locale);
6853 xmlFreeNode(request);
6854 return err;
6856 #endif /* HAVE_LIBCURL */
6859 /* Get data about all users assigned to given box.
6860 * @context is session context
6861 * @box_id is box ID
6862 * @users is automatically reallocated list of struct isds_DbUserInfo */
6863 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6864 struct isds_list **users) {
6865 isds_error err = IE_SUCCESS;
6866 #if HAVE_LIBCURL
6867 xmlDocPtr response = NULL;
6868 xmlXPathContextPtr xpath_ctx = NULL;
6869 xmlXPathObjectPtr result = NULL;
6870 int i;
6871 struct isds_list *item, *prev_item = NULL;
6872 #endif
6874 if (!context) return IE_INVALID_CONTEXT;
6875 zfree(context->long_message);
6876 if (!users || !box_id) return IE_INVAL;
6877 isds_list_free(users);
6880 #if HAVE_LIBCURL
6881 /* Do request and check for success */
6882 err = build_send_dbid_request_check_response(context,
6883 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6884 BAD_CAST box_id, NULL, &response, NULL);
6885 if (err) goto leave;
6888 /* Extract data */
6889 /* Prepare structure */
6890 xpath_ctx = xmlXPathNewContext(response);
6891 if (!xpath_ctx) {
6892 err = IE_ERROR;
6893 goto leave;
6895 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6896 err = IE_ERROR;
6897 goto leave;
6900 /* Set context node */
6901 result = xmlXPathEvalExpression(BAD_CAST
6902 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6903 xpath_ctx);
6904 if (!result) {
6905 err = IE_ERROR;
6906 goto leave;
6908 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6909 /* Iterate over all users */
6910 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6912 /* Prepare structure */
6913 item = calloc(1, sizeof(*item));
6914 if (!item) {
6915 err = IE_NOMEM;
6916 goto leave;
6918 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6919 if (i == 0) *users = item;
6920 else prev_item->next = item;
6921 prev_item = item;
6923 /* Extract it */
6924 xpath_ctx->node = result->nodesetval->nodeTab[i];
6925 err = extract_DbUserInfo(context,
6926 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6927 if (err) goto leave;
6931 leave:
6932 if (err) {
6933 isds_list_free(users);
6936 xmlXPathFreeObject(result);
6937 xmlXPathFreeContext(xpath_ctx);
6938 xmlFreeDoc(response);
6940 if (!err)
6941 isds_log(ILF_ISDS, ILL_DEBUG,
6942 _("GetDataBoxUsers request processed by server "
6943 "successfully.\n"));
6944 #else /* not HAVE_LIBCURL */
6945 err = IE_NOTSUP;
6946 #endif
6948 return err;
6952 /* Update data about user assigned to given box.
6953 * @context is session context
6954 * @box is box identification. aifoIsds, address->adCode,
6955 * address->adDistrict members are ignored.
6956 * @old_user identifies user to update, aifo_ticket member is ignored
6957 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6958 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6959 * NULL, if you don't care.*/
6960 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6961 const struct isds_DbOwnerInfo *box,
6962 const struct isds_DbUserInfo *old_user,
6963 const struct isds_DbUserInfo *new_user,
6964 char **refnumber) {
6965 isds_error err = IE_SUCCESS;
6966 #if HAVE_LIBCURL
6967 xmlNsPtr isds_ns = NULL;
6968 xmlNodePtr request = NULL;
6969 xmlNodePtr node;
6970 #endif
6973 if (!context) return IE_INVALID_CONTEXT;
6974 zfree(context->long_message);
6975 if (!box || !old_user || !new_user) return IE_INVAL;
6978 #if HAVE_LIBCURL
6979 /* Build UpdateDataBoxUser request */
6980 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6981 if (!request) {
6982 isds_log_message(context,
6983 _("Could build UpdateDataBoxUser request"));
6984 return IE_ERROR;
6986 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6987 if(!isds_ns) {
6988 isds_log_message(context, _("Could not create ISDS name space"));
6989 xmlFreeNode(request);
6990 return IE_ERROR;
6992 xmlSetNs(request, isds_ns);
6994 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6995 err = insert_DbOwnerInfo(context, box, 0, node);
6996 if (err) goto leave;
6998 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6999 err = insert_DbUserInfo(context, old_user, 0, node);
7000 if (err) goto leave;
7002 INSERT_ELEMENT(node, request, "dbNewUserInfo");
7003 err = insert_DbUserInfo(context, new_user, 0, node);
7004 if (err) goto leave;
7006 /* Send it to server and process response */
7007 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7008 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
7010 leave:
7011 xmlFreeNode(request);
7012 #else /* not HAVE_LIBCURL */
7013 err = IE_NOTSUP;
7014 #endif
7016 return err;
7020 /* Undocumented function.
7021 * @context is session context
7022 * @box_id is UTF-8 encoded box identifier
7023 * @token is UTF-8 encoded temporary password
7024 * @user_id outputs UTF-8 encoded reallocated user identifier
7025 * @password outpus UTF-8 encoded reallocated user password
7026 * Output arguments will be nulled in case of error */
7027 isds_error isds_activate(struct isds_ctx *context,
7028 const char *box_id, const char *token,
7029 char **user_id, char **password) {
7030 isds_error err = IE_SUCCESS;
7031 #if HAVE_LIBCURL
7032 xmlNsPtr isds_ns = NULL;
7033 xmlNodePtr request = NULL, node;
7034 xmlDocPtr response = NULL;
7035 xmlXPathContextPtr xpath_ctx = NULL;
7036 xmlXPathObjectPtr result = NULL;
7037 #endif
7040 if (!context) return IE_INVALID_CONTEXT;
7041 zfree(context->long_message);
7043 if (user_id) zfree(*user_id);
7044 if (password) zfree(*password);
7046 if (!box_id || !token || !user_id || !password) return IE_INVAL;
7049 #if HAVE_LIBCURL
7050 /* Build Activate request */
7051 request = xmlNewNode(NULL, BAD_CAST "Activate");
7052 if (!request) {
7053 isds_log_message(context, _("Could build Activate request"));
7054 return IE_ERROR;
7056 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7057 if(!isds_ns) {
7058 isds_log_message(context, _("Could not create ISDS name space"));
7059 xmlFreeNode(request);
7060 return IE_ERROR;
7062 xmlSetNs(request, isds_ns);
7064 INSERT_STRING(request, "dbAccessDataId", token);
7065 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
7066 INSERT_STRING(request, "dbID", box_id);
7069 /* Send request and check response*/
7070 err = send_destroy_request_check_response(context,
7071 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
7072 &response, NULL, NULL);
7073 if (err) goto leave;
7076 /* Extract data */
7077 xpath_ctx = xmlXPathNewContext(response);
7078 if (!xpath_ctx) {
7079 err = IE_ERROR;
7080 goto leave;
7082 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7083 err = IE_ERROR;
7084 goto leave;
7086 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
7087 xpath_ctx);
7088 if (!result) {
7089 err = IE_ERROR;
7090 goto leave;
7092 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7093 isds_log_message(context, _("Missing ActivateResponse element"));
7094 err = IE_ISDS;
7095 goto leave;
7097 if (result->nodesetval->nodeNr > 1) {
7098 isds_log_message(context, _("Multiple ActivateResponse element"));
7099 err = IE_ISDS;
7100 goto leave;
7102 xpath_ctx->node = result->nodesetval->nodeTab[0];
7103 xmlXPathFreeObject(result); result = NULL;
7105 EXTRACT_STRING("isds:userId", *user_id);
7106 if (!*user_id)
7107 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
7108 "but did not return `userId' element.\n"));
7110 EXTRACT_STRING("isds:password", *password);
7111 if (!*password)
7112 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
7113 "but did not return `password' element.\n"));
7115 leave:
7116 xmlXPathFreeObject(result);
7117 xmlXPathFreeContext(xpath_ctx);
7118 xmlFreeDoc(response);
7119 xmlFreeNode(request);
7121 if (!err)
7122 isds_log(ILF_ISDS, ILL_DEBUG,
7123 _("Activate request processed by server successfully.\n"));
7124 #else /* not HAVE_LIBCURL */
7125 err = IE_NOTSUP;
7126 #endif
7128 return err;
7132 /* Reset credentials of user assigned to given box.
7133 * @context is session context
7134 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7135 * members are ignored.
7136 * @user identifies user to reset password, aifo_ticket member is ignored
7137 * @fee_paid is true if fee has been paid, false otherwise
7138 * @approval is optional external approval of box manipulation
7139 * @credentials_delivery is NULL if new password should be delivered off-line
7140 * to the user. It is valid pointer if user should obtain new password on-line
7141 * on dedicated web server. Then input @credentials_delivery.email value is
7142 * user's e-mail address user must provide to dedicated web server together
7143 * with @credentials_delivery.token. The output reallocated token user needs
7144 * to use to authorize on the web server to view his new password. Output
7145 * reallocated @credentials_delivery.new_user_name is user's log-in name that
7146 * ISDS changed up on this call. (No reason why server could change the name
7147 * is known now.)
7148 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7149 * NULL, if you don't care.*/
7150 isds_error isds_reset_password(struct isds_ctx *context,
7151 const struct isds_DbOwnerInfo *box,
7152 const struct isds_DbUserInfo *user,
7153 const _Bool fee_paid, const struct isds_approval *approval,
7154 struct isds_credentials_delivery *credentials_delivery,
7155 char **refnumber) {
7156 isds_error err = IE_SUCCESS;
7157 #if HAVE_LIBCURL
7158 xmlNsPtr isds_ns = NULL;
7159 xmlNodePtr request = NULL, node;
7160 xmlDocPtr response = NULL;
7161 #endif
7164 if (!context) return IE_INVALID_CONTEXT;
7165 zfree(context->long_message);
7167 if (credentials_delivery) {
7168 zfree(credentials_delivery->token);
7169 zfree(credentials_delivery->new_user_name);
7171 if (!box || !user) return IE_INVAL;
7174 #if HAVE_LIBCURL
7175 /* Build NewAccessData request */
7176 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
7177 if (!request) {
7178 isds_log_message(context,
7179 _("Could build NewAccessData request"));
7180 return IE_ERROR;
7182 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7183 if(!isds_ns) {
7184 isds_log_message(context, _("Could not create ISDS name space"));
7185 xmlFreeNode(request);
7186 return IE_ERROR;
7188 xmlSetNs(request, isds_ns);
7190 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7191 err = insert_DbOwnerInfo(context, box, 0, node);
7192 if (err) goto leave;
7194 INSERT_ELEMENT(node, request, "dbUserInfo");
7195 err = insert_DbUserInfo(context, user, 0, node);
7196 if (err) goto leave;
7198 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
7200 err = insert_credentials_delivery(context, credentials_delivery, request);
7201 if (err) goto leave;
7203 err = insert_GExtApproval(context, approval, request);
7204 if (err) goto leave;
7206 /* Send request and check response*/
7207 err = send_destroy_request_check_response(context,
7208 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
7209 &response, (xmlChar **) refnumber, NULL);
7210 if (err) goto leave;
7213 /* Extract optional token */
7214 err = extract_credentials_delivery(context, credentials_delivery,
7215 response, "NewAccessData");
7217 leave:
7218 xmlFreeDoc(response);
7219 xmlFreeNode(request);
7221 if (!err)
7222 isds_log(ILF_ISDS, ILL_DEBUG,
7223 _("NewAccessData request processed by server "
7224 "successfully.\n"));
7225 #else /* not HAVE_LIBCURL */
7226 err = IE_NOTSUP;
7227 #endif
7229 return err;
7233 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7234 * code, destroy response and log success.
7235 * @context is ISDS session context.
7236 * @service_name is name of SERVICE_DB_MANIPULATION service
7237 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7238 * members are ignored.
7239 * @user identifies user to remove
7240 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7241 * @credentials_delivery is NULL if new user's password should be delivered
7242 * off-line to the user. It is valid pointer if user should obtain new
7243 * password on-line on dedicated web server. Then input
7244 * @credentials_delivery.email value is user's e-mail address user must
7245 * provide to dedicated web server together with @credentials_delivery.token.
7246 * The output reallocated token user needs to use to authorize on the web
7247 * server to view his new password. Output reallocated
7248 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7249 * assingned or changed up on this call.
7250 * @approval is optional external approval of box manipulation
7251 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7252 * NULL, if you don't care. */
7253 static isds_error build_send_manipulationboxuser_request_check_drop_response(
7254 struct isds_ctx *context, const xmlChar *service_name,
7255 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7256 _Bool honor_aifo_ticket,
7257 struct isds_credentials_delivery *credentials_delivery,
7258 const struct isds_approval *approval, xmlChar **refnumber) {
7259 isds_error err = IE_SUCCESS;
7260 #if HAVE_LIBCURL
7261 xmlNsPtr isds_ns = NULL;
7262 xmlNodePtr request = NULL, node;
7263 xmlDocPtr response = NULL;
7264 #endif
7267 if (!context) return IE_INVALID_CONTEXT;
7268 zfree(context->long_message);
7269 if (credentials_delivery) {
7270 zfree(credentials_delivery->token);
7271 zfree(credentials_delivery->new_user_name);
7273 if (!service_name || service_name[0] == '\0' || !box || !user)
7274 return IE_INVAL;
7277 #if HAVE_LIBCURL
7278 /* Build NewAccessData or similar request */
7279 request = xmlNewNode(NULL, service_name);
7280 if (!request) {
7281 char *service_name_locale = _isds_utf82locale((char *) service_name);
7282 isds_printf_message(context, _("Could not build %s request"),
7283 service_name_locale);
7284 free(service_name_locale);
7285 return IE_ERROR;
7287 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7288 if(!isds_ns) {
7289 isds_log_message(context, _("Could not create ISDS name space"));
7290 xmlFreeNode(request);
7291 return IE_ERROR;
7293 xmlSetNs(request, isds_ns);
7295 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7296 err = insert_DbOwnerInfo(context, box, 0, node);
7297 if (err) goto leave;
7299 INSERT_ELEMENT(node, request, "dbUserInfo");
7300 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7301 if (err) goto leave;
7303 err = insert_credentials_delivery(context, credentials_delivery, request);
7304 if (err) goto leave;
7306 err = insert_GExtApproval(context, approval, request);
7307 if (err) goto leave;
7310 /* Send request and check response*/
7311 err = send_destroy_request_check_response(context,
7312 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7313 refnumber, NULL);
7315 xmlFreeNode(request);
7316 request = NULL;
7318 /* Pick up credentials_delivery if requested */
7319 err = extract_credentials_delivery(context, credentials_delivery, response,
7320 (char *)service_name);
7322 leave:
7323 xmlFreeDoc(response);
7324 if (request) xmlFreeNode(request);
7326 if (!err) {
7327 char *service_name_locale = _isds_utf82locale((char *) service_name);
7328 isds_log(ILF_ISDS, ILL_DEBUG,
7329 _("%s request processed by server successfully.\n"),
7330 service_name_locale);
7331 free(service_name_locale);
7333 #else /* not HAVE_LIBCURL */
7334 err = IE_NOTSUP;
7335 #endif
7337 return err;
7341 /* Assign new user to given box.
7342 * @context is session context
7343 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7344 * members are ignored.
7345 * @user defines new user to add
7346 * @credentials_delivery is NULL if new user's password should be delivered
7347 * off-line to the user. It is valid pointer if user should obtain new
7348 * password on-line on dedicated web server. Then input
7349 * @credentials_delivery.email value is user's e-mail address user must
7350 * provide to dedicated web server together with @credentials_delivery.token.
7351 * The output reallocated token user needs to use to authorize on the web
7352 * server to view his new password. Output reallocated
7353 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7354 * assingned up on this call.
7355 * @approval is optional external approval of box manipulation
7356 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7357 * NULL, if you don't care.*/
7358 isds_error isds_add_user(struct isds_ctx *context,
7359 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7360 struct isds_credentials_delivery *credentials_delivery,
7361 const struct isds_approval *approval, char **refnumber) {
7362 return build_send_manipulationboxuser_request_check_drop_response(context,
7363 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7364 approval, (xmlChar **) refnumber);
7368 /* Remove user assigned to given box.
7369 * @context is session context
7370 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7371 * members are ignored.
7372 * @user identifies user to remove, aifo_ticket member is ignored
7373 * @approval is optional external approval of box manipulation
7374 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7375 * NULL, if you don't care.*/
7376 isds_error isds_delete_user(struct isds_ctx *context,
7377 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7378 const struct isds_approval *approval, char **refnumber) {
7379 return build_send_manipulationboxuser_request_check_drop_response(context,
7380 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7381 (xmlChar **) refnumber);
7385 /* Get list of boxes in ZIP archive.
7386 * @context is session context
7387 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7388 * System recognizes following values currently: ALL (all boxes), UPG
7389 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7390 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7391 * commercial messages). This argument is a string because specification
7392 * states new values can appear in the future. Not all list types are
7393 * available to all users.
7394 * @buffer is automatically reallocated memory to store the list of boxes. The
7395 * list is zipped CSV file.
7396 * @buffer_length is size of @buffer data in bytes.
7397 * In case of error @buffer will be freed and @buffer_length will be
7398 * undefined.*/
7399 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7400 const char *list_identifier, void **buffer, size_t *buffer_length) {
7401 isds_error err = IE_SUCCESS;
7402 #if HAVE_LIBCURL
7403 xmlNsPtr isds_ns = NULL;
7404 xmlNodePtr request = NULL, node;
7405 xmlDocPtr response = NULL;
7406 xmlXPathContextPtr xpath_ctx = NULL;
7407 xmlXPathObjectPtr result = NULL;
7408 char *string = NULL;
7409 #endif
7412 if (!context) return IE_INVALID_CONTEXT;
7413 zfree(context->long_message);
7414 if (buffer) zfree(*buffer);
7415 if (!buffer || !buffer_length) return IE_INVAL;
7418 #if HAVE_LIBCURL
7419 /* Check if connection is established
7420 * TODO: This check should be done downstairs. */
7421 if (!context->curl) return IE_CONNECTION_CLOSED;
7424 /* Build AuthenticateMessage request */
7425 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7426 if (!request) {
7427 isds_log_message(context,
7428 _("Could not build GetDataBoxList request"));
7429 return IE_ERROR;
7431 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7432 if(!isds_ns) {
7433 isds_log_message(context, _("Could not create ISDS name space"));
7434 xmlFreeNode(request);
7435 return IE_ERROR;
7437 xmlSetNs(request, isds_ns);
7438 INSERT_STRING(request, "dblType", list_identifier);
7440 /* Send request to server and process response */
7441 err = send_destroy_request_check_response(context,
7442 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7443 &response, NULL, NULL);
7444 if (err) goto leave;
7447 /* Extract Base-64 encoded ZIP file */
7448 xpath_ctx = xmlXPathNewContext(response);
7449 if (!xpath_ctx) {
7450 err = IE_ERROR;
7451 goto leave;
7453 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7454 err = IE_ERROR;
7455 goto leave;
7457 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7459 /* Decode non-empty archive */
7460 if (string && string[0] != '\0') {
7461 *buffer_length = _isds_b64decode(string, buffer);
7462 if (*buffer_length == (size_t) -1) {
7463 isds_printf_message(context,
7464 _("Error while Base64-decoding box list archive"));
7465 err = IE_ERROR;
7466 goto leave;
7471 leave:
7472 free(string);
7473 xmlXPathFreeObject(result);
7474 xmlXPathFreeContext(xpath_ctx);
7475 xmlFreeDoc(response);
7476 xmlFreeNode(request);
7478 if (!err) {
7479 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7480 "processed by server successfully.\n"));
7482 #else /* not HAVE_LIBCURL */
7483 err = IE_NOTSUP;
7484 #endif
7486 return err;
7490 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7491 * send it, check for error code, extract list of results, destroy response
7492 * and log success.
7493 * @context is ISDS session context.
7494 * @service_name is name of SERVICE_DB_SEARCH service
7495 * @pfo_service is false if tDbOwnerInfo request should be built from
7496 * @criteria and corresponding result extracted. It is true if
7497 * tDbPersonalOwnerInfoRequest request should be built. The request and
7498 * response differ subset of significant isds_DbOwnerInfo structure members.
7499 * @criteria is filter. You should fill in at least some members.
7500 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7501 * members will be ignored. If @pfo_service is true, dbType, ic,
7502 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7503 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7504 * ignored.
7505 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7506 * possibly empty. Input NULL or valid old structure. The same memebers as
7507 * in described for @criteria argument will be NULL according to @pfo_service
7508 * switch.
7509 * @return:
7510 * IE_SUCCESS if search succeeded, @boxes contains useful data
7511 * IE_NOEXIST if no such box exists, @boxes will be NULL
7512 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7513 * contains still valid data
7514 * other code if something bad happens. @boxes will be NULL. */
7515 static isds_error build_send_findbox_request_check_parse_drop_response(
7516 struct isds_ctx *context, const xmlChar *service_name,
7517 _Bool pfo_service, const struct isds_DbOwnerInfo *criteria,
7518 struct isds_list **boxes) {
7519 isds_error err = IE_SUCCESS;
7520 #if HAVE_LIBCURL
7521 char *service_name_locale = NULL;
7522 _Bool truncated = 0;
7523 xmlNsPtr isds_ns = NULL;
7524 xmlNodePtr request = NULL;
7525 xmlDocPtr response = NULL;
7526 xmlChar *code = NULL, *message = NULL;
7527 xmlNodePtr db_owner_info;
7528 xmlXPathContextPtr xpath_ctx = NULL;
7529 xmlXPathObjectPtr result = NULL;
7530 xmlChar *string = NULL;
7531 #endif
7534 if (!context) return IE_INVALID_CONTEXT;
7535 zfree(context->long_message);
7536 if (!boxes) return IE_INVAL;
7537 isds_list_free(boxes);
7539 if (!criteria) {
7540 return IE_INVAL;
7543 #if HAVE_LIBCURL
7544 /* Check if connection is established
7545 * TODO: This check should be done downstairs. */
7546 if (!context->curl) return IE_CONNECTION_CLOSED;
7547 service_name_locale = _isds_utf82locale((char *) service_name);
7549 /* Build request */
7550 request = xmlNewNode(NULL, service_name);
7551 if (!request) {
7552 isds_printf_message(context, _("Could not build %s request"),
7553 service_name_locale);
7554 free(service_name_locale);
7555 return IE_ERROR;
7557 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7558 if(!isds_ns) {
7559 isds_log_message(context, _("Could not create ISDS name space"));
7560 free(service_name_locale);
7561 xmlFreeNode(request);
7562 return IE_ERROR;
7564 xmlSetNs(request, isds_ns);
7565 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7566 if (!db_owner_info) {
7567 isds_printf_message(context,
7568 _("Could not add dbOwnerInfo child to %s element"),
7569 service_name_locale);
7570 free(service_name_locale);
7571 xmlFreeNode(request);
7572 return IE_ERROR;
7575 err = insert_DbOwnerInfo(context, criteria, pfo_service, db_owner_info);
7576 if (err) goto leave;
7579 /* Send request */
7580 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
7581 service_name_locale);
7582 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7584 /* Destroy request */
7585 xmlFreeNode(request); request = NULL;
7587 if (err) {
7588 isds_log(ILF_ISDS, ILL_DEBUG,
7589 _("Processing ISDS response on %s request failed\n"),
7590 service_name_locale);
7591 goto leave;
7594 /* Check for response status */
7595 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7596 &code, &message, NULL);
7597 if (err) {
7598 isds_log(ILF_ISDS, ILL_DEBUG,
7599 _("ISDS response on %s request is missing status\n"),
7600 service_name_locale);
7601 goto leave;
7604 /* Request processed, but nothing found */
7605 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7606 !xmlStrcmp(code, BAD_CAST "5001")) {
7607 char *code_locale = _isds_utf82locale((char*)code);
7608 char *message_locale = _isds_utf82locale((char*)message);
7609 isds_log(ILF_ISDS, ILL_DEBUG,
7610 _("Server did not find any box on %s request "
7611 "(code=%s, message=%s)\n"), service_name_locale,
7612 code_locale, message_locale);
7613 isds_log_message(context, message_locale);
7614 free(code_locale);
7615 free(message_locale);
7616 err = IE_NOEXIST;
7617 goto leave;
7620 /* Warning, not an error */
7621 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7622 char *code_locale = _isds_utf82locale((char*)code);
7623 char *message_locale = _isds_utf82locale((char*)message);
7624 isds_log(ILF_ISDS, ILL_DEBUG,
7625 _("Server truncated response on %s request "
7626 "(code=%s, message=%s)\n"), service_name_locale,
7627 code_locale, message_locale);
7628 isds_log_message(context, message_locale);
7629 free(code_locale);
7630 free(message_locale);
7631 truncated = 1;
7634 /* Other error */
7635 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7636 char *code_locale = _isds_utf82locale((char*)code);
7637 char *message_locale = _isds_utf82locale((char*)message);
7638 isds_log(ILF_ISDS, ILL_DEBUG,
7639 _("Server refused %s request (code=%s, message=%s)\n"),
7640 service_name_locale, code_locale, message_locale);
7641 isds_log_message(context, message_locale);
7642 free(code_locale);
7643 free(message_locale);
7644 err = IE_ISDS;
7645 goto leave;
7648 xpath_ctx = xmlXPathNewContext(response);
7649 if (!xpath_ctx) {
7650 err = IE_ERROR;
7651 goto leave;
7653 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7654 err = IE_ERROR;
7655 goto leave;
7658 /* Extract boxes if they present */
7659 if (-1 == isds_asprintf((char **)&string,
7660 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7661 service_name)) {
7662 err = IE_NOMEM;
7663 goto leave;
7665 result = xmlXPathEvalExpression(string, xpath_ctx);
7666 zfree(string);
7667 if (!result) {
7668 err = IE_ERROR;
7669 goto leave;
7671 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7672 struct isds_list *item, *prev_item = NULL;
7673 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7674 item = calloc(1, sizeof(*item));
7675 if (!item) {
7676 err = IE_NOMEM;
7677 goto leave;
7680 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7681 if (i == 0) *boxes = item;
7682 else prev_item->next = item;
7683 prev_item = item;
7685 xpath_ctx->node = result->nodesetval->nodeTab[i];
7686 err = extract_DbOwnerInfo(context,
7687 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7688 if (err) goto leave;
7692 leave:
7693 if (err) {
7694 isds_list_free(boxes);
7695 } else {
7696 if (truncated) err = IE_2BIG;
7699 free(string);
7700 xmlFreeNode(request);
7701 xmlXPathFreeObject(result);
7702 xmlXPathFreeContext(xpath_ctx);
7704 free(code);
7705 free(message);
7706 xmlFreeDoc(response);
7708 if (!err)
7709 isds_log(ILF_ISDS, ILL_DEBUG,
7710 _("%s request processed by server successfully.\n"),
7711 service_name_locale);
7712 free(service_name_locale);
7713 #else /* not HAVE_LIBCURL */
7714 err = IE_NOTSUP;
7715 #endif
7717 return err;
7721 /* Find boxes suiting given criteria.
7722 * @criteria is filter. You should fill in at least some members. aifoIsds,
7723 * address->adCode, address->adDistrict members are ignored.
7724 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7725 * possibly empty. Input NULL or valid old structure.
7726 * @return:
7727 * IE_SUCCESS if search succeeded, @boxes contains useful data
7728 * IE_NOEXIST if no such box exists, @boxes will be NULL
7729 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7730 * contains still valid data
7731 * other code if something bad happens. @boxes will be NULL. */
7732 isds_error isds_FindDataBox(struct isds_ctx *context,
7733 const struct isds_DbOwnerInfo *criteria,
7734 struct isds_list **boxes) {
7735 return build_send_findbox_request_check_parse_drop_response(context,
7736 BAD_CAST "FindDataBox", 0, criteria, boxes);
7740 /* Find accessible FO-type boxes suiting given criteria.
7741 * @criteria is filter. You should fill in at least some members. dbType, ic,
7742 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7743 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7744 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7745 * possibly empty. Input NULL or valid old structure.
7746 * @return:
7747 * IE_SUCCESS if search succeeded, @boxes contains useful data
7748 * IE_NOEXIST if no such box exists, @boxes will be NULL
7749 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7750 * contains still valid data
7751 * other code if something bad happens. @boxes will be NULL. */
7752 isds_error isds_FindPersonalDataBox(struct isds_ctx *context,
7753 const struct isds_DbOwnerInfo *criteria,
7754 struct isds_list **boxes) {
7755 return build_send_findbox_request_check_parse_drop_response(context,
7756 BAD_CAST "FindPersonalDataBox", 1, criteria, boxes);
7760 #if HAVE_LIBCURL
7761 /* Convert a string with match markers into a plain string with list of
7762 * pointers to the matches
7763 * @string is an UTF-8 encoded non-constant string with match markers
7764 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7765 * The markers will be removed from the string.
7766 * @starts is a reallocated list of static pointers into the @string pointing
7767 * to places where match start markers occured.
7768 * @ends is a reallocated list of static pointers into the @string pointing
7769 * to places where match end markers occured.
7770 * @return IE_SUCCESS in case of no failure. */
7771 static isds_error interpret_matches(xmlChar *string,
7772 struct isds_list **starts, struct isds_list **ends) {
7773 isds_error err = IE_SUCCESS;
7774 xmlChar *pointer, *destination, *source;
7775 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7777 isds_list_free(starts);
7778 isds_list_free(ends);
7779 if (NULL == starts || NULL == ends) return IE_INVAL;
7780 if (NULL == string) return IE_SUCCESS;
7782 for (pointer = string; *pointer != '\0';) {
7783 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7784 /* Remove the start marker */
7785 for (source = pointer + 14, destination = pointer;
7786 *source != '\0'; source++, destination++) {
7787 *destination = *source;
7789 *destination = '\0';
7790 /* Append the pointer into the list */
7791 item = calloc(1, sizeof(*item));
7792 if (!item) {
7793 err = IE_NOMEM;
7794 goto leave;
7796 item->destructor = (void (*)(void **))NULL;
7797 item->data = pointer;
7798 if (NULL == prev_start) *starts = item;
7799 else prev_start->next = item;
7800 prev_start = item;
7801 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7802 /* Remove the end marker */
7803 for (source = pointer + 12, destination = pointer;
7804 *source != '\0'; source++, destination++) {
7805 *destination = *source;
7807 *destination = '\0';
7808 /* Append the pointer into the list */
7809 item = calloc(1, sizeof(*item));
7810 if (!item) {
7811 err = IE_NOMEM;
7812 goto leave;
7814 item->destructor = (void (*)(void **))NULL;
7815 item->data = pointer;
7816 if (NULL == prev_end) *ends = item;
7817 else prev_end->next = item;
7818 prev_end = item;
7819 } else {
7820 pointer++;
7824 leave:
7825 if (err) {
7826 isds_list_free(starts);
7827 isds_list_free(ends);
7829 return err;
7833 /* Convert isds:dbResult XML tree into structure
7834 * @context is ISDS context.
7835 * @fulltext_result is automatically reallocated found box structure.
7836 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7837 * @collect_matches is true to interpret match markers.
7838 * In case of error @result will be freed. */
7839 static isds_error extract_dbResult(struct isds_ctx *context,
7840 struct isds_fulltext_result **fulltext_result,
7841 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7842 isds_error err = IE_SUCCESS;
7843 xmlXPathObjectPtr result = NULL;
7844 char *string = NULL;
7846 if (NULL == context) return IE_INVALID_CONTEXT;
7847 if (NULL == fulltext_result) return IE_INVAL;
7848 isds_fulltext_result_free(fulltext_result);
7849 if (!xpath_ctx) return IE_INVAL;
7852 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7853 if (NULL == *fulltext_result) {
7854 err = IE_NOMEM;
7855 goto leave;
7858 /* Extract data */
7859 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7861 EXTRACT_STRING("isds:dbType", string);
7862 if (NULL == string) {
7863 err = IE_ISDS;
7864 isds_log_message(context, _("Empty isds:dbType element"));
7865 goto leave;
7867 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7868 if (err) {
7869 if (err == IE_ENUM) {
7870 err = IE_ISDS;
7871 char *string_locale = _isds_utf82locale(string);
7872 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7873 string_locale);
7874 free(string_locale);
7876 goto leave;
7878 zfree(string);
7880 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7881 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7883 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7884 if (err) goto leave;
7886 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7887 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7888 (*fulltext_result)->dbEffectiveOVM);
7890 EXTRACT_STRING("isds:dbSendOptions", string);
7891 if (NULL == string) {
7892 err = IE_ISDS;
7893 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7894 goto leave;
7896 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7897 (*fulltext_result)->active = 1;
7898 (*fulltext_result)->public_sending = 1;
7899 (*fulltext_result)->commercial_sending = 0;
7900 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7901 (*fulltext_result)->active = 1;
7902 (*fulltext_result)->public_sending = 1;
7903 (*fulltext_result)->commercial_sending = 1;
7904 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7905 (*fulltext_result)->active = 1;
7906 (*fulltext_result)->public_sending = 0;
7907 (*fulltext_result)->commercial_sending = 1;
7908 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7909 (*fulltext_result)->active = 1;
7910 (*fulltext_result)->public_sending = 0;
7911 (*fulltext_result)->commercial_sending = 0;
7912 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7913 (*fulltext_result)->active = 0;
7914 (*fulltext_result)->public_sending = 0;
7915 (*fulltext_result)->commercial_sending = 0;
7916 } else {
7917 err = IE_ISDS;
7918 char *string_locale = _isds_utf82locale(string);
7919 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7920 string_locale);
7921 free(string_locale);
7922 goto leave;
7924 zfree(string);
7926 /* Interpret match marks */
7927 if (collect_matches) {
7928 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7929 &((*fulltext_result)->name_match_start),
7930 &((*fulltext_result)->name_match_end));
7931 if (err) goto leave;
7932 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7933 &((*fulltext_result)->address_match_start),
7934 &((*fulltext_result)->address_match_end));
7935 if (err) goto leave;
7938 leave:
7939 if (err) isds_fulltext_result_free(fulltext_result);
7940 free(string);
7941 xmlXPathFreeObject(result);
7942 return err;
7944 #endif /* HAVE_LIBCURL */
7947 /* Find boxes matching a given full-text criteria.
7948 * @context is a session context
7949 * @query is a non-empty string which consists of words to search
7950 * @target selects box attributes to search for @query words. Pass NULL if you
7951 * don't care.
7952 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7953 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7954 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7955 * which is DBTYPE_SYSTEM.
7956 * @page_size defines count of boxes to constitute a response page. It counts
7957 * from zero. Pass NULL to let server to use a default value (50 now).
7958 * @page_number defines ordinar number of the response page to return. It
7959 * counts from zero. Pass NULL to let server to use a default value (0 now).
7960 * @track_matches points to true for marking @query words found in the box
7961 * attributes. It points to false for not marking. Pass NULL to let the server
7962 * to use default value (false now).
7963 * @total_matching_boxes outputs reallocated number of all boxes matching the
7964 * query. Will be pointer to NULL if server did not provide the value.
7965 * Pass NULL if you don't care.
7966 * @current_page_beginning outputs reallocated ordinar number of the first box
7967 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7968 * server did not provide the value. Pass NULL if you don't care.
7969 * @current_page_size outputs reallocated count of boxes in the this @boxes
7970 * page. It will be pointer to NULL if the server did not provide the value.
7971 * Pass NULL if you don't care.
7972 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7973 * is the last one, false if more boxes match, NULL if the server did not
7974 * provude the value. Pass NULL if you don't care.
7975 * @boxes outputs reallocated list of isds_fulltext_result structures,
7976 * possibly empty.
7977 * @return:
7978 * IE_SUCCESS if search succeeded
7979 * IE_2BIG if @page_size is too large
7980 * other code if something bad happens; output arguments will be NULL. */
7981 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7982 const char *query,
7983 const isds_fulltext_target *target,
7984 const isds_DbType *box_type,
7985 const unsigned long int *page_size,
7986 const unsigned long int *page_number,
7987 const _Bool *track_matches,
7988 unsigned long int **total_matching_boxes,
7989 unsigned long int **current_page_beginning,
7990 unsigned long int **current_page_size,
7991 _Bool **last_page,
7992 struct isds_list **boxes) {
7993 isds_error err = IE_SUCCESS;
7994 #if HAVE_LIBCURL
7995 xmlNsPtr isds_ns = NULL;
7996 xmlNodePtr request = NULL;
7997 xmlDocPtr response = NULL;
7998 xmlNodePtr node;
7999 xmlXPathContextPtr xpath_ctx = NULL;
8000 xmlXPathObjectPtr result = NULL;
8001 const xmlChar *static_string = NULL;
8002 xmlChar *string = NULL;
8004 const xmlChar *codes[] = {
8005 BAD_CAST "1004",
8006 BAD_CAST "1152",
8007 BAD_CAST "1153",
8008 BAD_CAST "1154",
8009 BAD_CAST "1155",
8010 BAD_CAST "1156",
8011 BAD_CAST "9002",
8012 NULL
8014 const char *meanings[] = {
8015 N_("You are not allowed to perform the search"),
8016 N_("The query string is empty"),
8017 N_("Searched box ID is malformed"),
8018 N_("Searched organization ID is malformed"),
8019 N_("Invalid input"),
8020 N_("Requested page size is too large"),
8021 N_("Search engine internal error")
8023 const isds_error errors[] = {
8024 IE_ISDS,
8025 IE_INVAL,
8026 IE_INVAL,
8027 IE_INVAL,
8028 IE_INVAL,
8029 IE_2BIG,
8030 IE_ISDS
8032 struct code_map_isds_error map = {
8033 .codes = codes,
8034 .meanings = meanings,
8035 .errors = errors
8037 #endif
8040 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
8041 if (NULL != current_page_beginning) zfree(*current_page_beginning);
8042 if (NULL != current_page_size) zfree(*current_page_size);
8043 if (NULL != last_page) zfree(*last_page);
8044 isds_list_free(boxes);
8046 if (NULL == context) return IE_INVALID_CONTEXT;
8047 zfree(context->long_message);
8049 if (NULL == boxes) return IE_INVAL;
8051 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
8052 isds_log_message(context, _("Query string must be non-empty"));
8053 return IE_INVAL;
8056 #if HAVE_LIBCURL
8057 /* Check if connection is established
8058 * TODO: This check should be done downstairs. */
8059 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8061 /* Build FindDataBox request */
8062 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
8063 if (NULL == request) {
8064 isds_log_message(context,
8065 _("Could not build ISDSSearch2 request"));
8066 return IE_ERROR;
8068 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8069 if(NULL == isds_ns) {
8070 isds_log_message(context, _("Could not create ISDS name space"));
8071 xmlFreeNode(request);
8072 return IE_ERROR;
8074 xmlSetNs(request, isds_ns);
8076 INSERT_STRING(request, "searchText", query);
8078 if (NULL != target) {
8079 static_string = isds_fulltext_target2string(*(target));
8080 if (NULL == static_string) {
8081 isds_printf_message(context, _("Invalid target value: %d"),
8082 *(target));
8083 err = IE_ENUM;
8084 goto leave;
8087 INSERT_STRING(request, "searchType", static_string);
8088 static_string = NULL;
8090 if (NULL != box_type) {
8091 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
8092 if (DBTYPE_SYSTEM == *box_type) {
8093 static_string = BAD_CAST "ALL";
8094 } else if (DBTYPE_OVM_MAIN == *box_type) {
8095 static_string = BAD_CAST "OVM_MAIN";
8096 } else {
8097 static_string = isds_DbType2string(*(box_type));
8098 if (NULL == static_string) {
8099 isds_printf_message(context, _("Invalid box type value: %d"),
8100 *(box_type));
8101 err = IE_ENUM;
8102 goto leave;
8106 INSERT_STRING(request, "searchScope", static_string);
8107 static_string = NULL;
8109 INSERT_ULONGINT(request, "page", page_number, string);
8110 INSERT_ULONGINT(request, "pageSize", page_size, string);
8111 INSERT_BOOLEAN(request, "highlighting", track_matches);
8113 /* Send request and check response */
8114 err = send_destroy_request_check_response(context,
8115 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
8116 &request, &response, NULL, &map);
8117 if (err) goto leave;
8119 /* Parse response */
8120 xpath_ctx = xmlXPathNewContext(response);
8121 if (NULL == xpath_ctx) {
8122 err = IE_ERROR;
8123 goto leave;
8125 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8126 err = IE_ERROR;
8127 goto leave;
8129 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
8130 xpath_ctx);
8131 if (!result) {
8132 err = IE_ERROR;
8133 goto leave;
8135 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8136 isds_log_message(context, _("Missing ISDSSearch2 element"));
8137 err = IE_ISDS;
8138 goto leave;
8140 if (result->nodesetval->nodeNr > 1) {
8141 isds_log_message(context, _("Multiple ISDSSearch2 element"));
8142 err = IE_ISDS;
8143 goto leave;
8145 xpath_ctx->node = result->nodesetval->nodeTab[0];
8146 xmlXPathFreeObject(result); result = NULL;
8149 /* Extract counters */
8150 if (NULL != total_matching_boxes) {
8151 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
8153 if (NULL != current_page_size) {
8154 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
8156 if (NULL != current_page_beginning) {
8157 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
8159 if (NULL != last_page) {
8160 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
8162 xmlXPathFreeObject(result); result = NULL;
8164 /* Extract boxes if they present */
8165 result = xmlXPathEvalExpression(BAD_CAST
8166 "isds:dbResults/isds:dbResult", xpath_ctx);
8167 if (NULL == result) {
8168 err = IE_ERROR;
8169 goto leave;
8171 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8172 struct isds_list *item, *prev_item = NULL;
8173 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8174 item = calloc(1, sizeof(*item));
8175 if (!item) {
8176 err = IE_NOMEM;
8177 goto leave;
8180 item->destructor = (void (*)(void **))isds_fulltext_result_free;
8181 if (i == 0) *boxes = item;
8182 else prev_item->next = item;
8183 prev_item = item;
8185 xpath_ctx->node = result->nodesetval->nodeTab[i];
8186 err = extract_dbResult(context,
8187 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
8188 (NULL == track_matches) ? 0 : *track_matches);
8189 if (err) goto leave;
8193 leave:
8194 if (err) {
8195 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
8196 if (NULL != current_page_beginning) zfree(*current_page_beginning);
8197 if (NULL != current_page_size) zfree(*current_page_size);
8198 if (NULL != last_page) zfree(*last_page);
8199 isds_list_free(boxes);
8202 free(string);
8203 xmlFreeNode(request);
8204 xmlXPathFreeObject(result);
8205 xmlXPathFreeContext(xpath_ctx);
8206 xmlFreeDoc(response);
8208 if (!err)
8209 isds_log(ILF_ISDS, ILL_DEBUG,
8210 _("ISDSSearch2 request processed by server successfully.\n"));
8211 #else /* not HAVE_LIBCURL */
8212 err = IE_NOTSUP;
8213 #endif
8215 return err;
8219 /* Get status of a box.
8220 * @context is ISDS session context.
8221 * @box_id is UTF-8 encoded box identifier as zero terminated string
8222 * @box_status is return value of box status.
8223 * @return:
8224 * IE_SUCCESS if box has been found and its status retrieved
8225 * IE_NOEXIST if box is not known to ISDS server
8226 * or other appropriate error.
8227 * You can use isds_DbState to enumerate box status. However out of enum
8228 * range value can be returned too. This is feature because ISDS
8229 * specification leaves the set of values open.
8230 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8231 * the box has been deleted, but ISDS still lists its former existence. */
8232 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
8233 long int *box_status) {
8234 isds_error err = IE_SUCCESS;
8235 #if HAVE_LIBCURL
8236 xmlNsPtr isds_ns = NULL;
8237 xmlNodePtr request = NULL, db_id;
8238 xmlDocPtr response = NULL;
8239 xmlXPathContextPtr xpath_ctx = NULL;
8240 xmlXPathObjectPtr result = NULL;
8241 xmlChar *string = NULL;
8243 const xmlChar *codes[] = {
8244 BAD_CAST "5001",
8245 BAD_CAST "1007",
8246 BAD_CAST "2011",
8247 NULL
8249 const char *meanings[] = {
8250 "The box does not exist",
8251 "Box ID is malformed",
8252 "Box ID malformed",
8254 const isds_error errors[] = {
8255 IE_NOEXIST,
8256 IE_INVAL,
8257 IE_INVAL,
8259 struct code_map_isds_error map = {
8260 .codes = codes,
8261 .meanings = meanings,
8262 .errors = errors
8264 #endif
8266 if (!context) return IE_INVALID_CONTEXT;
8267 zfree(context->long_message);
8268 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
8270 #if HAVE_LIBCURL
8271 /* Check if connection is established
8272 * TODO: This check should be done downstairs. */
8273 if (!context->curl) return IE_CONNECTION_CLOSED;
8276 /* Build CheckDataBox request */
8277 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
8278 if (!request) {
8279 isds_log_message(context,
8280 _("Could build CheckDataBox request"));
8281 return IE_ERROR;
8283 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8284 if(!isds_ns) {
8285 isds_log_message(context, _("Could not create ISDS name space"));
8286 xmlFreeNode(request);
8287 return IE_ERROR;
8289 xmlSetNs(request, isds_ns);
8290 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
8291 if (!db_id) {
8292 isds_log_message(context, _("Could not add dbID child to "
8293 "CheckDataBox element"));
8294 xmlFreeNode(request);
8295 return IE_ERROR;
8299 /* Send request and check response*/
8300 err = send_destroy_request_check_response(context,
8301 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
8302 &request, &response, NULL, &map);
8303 if (err) goto leave;
8306 /* Extract data */
8307 xpath_ctx = xmlXPathNewContext(response);
8308 if (!xpath_ctx) {
8309 err = IE_ERROR;
8310 goto leave;
8312 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8313 err = IE_ERROR;
8314 goto leave;
8316 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
8317 xpath_ctx);
8318 if (!result) {
8319 err = IE_ERROR;
8320 goto leave;
8322 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8323 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
8324 err = IE_ISDS;
8325 goto leave;
8327 if (result->nodesetval->nodeNr > 1) {
8328 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
8329 err = IE_ISDS;
8330 goto leave;
8332 xpath_ctx->node = result->nodesetval->nodeTab[0];
8333 xmlXPathFreeObject(result); result = NULL;
8335 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8338 leave:
8339 free(string);
8340 xmlXPathFreeObject(result);
8341 xmlXPathFreeContext(xpath_ctx);
8343 xmlFreeDoc(response);
8345 if (!err)
8346 isds_log(ILF_ISDS, ILL_DEBUG,
8347 _("CheckDataBox request processed by server successfully.\n"));
8348 #else /* not HAVE_LIBCURL */
8349 err = IE_NOTSUP;
8350 #endif
8352 return err;
8356 #if HAVE_LIBCURL
8357 /* Convert XSD:tdbPeriod XML tree into structure
8358 * @context is ISDS context.
8359 * @period is automatically reallocated found box status period structure.
8360 * @xpath_ctx is XPath context with current node as element of
8361 * XSD:tDbPeriod type.
8362 * In case of error @period will be freed. */
8363 static isds_error extract_Period(struct isds_ctx *context,
8364 struct isds_box_state_period **period, xmlXPathContextPtr xpath_ctx) {
8365 isds_error err = IE_SUCCESS;
8366 xmlXPathObjectPtr result = NULL;
8367 char *string = NULL;
8368 long int *dbState_ptr;
8370 if (NULL == context) return IE_INVALID_CONTEXT;
8371 if (NULL == period) return IE_INVAL;
8372 isds_box_state_period_free(period);
8373 if (!xpath_ctx) return IE_INVAL;
8376 *period = calloc(1, sizeof(**period));
8377 if (NULL == *period) {
8378 err = IE_NOMEM;
8379 goto leave;
8382 /* Extract data */
8383 EXTRACT_STRING("isds:PeriodFrom", string);
8384 if (NULL == string) {
8385 err = IE_XML;
8386 isds_log_message(context,
8387 _("Could not find PeriodFrom element value"));
8388 goto leave;
8390 err = timestring2static_timeval((xmlChar *) string,
8391 &((*period)->from));
8392 if (err) {
8393 char *string_locale = _isds_utf82locale(string);
8394 if (err == IE_DATE) err = IE_ISDS;
8395 isds_printf_message(context,
8396 _("Could not convert PeriodFrom as ISO time: %s"),
8397 string_locale);
8398 free(string_locale);
8399 goto leave;
8401 zfree(string);
8403 EXTRACT_STRING("isds:PeriodTo", string);
8404 if (NULL == string) {
8405 err = IE_XML;
8406 isds_log_message(context,
8407 _("Could not find PeriodTo element value"));
8408 goto leave;
8410 err = timestring2static_timeval((xmlChar *) string,
8411 &((*period)->to));
8412 if (err) {
8413 char *string_locale = _isds_utf82locale(string);
8414 if (err == IE_DATE) err = IE_ISDS;
8415 isds_printf_message(context,
8416 _("Could not convert PeriodTo as ISO time: %s"),
8417 string_locale);
8418 free(string_locale);
8419 goto leave;
8421 zfree(string);
8423 dbState_ptr = &((*period)->dbState);
8424 EXTRACT_LONGINT("isds:DbState", dbState_ptr, 1);
8426 leave:
8427 if (err) isds_box_state_period_free(period);
8428 free(string);
8429 xmlXPathFreeObject(result);
8430 return err;
8432 #endif /* HAVE_LIBCURL */
8435 /* Get history of box state changes.
8436 * @context is ISDS session context.
8437 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8438 * @from_time is first second of history to return in @history. Server ignores
8439 * subseconds. NULL means time of creating the box.
8440 * @to_time is last second of history to return in @history. Server ignores
8441 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8442 * interval is closed from both ends. NULL means now.
8443 * @history outputs auto-reallocated list of pointers to struct
8444 * isds_box_state_period. Each item describes a continues time when the box
8445 * was in one state. The state is 1 for accessible box. Otherwise the box
8446 * is inaccessible (priviledged users will get exact box state as enumerated
8447 * in isds_DbState, other users 0).
8448 * @return:
8449 * IE_SUCCESS if the history has been obtained correctly,
8450 * or other appropriate error. Please note that server allows to retrieve
8451 * the history only to some users. */
8452 isds_error isds_get_box_state_history(struct isds_ctx *context,
8453 const char *box_id,
8454 const struct timeval *from_time, const struct timeval *to_time,
8455 struct isds_list **history) {
8456 isds_error err = IE_SUCCESS;
8457 #if HAVE_LIBCURL
8458 char *box_id_locale = NULL;
8459 xmlNodePtr request = NULL, node;
8460 xmlNsPtr isds_ns = NULL;
8461 xmlChar *string = NULL;
8463 xmlDocPtr response = NULL;
8464 xmlXPathContextPtr xpath_ctx = NULL;
8465 xmlXPathObjectPtr result = NULL;
8466 #endif
8468 if (!context) return IE_INVALID_CONTEXT;
8469 zfree(context->long_message);
8471 /* Free output argument */
8472 isds_list_free(history);
8474 #if HAVE_LIBCURL
8475 /* Check if connection is established */
8476 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8478 /* ??? XML schema allows empty box ID, textual documentation
8479 * requries the value. */
8480 /* Allow undefined box_id */
8481 if (NULL != box_id) {
8482 box_id_locale = _isds_utf82locale((char*)box_id);
8483 if (NULL == box_id_locale) {
8484 err = IE_NOMEM;
8485 goto leave;
8489 /* Build request */
8490 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxActivityStatus");
8491 if (NULL == request) {
8492 isds_printf_message(context,
8493 _("Could not build GetDataBoxActivityStatus request "
8494 "for %s box"),
8495 box_id_locale);
8496 err = IE_ERROR;
8497 goto leave;
8499 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8500 if(!isds_ns) {
8501 isds_log_message(context, _("Could not create ISDS name space"));
8502 err = IE_ERROR;
8503 goto leave;
8505 xmlSetNs(request, isds_ns);
8507 /* Add mandatory XSD:tIdDbInput child */
8508 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8509 /* Add times elements only when defined */
8510 /* ???: XML schema requires the values, textual documentation does not. */
8511 if (from_time) {
8512 err = timeval2timestring(from_time, &string);
8513 if (err) {
8514 isds_log_message(context,
8515 _("Could not convert `from_time' argument to ISO time "
8516 "string"));
8517 goto leave;
8519 INSERT_STRING(request, "baFrom", string);
8520 zfree(string);
8522 if (to_time) {
8523 err = timeval2timestring(to_time, &string);
8524 if (err) {
8525 isds_log_message(context,
8526 _("Could not convert `to_time' argument to ISO time "
8527 "string"));
8528 goto leave;
8530 INSERT_STRING(request, "baTo", string);
8531 zfree(string);
8534 /* Send request and check response*/
8535 err = send_destroy_request_check_response(context,
8536 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxActivityStatus",
8537 &request, &response, NULL, NULL);
8538 if (err) goto leave;
8541 /* Extract data */
8542 /* Set context to the root */
8543 xpath_ctx = xmlXPathNewContext(response);
8544 if (!xpath_ctx) {
8545 err = IE_ERROR;
8546 goto leave;
8548 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8549 err = IE_ERROR;
8550 goto leave;
8552 result = xmlXPathEvalExpression(BAD_CAST "/isds:GetDataBoxActivityStatusResponse",
8553 xpath_ctx);
8554 if (!result) {
8555 err = IE_ERROR;
8556 goto leave;
8558 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8559 isds_log_message(context, _("Missing GetDataBoxActivityStatusResponse element"));
8560 err = IE_ISDS;
8561 goto leave;
8563 if (result->nodesetval->nodeNr > 1) {
8564 isds_log_message(context, _("Multiple GetDataBoxActivityStatusResponse element"));
8565 err = IE_ISDS;
8566 goto leave;
8568 xpath_ctx->node = result->nodesetval->nodeTab[0];
8569 xmlXPathFreeObject(result); result = NULL;
8571 /* Ignore dbID, it's the same as the input argument. */
8573 /* Extract records */
8574 if (NULL == history) goto leave;
8575 result = xmlXPathEvalExpression(BAD_CAST "isds:Periods/isds:Period",
8576 xpath_ctx);
8577 if (!result) {
8578 err = IE_ERROR;
8579 goto leave;
8581 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8582 struct isds_list *prev_item = NULL;
8584 /* Iterate over all records */
8585 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8586 struct isds_list *item;
8588 /* Prepare structure */
8589 item = calloc(1, sizeof(*item));
8590 if (!item) {
8591 err = IE_NOMEM;
8592 goto leave;
8594 item->destructor = (void(*)(void**))isds_box_state_period_free;
8595 if (i == 0) *history = item;
8596 else prev_item->next = item;
8597 prev_item = item;
8599 /* Extract it */
8600 xpath_ctx->node = result->nodesetval->nodeTab[i];
8601 err = extract_Period(context,
8602 (struct isds_box_state_period **) (&item->data),
8603 xpath_ctx);
8604 if (err) goto leave;
8608 leave:
8609 if (!err) {
8610 isds_log(ILF_ISDS, ILL_DEBUG,
8611 _("GetDataBoxActivityStatus request for %s box "
8612 "processed by server successfully.\n"), box_id_locale);
8614 if (err) {
8615 isds_list_free(history);
8618 free(box_id_locale);
8619 xmlXPathFreeObject(result);
8620 xmlXPathFreeContext(xpath_ctx);
8621 xmlFreeDoc(response);
8623 #else /* not HAVE_LIBCURL */
8624 err = IE_NOTSUP;
8625 #endif
8627 return err;
8631 /* Get list of permissions to send commercial messages.
8632 * @context is ISDS session context.
8633 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8634 * @permissions is a reallocated list of permissions (struct
8635 * isds_commercial_permission*) to send commercial messages from @box_id. The
8636 * order of permissions is significant as the server applies the permissions
8637 * and associated pre-paid credits in the order. Empty list means no
8638 * permission.
8639 * @return:
8640 * IE_SUCCESS if the list has been obtained correctly,
8641 * or other appropriate error. */
8642 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8643 const char *box_id, struct isds_list **permissions) {
8644 isds_error err = IE_SUCCESS;
8645 #if HAVE_LIBCURL
8646 xmlDocPtr response = NULL;
8647 xmlXPathContextPtr xpath_ctx = NULL;
8648 xmlXPathObjectPtr result = NULL;
8649 #endif
8651 if (!context) return IE_INVALID_CONTEXT;
8652 zfree(context->long_message);
8653 if (NULL == permissions) return IE_INVAL;
8654 isds_list_free(permissions);
8655 if (NULL == box_id) return IE_INVAL;
8657 #if HAVE_LIBCURL
8658 /* Check if connection is established */
8659 if (!context->curl) return IE_CONNECTION_CLOSED;
8661 /* Do request and check for success */
8662 err = build_send_dbid_request_check_response(context,
8663 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8664 BAD_CAST box_id, NULL, &response, NULL);
8665 if (!err) {
8666 isds_log(ILF_ISDS, ILL_DEBUG,
8667 _("PDZInfo request processed by server successfully.\n"));
8670 /* Extract data */
8671 /* Prepare structure */
8672 xpath_ctx = xmlXPathNewContext(response);
8673 if (!xpath_ctx) {
8674 err = IE_ERROR;
8675 goto leave;
8677 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8678 err = IE_ERROR;
8679 goto leave;
8682 /* Set context node */
8683 result = xmlXPathEvalExpression(BAD_CAST
8684 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8685 xpath_ctx);
8686 if (!result) {
8687 err = IE_ERROR;
8688 goto leave;
8690 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8691 struct isds_list *prev_item = NULL;
8693 /* Iterate over all permission records */
8694 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8695 struct isds_list *item;
8697 /* Prepare structure */
8698 item = calloc(1, sizeof(*item));
8699 if (!item) {
8700 err = IE_NOMEM;
8701 goto leave;
8703 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8704 if (i == 0) *permissions = item;
8705 else prev_item->next = item;
8706 prev_item = item;
8708 /* Extract it */
8709 xpath_ctx->node = result->nodesetval->nodeTab[i];
8710 err = extract_DbPDZRecord(context,
8711 (struct isds_commercial_permission **) (&item->data),
8712 xpath_ctx);
8713 if (err) goto leave;
8717 leave:
8718 if (err) {
8719 isds_list_free(permissions);
8722 xmlXPathFreeObject(result);
8723 xmlXPathFreeContext(xpath_ctx);
8724 xmlFreeDoc(response);
8726 #else /* not HAVE_LIBCURL */
8727 err = IE_NOTSUP;
8728 #endif
8730 return err;
8734 /* Get details about credit for sending pre-paid commercial messages.
8735 * @context is ISDS session context.
8736 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8737 * @from_date is first day of credit history to return in @history. Only
8738 * tm_year, tm_mon and tm_mday carry sane value.
8739 * @to_date is last day of credit history to return in @history. Only
8740 * tm_year, tm_mon and tm_mday carry sane value.
8741 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8742 * if you don't care. This and all other credit values are integers in
8743 * hundredths of Czech Crowns.
8744 * @email outputs notification e-mail address where notifications about credit
8745 * are sent. This is automatically reallocated string. Pass NULL if you don't
8746 * care. It can return NULL if no address is defined.
8747 * @history outputs auto-reallocated list of pointers to struct
8748 * isds_credit_event. Events in closed interval @from_time to @to_time are
8749 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8750 * are sorted by time.
8751 * @return:
8752 * IE_SUCCESS if the credit details have been obtained correctly,
8753 * or other appropriate error. Please note that server allows to retrieve
8754 * only limited history of events. */
8755 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8756 const char *box_id,
8757 const struct tm *from_date, const struct tm *to_date,
8758 long int *credit, char **email, struct isds_list **history) {
8759 isds_error err = IE_SUCCESS;
8760 #if HAVE_LIBCURL
8761 char *box_id_locale = NULL;
8762 xmlNodePtr request = NULL, node;
8763 xmlNsPtr isds_ns = NULL;
8764 xmlChar *string = NULL;
8766 xmlDocPtr response = NULL;
8767 xmlXPathContextPtr xpath_ctx = NULL;
8768 xmlXPathObjectPtr result = NULL;
8770 const xmlChar *codes[] = {
8771 BAD_CAST "1004",
8772 BAD_CAST "2011",
8773 BAD_CAST "1093",
8774 BAD_CAST "1137",
8775 BAD_CAST "1058",
8776 NULL
8778 const char *meanings[] = {
8779 "Insufficient priviledges for the box",
8780 "The box does not exist",
8781 "Date is too long (history is not available after 15 months)",
8782 "Interval is too long (limit is 3 months)",
8783 "Invalid date"
8785 const isds_error errors[] = {
8786 IE_ISDS,
8787 IE_NOEXIST,
8788 IE_DATE,
8789 IE_DATE,
8790 IE_DATE,
8792 struct code_map_isds_error map = {
8793 .codes = codes,
8794 .meanings = meanings,
8795 .errors = errors
8797 #endif
8799 if (!context) return IE_INVALID_CONTEXT;
8800 zfree(context->long_message);
8802 /* Free output argument */
8803 if (NULL != credit) *credit = 0;
8804 if (NULL != email) zfree(*email);
8805 isds_list_free(history);
8807 if (NULL == box_id) return IE_INVAL;
8809 #if HAVE_LIBCURL
8810 /* Check if connection is established */
8811 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8813 box_id_locale = _isds_utf82locale((char*)box_id);
8814 if (NULL == box_id_locale) {
8815 err = IE_NOMEM;
8816 goto leave;
8819 /* Build request */
8820 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8821 if (NULL == request) {
8822 isds_printf_message(context,
8823 _("Could not build DataBoxCreditInfo request for %s box"),
8824 box_id_locale);
8825 err = IE_ERROR;
8826 goto leave;
8828 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8829 if(!isds_ns) {
8830 isds_log_message(context, _("Could not create ISDS name space"));
8831 err = IE_ERROR;
8832 goto leave;
8834 xmlSetNs(request, isds_ns);
8836 /* Add mandatory XSD:tIdDbInput child */
8837 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8838 /* Add mandatory dates elements with optional values */
8839 if (from_date) {
8840 err = tm2datestring(from_date, &string);
8841 if (err) {
8842 isds_log_message(context,
8843 _("Could not convert `from_date' argument to ISO date "
8844 "string"));
8845 goto leave;
8847 INSERT_STRING(request, "ciFromDate", string);
8848 zfree(string);
8849 } else {
8850 INSERT_STRING(request, "ciFromDate", NULL);
8852 if (to_date) {
8853 err = tm2datestring(to_date, &string);
8854 if (err) {
8855 isds_log_message(context,
8856 _("Could not convert `to_date' argument to ISO date "
8857 "string"));
8858 goto leave;
8860 INSERT_STRING(request, "ciTodate", string);
8861 zfree(string);
8862 } else {
8863 INSERT_STRING(request, "ciTodate", NULL);
8866 /* Send request and check response*/
8867 err = send_destroy_request_check_response(context,
8868 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8869 &request, &response, NULL, &map);
8870 if (err) goto leave;
8873 /* Extract data */
8874 /* Set context to the root */
8875 xpath_ctx = xmlXPathNewContext(response);
8876 if (!xpath_ctx) {
8877 err = IE_ERROR;
8878 goto leave;
8880 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8881 err = IE_ERROR;
8882 goto leave;
8884 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8885 xpath_ctx);
8886 if (!result) {
8887 err = IE_ERROR;
8888 goto leave;
8890 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8891 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8892 err = IE_ISDS;
8893 goto leave;
8895 if (result->nodesetval->nodeNr > 1) {
8896 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8897 err = IE_ISDS;
8898 goto leave;
8900 xpath_ctx->node = result->nodesetval->nodeTab[0];
8901 xmlXPathFreeObject(result); result = NULL;
8903 /* Extract common data */
8904 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8905 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8907 /* result gets overwritten in next step */
8908 xmlXPathFreeObject(result); result = NULL;
8910 /* Extract records */
8911 if (NULL == history) goto leave;
8912 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8913 xpath_ctx);
8914 if (!result) {
8915 err = IE_ERROR;
8916 goto leave;
8918 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8919 struct isds_list *prev_item = NULL;
8921 /* Iterate over all records */
8922 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8923 struct isds_list *item;
8925 /* Prepare structure */
8926 item = calloc(1, sizeof(*item));
8927 if (!item) {
8928 err = IE_NOMEM;
8929 goto leave;
8931 item->destructor = (void(*)(void**))isds_credit_event_free;
8932 if (i == 0) *history = item;
8933 else prev_item->next = item;
8934 prev_item = item;
8936 /* Extract it */
8937 xpath_ctx->node = result->nodesetval->nodeTab[i];
8938 err = extract_CiRecord(context,
8939 (struct isds_credit_event **) (&item->data),
8940 xpath_ctx);
8941 if (err) goto leave;
8945 leave:
8946 if (!err) {
8947 isds_log(ILF_ISDS, ILL_DEBUG,
8948 _("DataBoxCreditInfo request processed by server successfully.\n"));
8950 if (err) {
8951 isds_list_free(history);
8952 if (NULL != email) zfree(*email)
8955 free(box_id_locale);
8956 xmlXPathFreeObject(result);
8957 xmlXPathFreeContext(xpath_ctx);
8958 xmlFreeDoc(response);
8960 #else /* not HAVE_LIBCURL */
8961 err = IE_NOTSUP;
8962 #endif
8964 return err;
8968 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8969 * code, destroy response and log success.
8970 * @context is ISDS session context.
8971 * @service_name is name of SERVICE_DB_MANIPULATION service
8972 * @box_id is UTF-8 encoded box identifier as zero terminated string
8973 * @approval is optional external approval of box manipulation
8974 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8975 * NULL, if you don't care. */
8976 static isds_error build_send_manipulationdbid_request_check_drop_response(
8977 struct isds_ctx *context, const xmlChar *service_name,
8978 const xmlChar *box_id, const struct isds_approval *approval,
8979 xmlChar **refnumber) {
8980 isds_error err = IE_SUCCESS;
8981 #if HAVE_LIBCURL
8982 xmlDocPtr response = NULL;
8983 #endif
8985 if (!context) return IE_INVALID_CONTEXT;
8986 zfree(context->long_message);
8987 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8989 #if HAVE_LIBCURL
8990 /* Check if connection is established */
8991 if (!context->curl) return IE_CONNECTION_CLOSED;
8993 /* Do request and check for success */
8994 err = build_send_dbid_request_check_response(context,
8995 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8996 &response, refnumber);
8997 xmlFreeDoc(response);
8999 if (!err) {
9000 char *service_name_locale = _isds_utf82locale((char *) service_name);
9001 isds_log(ILF_ISDS, ILL_DEBUG,
9002 _("%s request processed by server successfully.\n"),
9003 service_name_locale);
9004 free(service_name_locale);
9006 #else /* not HAVE_LIBCURL */
9007 err = IE_NOTSUP;
9008 #endif
9010 return err;
9014 /* Switch box into state where box can receive commercial messages (off by
9015 * default)
9016 * @context is ISDS session context.
9017 * @box_id is UTF-8 encoded box identifier as zero terminated string
9018 * @allow is true for enable, false for disable commercial messages income
9019 * @approval is optional external approval of box manipulation
9020 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9021 * NULL, if you don't care. */
9022 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
9023 const char *box_id, const _Bool allow,
9024 const struct isds_approval *approval, char **refnumber) {
9025 return build_send_manipulationdbid_request_check_drop_response(context,
9026 (allow) ? BAD_CAST "SetOpenAddressing" :
9027 BAD_CAST "ClearOpenAddressing",
9028 BAD_CAST box_id, approval, (xmlChar **) refnumber);
9032 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
9033 * message acceptance). This is just a box permission. Sender must apply
9034 * such role by sending each message.
9035 * @context is ISDS session context.
9036 * @box_id is UTF-8 encoded box identifier as zero terminated string
9037 * @allow is true for enable, false for disable OVM role permission
9038 * @approval is optional external approval of box manipulation
9039 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9040 * NULL, if you don't care. */
9041 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
9042 const char *box_id, const _Bool allow,
9043 const struct isds_approval *approval, char **refnumber) {
9044 return build_send_manipulationdbid_request_check_drop_response(context,
9045 (allow) ? BAD_CAST "SetEffectiveOVM" :
9046 BAD_CAST "ClearEffectiveOVM",
9047 BAD_CAST box_id, approval, (xmlChar **) refnumber);
9051 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
9052 * code, destroy response and log success.
9053 * @context is ISDS session context.
9054 * @service_name is name of SERVICE_DB_MANIPULATION service
9055 * @owner is structure describing box. aifoIsds, address->adCode,
9056 * address->adDistrict members are ignored.
9057 * @approval is optional external approval of box manipulation
9058 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9059 * NULL, if you don't care. */
9060 static isds_error build_send_manipulationdbowner_request_check_drop_response(
9061 struct isds_ctx *context, const xmlChar *service_name,
9062 const struct isds_DbOwnerInfo *owner,
9063 const struct isds_approval *approval, xmlChar **refnumber) {
9064 isds_error err = IE_SUCCESS;
9065 #if HAVE_LIBCURL
9066 char *service_name_locale = NULL;
9067 xmlNodePtr request = NULL, db_owner_info;
9068 xmlNsPtr isds_ns = NULL;
9069 #endif
9072 if (!context) return IE_INVALID_CONTEXT;
9073 zfree(context->long_message);
9074 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
9076 #if HAVE_LIBCURL
9077 service_name_locale = _isds_utf82locale((char*)service_name);
9078 if (!service_name_locale) {
9079 err = IE_NOMEM;
9080 goto leave;
9083 /* Build request */
9084 request = xmlNewNode(NULL, service_name);
9085 if (!request) {
9086 isds_printf_message(context,
9087 _("Could not build %s request"), service_name_locale);
9088 err = IE_ERROR;
9089 goto leave;
9091 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9092 if(!isds_ns) {
9093 isds_log_message(context, _("Could not create ISDS name space"));
9094 err = IE_ERROR;
9095 goto leave;
9097 xmlSetNs(request, isds_ns);
9100 /* Add XSD:tOwnerInfoInput child*/
9101 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
9102 err = insert_DbOwnerInfo(context, owner, 0, db_owner_info);
9103 if (err) goto leave;
9105 /* Add XSD:gExtApproval*/
9106 err = insert_GExtApproval(context, approval, request);
9107 if (err) goto leave;
9109 /* Send it to server and process response */
9110 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
9111 service_name, &request, refnumber);
9113 leave:
9114 xmlFreeNode(request);
9115 free(service_name_locale);
9116 #else /* not HAVE_LIBCURL */
9117 err = IE_NOTSUP;
9118 #endif
9120 return err;
9124 /* Switch box accessibility state on request of box owner.
9125 * Despite the name, owner must do the request off-line. This function is
9126 * designed for such off-line meeting points (e.g. Czech POINT).
9127 * @context is ISDS session context.
9128 * @box identifies box to switch accessibility state. aifoIsds,
9129 * address->adCode, address->adDistrict members are ignored.
9130 * @allow is true for making accessible, false to disallow access.
9131 * @approval is optional external approval of box manipulation
9132 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9133 * NULL, if you don't care. */
9134 isds_error isds_switch_box_accessibility_on_owner_request(
9135 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
9136 const _Bool allow, const struct isds_approval *approval,
9137 char **refnumber) {
9138 return build_send_manipulationdbowner_request_check_drop_response(context,
9139 (allow) ? BAD_CAST "EnableOwnDataBox" :
9140 BAD_CAST "DisableOwnDataBox",
9141 box, approval, (xmlChar **) refnumber);
9145 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
9146 * date.
9147 * @context is ISDS session context.
9148 * @box identifies box to switch accessibility state. aifoIsds,
9149 * address->adCode, address->adDistrict members are ignored.
9150 * @since is date since accessibility has been denied. This can be past too.
9151 * Only tm_year, tm_mon and tm_mday carry sane value.
9152 * @approval is optional external approval of box manipulation
9153 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9154 * NULL, if you don't care. */
9155 isds_error isds_disable_box_accessibility_externaly(
9156 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
9157 const struct tm *since, const struct isds_approval *approval,
9158 char **refnumber) {
9159 isds_error err = IE_SUCCESS;
9160 #if HAVE_LIBCURL
9161 char *service_name_locale = NULL;
9162 xmlNodePtr request = NULL, node;
9163 xmlNsPtr isds_ns = NULL;
9164 xmlChar *string = NULL;
9165 #endif
9168 if (!context) return IE_INVALID_CONTEXT;
9169 zfree(context->long_message);
9170 if (!box || !since) return IE_INVAL;
9172 #if HAVE_LIBCURL
9173 /* Build request */
9174 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
9175 if (!request) {
9176 isds_printf_message(context,
9177 _("Could not build %s request"), "DisableDataBoxExternally");
9178 err = IE_ERROR;
9179 goto leave;
9181 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9182 if(!isds_ns) {
9183 isds_log_message(context, _("Could not create ISDS name space"));
9184 err = IE_ERROR;
9185 goto leave;
9187 xmlSetNs(request, isds_ns);
9190 /* Add @box identification */
9191 INSERT_ELEMENT(node, request, "dbOwnerInfo");
9192 err = insert_DbOwnerInfo(context, box, 0, node);
9193 if (err) goto leave;
9195 /* Add @since date */
9196 err = tm2datestring(since, &string);
9197 if(err) {
9198 isds_log_message(context,
9199 _("Could not convert `since' argument to ISO date string"));
9200 goto leave;
9202 INSERT_STRING(request, "dbOwnerDisableDate", string);
9203 zfree(string);
9205 /* Add @approval */
9206 err = insert_GExtApproval(context, approval, request);
9207 if (err) goto leave;
9209 /* Send it to server and process response */
9210 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
9211 BAD_CAST "DisableDataBoxExternally", &request,
9212 (xmlChar **) refnumber);
9214 leave:
9215 free(string);
9216 xmlFreeNode(request);
9217 free(service_name_locale);
9218 #else /* not HAVE_LIBCURL */
9219 err = IE_NOTSUP;
9220 #endif
9222 return err;
9226 #if HAVE_LIBCURL
9227 /* Insert struct isds_message data (envelope (recipient data optional) and
9228 * documents into XML tree
9229 * @context is session context
9230 * @outgoing_message is libisds structure with message data
9231 * @create_message is XML CreateMessage or CreateMultipleMessage element
9232 * @process_recipient true for recipient data serialization, false for no
9233 * serialization */
9234 static isds_error insert_envelope_files(struct isds_ctx *context,
9235 const struct isds_message *outgoing_message, xmlNodePtr create_message,
9236 const _Bool process_recipient) {
9238 isds_error err = IE_SUCCESS;
9239 xmlNodePtr envelope, dm_files, node;
9240 xmlChar *string = NULL;
9242 if (!context) return IE_INVALID_CONTEXT;
9243 if (!outgoing_message || !create_message) return IE_INVAL;
9246 /* Build envelope */
9247 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
9248 if (!envelope) {
9249 isds_printf_message(context, _("Could not add dmEnvelope child to "
9250 "%s element"), create_message->name);
9251 return IE_ERROR;
9254 if (!outgoing_message->envelope) {
9255 isds_log_message(context, _("Outgoing message is missing envelope"));
9256 err = IE_INVAL;
9257 goto leave;
9260 /* Insert optional message type */
9261 err = insert_message_type(context, outgoing_message->envelope->dmType,
9262 envelope);
9263 if (err) goto leave;
9265 INSERT_STRING(envelope, "dmSenderOrgUnit",
9266 outgoing_message->envelope->dmSenderOrgUnit);
9267 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
9268 outgoing_message->envelope->dmSenderOrgUnitNum, string);
9270 if (process_recipient) {
9271 if (!outgoing_message->envelope->dbIDRecipient) {
9272 isds_log_message(context,
9273 _("Outgoing message is missing recipient box identifier"));
9274 err = IE_INVAL;
9275 goto leave;
9277 INSERT_STRING(envelope, "dbIDRecipient",
9278 outgoing_message->envelope->dbIDRecipient);
9280 INSERT_STRING(envelope, "dmRecipientOrgUnit",
9281 outgoing_message->envelope->dmRecipientOrgUnit);
9282 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
9283 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
9284 INSERT_STRING(envelope, "dmToHands",
9285 outgoing_message->envelope->dmToHands);
9288 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
9289 "dmAnnotation");
9290 INSERT_STRING(envelope, "dmAnnotation",
9291 outgoing_message->envelope->dmAnnotation);
9293 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
9294 0, 50, "dmRecipientRefNumber");
9295 INSERT_STRING(envelope, "dmRecipientRefNumber",
9296 outgoing_message->envelope->dmRecipientRefNumber);
9298 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
9299 0, 50, "dmSenderRefNumber");
9300 INSERT_STRING(envelope, "dmSenderRefNumber",
9301 outgoing_message->envelope->dmSenderRefNumber);
9303 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
9304 0, 50, "dmRecipientIdent");
9305 INSERT_STRING(envelope, "dmRecipientIdent",
9306 outgoing_message->envelope->dmRecipientIdent);
9308 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
9309 0, 50, "dmSenderIdent");
9310 INSERT_STRING(envelope, "dmSenderIdent",
9311 outgoing_message->envelope->dmSenderIdent);
9313 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
9314 outgoing_message->envelope->dmLegalTitleLaw, string);
9315 INSERT_LONGINT(envelope, "dmLegalTitleYear",
9316 outgoing_message->envelope->dmLegalTitleYear, string);
9317 INSERT_STRING(envelope, "dmLegalTitleSect",
9318 outgoing_message->envelope->dmLegalTitleSect);
9319 INSERT_STRING(envelope, "dmLegalTitlePar",
9320 outgoing_message->envelope->dmLegalTitlePar);
9321 INSERT_STRING(envelope, "dmLegalTitlePoint",
9322 outgoing_message->envelope->dmLegalTitlePoint);
9324 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
9325 outgoing_message->envelope->dmPersonalDelivery);
9326 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
9327 outgoing_message->envelope->dmAllowSubstDelivery);
9329 /* ???: Should we require value for dbEffectiveOVM sender?
9330 * ISDS has default as true */
9331 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
9332 INSERT_BOOLEAN(envelope, "dmPublishOwnID",
9333 outgoing_message->envelope->dmPublishOwnID);
9336 /* Append dmFiles */
9337 if (!outgoing_message->documents) {
9338 isds_log_message(context,
9339 _("Outgoing message is missing list of documents"));
9340 err = IE_INVAL;
9341 goto leave;
9343 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
9344 if (!dm_files) {
9345 isds_printf_message(context, _("Could not add dmFiles child to "
9346 "%s element"), create_message->name);
9347 err = IE_ERROR;
9348 goto leave;
9351 /* Check for document hierarchy */
9352 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
9353 if (err) goto leave;
9355 /* Process each document */
9356 for (struct isds_list *item =
9357 (struct isds_list *) outgoing_message->documents;
9358 item; item = item->next) {
9359 if (!item->data) {
9360 isds_log_message(context,
9361 _("List of documents contains empty item"));
9362 err = IE_INVAL;
9363 goto leave;
9365 /* FIXME: Check for dmFileMetaType and for document references.
9366 * Only first document can be of MAIN type */
9367 err = insert_document(context, (struct isds_document*) item->data,
9368 dm_files);
9370 if (err) goto leave;
9373 leave:
9374 free(string);
9375 return err;
9377 #endif /* HAVE_LIBCURL */
9380 /* Send a message via ISDS to a recipient
9381 * @context is session context
9382 * @outgoing_message is message to send; Some members are mandatory (like
9383 * dbIDRecipient), some are optional and some are irrelevant (especially data
9384 * about sender). Included pointer to isds_list documents must contain at
9385 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9386 * members will be filled with valid data from ISDS. Exact list of write
9387 * members is subject to change. Currently dmID is changed.
9388 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9389 isds_error isds_send_message(struct isds_ctx *context,
9390 struct isds_message *outgoing_message) {
9392 isds_error err = IE_SUCCESS;
9393 #if HAVE_LIBCURL
9394 xmlNsPtr isds_ns = NULL;
9395 xmlNodePtr request = NULL;
9396 xmlDocPtr response = NULL;
9397 xmlChar *code = NULL, *message = NULL;
9398 xmlXPathContextPtr xpath_ctx = NULL;
9399 xmlXPathObjectPtr result = NULL;
9400 /*_Bool message_is_complete = 0;*/
9401 #endif
9403 if (!context) return IE_INVALID_CONTEXT;
9404 zfree(context->long_message);
9405 if (!outgoing_message) return IE_INVAL;
9407 #if HAVE_LIBCURL
9408 /* Check if connection is established
9409 * TODO: This check should be done downstairs. */
9410 if (!context->curl) return IE_CONNECTION_CLOSED;
9413 /* Build CreateMessage request */
9414 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
9415 if (!request) {
9416 isds_log_message(context,
9417 _("Could not build CreateMessage request"));
9418 return IE_ERROR;
9420 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9421 if(!isds_ns) {
9422 isds_log_message(context, _("Could not create ISDS name space"));
9423 xmlFreeNode(request);
9424 return IE_ERROR;
9426 xmlSetNs(request, isds_ns);
9428 /* Append envelope and files */
9429 err = insert_envelope_files(context, outgoing_message, request, 1);
9430 if (err) goto leave;
9433 /* Signal we can serialize message since now */
9434 /*message_is_complete = 1;*/
9437 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
9439 /* Sent request */
9440 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9442 /* Don't' destroy request, we want to provide it to application later */
9444 if (err) {
9445 isds_log(ILF_ISDS, ILL_DEBUG,
9446 _("Processing ISDS response on CreateMessage "
9447 "request failed\n"));
9448 goto leave;
9451 /* Check for response status */
9452 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9453 &code, &message, NULL);
9454 if (err) {
9455 isds_log(ILF_ISDS, ILL_DEBUG,
9456 _("ISDS response on CreateMessage request "
9457 "is missing status\n"));
9458 goto leave;
9461 /* Request processed, but refused by server or server failed */
9462 if (xmlStrcmp(code, BAD_CAST "0000")) {
9463 char *box_id_locale =
9464 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9465 char *code_locale = _isds_utf82locale((char*)code);
9466 char *message_locale = _isds_utf82locale((char*)message);
9467 isds_log(ILF_ISDS, ILL_DEBUG,
9468 _("Server did not accept message for %s on CreateMessage "
9469 "request (code=%s, message=%s)\n"),
9470 box_id_locale, code_locale, message_locale);
9471 isds_log_message(context, message_locale);
9472 free(box_id_locale);
9473 free(code_locale);
9474 free(message_locale);
9475 err = IE_ISDS;
9476 goto leave;
9480 /* Extract data */
9481 xpath_ctx = xmlXPathNewContext(response);
9482 if (!xpath_ctx) {
9483 err = IE_ERROR;
9484 goto leave;
9486 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9487 err = IE_ERROR;
9488 goto leave;
9490 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
9491 xpath_ctx);
9492 if (!result) {
9493 err = IE_ERROR;
9494 goto leave;
9496 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9497 isds_log_message(context, _("Missing CreateMessageResponse element"));
9498 err = IE_ISDS;
9499 goto leave;
9501 if (result->nodesetval->nodeNr > 1) {
9502 isds_log_message(context, _("Multiple CreateMessageResponse element"));
9503 err = IE_ISDS;
9504 goto leave;
9506 xpath_ctx->node = result->nodesetval->nodeTab[0];
9507 xmlXPathFreeObject(result); result = NULL;
9509 if (outgoing_message->envelope->dmID) {
9510 free(outgoing_message->envelope->dmID);
9511 outgoing_message->envelope->dmID = NULL;
9513 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
9514 if (!outgoing_message->envelope->dmID) {
9515 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
9516 "but did not return assigned message ID\n"));
9519 leave:
9520 /* TODO: Serialize message into structure member raw */
9521 /* XXX: Each web service transport message in different format.
9522 * Therefore it's not possible to save them directly.
9523 * To save them, one must figure out common format.
9524 * We can leave it on application, or we can implement the ESS format. */
9525 /*if (message_is_complete) {
9526 if (outgoing_message->envelope->dmID) {
9528 /* Add assigned message ID as first child*/
9529 /*xmlNodePtr dmid_text = xmlNewText(
9530 (xmlChar *) outgoing_message->envelope->dmID);
9531 if (!dmid_text) goto serialization_failed;
9533 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9534 BAD_CAST "dmID");
9535 if (!dmid_element) {
9536 xmlFreeNode(dmid_text);
9537 goto serialization_failed;
9540 xmlNodePtr dmid_element_with_text =
9541 xmlAddChild(dmid_element, dmid_text);
9542 if (!dmid_element_with_text) {
9543 xmlFreeNode(dmid_element);
9544 xmlFreeNode(dmid_text);
9545 goto serialization_failed;
9548 node = xmlAddPrevSibling(envelope->childern,
9549 dmid_element_with_text);
9550 if (!node) {
9551 xmlFreeNodeList(dmid_element_with_text);
9552 goto serialization_failed;
9556 /* Serialize message with ID into raw */
9557 /*buffer = serialize_element(envelope)*/
9558 /* }
9560 serialization_failed:
9564 /* Clean up */
9565 xmlXPathFreeObject(result);
9566 xmlXPathFreeContext(xpath_ctx);
9568 free(code);
9569 free(message);
9570 xmlFreeDoc(response);
9571 xmlFreeNode(request);
9573 if (!err)
9574 isds_log(ILF_ISDS, ILL_DEBUG,
9575 _("CreateMessage request processed by server "
9576 "successfully.\n"));
9577 #else /* not HAVE_LIBCURL */
9578 err = IE_NOTSUP;
9579 #endif
9581 return err;
9585 /* Send a message via ISDS to a multiple recipients
9586 * @context is session context
9587 * @outgoing_message is message to send; Some members are mandatory,
9588 * some are optional and some are irrelevant (especially data
9589 * about sender). Data about recipient will be substituted by ISDS from
9590 * @copies. Included pointer to isds_list documents must
9591 * contain at least one document of FILEMETATYPE_MAIN.
9592 * @copies is list of isds_message_copy structures addressing all desired
9593 * recipients. This is read-write structure, some members will be filled with
9594 * valid data from ISDS (message IDs, error codes, error descriptions).
9595 * @return
9596 * ISDS_SUCCESS if all messages have been sent
9597 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9598 * succeeded messages can be identified by copies->data->error),
9599 * or other error code if something other goes wrong. */
9600 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
9601 const struct isds_message *outgoing_message,
9602 struct isds_list *copies) {
9604 isds_error err = IE_SUCCESS;
9605 #if HAVE_LIBCURL
9606 isds_error append_err;
9607 xmlNsPtr isds_ns = NULL;
9608 xmlNodePtr request = NULL, recipients, recipient, node;
9609 struct isds_list *item;
9610 struct isds_message_copy *copy;
9611 xmlDocPtr response = NULL;
9612 xmlChar *code = NULL, *message = NULL;
9613 xmlXPathContextPtr xpath_ctx = NULL;
9614 xmlXPathObjectPtr result = NULL;
9615 xmlChar *string = NULL;
9616 int i;
9617 #endif
9619 if (!context) return IE_INVALID_CONTEXT;
9620 zfree(context->long_message);
9621 if (!outgoing_message || !copies) return IE_INVAL;
9623 #if HAVE_LIBCURL
9624 /* Check if connection is established
9625 * TODO: This check should be done downstairs. */
9626 if (!context->curl) return IE_CONNECTION_CLOSED;
9629 /* Build CreateMultipleMessage request */
9630 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9631 if (!request) {
9632 isds_log_message(context,
9633 _("Could not build CreateMultipleMessage request"));
9634 return IE_ERROR;
9636 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9637 if(!isds_ns) {
9638 isds_log_message(context, _("Could not create ISDS name space"));
9639 xmlFreeNode(request);
9640 return IE_ERROR;
9642 xmlSetNs(request, isds_ns);
9645 /* Build recipients */
9646 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9647 if (!recipients) {
9648 isds_log_message(context, _("Could not add dmRecipients child to "
9649 "CreateMultipleMessage element"));
9650 xmlFreeNode(request);
9651 return IE_ERROR;
9654 /* Insert each recipient */
9655 for (item = copies; item; item = item->next) {
9656 copy = (struct isds_message_copy *) item->data;
9657 if (!copy) {
9658 isds_log_message(context,
9659 _("`copies' list item contains empty data"));
9660 err = IE_INVAL;
9661 goto leave;
9664 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9665 if (!recipient) {
9666 isds_log_message(context, _("Could not add dmRecipient child to "
9667 "dmRecipients element"));
9668 err = IE_ERROR;
9669 goto leave;
9672 if (!copy->dbIDRecipient) {
9673 isds_log_message(context,
9674 _("Message copy is missing recipient box identifier"));
9675 err = IE_INVAL;
9676 goto leave;
9678 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9679 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9680 copy->dmRecipientOrgUnit);
9681 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9682 copy->dmRecipientOrgUnitNum, string);
9683 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9686 /* Append envelope and files */
9687 err = insert_envelope_files(context, outgoing_message, request, 0);
9688 if (err) goto leave;
9691 isds_log(ILF_ISDS, ILL_DEBUG,
9692 _("Sending CreateMultipleMessage request to ISDS\n"));
9694 /* Sent request */
9695 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9696 if (err) {
9697 isds_log(ILF_ISDS, ILL_DEBUG,
9698 _("Processing ISDS response on CreateMultipleMessage "
9699 "request failed\n"));
9700 goto leave;
9703 /* Check for response status */
9704 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9705 &code, &message, NULL);
9706 if (err) {
9707 isds_log(ILF_ISDS, ILL_DEBUG,
9708 _("ISDS response on CreateMultipleMessage request "
9709 "is missing status\n"));
9710 goto leave;
9713 /* Request processed, but some copies failed */
9714 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9715 char *box_id_locale =
9716 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9717 char *code_locale = _isds_utf82locale((char*)code);
9718 char *message_locale = _isds_utf82locale((char*)message);
9719 isds_log(ILF_ISDS, ILL_DEBUG,
9720 _("Server did accept message for multiple recipients "
9721 "on CreateMultipleMessage request but delivery to "
9722 "some of them failed (code=%s, message=%s)\n"),
9723 box_id_locale, code_locale, message_locale);
9724 isds_log_message(context, message_locale);
9725 free(box_id_locale);
9726 free(code_locale);
9727 free(message_locale);
9728 err = IE_PARTIAL_SUCCESS;
9731 /* Request refused by server as whole */
9732 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9733 char *box_id_locale =
9734 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9735 char *code_locale = _isds_utf82locale((char*)code);
9736 char *message_locale = _isds_utf82locale((char*)message);
9737 isds_log(ILF_ISDS, ILL_DEBUG,
9738 _("Server did not accept message for multiple recipients "
9739 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9740 box_id_locale, code_locale, message_locale);
9741 isds_log_message(context, message_locale);
9742 free(box_id_locale);
9743 free(code_locale);
9744 free(message_locale);
9745 err = IE_ISDS;
9746 goto leave;
9750 /* Extract data */
9751 xpath_ctx = xmlXPathNewContext(response);
9752 if (!xpath_ctx) {
9753 err = IE_ERROR;
9754 goto leave;
9756 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9757 err = IE_ERROR;
9758 goto leave;
9760 result = xmlXPathEvalExpression(
9761 BAD_CAST "/isds:CreateMultipleMessageResponse"
9762 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9763 xpath_ctx);
9764 if (!result) {
9765 err = IE_ERROR;
9766 goto leave;
9768 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9769 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9770 err = IE_ISDS;
9771 goto leave;
9774 /* Extract message ID and delivery status for each copy */
9775 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9776 item = item->next, i++) {
9777 copy = (struct isds_message_copy *) item->data;
9778 xpath_ctx->node = result->nodesetval->nodeTab[i];
9780 append_err = append_TMStatus(context, copy, xpath_ctx);
9781 if (append_err) {
9782 err = append_err;
9783 goto leave;
9786 if (item || i < result->nodesetval->nodeNr) {
9787 isds_printf_message(context, _("ISDS returned unexpected number of "
9788 "message copy delivery states: %d"),
9789 result->nodesetval->nodeNr);
9790 err = IE_ISDS;
9791 goto leave;
9795 leave:
9796 /* Clean up */
9797 free(string);
9798 xmlXPathFreeObject(result);
9799 xmlXPathFreeContext(xpath_ctx);
9801 free(code);
9802 free(message);
9803 xmlFreeDoc(response);
9804 xmlFreeNode(request);
9806 if (!err)
9807 isds_log(ILF_ISDS, ILL_DEBUG,
9808 _("CreateMultipleMessageResponse request processed by server "
9809 "successfully.\n"));
9810 #else /* not HAVE_LIBCURL */
9811 err = IE_NOTSUP;
9812 #endif
9814 return err;
9818 /* Get list of messages. This is common core for getting sent or received
9819 * messages.
9820 * Any criterion argument can be NULL, if you don't care about it.
9821 * @context is session context. Must not be NULL.
9822 * @outgoing_direction is true if you want list of outgoing messages,
9823 * it's false if you want incoming messages.
9824 * @from_time is minimal time and date of message sending inclusive.
9825 * @to_time is maximal time and date of message sending inclusive
9826 * @organization_unit_number is number of sender/recipient respectively.
9827 * @status_filter is bit field of isds_message_status values. Use special
9828 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9829 * all values, you can use bit-wise arithmetic if you want.)
9830 * @offset is index of first message we are interested in. First message is 1.
9831 * Set to 0 (or 1) if you don't care.
9832 * @number is maximal length of list you want to get as input value, outputs
9833 * number of messages matching these criteria. Can be NULL if you don't care
9834 * (applies to output value either).
9835 * @messages is automatically reallocated list of isds_message's. Be ware that
9836 * it returns only brief overview (envelope and some other fields) about each
9837 * message, not the complete message. FIXME: Specify exact fields.
9838 * The list is sorted by delivery time in ascending order.
9839 * Use NULL if you don't care about don't need the data (useful if you want to
9840 * know only the @number). If you provide &NULL, list will be allocated on
9841 * heap, if you provide pointer to non-NULL, list will be freed automatically
9842 * at first. Also in case of error the list will be NULLed.
9843 * @return IE_SUCCESS or appropriate error code. */
9844 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9845 _Bool outgoing_direction,
9846 const struct timeval *from_time, const struct timeval *to_time,
9847 const long int *organization_unit_number,
9848 const unsigned int status_filter,
9849 const unsigned long int offset, unsigned long int *number,
9850 struct isds_list **messages) {
9852 isds_error err = IE_SUCCESS;
9853 #if HAVE_LIBCURL
9854 xmlNsPtr isds_ns = NULL;
9855 xmlNodePtr request = NULL, node;
9856 xmlDocPtr response = NULL;
9857 xmlChar *code = NULL, *message = NULL;
9858 xmlXPathContextPtr xpath_ctx = NULL;
9859 xmlXPathObjectPtr result = NULL;
9860 xmlChar *string = NULL;
9861 int count = 0;
9862 #endif
9864 if (!context) return IE_INVALID_CONTEXT;
9865 zfree(context->long_message);
9867 /* Free former message list if any */
9868 if (messages) isds_list_free(messages);
9870 #if HAVE_LIBCURL
9871 /* Check if connection is established
9872 * TODO: This check should be done downstairs. */
9873 if (!context->curl) return IE_CONNECTION_CLOSED;
9875 /* Build GetListOf*Messages request */
9876 request = xmlNewNode(NULL,
9877 (outgoing_direction) ?
9878 BAD_CAST "GetListOfSentMessages" :
9879 BAD_CAST "GetListOfReceivedMessages"
9881 if (!request) {
9882 isds_log_message(context,
9883 (outgoing_direction) ?
9884 _("Could not build GetListOfSentMessages request") :
9885 _("Could not build GetListOfReceivedMessages request")
9887 return IE_ERROR;
9889 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9890 if(!isds_ns) {
9891 isds_log_message(context, _("Could not create ISDS name space"));
9892 xmlFreeNode(request);
9893 return IE_ERROR;
9895 xmlSetNs(request, isds_ns);
9898 if (from_time) {
9899 err = timeval2timestring(from_time, &string);
9900 if (err) goto leave;
9902 INSERT_STRING(request, "dmFromTime", string);
9903 free(string); string = NULL;
9905 if (to_time) {
9906 err = timeval2timestring(to_time, &string);
9907 if (err) goto leave;
9909 INSERT_STRING(request, "dmToTime", string);
9910 free(string); string = NULL;
9912 if (outgoing_direction) {
9913 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9914 organization_unit_number, string);
9915 } else {
9916 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9917 organization_unit_number, string);
9920 if (status_filter > MESSAGESTATE_ANY) {
9921 isds_printf_message(context,
9922 _("Invalid message state filter value: %ld"), status_filter);
9923 err = IE_INVAL;
9924 goto leave;
9926 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9928 if (offset > 0 ) {
9929 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9930 } else {
9931 INSERT_STRING(request, "dmOffset", "1");
9934 /* number 0 means no limit */
9935 if (number && *number == 0) {
9936 INSERT_STRING(request, "dmLimit", NULL);
9937 } else {
9938 INSERT_ULONGINT(request, "dmLimit", number, string);
9942 isds_log(ILF_ISDS, ILL_DEBUG,
9943 (outgoing_direction) ?
9944 _("Sending GetListOfSentMessages request to ISDS\n") :
9945 _("Sending GetListOfReceivedMessages request to ISDS\n")
9948 /* Sent request */
9949 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9950 xmlFreeNode(request); request = NULL;
9952 if (err) {
9953 isds_log(ILF_ISDS, ILL_DEBUG,
9954 (outgoing_direction) ?
9955 _("Processing ISDS response on GetListOfSentMessages "
9956 "request failed\n") :
9957 _("Processing ISDS response on GetListOfReceivedMessages "
9958 "request failed\n")
9960 goto leave;
9963 /* Check for response status */
9964 err = isds_response_status(context, SERVICE_DM_INFO, response,
9965 &code, &message, NULL);
9966 if (err) {
9967 isds_log(ILF_ISDS, ILL_DEBUG,
9968 (outgoing_direction) ?
9969 _("ISDS response on GetListOfSentMessages request "
9970 "is missing status\n") :
9971 _("ISDS response on GetListOfReceivedMessages request "
9972 "is missing status\n")
9974 goto leave;
9977 /* Request processed, but nothing found */
9978 if (xmlStrcmp(code, BAD_CAST "0000")) {
9979 char *code_locale = _isds_utf82locale((char*)code);
9980 char *message_locale = _isds_utf82locale((char*)message);
9981 isds_log(ILF_ISDS, ILL_DEBUG,
9982 (outgoing_direction) ?
9983 _("Server refused GetListOfSentMessages request "
9984 "(code=%s, message=%s)\n") :
9985 _("Server refused GetListOfReceivedMessages request "
9986 "(code=%s, message=%s)\n"),
9987 code_locale, message_locale);
9988 isds_log_message(context, message_locale);
9989 free(code_locale);
9990 free(message_locale);
9991 err = IE_ISDS;
9992 goto leave;
9996 /* Extract data */
9997 xpath_ctx = xmlXPathNewContext(response);
9998 if (!xpath_ctx) {
9999 err = IE_ERROR;
10000 goto leave;
10002 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10003 err = IE_ERROR;
10004 goto leave;
10006 result = xmlXPathEvalExpression(
10007 (outgoing_direction) ?
10008 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
10009 "isds:dmRecords/isds:dmRecord" :
10010 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
10011 "isds:dmRecords/isds:dmRecord",
10012 xpath_ctx);
10013 if (!result) {
10014 err = IE_ERROR;
10015 goto leave;
10018 /* Fill output arguments in */
10019 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10020 struct isds_envelope *envelope;
10021 struct isds_list *item = NULL, *last_item = NULL;
10023 for (count = 0; count < result->nodesetval->nodeNr; count++) {
10024 /* Create new message */
10025 item = calloc(1, sizeof(*item));
10026 if (!item) {
10027 err = IE_NOMEM;
10028 goto leave;
10030 item->destructor = (void(*)(void**)) &isds_message_free;
10031 item->data = calloc(1, sizeof(struct isds_message));
10032 if (!item->data) {
10033 isds_list_free(&item);
10034 err = IE_NOMEM;
10035 goto leave;
10038 /* Extract envelope data */
10039 xpath_ctx->node = result->nodesetval->nodeTab[count];
10040 envelope = NULL;
10041 err = extract_DmRecord(context, &envelope, xpath_ctx);
10042 if (err) {
10043 isds_list_free(&item);
10044 goto leave;
10047 /* Attach extracted envelope */
10048 ((struct isds_message *) item->data)->envelope = envelope;
10050 /* Append new message into the list */
10051 if (!*messages) {
10052 *messages = last_item = item;
10053 } else {
10054 last_item->next = item;
10055 last_item = item;
10059 if (number) *number = count;
10061 leave:
10062 if (err) {
10063 isds_list_free(messages);
10066 free(string);
10067 xmlXPathFreeObject(result);
10068 xmlXPathFreeContext(xpath_ctx);
10070 free(code);
10071 free(message);
10072 xmlFreeDoc(response);
10073 xmlFreeNode(request);
10075 if (!err)
10076 isds_log(ILF_ISDS, ILL_DEBUG,
10077 (outgoing_direction) ?
10078 _("GetListOfSentMessages request processed by server "
10079 "successfully.\n") :
10080 _("GetListOfReceivedMessages request processed by server "
10081 "successfully.\n")
10083 #else /* not HAVE_LIBCURL */
10084 err = IE_NOTSUP;
10085 #endif
10086 return err;
10090 /* Get list of outgoing (already sent) messages.
10091 * Any criterion argument can be NULL, if you don't care about it.
10092 * @context is session context. Must not be NULL.
10093 * @from_time is minimal time and date of message sending inclusive.
10094 * @to_time is maximal time and date of message sending inclusive
10095 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
10096 * @status_filter is bit field of isds_message_status values. Use special
10097 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10098 * all values, you can use bit-wise arithmetic if you want.)
10099 * @offset is index of first message we are interested in. First message is 1.
10100 * Set to 0 (or 1) if you don't care.
10101 * @number is maximal length of list you want to get as input value, outputs
10102 * number of messages matching these criteria. Can be NULL if you don't care
10103 * (applies to output value either).
10104 * @messages is automatically reallocated list of isds_message's. Be ware that
10105 * it returns only brief overview (envelope and some other fields) about each
10106 * message, not the complete message. FIXME: Specify exact fields.
10107 * The list is sorted by delivery time in ascending order.
10108 * Use NULL if you don't care about the meta data (useful if you want to know
10109 * only the @number). If you provide &NULL, list will be allocated on heap,
10110 * if you provide pointer to non-NULL, list will be freed automatically at
10111 * first. Also in case of error the list will be NULLed.
10112 * @return IE_SUCCESS or appropriate error code. */
10113 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
10114 const struct timeval *from_time, const struct timeval *to_time,
10115 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
10116 const unsigned long int offset, unsigned long int *number,
10117 struct isds_list **messages) {
10119 return isds_get_list_of_messages(
10120 context, 1,
10121 from_time, to_time, dmSenderOrgUnitNum, status_filter,
10122 offset, number,
10123 messages);
10127 /* Get list of incoming (addressed to you) messages.
10128 * Any criterion argument can be NULL, if you don't care about it.
10129 * @context is session context. Must not be NULL.
10130 * @from_time is minimal time and date of message sending inclusive.
10131 * @to_time is maximal time and date of message sending inclusive
10132 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
10133 * @status_filter is bit field of isds_message_status values. Use special
10134 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10135 * all values, you can use bit-wise arithmetic if you want.)
10136 * @offset is index of first message we are interested in. First message is 1.
10137 * Set to 0 (or 1) if you don't care.
10138 * @number is maximal length of list you want to get as input value, outputs
10139 * number of messages matching these criteria. Can be NULL if you don't care
10140 * (applies to output value either).
10141 * @messages is automatically reallocated list of isds_message's. Be ware that
10142 * it returns only brief overview (envelope and some other fields) about each
10143 * message, not the complete message. FIXME: Specify exact fields.
10144 * Use NULL if you don't care about the meta data (useful if you want to know
10145 * only the @number). If you provide &NULL, list will be allocated on heap,
10146 * if you provide pointer to non-NULL, list will be freed automatically at
10147 * first. Also in case of error the list will be NULLed.
10148 * @return IE_SUCCESS or appropriate error code. */
10149 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
10150 const struct timeval *from_time, const struct timeval *to_time,
10151 const long int *dmRecipientOrgUnitNum,
10152 const unsigned int status_filter,
10153 const unsigned long int offset, unsigned long int *number,
10154 struct isds_list **messages) {
10156 return isds_get_list_of_messages(
10157 context, 0,
10158 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
10159 offset, number,
10160 messages);
10164 /* Get list of sent message state changes.
10165 * Any criterion argument can be NULL, if you don't care about it.
10166 * @context is session context. Must not be NULL.
10167 * @from_time is minimal time and date of status changes inclusive
10168 * @to_time is maximal time and date of status changes inclusive
10169 * @changed_states is automatically reallocated list of
10170 * isds_message_status_change's. If you provide &NULL, list will be allocated
10171 * on heap, if you provide pointer to non-NULL, list will be freed
10172 * automatically at first. Also in case of error the list will be NULLed.
10173 * XXX: The list item ordering is not specified.
10174 * XXX: Server provides only `recent' changes.
10175 * @return IE_SUCCESS or appropriate error code. */
10176 isds_error isds_get_list_of_sent_message_state_changes(
10177 struct isds_ctx *context,
10178 const struct timeval *from_time, const struct timeval *to_time,
10179 struct isds_list **changed_states) {
10181 isds_error err = IE_SUCCESS;
10182 #if HAVE_LIBCURL
10183 xmlNsPtr isds_ns = NULL;
10184 xmlNodePtr request = NULL, node;
10185 xmlDocPtr response = NULL;
10186 xmlXPathContextPtr xpath_ctx = NULL;
10187 xmlXPathObjectPtr result = NULL;
10188 xmlChar *string = NULL;
10189 int count = 0;
10190 #endif
10192 if (!context) return IE_INVALID_CONTEXT;
10193 zfree(context->long_message);
10195 /* Free former message list if any */
10196 isds_list_free(changed_states);
10198 #if HAVE_LIBCURL
10199 /* Check if connection is established
10200 * TODO: This check should be done downstairs. */
10201 if (!context->curl) return IE_CONNECTION_CLOSED;
10203 /* Build GetMessageStateChanges request */
10204 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
10205 if (!request) {
10206 isds_log_message(context,
10207 _("Could not build GetMessageStateChanges request"));
10208 return IE_ERROR;
10210 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10211 if(!isds_ns) {
10212 isds_log_message(context, _("Could not create ISDS name space"));
10213 xmlFreeNode(request);
10214 return IE_ERROR;
10216 xmlSetNs(request, isds_ns);
10219 if (from_time) {
10220 err = timeval2timestring(from_time, &string);
10221 if (err) goto leave;
10223 INSERT_STRING(request, "dmFromTime", string);
10224 zfree(string);
10226 if (to_time) {
10227 err = timeval2timestring(to_time, &string);
10228 if (err) goto leave;
10230 INSERT_STRING(request, "dmToTime", string);
10231 zfree(string);
10234 /* Sent request */
10235 err = send_destroy_request_check_response(context,
10236 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
10237 &response, NULL, NULL);
10238 if (err) goto leave;
10241 /* Extract data */
10242 xpath_ctx = xmlXPathNewContext(response);
10243 if (!xpath_ctx) {
10244 err = IE_ERROR;
10245 goto leave;
10247 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10248 err = IE_ERROR;
10249 goto leave;
10251 result = xmlXPathEvalExpression(
10252 BAD_CAST "/isds:GetMessageStateChangesResponse/"
10253 "isds:dmRecords/isds:dmRecord", xpath_ctx);
10254 if (!result) {
10255 err = IE_ERROR;
10256 goto leave;
10259 /* Fill output arguments in */
10260 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10261 struct isds_list *item = NULL, *last_item = NULL;
10263 for (count = 0; count < result->nodesetval->nodeNr; count++) {
10264 /* Create new status change */
10265 item = calloc(1, sizeof(*item));
10266 if (!item) {
10267 err = IE_NOMEM;
10268 goto leave;
10270 item->destructor =
10271 (void(*)(void**)) &isds_message_status_change_free;
10273 /* Extract message status change */
10274 xpath_ctx->node = result->nodesetval->nodeTab[count];
10275 err = extract_StateChangesRecord(context,
10276 (struct isds_message_status_change **) &item->data,
10277 xpath_ctx);
10278 if (err) {
10279 isds_list_free(&item);
10280 goto leave;
10283 /* Append new message status change into the list */
10284 if (!*changed_states) {
10285 *changed_states = last_item = item;
10286 } else {
10287 last_item->next = item;
10288 last_item = item;
10293 leave:
10294 if (err) {
10295 isds_list_free(changed_states);
10298 free(string);
10299 xmlXPathFreeObject(result);
10300 xmlXPathFreeContext(xpath_ctx);
10301 xmlFreeDoc(response);
10302 xmlFreeNode(request);
10304 if (!err)
10305 isds_log(ILF_ISDS, ILL_DEBUG,
10306 _("GetMessageStateChanges request processed by server "
10307 "successfully.\n"));
10308 #else /* not HAVE_LIBCURL */
10309 err = IE_NOTSUP;
10310 #endif
10311 return err;
10315 #if HAVE_LIBCURL
10316 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10317 * code
10318 * @context is session context
10319 * @service is ISDS WS service handler
10320 * @service_name is name of SERVICE_DM_OPERATIONS
10321 * @message_id is message ID to send as service argument to ISDS
10322 * @response is reallocated server SOAP body response as XML document
10323 * @raw_response is reallocated bit stream with response body. Use
10324 * NULL if you don't care
10325 * @raw_response_length is size of @raw_response in bytes
10326 * @code is reallocated ISDS status code
10327 * @status_message is reallocated ISDS status message
10328 * @return error coded from lower layer, context message will be set up
10329 * appropriately. */
10330 static isds_error build_send_check_message_request(struct isds_ctx *context,
10331 const isds_service service, const xmlChar *service_name,
10332 const char *message_id,
10333 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
10334 xmlChar **code, xmlChar **status_message) {
10336 isds_error err = IE_SUCCESS;
10337 char *service_name_locale = NULL, *message_id_locale = NULL;
10338 xmlNodePtr request = NULL, node;
10339 xmlNsPtr isds_ns = NULL;
10341 if (!context) return IE_INVALID_CONTEXT;
10342 if (!service_name || !message_id) return IE_INVAL;
10343 if (!response || !code || !status_message) return IE_INVAL;
10344 if (!raw_response_length && raw_response) return IE_INVAL;
10346 /* Free output argument */
10347 xmlFreeDoc(*response); *response = NULL;
10348 if (raw_response) zfree(*raw_response);
10349 zfree(*code);
10350 zfree(*status_message);
10353 /* Check if connection is established
10354 * TODO: This check should be done downstairs. */
10355 if (!context->curl) return IE_CONNECTION_CLOSED;
10357 service_name_locale = _isds_utf82locale((char*)service_name);
10358 message_id_locale = _isds_utf82locale(message_id);
10359 if (!service_name_locale || !message_id_locale) {
10360 err = IE_NOMEM;
10361 goto leave;
10364 /* Build request */
10365 request = xmlNewNode(NULL, service_name);
10366 if (!request) {
10367 isds_printf_message(context,
10368 _("Could not build %s request for %s message ID"),
10369 service_name_locale, message_id_locale);
10370 err = IE_ERROR;
10371 goto leave;
10373 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10374 if(!isds_ns) {
10375 isds_log_message(context, _("Could not create ISDS name space"));
10376 err = IE_ERROR;
10377 goto leave;
10379 xmlSetNs(request, isds_ns);
10382 /* Add requested ID */
10383 err = validate_message_id_length(context, (xmlChar *) message_id);
10384 if (err) goto leave;
10385 INSERT_STRING(request, "dmID", message_id);
10388 isds_log(ILF_ISDS, ILL_DEBUG,
10389 _("Sending %s request for %s message ID to ISDS\n"),
10390 service_name_locale, message_id_locale);
10392 /* Send request */
10393 err = _isds(context, service, request, response,
10394 raw_response, raw_response_length);
10395 xmlFreeNode(request); request = NULL;
10397 if (err) {
10398 isds_log(ILF_ISDS, ILL_DEBUG,
10399 _("Processing ISDS response on %s request failed\n"),
10400 service_name_locale);
10401 goto leave;
10404 /* Check for response status */
10405 err = isds_response_status(context, service, *response,
10406 code, status_message, NULL);
10407 if (err) {
10408 isds_log(ILF_ISDS, ILL_DEBUG,
10409 _("ISDS response on %s request is missing status\n"),
10410 service_name_locale);
10411 goto leave;
10414 /* Request processed, but nothing found */
10415 if (xmlStrcmp(*code, BAD_CAST "0000")) {
10416 char *code_locale = _isds_utf82locale((char*) *code);
10417 char *status_message_locale = _isds_utf82locale((char*) *status_message);
10418 isds_log(ILF_ISDS, ILL_DEBUG,
10419 _("Server refused %s request for %s message ID "
10420 "(code=%s, message=%s)\n"),
10421 service_name_locale, message_id_locale,
10422 code_locale, status_message_locale);
10423 isds_log_message(context, status_message_locale);
10424 free(code_locale);
10425 free(status_message_locale);
10426 err = IE_ISDS;
10427 goto leave;
10430 leave:
10431 free(message_id_locale);
10432 free(service_name_locale);
10433 xmlFreeNode(request);
10434 return err;
10438 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10439 * signed data and free ISDS response.
10440 * @context is session context
10441 * @message_id is UTF-8 encoded message ID for logging purpose
10442 * @response is parsed XML document. It will be freed and NULLed in the middle
10443 * of function run to save memory. This is not guaranteed in case of error.
10444 * @request_name is name of ISDS request used to construct response root
10445 * element name and for logging purpose.
10446 * @raw is reallocated output buffer with DER encoded CMS data
10447 * @raw_length is size of @raw buffer in bytes
10448 * @returns standard error codes, in case of error, @raw will be freed and
10449 * NULLed, @response sometimes. */
10450 static isds_error find_extract_signed_data_free_response(
10451 struct isds_ctx *context, const xmlChar *message_id,
10452 xmlDocPtr *response, const xmlChar *request_name,
10453 void **raw, size_t *raw_length) {
10455 isds_error err = IE_SUCCESS;
10456 char *xpath_expression = NULL;
10457 xmlXPathContextPtr xpath_ctx = NULL;
10458 xmlXPathObjectPtr result = NULL;
10459 char *encoded_structure = NULL;
10461 if (!context) return IE_INVALID_CONTEXT;
10462 if (!raw) return IE_INVAL;
10463 zfree(*raw);
10464 if (!message_id || !response || !*response || !request_name || !raw_length)
10465 return IE_INVAL;
10467 /* Build XPath expression */
10468 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
10469 "Response/isds:dmSignature");
10470 if (!xpath_expression) return IE_NOMEM;
10472 /* Extract data */
10473 xpath_ctx = xmlXPathNewContext(*response);
10474 if (!xpath_ctx) {
10475 err = IE_ERROR;
10476 goto leave;
10478 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10479 err = IE_ERROR;
10480 goto leave;
10482 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
10483 if (!result) {
10484 err = IE_ERROR;
10485 goto leave;
10487 /* Empty response */
10488 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10489 char *message_id_locale = _isds_utf82locale((char*) message_id);
10490 isds_printf_message(context,
10491 _("Server did not return any signed data for message ID `%s' "
10492 "on %s request"),
10493 message_id_locale, request_name);
10494 free(message_id_locale);
10495 err = IE_ISDS;
10496 goto leave;
10498 /* More responses */
10499 if (result->nodesetval->nodeNr > 1) {
10500 char *message_id_locale = _isds_utf82locale((char*) message_id);
10501 isds_printf_message(context,
10502 _("Server did return more signed data for message ID `%s' "
10503 "on %s request"),
10504 message_id_locale, request_name);
10505 free(message_id_locale);
10506 err = IE_ISDS;
10507 goto leave;
10509 /* One response */
10510 xpath_ctx->node = result->nodesetval->nodeTab[0];
10512 /* Extract PKCS#7 structure */
10513 EXTRACT_STRING(".", encoded_structure);
10514 if (!encoded_structure) {
10515 isds_log_message(context, _("dmSignature element is empty"));
10518 /* Here we have delivery info as standalone CMS in encoded_structure.
10519 * We don't need any other data, free them: */
10520 xmlXPathFreeObject(result); result = NULL;
10521 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
10522 xmlFreeDoc(*response); *response = NULL;
10525 /* Decode PKCS#7 to DER format */
10526 *raw_length = _isds_b64decode(encoded_structure, raw);
10527 if (*raw_length == (size_t) -1) {
10528 isds_log_message(context,
10529 _("Error while Base64-decoding PKCS#7 structure"));
10530 err = IE_ERROR;
10531 goto leave;
10534 leave:
10535 if (err) {
10536 zfree(*raw);
10537 raw_length = 0;
10540 free(encoded_structure);
10541 xmlXPathFreeObject(result);
10542 xmlXPathFreeContext(xpath_ctx);
10543 free(xpath_expression);
10545 return err;
10547 #endif /* HAVE_LIBCURL */
10550 /* Download incoming message envelope identified by ID.
10551 * @context is session context
10552 * @message_id is message identifier (you can get them from
10553 * isds_get_list_of_received_messages())
10554 * @message is automatically reallocated message retrieved from ISDS.
10555 * It will miss documents per se. Use isds_get_received_message(), if you are
10556 * interested in documents (content) too.
10557 * Returned hash and timestamp require documents to be verifiable. */
10558 isds_error isds_get_received_envelope(struct isds_ctx *context,
10559 const char *message_id, struct isds_message **message) {
10561 isds_error err = IE_SUCCESS;
10562 #if HAVE_LIBCURL
10563 xmlDocPtr response = NULL;
10564 xmlChar *code = NULL, *status_message = NULL;
10565 xmlXPathContextPtr xpath_ctx = NULL;
10566 xmlXPathObjectPtr result = NULL;
10567 #endif
10569 if (!context) return IE_INVALID_CONTEXT;
10570 zfree(context->long_message);
10572 /* Free former message if any */
10573 if (!message) return IE_INVAL;
10574 isds_message_free(message);
10576 #if HAVE_LIBCURL
10577 /* Do request and check for success */
10578 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10579 BAD_CAST "MessageEnvelopeDownload", message_id,
10580 &response, NULL, NULL, &code, &status_message);
10581 if (err) goto leave;
10583 /* Extract data */
10584 xpath_ctx = xmlXPathNewContext(response);
10585 if (!xpath_ctx) {
10586 err = IE_ERROR;
10587 goto leave;
10589 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10590 err = IE_ERROR;
10591 goto leave;
10593 result = xmlXPathEvalExpression(
10594 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
10595 "isds:dmReturnedMessageEnvelope",
10596 xpath_ctx);
10597 if (!result) {
10598 err = IE_ERROR;
10599 goto leave;
10601 /* Empty response */
10602 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10603 char *message_id_locale = _isds_utf82locale((char*) message_id);
10604 isds_printf_message(context,
10605 _("Server did not return any envelope for ID `%s' "
10606 "on MessageEnvelopeDownload request"), message_id_locale);
10607 free(message_id_locale);
10608 err = IE_ISDS;
10609 goto leave;
10611 /* More envelops */
10612 if (result->nodesetval->nodeNr > 1) {
10613 char *message_id_locale = _isds_utf82locale((char*) message_id);
10614 isds_printf_message(context,
10615 _("Server did return more envelopes for ID `%s' "
10616 "on MessageEnvelopeDownload request"), message_id_locale);
10617 free(message_id_locale);
10618 err = IE_ISDS;
10619 goto leave;
10621 /* One message */
10622 xpath_ctx->node = result->nodesetval->nodeTab[0];
10624 /* Extract the envelope (= message without documents, hence 0) */
10625 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10626 if (err) goto leave;
10628 /* Save XML blob */
10629 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10630 &(*message)->raw_length);
10632 leave:
10633 if (err) {
10634 isds_message_free(message);
10637 xmlXPathFreeObject(result);
10638 xmlXPathFreeContext(xpath_ctx);
10640 free(code);
10641 free(status_message);
10642 if (!*message || !(*message)->xml) {
10643 xmlFreeDoc(response);
10646 if (!err)
10647 isds_log(ILF_ISDS, ILL_DEBUG,
10648 _("MessageEnvelopeDownload request processed by server "
10649 "successfully.\n")
10651 #else /* not HAVE_LIBCURL */
10652 err = IE_NOTSUP;
10653 #endif
10654 return err;
10658 /* Load delivery info of any format from buffer.
10659 * @context is session context
10660 * @raw_type advertises format of @buffer content. Only delivery info types
10661 * are accepted.
10662 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10663 * retrieve such data from message->raw after calling
10664 * isds_get_signed_delivery_info().
10665 * @length is length of buffer in bytes.
10666 * @message is automatically reallocated message parsed from @buffer.
10667 * @strategy selects how buffer will be attached into raw isds_message member.
10668 * */
10669 isds_error isds_load_delivery_info(struct isds_ctx *context,
10670 const isds_raw_type raw_type,
10671 const void *buffer, const size_t length,
10672 struct isds_message **message, const isds_buffer_strategy strategy) {
10674 isds_error err = IE_SUCCESS;
10675 message_ns_type message_ns;
10676 xmlDocPtr message_doc = NULL;
10677 xmlXPathContextPtr xpath_ctx = NULL;
10678 xmlXPathObjectPtr result = NULL;
10679 void *xml_stream = NULL;
10680 size_t xml_stream_length = 0;
10682 if (!context) return IE_INVALID_CONTEXT;
10683 zfree(context->long_message);
10684 if (!message) return IE_INVAL;
10685 isds_message_free(message);
10686 if (!buffer) return IE_INVAL;
10689 /* Select buffer format and extract XML from CMS*/
10690 switch (raw_type) {
10691 case RAWTYPE_DELIVERYINFO:
10692 message_ns = MESSAGE_NS_UNSIGNED;
10693 xml_stream = (void *) buffer;
10694 xml_stream_length = length;
10695 break;
10697 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10698 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10699 xml_stream = (void *) buffer;
10700 xml_stream_length = length;
10701 break;
10703 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10704 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10705 err = _isds_extract_cms_data(context, buffer, length,
10706 &xml_stream, &xml_stream_length);
10707 if (err) goto leave;
10708 break;
10710 default:
10711 isds_log_message(context, _("Bad raw delivery representation type"));
10712 return IE_INVAL;
10713 break;
10716 if (_isds_sizet2int(xml_stream_length) >= 0) {
10717 isds_log(ILF_ISDS, ILL_DEBUG,
10718 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10719 _isds_sizet2int(xml_stream_length), xml_stream);
10722 /* Convert delivery info XML stream into XPath context */
10723 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10724 if (!message_doc) {
10725 err = IE_XML;
10726 goto leave;
10728 xpath_ctx = xmlXPathNewContext(message_doc);
10729 if (!xpath_ctx) {
10730 err = IE_ERROR;
10731 goto leave;
10733 /* XXX: Name spaces mangled for signed delivery info:
10734 * http://isds.czechpoint.cz/v20/delivery:
10736 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10737 * <q:dmDelivery>
10738 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10739 * <p:dmID>170272</p:dmID>
10740 * ...
10741 * </p:dmDm>
10742 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10743 * ...
10744 * </q:dmEvents>...</q:dmEvents>
10745 * </q:dmDelivery>
10746 * </q:GetDeliveryInfoResponse>
10747 * */
10748 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10749 err = IE_ERROR;
10750 goto leave;
10752 result = xmlXPathEvalExpression(
10753 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10754 xpath_ctx);
10755 if (!result) {
10756 err = IE_ERROR;
10757 goto leave;
10759 /* Empty delivery info */
10760 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10761 isds_printf_message(context,
10762 _("XML document is not sisds:dmDelivery document"));
10763 err = IE_ISDS;
10764 goto leave;
10766 /* More delivery info's */
10767 if (result->nodesetval->nodeNr > 1) {
10768 isds_printf_message(context,
10769 _("XML document has more sisds:dmDelivery elements"));
10770 err = IE_ISDS;
10771 goto leave;
10773 /* One delivery info */
10774 xpath_ctx->node = result->nodesetval->nodeTab[0];
10776 /* Extract the envelope (= message without documents, hence 0).
10777 * XXX: extract_TReturnedMessage() can obtain attachments size,
10778 * but delivery info carries none. It's coded as option elements,
10779 * so it should work. */
10780 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10781 if (err) goto leave;
10783 /* Extract events */
10784 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10785 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10786 if (err) { err = IE_ERROR; goto leave; }
10787 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10788 if (err) goto leave;
10790 /* Append raw CMS structure into message */
10791 (*message)->raw_type = raw_type;
10792 switch (strategy) {
10793 case BUFFER_DONT_STORE:
10794 break;
10795 case BUFFER_COPY:
10796 (*message)->raw = malloc(length);
10797 if (!(*message)->raw) {
10798 err = IE_NOMEM;
10799 goto leave;
10801 memcpy((*message)->raw, buffer, length);
10802 (*message)->raw_length = length;
10803 break;
10804 case BUFFER_MOVE:
10805 (*message)->raw = (void *) buffer;
10806 (*message)->raw_length = length;
10807 break;
10808 default:
10809 err = IE_ENUM;
10810 goto leave;
10813 leave:
10814 if (err) {
10815 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10816 isds_message_free(message);
10819 xmlXPathFreeObject(result);
10820 xmlXPathFreeContext(xpath_ctx);
10821 if (!*message || !(*message)->xml) {
10822 xmlFreeDoc(message_doc);
10824 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10826 if (!err)
10827 isds_log(ILF_ISDS, ILL_DEBUG,
10828 _("Delivery info loaded successfully.\n"));
10829 return err;
10833 /* Download signed delivery info-sheet of given message identified by ID.
10834 * @context is session context
10835 * @message_id is message identifier (you can get them from
10836 * isds_get_list_of_{sent,received}_messages())
10837 * @message is automatically reallocated message retrieved from ISDS.
10838 * It will miss documents per se. Use isds_get_signed_received_message(),
10839 * if you are interested in documents (content). OTOH, only this function
10840 * can get list events message has gone through. */
10841 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10842 const char *message_id, struct isds_message **message) {
10844 isds_error err = IE_SUCCESS;
10845 #if HAVE_LIBCURL
10846 xmlDocPtr response = NULL;
10847 xmlChar *code = NULL, *status_message = NULL;
10848 void *raw = NULL;
10849 size_t raw_length = 0;
10850 #endif
10852 if (!context) return IE_INVALID_CONTEXT;
10853 zfree(context->long_message);
10855 /* Free former message if any */
10856 if (!message) return IE_INVAL;
10857 isds_message_free(message);
10859 #if HAVE_LIBCURL
10860 /* Do request and check for success */
10861 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10862 BAD_CAST "GetSignedDeliveryInfo", message_id,
10863 &response, NULL, NULL, &code, &status_message);
10864 if (err) goto leave;
10866 /* Find signed delivery info, extract it into raw and maybe free
10867 * response */
10868 err = find_extract_signed_data_free_response(context,
10869 (xmlChar *)message_id, &response,
10870 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10871 if (err) goto leave;
10873 /* Parse delivery info */
10874 err = isds_load_delivery_info(context,
10875 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10876 message, BUFFER_MOVE);
10877 if (err) goto leave;
10879 raw = NULL;
10881 leave:
10882 if (err) {
10883 isds_message_free(message);
10886 free(raw);
10887 free(code);
10888 free(status_message);
10889 xmlFreeDoc(response);
10891 if (!err)
10892 isds_log(ILF_ISDS, ILL_DEBUG,
10893 _("GetSignedDeliveryInfo request processed by server "
10894 "successfully.\n")
10896 #else /* not HAVE_LIBCURL */
10897 err = IE_NOTSUP;
10898 #endif
10899 return err;
10903 /* Download delivery info-sheet of given message identified by ID.
10904 * @context is session context
10905 * @message_id is message identifier (you can get them from
10906 * isds_get_list_of_{sent,received}_messages())
10907 * @message is automatically reallocated message retrieved from ISDS.
10908 * It will miss documents per se. Use isds_get_received_message(), if you are
10909 * interested in documents (content). OTOH, only this function can get list
10910 * of events message has gone through. */
10911 isds_error isds_get_delivery_info(struct isds_ctx *context,
10912 const char *message_id, struct isds_message **message) {
10914 isds_error err = IE_SUCCESS;
10915 #if HAVE_LIBCURL
10916 xmlDocPtr response = NULL;
10917 xmlChar *code = NULL, *status_message = NULL;
10918 xmlNodePtr delivery_node = NULL;
10919 void *raw = NULL;
10920 size_t raw_length = 0;
10921 #endif
10923 if (!context) return IE_INVALID_CONTEXT;
10924 zfree(context->long_message);
10926 /* Free former message if any */
10927 if (!message) return IE_INVAL;
10928 isds_message_free(message);
10930 #if HAVE_LIBCURL
10931 /* Do request and check for success */
10932 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10933 BAD_CAST "GetDeliveryInfo", message_id,
10934 &response, NULL, NULL, &code, &status_message);
10935 if (err) goto leave;
10938 /* Serialize delivery info */
10939 delivery_node = xmlDocGetRootElement(response);
10940 if (!delivery_node) {
10941 char *message_id_locale = _isds_utf82locale((char*) message_id);
10942 isds_printf_message(context,
10943 _("Server did not return any delivery info for ID `%s' "
10944 "on GetDeliveryInfo request"), message_id_locale);
10945 free(message_id_locale);
10946 err = IE_ISDS;
10947 goto leave;
10949 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10950 if (err) goto leave;
10952 /* Parse delivery info */
10953 /* TODO: Here we parse the response second time. We could single delivery
10954 * parser from isds_load_delivery_info() to make things faster. */
10955 err = isds_load_delivery_info(context,
10956 RAWTYPE_DELIVERYINFO, raw, raw_length,
10957 message, BUFFER_MOVE);
10958 if (err) goto leave;
10960 raw = NULL;
10963 leave:
10964 if (err) {
10965 isds_message_free(message);
10968 free(raw);
10969 free(code);
10970 free(status_message);
10971 xmlFreeDoc(response);
10973 if (!err)
10974 isds_log(ILF_ISDS, ILL_DEBUG,
10975 _("GetDeliveryInfo request processed by server "
10976 "successfully.\n")
10978 #else /* not HAVE_LIBCURL */
10979 err = IE_NOTSUP;
10980 #endif
10981 return err;
10985 /* Download incoming message identified by ID.
10986 * @context is session context
10987 * @message_id is message identifier (you can get them from
10988 * isds_get_list_of_received_messages())
10989 * @message is automatically reallocated message retrieved from ISDS */
10990 isds_error isds_get_received_message(struct isds_ctx *context,
10991 const char *message_id, struct isds_message **message) {
10993 isds_error err = IE_SUCCESS;
10994 #if HAVE_LIBCURL
10995 xmlDocPtr response = NULL;
10996 void *xml_stream = NULL;
10997 size_t xml_stream_length;
10998 xmlChar *code = NULL, *status_message = NULL;
10999 xmlXPathContextPtr xpath_ctx = NULL;
11000 xmlXPathObjectPtr result = NULL;
11001 char *phys_path = NULL;
11002 size_t phys_start, phys_end;
11003 #endif
11005 if (!context) return IE_INVALID_CONTEXT;
11006 zfree(context->long_message);
11008 /* Free former message if any */
11009 if (NULL == message) return IE_INVAL;
11010 if (message) isds_message_free(message);
11012 #if HAVE_LIBCURL
11013 /* Do request and check for success */
11014 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
11015 BAD_CAST "MessageDownload", message_id,
11016 &response, &xml_stream, &xml_stream_length,
11017 &code, &status_message);
11018 if (err) goto leave;
11020 /* Extract data */
11021 xpath_ctx = xmlXPathNewContext(response);
11022 if (!xpath_ctx) {
11023 err = IE_ERROR;
11024 goto leave;
11026 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11027 err = IE_ERROR;
11028 goto leave;
11030 result = xmlXPathEvalExpression(
11031 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
11032 xpath_ctx);
11033 if (!result) {
11034 err = IE_ERROR;
11035 goto leave;
11037 /* Empty response */
11038 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11039 char *message_id_locale = _isds_utf82locale((char*) message_id);
11040 isds_printf_message(context,
11041 _("Server did not return any message for ID `%s' "
11042 "on MessageDownload request"), message_id_locale);
11043 free(message_id_locale);
11044 err = IE_ISDS;
11045 goto leave;
11047 /* More messages */
11048 if (result->nodesetval->nodeNr > 1) {
11049 char *message_id_locale = _isds_utf82locale((char*) message_id);
11050 isds_printf_message(context,
11051 _("Server did return more messages for ID `%s' "
11052 "on MessageDownload request"), message_id_locale);
11053 free(message_id_locale);
11054 err = IE_ISDS;
11055 goto leave;
11057 /* One message */
11058 xpath_ctx->node = result->nodesetval->nodeTab[0];
11060 /* Extract the message */
11061 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
11062 if (err) goto leave;
11064 /* Locate raw XML blob */
11065 phys_path = strdup(
11066 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
11067 PHYSXML_ELEMENT_SEPARATOR
11068 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
11069 PHYSXML_ELEMENT_SEPARATOR
11070 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11072 if (!phys_path) {
11073 err = IE_NOMEM;
11074 goto leave;
11076 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11077 phys_path, &phys_start, &phys_end);
11078 zfree(phys_path);
11079 if (err) {
11080 isds_log_message(context,
11081 _("Substring with isds:MessageDownloadResponse element "
11082 "could not be located in raw SOAP message"));
11083 goto leave;
11085 /* Save XML blob */
11086 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
11087 &(*message)->raw_length);*/
11088 /* TODO: Store name space declarations from ancestors */
11089 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
11090 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
11091 (*message)->raw_length = phys_end - phys_start + 1;
11092 (*message)->raw = malloc((*message)->raw_length);
11093 if (!(*message)->raw) {
11094 err = IE_NOMEM;
11095 goto leave;
11097 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
11100 leave:
11101 if (err) {
11102 isds_message_free(message);
11105 free(phys_path);
11107 xmlXPathFreeObject(result);
11108 xmlXPathFreeContext(xpath_ctx);
11110 free(code);
11111 free(status_message);
11112 free(xml_stream);
11113 if (!*message || !(*message)->xml) {
11114 xmlFreeDoc(response);
11117 if (!err)
11118 isds_log(ILF_ISDS, ILL_DEBUG,
11119 _("MessageDownload request processed by server "
11120 "successfully.\n")
11122 #else /* not HAVE_LIBCURL */
11123 err = IE_NOTSUP;
11124 #endif
11125 return err;
11129 /* Load message of any type from buffer.
11130 * @context is session context
11131 * @raw_type defines content type of @buffer. Only message types are allowed.
11132 * @buffer is message raw representation. Format (CMS, plain signed,
11133 * message direction) is defined in @raw_type. You can retrieve such data
11134 * from message->raw after calling isds_get_[signed]{received,sent}_message().
11135 * @length is length of buffer in bytes.
11136 * @message is automatically reallocated message parsed from @buffer.
11137 * @strategy selects how buffer will be attached into raw isds_message member.
11138 * */
11139 isds_error isds_load_message(struct isds_ctx *context,
11140 const isds_raw_type raw_type, const void *buffer, const size_t length,
11141 struct isds_message **message, const isds_buffer_strategy strategy) {
11143 isds_error err = IE_SUCCESS;
11144 void *xml_stream = NULL;
11145 size_t xml_stream_length = 0;
11146 message_ns_type message_ns;
11147 xmlDocPtr message_doc = NULL;
11148 xmlXPathContextPtr xpath_ctx = NULL;
11149 xmlXPathObjectPtr result = NULL;
11151 if (!context) return IE_INVALID_CONTEXT;
11152 zfree(context->long_message);
11153 if (!message) return IE_INVAL;
11154 isds_message_free(message);
11155 if (!buffer) return IE_INVAL;
11158 /* Select buffer format and extract XML from CMS*/
11159 switch (raw_type) {
11160 case RAWTYPE_INCOMING_MESSAGE:
11161 message_ns = MESSAGE_NS_UNSIGNED;
11162 xml_stream = (void *) buffer;
11163 xml_stream_length = length;
11164 break;
11166 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11167 message_ns = MESSAGE_NS_SIGNED_INCOMING;
11168 xml_stream = (void *) buffer;
11169 xml_stream_length = length;
11170 break;
11172 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11173 message_ns = MESSAGE_NS_SIGNED_INCOMING;
11174 err = _isds_extract_cms_data(context, buffer, length,
11175 &xml_stream, &xml_stream_length);
11176 if (err) goto leave;
11177 break;
11179 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11180 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11181 xml_stream = (void *) buffer;
11182 xml_stream_length = length;
11183 break;
11185 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11186 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
11187 err = _isds_extract_cms_data(context, buffer, length,
11188 &xml_stream, &xml_stream_length);
11189 if (err) goto leave;
11190 break;
11192 default:
11193 isds_log_message(context, _("Bad raw message representation type"));
11194 return IE_INVAL;
11195 break;
11198 if (_isds_sizet2int(xml_stream_length) >= 0) {
11199 isds_log(ILF_ISDS, ILL_DEBUG,
11200 _("Loading message:\n%.*s\nEnd of message\n"),
11201 _isds_sizet2int(xml_stream_length), xml_stream);
11204 /* Convert messages XML stream into XPath context */
11205 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
11206 if (!message_doc) {
11207 err = IE_XML;
11208 goto leave;
11210 xpath_ctx = xmlXPathNewContext(message_doc);
11211 if (!xpath_ctx) {
11212 err = IE_ERROR;
11213 goto leave;
11215 /* XXX: Standard name space for unsigned incoming direction:
11216 * http://isds.czechpoint.cz/v20/
11218 * XXX: Name spaces mangled for signed outgoing direction:
11219 * http://isds.czechpoint.cz/v20/SentMessage:
11221 * <q:MessageDownloadResponse
11222 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11223 * <q:dmReturnedMessage>
11224 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11225 * <p:dmID>151916</p:dmID>
11226 * ...
11227 * </p:dmDm>
11228 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11229 * ...
11230 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11231 * </q:dmReturnedMessage>
11232 * </q:MessageDownloadResponse>
11234 * XXX: Name spaces mangled for signed incoming direction:
11235 * http://isds.czechpoint.cz/v20/message:
11237 * <q:MessageDownloadResponse
11238 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11239 * <q:dmReturnedMessage>
11240 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11241 * <p:dmID>151916</p:dmID>
11242 * ...
11243 * </p:dmDm>
11244 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11245 * ...
11246 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11247 * </q:dmReturnedMessage>
11248 * </q:MessageDownloadResponse>
11250 * Stupidity of ISDS developers is unlimited */
11251 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
11252 err = IE_ERROR;
11253 goto leave;
11255 result = xmlXPathEvalExpression(
11256 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11257 xpath_ctx);
11258 if (!result) {
11259 err = IE_ERROR;
11260 goto leave;
11262 /* Empty message */
11263 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11264 isds_printf_message(context,
11265 _("XML document does not contain "
11266 "sisds:dmReturnedMessage element"));
11267 err = IE_ISDS;
11268 goto leave;
11270 /* More messages */
11271 if (result->nodesetval->nodeNr > 1) {
11272 isds_printf_message(context,
11273 _("XML document has more sisds:dmReturnedMessage elements"));
11274 err = IE_ISDS;
11275 goto leave;
11277 /* One message */
11278 xpath_ctx->node = result->nodesetval->nodeTab[0];
11280 /* Extract the message */
11281 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
11282 if (err) goto leave;
11284 /* Append raw buffer into message */
11285 (*message)->raw_type = raw_type;
11286 switch (strategy) {
11287 case BUFFER_DONT_STORE:
11288 break;
11289 case BUFFER_COPY:
11290 (*message)->raw = malloc(length);
11291 if (!(*message)->raw) {
11292 err = IE_NOMEM;
11293 goto leave;
11295 memcpy((*message)->raw, buffer, length);
11296 (*message)->raw_length = length;
11297 break;
11298 case BUFFER_MOVE:
11299 (*message)->raw = (void *) buffer;
11300 (*message)->raw_length = length;
11301 break;
11302 default:
11303 err = IE_ENUM;
11304 goto leave;
11308 leave:
11309 if (err) {
11310 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
11311 isds_message_free(message);
11314 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11315 xmlXPathFreeObject(result);
11316 xmlXPathFreeContext(xpath_ctx);
11317 if (!*message || !(*message)->xml) {
11318 xmlFreeDoc(message_doc);
11321 if (!err)
11322 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
11323 return err;
11327 /* Determine type of raw message or delivery info according some heuristics.
11328 * It does not validate the raw blob.
11329 * @context is session context
11330 * @raw_type returns content type of @buffer. Valid only if exit code of this
11331 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11332 * reallocated memory.
11333 * @buffer is message raw representation.
11334 * @length is length of buffer in bytes. */
11335 isds_error isds_guess_raw_type(struct isds_ctx *context,
11336 isds_raw_type *raw_type, const void *buffer, const size_t length) {
11337 isds_error err;
11338 void *xml_stream = NULL;
11339 size_t xml_stream_length = 0;
11340 xmlDocPtr document = NULL;
11341 xmlNodePtr root = NULL;
11343 if (!context) return IE_INVALID_CONTEXT;
11344 zfree(context->long_message);
11345 if (length == 0 || !buffer) return IE_INVAL;
11346 if (!raw_type) return IE_INVAL;
11348 /* Try CMS */
11349 err = _isds_extract_cms_data(context, buffer, length,
11350 &xml_stream, &xml_stream_length);
11351 if (err) {
11352 xml_stream = (void *) buffer;
11353 xml_stream_length = (size_t) length;
11354 err = IE_SUCCESS;
11357 /* Try XML */
11358 document = xmlParseMemory(xml_stream, xml_stream_length);
11359 if (!document) {
11360 isds_printf_message(context,
11361 _("Could not parse data as XML document"));
11362 err = IE_NOTSUP;
11363 goto leave;
11366 /* Get root element */
11367 root = xmlDocGetRootElement(document);
11368 if (!root) {
11369 isds_printf_message(context,
11370 _("XML document is missing root element"));
11371 err = IE_XML;
11372 goto leave;
11375 if (!root->ns || !root->ns->href) {
11376 isds_printf_message(context,
11377 _("Root element does not belong to any name space"));
11378 err = IE_NOTSUP;
11379 goto leave;
11382 /* Test name space */
11383 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
11384 if (xml_stream == buffer)
11385 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
11386 else
11387 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
11388 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
11389 if (xml_stream == buffer)
11390 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
11391 else
11392 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
11393 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
11394 if (xml_stream == buffer)
11395 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
11396 else
11397 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
11398 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
11399 if (xml_stream != buffer) {
11400 isds_printf_message(context,
11401 _("Document in ISDS name space is encapsulated into CMS" ));
11402 err = IE_NOTSUP;
11403 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
11404 *raw_type = RAWTYPE_INCOMING_MESSAGE;
11405 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
11406 *raw_type = RAWTYPE_DELIVERYINFO;
11407 else {
11408 isds_printf_message(context,
11409 _("Unknown root element in ISDS name space"));
11410 err = IE_NOTSUP;
11412 } else {
11413 isds_printf_message(context,
11414 _("Unknown name space"));
11415 err = IE_NOTSUP;
11418 leave:
11419 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
11420 xmlFreeDoc(document);
11421 return err;
11425 /* Download signed incoming/outgoing message identified by ID.
11426 * @context is session context
11427 * @output is true for outgoing message, false for incoming message
11428 * @message_id is message identifier (you can get them from
11429 * isds_get_list_of_{sent,received}_messages())
11430 * @message is automatically reallocated message retrieved from ISDS. The raw
11431 * member will be filled with PKCS#7 structure in DER format. */
11432 static isds_error isds_get_signed_message(struct isds_ctx *context,
11433 const _Bool outgoing, const char *message_id,
11434 struct isds_message **message) {
11436 isds_error err = IE_SUCCESS;
11437 #if HAVE_LIBCURL
11438 xmlDocPtr response = NULL;
11439 xmlChar *code = NULL, *status_message = NULL;
11440 xmlXPathContextPtr xpath_ctx = NULL;
11441 xmlXPathObjectPtr result = NULL;
11442 char *encoded_structure = NULL;
11443 void *raw = NULL;
11444 size_t raw_length = 0;
11445 #endif
11447 if (!context) return IE_INVALID_CONTEXT;
11448 zfree(context->long_message);
11449 if (!message) return IE_INVAL;
11450 isds_message_free(message);
11452 #if HAVE_LIBCURL
11453 /* Do request and check for success */
11454 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
11455 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11456 BAD_CAST "SignedMessageDownload",
11457 message_id, &response, NULL, NULL, &code, &status_message);
11458 if (err) goto leave;
11460 /* Find signed message, extract it into raw and maybe free
11461 * response */
11462 err = find_extract_signed_data_free_response(context,
11463 (xmlChar *)message_id, &response,
11464 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
11465 BAD_CAST "SignedMessageDownload",
11466 &raw, &raw_length);
11467 if (err) goto leave;
11469 /* Parse message */
11470 err = isds_load_message(context,
11471 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
11472 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
11473 raw, raw_length, message, BUFFER_MOVE);
11474 if (err) goto leave;
11476 raw = NULL;
11478 leave:
11479 if (err) {
11480 isds_message_free(message);
11483 free(encoded_structure);
11484 xmlXPathFreeObject(result);
11485 xmlXPathFreeContext(xpath_ctx);
11486 free(raw);
11488 free(code);
11489 free(status_message);
11490 xmlFreeDoc(response);
11492 if (!err)
11493 isds_log(ILF_ISDS, ILL_DEBUG,
11494 (outgoing) ?
11495 _("SignedSentMessageDownload request processed by server "
11496 "successfully.\n") :
11497 _("SignedMessageDownload request processed by server "
11498 "successfully.\n")
11500 #else /* not HAVE_LIBCURL */
11501 err = IE_NOTSUP;
11502 #endif
11503 return err;
11507 /* Download signed incoming message identified by ID.
11508 * @context is session context
11509 * @message_id is message identifier (you can get them from
11510 * isds_get_list_of_received_messages())
11511 * @message is automatically reallocated message retrieved from ISDS. The raw
11512 * member will be filled with PKCS#7 structure in DER format. */
11513 isds_error isds_get_signed_received_message(struct isds_ctx *context,
11514 const char *message_id, struct isds_message **message) {
11515 return isds_get_signed_message(context, 0, message_id, message);
11519 /* Download signed outgoing message identified by ID.
11520 * @context is session context
11521 * @message_id is message identifier (you can get them from
11522 * isds_get_list_of_sent_messages())
11523 * @message is automatically reallocated message retrieved from ISDS. The raw
11524 * member will be filled with PKCS#7 structure in DER format. */
11525 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
11526 const char *message_id, struct isds_message **message) {
11527 return isds_get_signed_message(context, 1, message_id, message);
11531 /* Get type and name of user who sent a message identified by ID.
11532 * @context is session context
11533 * @message_id is message identifier
11534 * @sender_type is pointer to automatically allocated type of sender detected
11535 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11536 * library or to the server, NULL will be returned. Pass NULL if you don't
11537 * care about it.
11538 * @raw_sender_type is automatically reallocated UTF-8 string describing
11539 * sender type or NULL if not known to server. Pass NULL if you don't care.
11540 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11541 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11542 isds_error isds_get_message_sender(struct isds_ctx *context,
11543 const char *message_id, isds_sender_type **sender_type,
11544 char **raw_sender_type, char **sender_name) {
11545 isds_error err = IE_SUCCESS;
11546 #if HAVE_LIBCURL
11547 xmlDocPtr response = NULL;
11548 xmlChar *code = NULL, *status_message = NULL;
11549 xmlXPathContextPtr xpath_ctx = NULL;
11550 xmlXPathObjectPtr result = NULL;
11551 char *type_string = NULL;
11552 #endif
11554 if (!context) return IE_INVALID_CONTEXT;
11555 zfree(context->long_message);
11556 if (sender_type) zfree(*sender_type);
11557 if (raw_sender_type) zfree(*raw_sender_type);
11558 if (sender_name) zfree(*sender_name);
11559 if (!message_id) return IE_INVAL;
11561 #if HAVE_LIBCURL
11562 /* Do request and check for success */
11563 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11564 BAD_CAST "GetMessageAuthor",
11565 message_id, &response, NULL, NULL, &code, &status_message);
11566 if (err) goto leave;
11568 /* Extract data */
11569 xpath_ctx = xmlXPathNewContext(response);
11570 if (!xpath_ctx) {
11571 err = IE_ERROR;
11572 goto leave;
11574 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11575 err = IE_ERROR;
11576 goto leave;
11578 result = xmlXPathEvalExpression(
11579 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
11580 if (!result) {
11581 err = IE_ERROR;
11582 goto leave;
11584 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11585 isds_log_message(context,
11586 _("Missing GetMessageAuthorResponse element"));
11587 err = IE_ISDS;
11588 goto leave;
11590 if (result->nodesetval->nodeNr > 1) {
11591 isds_log_message(context,
11592 _("Multiple GetMessageAuthorResponse element"));
11593 err = IE_ISDS;
11594 goto leave;
11596 xpath_ctx->node = result->nodesetval->nodeTab[0];
11597 xmlXPathFreeObject(result); result = NULL;
11599 /* Fill output arguments in */
11600 EXTRACT_STRING("isds:userType", type_string);
11601 if (NULL != type_string) {
11602 if (NULL != sender_type) {
11603 *sender_type = calloc(1, sizeof(**sender_type));
11604 if (NULL == *sender_type) {
11605 err = IE_NOMEM;
11606 goto leave;
11609 err = string2isds_sender_type((xmlChar *)type_string,
11610 *sender_type);
11611 if (err) {
11612 zfree(*sender_type);
11613 if (err == IE_ENUM) {
11614 err = IE_SUCCESS;
11615 char *type_string_locale = _isds_utf82locale(type_string);
11616 isds_log(ILF_ISDS, ILL_WARNING,
11617 _("Unknown isds:userType value: %s"),
11618 type_string_locale);
11619 free(type_string_locale);
11624 if (NULL == raw_sender_type)
11625 zfree(type_string);
11626 if (NULL != sender_name)
11627 EXTRACT_STRING("isds:authorName", *sender_name);
11629 leave:
11630 if (err) {
11631 if (NULL != sender_type) zfree(*sender_type);
11632 zfree(type_string);
11633 if (NULL != sender_name) zfree(*sender_name);
11635 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11637 xmlXPathFreeObject(result);
11638 xmlXPathFreeContext(xpath_ctx);
11640 free(code);
11641 free(status_message);
11642 xmlFreeDoc(response);
11644 if (!err)
11645 isds_log(ILF_ISDS, ILL_DEBUG,
11646 _("GetMessageAuthor request processed by server "
11647 "successfully.\n"));
11648 #else /* not HAVE_LIBCURL */
11649 err = IE_NOTSUP;
11650 #endif
11651 return err;
11655 /* Retrieve hash of message identified by ID stored in ISDS.
11656 * @context is session context
11657 * @message_id is message identifier
11658 * @hash is automatically reallocated message hash downloaded from ISDS.
11659 * Message must exist in system and must not be deleted. */
11660 isds_error isds_download_message_hash(struct isds_ctx *context,
11661 const char *message_id, struct isds_hash **hash) {
11663 isds_error err = IE_SUCCESS;
11664 #if HAVE_LIBCURL
11665 xmlDocPtr response = NULL;
11666 xmlChar *code = NULL, *status_message = NULL;
11667 xmlXPathContextPtr xpath_ctx = NULL;
11668 xmlXPathObjectPtr result = NULL;
11669 #endif
11671 if (!context) return IE_INVALID_CONTEXT;
11672 zfree(context->long_message);
11674 isds_hash_free(hash);
11676 #if HAVE_LIBCURL
11677 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11678 BAD_CAST "VerifyMessage", message_id,
11679 &response, NULL, NULL, &code, &status_message);
11680 if (err) goto leave;
11683 /* Extract data */
11684 xpath_ctx = xmlXPathNewContext(response);
11685 if (!xpath_ctx) {
11686 err = IE_ERROR;
11687 goto leave;
11689 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11690 err = IE_ERROR;
11691 goto leave;
11693 result = xmlXPathEvalExpression(
11694 BAD_CAST "/isds:VerifyMessageResponse",
11695 xpath_ctx);
11696 if (!result) {
11697 err = IE_ERROR;
11698 goto leave;
11700 /* Empty response */
11701 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11702 char *message_id_locale = _isds_utf82locale((char*) message_id);
11703 isds_printf_message(context,
11704 _("Server did not return any response for ID `%s' "
11705 "on VerifyMessage request"), message_id_locale);
11706 free(message_id_locale);
11707 err = IE_ISDS;
11708 goto leave;
11710 /* More responses */
11711 if (result->nodesetval->nodeNr > 1) {
11712 char *message_id_locale = _isds_utf82locale((char*) message_id);
11713 isds_printf_message(context,
11714 _("Server did return more responses for ID `%s' "
11715 "on VerifyMessage request"), message_id_locale);
11716 free(message_id_locale);
11717 err = IE_ISDS;
11718 goto leave;
11720 /* One response */
11721 xpath_ctx->node = result->nodesetval->nodeTab[0];
11723 /* Extract the hash */
11724 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11726 leave:
11727 if (err) {
11728 isds_hash_free(hash);
11731 xmlXPathFreeObject(result);
11732 xmlXPathFreeContext(xpath_ctx);
11734 free(code);
11735 free(status_message);
11736 xmlFreeDoc(response);
11738 if (!err)
11739 isds_log(ILF_ISDS, ILL_DEBUG,
11740 _("VerifyMessage request processed by server "
11741 "successfully.\n")
11743 #else /* not HAVE_LIBCURL */
11744 err = IE_NOTSUP;
11745 #endif
11746 return err;
11750 /* Erase message specified by @message_id from long term storage. Other
11751 * message cannot be erased on user request.
11752 * @context is session context
11753 * @message_id is message identifier.
11754 * @incoming is true for incoming message, false for outgoing message.
11755 * @return
11756 * IE_SUCCESS if message has ben removed
11757 * IE_INVAL if message does not exist in long term storage or message
11758 * belongs to different box
11759 * TODO: IE_NOEPRM if user has no permission to erase a message */
11760 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11761 const char *message_id, _Bool incoming) {
11762 isds_error err = IE_SUCCESS;
11763 #if HAVE_LIBCURL
11764 xmlNodePtr request = NULL, node;
11765 xmlNsPtr isds_ns = NULL;
11766 xmlDocPtr response = NULL;
11767 xmlChar *code = NULL, *status_message = NULL;
11768 #endif
11770 if (!context) return IE_INVALID_CONTEXT;
11771 zfree(context->long_message);
11772 if (NULL == message_id) return IE_INVAL;
11774 #if HAVE_LIBCURL
11775 /* Check if connection is established
11776 * TODO: This check should be done downstairs. */
11777 if (!context->curl) return IE_CONNECTION_CLOSED;
11779 /* Build request */
11780 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11781 if (!request) {
11782 isds_log_message(context,
11783 _("Could build EraseMessage request"));
11784 return IE_ERROR;
11786 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11787 if(!isds_ns) {
11788 isds_log_message(context, _("Could not create ISDS name space"));
11789 xmlFreeNode(request);
11790 return IE_ERROR;
11792 xmlSetNs(request, isds_ns);
11794 err = validate_message_id_length(context, (xmlChar *) message_id);
11795 if (err) goto leave;
11796 INSERT_STRING(request, "dmID", message_id);
11798 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11801 /* Send request */
11802 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11803 "message ID %s to ISDS\n"), message_id);
11804 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11805 xmlFreeNode(request); request = NULL;
11807 if (err) {
11808 isds_log(ILF_ISDS, ILL_DEBUG,
11809 _("Processing ISDS response on EraseMessage request "
11810 "failed\n"));
11811 goto leave;
11814 /* Check for response status */
11815 err = isds_response_status(context, SERVICE_DM_INFO, response,
11816 &code, &status_message, NULL);
11817 if (err) {
11818 isds_log(ILF_ISDS, ILL_DEBUG,
11819 _("ISDS response on EraseMessage request is missing "
11820 "status\n"));
11821 goto leave;
11824 /* Check server status code */
11825 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11826 isds_log_message(context, _("Message to erase belongs to other box"));
11827 err = IE_INVAL;
11828 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11829 isds_log_message(context, _("Message to erase is not saved in "
11830 "long term storage or the direction does not match"));
11831 err = IE_INVAL;
11832 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11833 char *code_locale = _isds_utf82locale((char*) code);
11834 char *message_locale = _isds_utf82locale((char*) status_message);
11835 isds_log(ILF_ISDS, ILL_DEBUG,
11836 _("Server refused EraseMessage request "
11837 "(code=%s, message=%s)\n"),
11838 code_locale, message_locale);
11839 isds_log_message(context, message_locale);
11840 free(code_locale);
11841 free(message_locale);
11842 err = IE_ISDS;
11843 goto leave;
11846 leave:
11847 free(code);
11848 free(status_message);
11849 xmlFreeDoc(response);
11850 xmlFreeNode(request);
11852 if (!err)
11853 isds_log(ILF_ISDS, ILL_DEBUG,
11854 _("EraseMessage request processed by server "
11855 "successfully.\n")
11857 #else /* not HAVE_LIBCURL */
11858 err = IE_NOTSUP;
11859 #endif
11860 return err;
11864 /* Mark message as read. This is a transactional commit function to acknowledge
11865 * to ISDS the message has been downloaded and processed by client properly.
11866 * @context is session context
11867 * @message_id is message identifier. */
11868 isds_error isds_mark_message_read(struct isds_ctx *context,
11869 const char *message_id) {
11871 isds_error err = IE_SUCCESS;
11872 #if HAVE_LIBCURL
11873 xmlDocPtr response = NULL;
11874 xmlChar *code = NULL, *status_message = NULL;
11875 #endif
11877 if (!context) return IE_INVALID_CONTEXT;
11878 zfree(context->long_message);
11880 #if HAVE_LIBCURL
11881 /* Do request and check for success */
11882 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11883 BAD_CAST "MarkMessageAsDownloaded", message_id,
11884 &response, NULL, NULL, &code, &status_message);
11886 free(code);
11887 free(status_message);
11888 xmlFreeDoc(response);
11890 if (!err)
11891 isds_log(ILF_ISDS, ILL_DEBUG,
11892 _("MarkMessageAsDownloaded request processed by server "
11893 "successfully.\n")
11895 #else /* not HAVE_LIBCURL */
11896 err = IE_NOTSUP;
11897 #endif
11898 return err;
11902 /* Mark message as received by recipient. This is applicable only to
11903 * commercial message. Use envelope->dmType message member to distinguish
11904 * commercial message from government message. Government message is
11905 * received automatically (by law), commercial message on recipient request.
11906 * @context is session context
11907 * @message_id is message identifier. */
11908 isds_error isds_mark_message_received(struct isds_ctx *context,
11909 const char *message_id) {
11911 isds_error err = IE_SUCCESS;
11912 #if HAVE_LIBCURL
11913 xmlDocPtr response = NULL;
11914 xmlChar *code = NULL, *status_message = NULL;
11915 #endif
11917 if (!context) return IE_INVALID_CONTEXT;
11918 zfree(context->long_message);
11920 #if HAVE_LIBCURL
11921 /* Do request and check for success */
11922 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11923 BAD_CAST "ConfirmDelivery", message_id,
11924 &response, NULL, NULL, &code, &status_message);
11926 free(code);
11927 free(status_message);
11928 xmlFreeDoc(response);
11930 if (!err)
11931 isds_log(ILF_ISDS, ILL_DEBUG,
11932 _("ConfirmDelivery request processed by server "
11933 "successfully.\n")
11935 #else /* not HAVE_LIBCURL */
11936 err = IE_NOTSUP;
11937 #endif
11938 return err;
11942 /* Send document for authorized conversion into Czech POINT system.
11943 * This is public anonymous service, no log-in necessary. Special context is
11944 * used to reuse keep-a-live HTTPS connection.
11945 * @context is Czech POINT session context. DO NOT use context connected to
11946 * ISDS server. Use new context or context used by this function previously.
11947 * @document is document to convert. Only data, data_length, dmFileDescr and
11948 * is_xml members are significant. Be ware that not all document formats can be
11949 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11950 * @id is reallocated identifier assigned by Czech POINT system to
11951 * your document on submit. Use is to tell it to Czech POINT officer.
11952 * @date is reallocated document submit date (submitted documents
11953 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11954 * value. */
11955 isds_error czp_convert_document(struct isds_ctx *context,
11956 const struct isds_document *document,
11957 char **id, struct tm **date) {
11958 isds_error err = IE_SUCCESS;
11959 #if HAVE_LIBCURL
11960 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11961 xmlNodePtr request = NULL, node;
11962 xmlDocPtr response = NULL;
11964 xmlXPathContextPtr xpath_ctx = NULL;
11965 xmlXPathObjectPtr result = NULL;
11966 long int status = -1;
11967 long int *status_ptr = &status;
11968 char *string = NULL;
11969 #endif
11972 if (!context) return IE_INVALID_CONTEXT;
11973 zfree(context->long_message);
11974 if (!document || !id || !date) return IE_INVAL;
11976 if (document->is_xml) {
11977 isds_log_message(context,
11978 _("XML documents cannot be submitted to conversion"));
11979 return IE_NOTSUP;
11982 /* Free output arguments */
11983 zfree(*id);
11984 zfree(*date);
11986 #if HAVE_LIBCURL
11987 /* Store configuration */
11988 context->type = CTX_TYPE_CZP;
11989 free(context->url);
11990 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11991 if (!(context->url))
11992 return IE_NOMEM;
11994 /* Prepare CURL handle if not yet connected */
11995 if (!context->curl) {
11996 context->curl = curl_easy_init();
11997 if (!(context->curl))
11998 return IE_ERROR;
12001 /* Build conversion request */
12002 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
12003 if (!request) {
12004 isds_log_message(context,
12005 _("Could not build Czech POINT conversion request"));
12006 return IE_ERROR;
12008 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
12009 if(!deposit_ns) {
12010 isds_log_message(context,
12011 _("Could not create Czech POINT deposit name space"));
12012 xmlFreeNode(request);
12013 return IE_ERROR;
12015 xmlSetNs(request, deposit_ns);
12017 /* Insert children. They are in empty namespace! */
12018 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
12019 if(!empty_ns) {
12020 isds_log_message(context, _("Could not create empty name space"));
12021 err = IE_ERROR;
12022 goto leave;
12024 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
12025 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
12026 document->dmFileDescr);
12028 /* Document encoded in Base64 */
12029 err = insert_base64_encoded_string(context, request, empty_ns, "document",
12030 document->data, document->data_length);
12031 if (err) goto leave;
12033 isds_log(ILF_ISDS, ILL_DEBUG,
12034 _("Submitting document for conversion into Czech POINT deposit"));
12036 /* Send conversion request */
12037 err = _czp_czpdeposit(context, request, &response);
12038 xmlFreeNode(request); request = NULL;
12040 if (err) {
12041 czp_do_close_connection(context);
12042 goto leave;
12046 /* Extract response */
12047 xpath_ctx = xmlXPathNewContext(response);
12048 if (!xpath_ctx) {
12049 err = IE_ERROR;
12050 goto leave;
12052 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12053 err = IE_ERROR;
12054 goto leave;
12056 result = xmlXPathEvalExpression(
12057 BAD_CAST "/deposit:saveDocumentResponse/return",
12058 xpath_ctx);
12059 if (!result) {
12060 err = IE_ERROR;
12061 goto leave;
12063 /* Empty response */
12064 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
12065 isds_printf_message(context,
12066 _("Missing `return' element in Czech POINT deposit response"));
12067 err = IE_ISDS;
12068 goto leave;
12070 /* More responses */
12071 if (result->nodesetval->nodeNr > 1) {
12072 isds_printf_message(context,
12073 _("Multiple `return' element in Czech POINT deposit response"));
12074 err = IE_ISDS;
12075 goto leave;
12077 /* One response */
12078 xpath_ctx->node = result->nodesetval->nodeTab[0];
12080 /* Get status */
12081 EXTRACT_LONGINT("status", status_ptr, 1);
12082 if (status) {
12083 EXTRACT_STRING("statusMsg", string);
12084 char *string_locale = _isds_utf82locale(string);
12085 isds_printf_message(context,
12086 _("Czech POINT deposit refused document for conversion "
12087 "(code=%ld, message=%s)"),
12088 status, string_locale);
12089 free(string_locale);
12090 err = IE_ISDS;
12091 goto leave;
12094 /* Get document ID */
12095 EXTRACT_STRING("documentID", *id);
12097 /* Get submit date */
12098 EXTRACT_STRING("dateInserted", string);
12099 if (string) {
12100 *date = calloc(1, sizeof(**date));
12101 if (!*date) {
12102 err = IE_NOMEM;
12103 goto leave;
12105 err = _isds_datestring2tm((xmlChar *)string, *date);
12106 if (err) {
12107 if (err == IE_NOTSUP) {
12108 err = IE_ISDS;
12109 char *string_locale = _isds_utf82locale(string);
12110 isds_printf_message(context,
12111 _("Invalid dateInserted value: %s"), string_locale);
12112 free(string_locale);
12114 goto leave;
12118 leave:
12119 free(string);
12120 xmlXPathFreeObject(result);
12121 xmlXPathFreeContext(xpath_ctx);
12123 xmlFreeDoc(response);
12124 xmlFreeNode(request);
12126 if (!err) {
12127 char *id_locale = _isds_utf82locale((char *) *id);
12128 isds_log(ILF_ISDS, ILL_DEBUG,
12129 _("Document %s has been submitted for conversion "
12130 "to server successfully\n"), id_locale);
12131 free(id_locale);
12133 #else /* not HAVE_LIBCURL */
12134 err = IE_NOTSUP;
12135 #endif
12136 return err;
12140 /* Close possibly opened connection to Czech POINT document deposit.
12141 * @context is Czech POINT session context. */
12142 isds_error czp_close_connection(struct isds_ctx *context) {
12143 if (!context) return IE_INVALID_CONTEXT;
12144 zfree(context->long_message);
12145 #if HAVE_LIBCURL
12146 return czp_do_close_connection(context);
12147 #else
12148 return IE_NOTSUP;
12149 #endif
12153 /* Send request for new box creation in testing ISDS instance.
12154 * It's not possible to request for a production box currently, as it
12155 * communicates via e-mail.
12156 * XXX: This function does not work either. Server complains about invalid
12157 * e-mail address.
12158 * XXX: Remove context->type hacks in isds.c and validator.c when removing
12159 * this function
12160 * @context is special session context for box creation request. DO NOT use
12161 * standard context as it could reveal your password. Use fresh new context or
12162 * context previously used by this function.
12163 * @box is box description to create including single primary user (in case of
12164 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
12165 * ignored. It outputs box ID assigned by ISDS in dbID element.
12166 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
12167 * box, or contact address of PFO box owner). The email member is mandatory as
12168 * it will be used to deliver credentials.
12169 * @former_names is former name of box owner. Pass NULL if you don't care.
12170 * @approval is optional external approval of box manipulation
12171 * @refnumber is reallocated serial number of request assigned by ISDS. Use
12172 * NULL, if you don't care.*/
12173 isds_error isds_request_new_testing_box(struct isds_ctx *context,
12174 struct isds_DbOwnerInfo *box, const struct isds_list *users,
12175 const char *former_names, const struct isds_approval *approval,
12176 char **refnumber) {
12177 isds_error err = IE_SUCCESS;
12178 #if HAVE_LIBCURL
12179 xmlNodePtr request = NULL;
12180 xmlDocPtr response = NULL;
12181 xmlXPathContextPtr xpath_ctx = NULL;
12182 xmlXPathObjectPtr result = NULL;
12183 #endif
12186 if (!context) return IE_INVALID_CONTEXT;
12187 zfree(context->long_message);
12188 if (!box) return IE_INVAL;
12190 #if HAVE_LIBCURL
12191 if (!box->email || box->email[0] == '\0') {
12192 isds_log_message(context, _("E-mail field is mandatory"));
12193 return IE_INVAL;
12196 /* Scratch box ID */
12197 zfree(box->dbID);
12199 /* Store configuration */
12200 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
12201 free(context->url);
12202 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
12203 if (!(context->url))
12204 return IE_NOMEM;
12206 /* Prepare CURL handle if not yet connected */
12207 if (!context->curl) {
12208 context->curl = curl_easy_init();
12209 if (!(context->curl))
12210 return IE_ERROR;
12213 /* Build CreateDataBox request */
12214 err = build_CreateDBInput_request(context,
12215 &request, BAD_CAST "CreateDataBox",
12216 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
12217 if (err) goto leave;
12219 /* Send it to server and process response */
12220 err = send_destroy_request_check_response(context,
12221 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
12222 &response, (xmlChar **) refnumber, NULL);
12223 if (err) goto leave;
12225 /* Extract box ID */
12226 xpath_ctx = xmlXPathNewContext(response);
12227 if (!xpath_ctx) {
12228 err = IE_ERROR;
12229 goto leave;
12231 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12232 err = IE_ERROR;
12233 goto leave;
12235 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
12237 leave:
12238 xmlXPathFreeObject(result);
12239 xmlXPathFreeContext(xpath_ctx);
12240 xmlFreeDoc(response);
12241 xmlFreeNode(request);
12243 if (!err) {
12244 isds_log(ILF_ISDS, ILL_DEBUG,
12245 _("CreateDataBox request processed by server successfully.\n"));
12247 #else /* not HAVE_LIBCURL */
12248 err = IE_NOTSUP;
12249 #endif
12251 return err;
12255 /* Submit CMS signed message to ISDS to verify its originality. This is
12256 * stronger form of isds_verify_message_hash() because ISDS does more checks
12257 * than simple one (potentialy old weak) hash comparison.
12258 * @context is session context
12259 * @message is memory with raw CMS signed message bit stream
12260 * @length is @message size in bytes
12261 * @return
12262 * IE_SUCCESS if message originates in ISDS
12263 * IE_NOTEQUAL if message is unknown to ISDS
12264 * other code for other errors */
12265 isds_error isds_authenticate_message(struct isds_ctx *context,
12266 const void *message, size_t length) {
12267 isds_error err = IE_SUCCESS;
12268 #if HAVE_LIBCURL
12269 xmlNsPtr isds_ns = NULL;
12270 xmlNodePtr request = NULL;
12271 xmlDocPtr response = NULL;
12272 xmlXPathContextPtr xpath_ctx = NULL;
12273 xmlXPathObjectPtr result = NULL;
12274 _Bool *authentic = NULL;
12275 #endif
12277 if (!context) return IE_INVALID_CONTEXT;
12278 zfree(context->long_message);
12279 if (!message || length == 0) return IE_INVAL;
12281 #if HAVE_LIBCURL
12282 /* Check if connection is established
12283 * TODO: This check should be done downstairs. */
12284 if (!context->curl) return IE_CONNECTION_CLOSED;
12287 /* Build AuthenticateMessage request */
12288 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
12289 if (!request) {
12290 isds_log_message(context,
12291 _("Could not build AuthenticateMessage request"));
12292 return IE_ERROR;
12294 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12295 if(!isds_ns) {
12296 isds_log_message(context, _("Could not create ISDS name space"));
12297 xmlFreeNode(request);
12298 return IE_ERROR;
12300 xmlSetNs(request, isds_ns);
12302 /* Insert Base64 encoded message */
12303 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
12304 message, length);
12305 if (err) goto leave;
12307 /* Send request to server and process response */
12308 err = send_destroy_request_check_response(context,
12309 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
12310 &response, NULL, NULL);
12311 if (err) goto leave;
12314 /* ISDS has decided */
12315 xpath_ctx = xmlXPathNewContext(response);
12316 if (!xpath_ctx) {
12317 err = IE_ERROR;
12318 goto leave;
12320 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12321 err = IE_ERROR;
12322 goto leave;
12325 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
12327 if (!authentic) {
12328 isds_log_message(context,
12329 _("Server did not return any response on "
12330 "AuthenticateMessage request"));
12331 err = IE_ISDS;
12332 goto leave;
12334 if (*authentic) {
12335 isds_log(ILF_ISDS, ILL_DEBUG,
12336 _("ISDS authenticated the message successfully\n"));
12337 } else {
12338 isds_log_message(context, _("ISDS does not know the message"));
12339 err = IE_NOTEQUAL;
12343 leave:
12344 free(authentic);
12345 xmlXPathFreeObject(result);
12346 xmlXPathFreeContext(xpath_ctx);
12348 xmlFreeDoc(response);
12349 xmlFreeNode(request);
12350 #else /* not HAVE_LIBCURL */
12351 err = IE_NOTSUP;
12352 #endif
12354 return err;
12358 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12359 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12360 * be re-signed.
12361 * @context is session context
12362 * @input_data is memory with raw CMS signed message or delivery info bit
12363 * stream to re-sign
12364 * @input_length is @input_data size in bytes
12365 * @output_data is pointer to auto-allocated memory where to store re-signed
12366 * input data blob. Caller must free it.
12367 * @output_data is pointer where to store @output_data size in bytes
12368 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12369 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12370 * @return
12371 * IE_SUCCESS if CMS blob has been re-signed successfully
12372 * other code for other errors */
12373 isds_error isds_resign_message(struct isds_ctx *context,
12374 const void *input_data, size_t input_length,
12375 void **output_data, size_t *output_length, struct tm **valid_to) {
12376 isds_error err = IE_SUCCESS;
12377 #if HAVE_LIBCURL
12378 xmlNsPtr isds_ns = NULL;
12379 xmlNodePtr request = NULL;
12380 xmlDocPtr response = NULL;
12381 xmlXPathContextPtr xpath_ctx = NULL;
12382 xmlXPathObjectPtr result = NULL;
12383 char *string = NULL;
12384 const xmlChar *codes[] = {
12385 BAD_CAST "2200",
12386 BAD_CAST "2201",
12387 BAD_CAST "2204",
12388 BAD_CAST "2207",
12389 NULL
12391 const char *meanings[] = {
12392 "Message is bad",
12393 "Message is not original",
12394 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12395 "Time stamp could not been generated in time"
12397 const isds_error errors[] = {
12398 IE_INVAL,
12399 IE_NOTUNIQ,
12400 IE_INVAL,
12401 IE_ISDS,
12403 struct code_map_isds_error map = {
12404 .codes = codes,
12405 .meanings = meanings,
12406 .errors = errors
12408 #endif
12410 if (NULL != output_data) *output_data = NULL;
12411 if (NULL != output_length) *output_length = 0;
12412 if (NULL != valid_to) *valid_to = NULL;
12414 if (NULL == context) return IE_INVALID_CONTEXT;
12415 zfree(context->long_message);
12416 if (NULL == input_data || 0 == input_length) {
12417 isds_log_message(context, _("Empty CMS blob on input"));
12418 return IE_INVAL;
12420 if (NULL == output_data || NULL == output_length) {
12421 isds_log_message(context,
12422 _("NULL pointer provided for output CMS blob"));
12423 return IE_INVAL;
12426 #if HAVE_LIBCURL
12427 /* Check if connection is established
12428 * TODO: This check should be done downstairs. */
12429 if (!context->curl) return IE_CONNECTION_CLOSED;
12432 /* Build Re-signISDSDocument request */
12433 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
12434 if (!request) {
12435 isds_log_message(context,
12436 _("Could not build Re-signISDSDocument request"));
12437 return IE_ERROR;
12439 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
12440 if(!isds_ns) {
12441 isds_log_message(context, _("Could not create ISDS name space"));
12442 xmlFreeNode(request);
12443 return IE_ERROR;
12445 xmlSetNs(request, isds_ns);
12447 /* Insert Base64 encoded CMS blob */
12448 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
12449 input_data, input_length);
12450 if (err) goto leave;
12452 /* Send request to server and process response */
12453 err = send_destroy_request_check_response(context,
12454 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
12455 &response, NULL, &map);
12456 if (err) goto leave;
12459 /* Extract re-signed data */
12460 xpath_ctx = xmlXPathNewContext(response);
12461 if (!xpath_ctx) {
12462 err = IE_ERROR;
12463 goto leave;
12465 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
12466 err = IE_ERROR;
12467 goto leave;
12469 result = xmlXPathEvalExpression(
12470 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
12471 if (!result) {
12472 err = IE_ERROR;
12473 goto leave;
12475 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
12476 isds_log_message(context,
12477 _("Missing Re-signISDSDocumentResponse element"));
12478 err = IE_ISDS;
12479 goto leave;
12481 if (result->nodesetval->nodeNr > 1) {
12482 isds_log_message(context,
12483 _("Multiple Re-signISDSDocumentResponse element"));
12484 err = IE_ISDS;
12485 goto leave;
12487 xpath_ctx->node = result->nodesetval->nodeTab[0];
12488 xmlXPathFreeObject(result); result = NULL;
12490 EXTRACT_STRING("isds:dmResultDoc", string);
12491 /* Decode non-empty data */
12492 if (NULL != string && string[0] != '\0') {
12493 *output_length = _isds_b64decode(string, output_data);
12494 if (*output_length == (size_t) -1) {
12495 isds_log_message(context,
12496 _("Error while Base64-decoding re-signed data"));
12497 err = IE_ERROR;
12498 goto leave;
12500 } else {
12501 isds_log_message(context, _("Server did not send re-signed data"));
12502 err = IE_ISDS;
12503 goto leave;
12505 zfree(string);
12507 if (NULL != valid_to) {
12508 /* Get time stamp expiration date */
12509 EXTRACT_STRING("isds:dmValidTo", string);
12510 if (NULL != string) {
12511 *valid_to = calloc(1, sizeof(**valid_to));
12512 if (!*valid_to) {
12513 err = IE_NOMEM;
12514 goto leave;
12516 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
12517 if (err) {
12518 if (err == IE_NOTSUP) {
12519 err = IE_ISDS;
12520 char *string_locale = _isds_utf82locale(string);
12521 isds_printf_message(context,
12522 _("Invalid dmValidTo value: %s"), string_locale);
12523 free(string_locale);
12525 goto leave;
12530 leave:
12531 free(string);
12533 xmlXPathFreeObject(result);
12534 xmlXPathFreeContext(xpath_ctx);
12536 xmlFreeDoc(response);
12537 xmlFreeNode(request);
12538 #else /* not HAVE_LIBCURL */
12539 err = IE_NOTSUP;
12540 #endif
12542 return err;
12545 #undef INSERT_ELEMENT
12546 #undef CHECK_FOR_STRING_LENGTH
12547 #undef INSERT_STRING_ATTRIBUTE
12548 #undef INSERT_ULONGINTNOPTR
12549 #undef INSERT_ULONGINT
12550 #undef INSERT_LONGINT
12551 #undef INSERT_BOOLEAN
12552 #undef INSERT_SCALAR_BOOLEAN
12553 #undef INSERT_STRING
12554 #undef INSERT_STRING_WITH_NS
12555 #undef EXTRACT_STRING_ATTRIBUTE
12556 #undef EXTRACT_ULONGINT
12557 #undef EXTRACT_LONGINT
12558 #undef EXTRACT_BOOLEAN
12559 #undef EXTRACT_STRING
12562 /* Compute hash of message from raw representation and store it into envelope.
12563 * Original hash structure will be destroyed in envelope.
12564 * @context is session context
12565 * @message is message carrying raw XML message blob
12566 * @algorithm is desired hash algorithm to use */
12567 isds_error isds_compute_message_hash(struct isds_ctx *context,
12568 struct isds_message *message, const isds_hash_algorithm algorithm) {
12569 isds_error err = IE_SUCCESS;
12570 const char *nsuri;
12571 void *xml_stream = NULL;
12572 size_t xml_stream_length;
12573 size_t phys_start, phys_end;
12574 char *phys_path = NULL;
12575 struct isds_hash *new_hash = NULL;
12578 if (!context) return IE_INVALID_CONTEXT;
12579 zfree(context->long_message);
12580 if (!message) return IE_INVAL;
12582 if (!message->raw) {
12583 isds_log_message(context,
12584 _("Message does not carry raw representation"));
12585 return IE_INVAL;
12588 switch (message->raw_type) {
12589 case RAWTYPE_INCOMING_MESSAGE:
12590 nsuri = ISDS_NS;
12591 xml_stream = message->raw;
12592 xml_stream_length = message->raw_length;
12593 break;
12595 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
12596 nsuri = SISDS_INCOMING_NS;
12597 xml_stream = message->raw;
12598 xml_stream_length = message->raw_length;
12599 break;
12601 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
12602 nsuri = SISDS_INCOMING_NS;
12603 err = _isds_extract_cms_data(context,
12604 message->raw, message->raw_length,
12605 &xml_stream, &xml_stream_length);
12606 if (err) goto leave;
12607 break;
12609 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
12610 nsuri = SISDS_OUTGOING_NS;
12611 xml_stream = message->raw;
12612 xml_stream_length = message->raw_length;
12613 break;
12615 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12616 nsuri = SISDS_OUTGOING_NS;
12617 err = _isds_extract_cms_data(context,
12618 message->raw, message->raw_length,
12619 &xml_stream, &xml_stream_length);
12620 if (err) goto leave;
12621 break;
12623 default:
12624 isds_log_message(context, _("Bad raw representation type"));
12625 return IE_INVAL;
12626 break;
12630 /* XXX: Hash is computed from original string representing isds:dmDm
12631 * subtree. That means no encoding, white space, xmlns attributes changes.
12632 * In other words, input for hash can be invalid XML stream. */
12633 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12634 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12635 PHYSXML_ELEMENT_SEPARATOR,
12636 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12637 PHYSXML_ELEMENT_SEPARATOR
12638 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12639 err = IE_NOMEM;
12640 goto leave;
12642 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12643 phys_path, &phys_start, &phys_end);
12644 zfree(phys_path);
12645 if (err) {
12646 isds_log_message(context,
12647 _("Substring with isds:dmDM element could not be located "
12648 "in raw message"));
12649 goto leave;
12653 /* Compute hash */
12654 new_hash = calloc(1, sizeof(*new_hash));
12655 if (!new_hash) {
12656 err = IE_NOMEM;
12657 goto leave;
12659 new_hash->algorithm = algorithm;
12660 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12661 new_hash);
12662 if (err) {
12663 isds_log_message(context, _("Could not compute message hash"));
12664 goto leave;
12667 /* Save computed hash */
12668 if (!message->envelope) {
12669 message->envelope = calloc(1, sizeof(*message->envelope));
12670 if (!message->envelope) {
12671 err = IE_NOMEM;
12672 goto leave;
12675 isds_hash_free(&message->envelope->hash);
12676 message->envelope->hash = new_hash;
12678 leave:
12679 if (err) {
12680 isds_hash_free(&new_hash);
12683 free(phys_path);
12684 if (xml_stream != message->raw) free(xml_stream);
12685 return err;
12689 /* Compare two hashes.
12690 * @h1 is first hash
12691 * @h2 is another hash
12692 * @return
12693 * IE_SUCCESS if hashes equal
12694 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12695 * IE_ENUM if not comparable, but both structures defined
12696 * IE_INVAL if some of the structures are undefined (NULL)
12697 * IE_ERROR if internal error occurs */
12698 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12699 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12700 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12701 if (h1->length != h2->length) return IE_ERROR;
12702 if (h1->length > 0 && !h1->value) return IE_ERROR;
12703 if (h2->length > 0 && !h2->value) return IE_ERROR;
12705 for (size_t i = 0; i < h1->length; i++) {
12706 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12707 return IE_NOTEQUAL;
12709 return IE_SUCCESS;
12713 /* Check message has gone through ISDS by comparing message hash stored in
12714 * ISDS and locally computed hash. You must provide message with valid raw
12715 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12716 * This is convenient wrapper for isds_download_message_hash(),
12717 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12718 * @context is session context
12719 * @message is message with valid raw and envelope member; envelope->hash
12720 * member will be changed during function run. Use envelope on heap only.
12721 * @return
12722 * IE_SUCCESS if message originates in ISDS
12723 * IE_NOTEQUAL if message is unknown to ISDS
12724 * other code for other errors */
12725 isds_error isds_verify_message_hash(struct isds_ctx *context,
12726 struct isds_message *message) {
12727 isds_error err = IE_SUCCESS;
12728 struct isds_hash *downloaded_hash = NULL;
12730 if (!context) return IE_INVALID_CONTEXT;
12731 zfree(context->long_message);
12732 if (!message) return IE_INVAL;
12734 if (!message->envelope) {
12735 isds_log_message(context,
12736 _("Given message structure is missing envelope"));
12737 return IE_INVAL;
12739 if (!message->raw) {
12740 isds_log_message(context,
12741 _("Given message structure is missing raw representation"));
12742 return IE_INVAL;
12745 err = isds_download_message_hash(context, message->envelope->dmID,
12746 &downloaded_hash);
12747 if (err) goto leave;
12749 err = isds_compute_message_hash(context, message,
12750 downloaded_hash->algorithm);
12751 if (err) goto leave;
12753 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12755 leave:
12756 isds_hash_free(&downloaded_hash);
12757 return err;
12761 /* Search for document by document ID in list of documents. IDs are compared
12762 * as UTF-8 string.
12763 * @documents is list of isds_documents
12764 * @id is document identifier
12765 * @return first matching document or NULL. */
12766 const struct isds_document *isds_find_document_by_id(
12767 const struct isds_list *documents, const char *id) {
12768 const struct isds_list *item;
12769 const struct isds_document *document;
12771 for (item = documents; item; item = item->next) {
12772 document = (struct isds_document *) item->data;
12773 if (!document) continue;
12775 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12776 return document;
12779 return NULL;
12783 /* Normalize @mime_type to be proper MIME type.
12784 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12785 * guess regular MIME type (e.g. "application/pdf").
12786 * @mime_type is UTF-8 encoded MIME type to fix
12787 * @return original @mime_type if no better interpretation exists, or
12788 * constant static UTF-8 encoded string with proper MIME type. */
12789 const char *isds_normalize_mime_type(const char *mime_type) {
12790 if (!mime_type) return NULL;
12792 for (size_t offset = 0;
12793 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12794 offset += 2) {
12795 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12796 extension_map_mime[offset]))
12797 return (const char *) extension_map_mime[offset + 1];
12800 return mime_type;
12804 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12805 struct isds_message **message);
12806 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12807 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12808 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12809 struct isds_address **address);
12811 int isds_message_free(struct isds_message **message);
12812 int isds_address_free(struct isds_address **address);
12816 /* Makes known all relevant namespaces to given XPath context
12817 * @xpath_ctx is XPath context
12818 * @message_ns selects proper message name space. Unsigned and signed
12819 * messages and delivery info's differ in prefix and URI. */
12820 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12821 const message_ns_type message_ns) {
12822 const xmlChar *message_namespace = NULL;
12824 if (!xpath_ctx) return IE_ERROR;
12826 switch(message_ns) {
12827 case MESSAGE_NS_1:
12828 message_namespace = BAD_CAST ISDS1_NS; break;
12829 case MESSAGE_NS_UNSIGNED:
12830 message_namespace = BAD_CAST ISDS_NS; break;
12831 case MESSAGE_NS_SIGNED_INCOMING:
12832 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12833 case MESSAGE_NS_SIGNED_OUTGOING:
12834 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12835 case MESSAGE_NS_SIGNED_DELIVERY:
12836 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12837 default:
12838 return IE_ENUM;
12841 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12842 return IE_ERROR;
12843 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12844 return IE_ERROR;
12845 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12846 return IE_ERROR;
12847 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12848 return IE_ERROR;
12849 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12850 return IE_ERROR;
12851 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12852 return IE_ERROR;
12853 return IE_SUCCESS;