Implement FindPersonalDataBox service as isds_FindPersonalDataBox()
[libisds.git] / src / isds.c
blob580141c5217d7f6c799b3d517eac438a4c6df84c
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include <inttypes.h> /* For PRIdMAX formatting macro */
10 #include "utils.h"
11 #if HAVE_LIBCURL
12 #include "soap.h"
13 #endif
14 #include "validator.h"
15 #include "crypto.h"
16 #include "physxml.h"
17 #include "system.h"
19 /* Global variables.
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities;
22 isds_log_level log_level;
23 isds_log_callback log_callback;
24 void *log_callback_data;
25 const char *version_gpgme = N_("n/a");
26 const char *version_gcrypt = N_("n/a");
27 const char *version_openssl = N_("n/a");
28 const char *version_expat = N_("n/a");
30 /* Locators */
31 /* Base URL of production ISDS instance */
32 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
33 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
34 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
36 /* Base URL of production ISDS instance */
37 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
38 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
39 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
41 /* Extension to MIME type map */
42 static const xmlChar *extension_map_mime[] = {
43 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
45 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
46 BAD_CAST "doc", BAD_CAST "application/msword",
47 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
48 "wordprocessingml.document",
49 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
50 BAD_CAST "prj", BAD_CAST "application/octet-stream",
51 BAD_CAST "qix", BAD_CAST "application/octet-stream",
52 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
53 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
54 BAD_CAST "shp", BAD_CAST "application/octet-stream",
55 BAD_CAST "shx", BAD_CAST "application/octet-stream",
56 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
57 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
58 BAD_CAST "edi", BAD_CAST "application/edifact",
59 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
60 BAD_CAST "gfs", BAD_CAST "application/xml",
61 BAD_CAST "gml", BAD_CAST "application/xml",
62 BAD_CAST "gif", BAD_CAST "image/gif",
63 BAD_CAST "htm", BAD_CAST "text/html",
64 BAD_CAST "html", BAD_CAST "text/html",
65 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
66 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
67 BAD_CAST "jfif", BAD_CAST "image/jpeg",
68 BAD_CAST "jpg", BAD_CAST "image/jpeg",
69 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
70 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
71 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
72 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
73 BAD_CAST "mpg", BAD_CAST "video/mpeg",
74 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
75 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
76 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
77 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
78 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
79 BAD_CAST "pdf", BAD_CAST "application/pdf",
80 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
81 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
82 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
83 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
84 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
85 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
86 BAD_CAST "png", BAD_CAST "image/png",
87 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
88 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
89 "presentationml.presentation",
90 BAD_CAST "rtf", BAD_CAST "application/rtf",
91 BAD_CAST "tif", BAD_CAST "image/tiff",
92 BAD_CAST "tiff", BAD_CAST "image/tiff",
93 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
94 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
95 BAD_CAST "txt", BAD_CAST "text/plain",
96 BAD_CAST "wav", BAD_CAST "audio/wav",
97 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
98 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
99 "spreadsheetml.sheet",
100 BAD_CAST "xml", BAD_CAST "application/xml",
101 BAD_CAST "xsd", BAD_CAST "application/xml",
102 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
105 /* Structure type to hold conversion table from status code to isds_error and
106 * long message */
107 struct code_map_isds_error {
108 const xmlChar **codes; /* NULL terminated array of status codes */
109 const char **meanings; /* Mapping to non-localized long messages */
110 const isds_error *errors; /* Mapping to isds_error code */
113 /* Deallocate structure isds_pki_credentials and NULL it.
114 * Pass-phrase is discarded.
115 * @pki credentials to to free */
116 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
117 if(!pki || !*pki) return;
119 free((*pki)->engine);
120 free((*pki)->certificate);
121 free((*pki)->key);
123 if ((*pki)->passphrase) {
124 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
125 free((*pki)->passphrase);
128 zfree((*pki));
132 /* Free isds_list with all member data.
133 * @list list to free, on return will be NULL */
134 void isds_list_free(struct isds_list **list) {
135 struct isds_list *item, *next_item;
137 if (!list || !*list) return;
139 for(item = *list; item; item = next_item) {
140 if (item->destructor) (item->destructor)(&(item->data));
141 next_item = item->next;
142 free(item);
145 *list = NULL;
149 /* Deallocate structure isds_hash and NULL it.
150 * @hash hash to to free */
151 void isds_hash_free(struct isds_hash **hash) {
152 if(!hash || !*hash) return;
153 free((*hash)->value);
154 zfree((*hash));
158 /* Deallocate structure isds_PersonName recursively and NULL it */
159 void isds_PersonName_free(struct isds_PersonName **person_name) {
160 if (!person_name || !*person_name) return;
162 free((*person_name)->pnFirstName);
163 free((*person_name)->pnMiddleName);
164 free((*person_name)->pnLastName);
165 free((*person_name)->pnLastNameAtBirth);
167 free(*person_name);
168 *person_name = NULL;
172 /* Deallocate structure isds_BirthInfo recursively and NULL it */
173 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
174 if (!birth_info || !*birth_info) return;
176 free((*birth_info)->biDate);
177 free((*birth_info)->biCity);
178 free((*birth_info)->biCounty);
179 free((*birth_info)->biState);
181 free(*birth_info);
182 *birth_info = NULL;
186 /* Deallocate structure isds_Address recursively and NULL it */
187 void isds_Address_free(struct isds_Address **address) {
188 if (!address || !*address) return;
190 free((*address)->adCode);
191 free((*address)->adCity);
192 free((*address)->adDistrict);
193 free((*address)->adStreet);
194 free((*address)->adNumberInStreet);
195 free((*address)->adNumberInMunicipality);
196 free((*address)->adZipCode);
197 free((*address)->adState);
199 free(*address);
200 *address = NULL;
204 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
205 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
206 if (!db_owner_info || !*db_owner_info) return;
208 free((*db_owner_info)->dbID);
209 free((*db_owner_info)->dbType);
210 free((*db_owner_info)->ic);
211 isds_PersonName_free(&((*db_owner_info)->personName));
212 free((*db_owner_info)->firmName);
213 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
214 isds_Address_free(&((*db_owner_info)->address));
215 free((*db_owner_info)->nationality);
216 free((*db_owner_info)->email);
217 free((*db_owner_info)->telNumber);
218 free((*db_owner_info)->identifier);
219 free((*db_owner_info)->aifoIsds);
220 free((*db_owner_info)->registryCode);
221 free((*db_owner_info)->dbState);
222 free((*db_owner_info)->dbEffectiveOVM);
223 free((*db_owner_info)->dbOpenAddressing);
225 free(*db_owner_info);
226 *db_owner_info = NULL;
229 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
230 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
231 if (!db_user_info || !*db_user_info) return;
233 free((*db_user_info)->userID);
234 free((*db_user_info)->userType);
235 free((*db_user_info)->userPrivils);
236 isds_PersonName_free(&((*db_user_info)->personName));
237 isds_Address_free(&((*db_user_info)->address));
238 free((*db_user_info)->biDate);
239 free((*db_user_info)->ic);
240 free((*db_user_info)->firmName);
241 free((*db_user_info)->caStreet);
242 free((*db_user_info)->caCity);
243 free((*db_user_info)->caZipCode);
244 free((*db_user_info)->caState);
245 free((*db_user_info)->aifo_ticket);
247 zfree(*db_user_info);
251 /* Deallocate struct isds_event recursively and NULL it */
252 void isds_event_free(struct isds_event **event) {
253 if (!event || !*event) return;
255 free((*event)->time);
256 free((*event)->type);
257 free((*event)->description);
258 zfree(*event);
262 /* Deallocate struct isds_envelope recursively and NULL it */
263 void isds_envelope_free(struct isds_envelope **envelope) {
264 if (!envelope || !*envelope) return;
266 free((*envelope)->dmID);
267 free((*envelope)->dbIDSender);
268 free((*envelope)->dmSender);
269 free((*envelope)->dmSenderAddress);
270 free((*envelope)->dmSenderType);
271 free((*envelope)->dmRecipient);
272 free((*envelope)->dmRecipientAddress);
273 free((*envelope)->dmAmbiguousRecipient);
274 free((*envelope)->dmType);
276 free((*envelope)->dmOrdinal);
277 free((*envelope)->dmMessageStatus);
278 free((*envelope)->dmDeliveryTime);
279 free((*envelope)->dmAcceptanceTime);
280 isds_hash_free(&(*envelope)->hash);
281 free((*envelope)->timestamp);
282 isds_list_free(&(*envelope)->events);
284 free((*envelope)->dmSenderOrgUnit);
285 free((*envelope)->dmSenderOrgUnitNum);
286 free((*envelope)->dbIDRecipient);
287 free((*envelope)->dmRecipientOrgUnit);
288 free((*envelope)->dmRecipientOrgUnitNum);
289 free((*envelope)->dmToHands);
290 free((*envelope)->dmAnnotation);
291 free((*envelope)->dmRecipientRefNumber);
292 free((*envelope)->dmSenderRefNumber);
293 free((*envelope)->dmRecipientIdent);
294 free((*envelope)->dmSenderIdent);
296 free((*envelope)->dmLegalTitleLaw);
297 free((*envelope)->dmLegalTitleYear);
298 free((*envelope)->dmLegalTitleSect);
299 free((*envelope)->dmLegalTitlePar);
300 free((*envelope)->dmLegalTitlePoint);
302 free((*envelope)->dmPersonalDelivery);
303 free((*envelope)->dmAllowSubstDelivery);
305 free((*envelope)->dmOVM);
306 free((*envelope)->dmPublishOwnID);
308 free(*envelope);
309 *envelope = NULL;
313 /* Deallocate struct isds_message recursively and NULL it */
314 void isds_message_free(struct isds_message **message) {
315 if (!message || !*message) return;
317 free((*message)->raw);
318 isds_envelope_free(&((*message)->envelope));
319 isds_list_free(&((*message)->documents));
320 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
322 free(*message);
323 *message = NULL;
327 /* Deallocate struct isds_document recursively and NULL it */
328 void isds_document_free(struct isds_document **document) {
329 if (!document || !*document) return;
331 if (!(*document)->is_xml) {
332 free((*document)->data);
334 free((*document)->dmMimeType);
335 free((*document)->dmFileGuid);
336 free((*document)->dmUpFileGuid);
337 free((*document)->dmFileDescr);
338 free((*document)->dmFormat);
340 free(*document);
341 *document = NULL;
345 /* Deallocate struct isds_message_copy recursively and NULL it */
346 void isds_message_copy_free(struct isds_message_copy **copy) {
347 if (!copy || !*copy) return;
349 free((*copy)->dbIDRecipient);
350 free((*copy)->dmRecipientOrgUnit);
351 free((*copy)->dmRecipientOrgUnitNum);
352 free((*copy)->dmToHands);
354 free((*copy)->dmStatus);
355 free((*copy)->dmID);
357 zfree(*copy);
361 /* Deallocate struct isds_message_status_change recursively and NULL it */
362 void isds_message_status_change_free(
363 struct isds_message_status_change **message_status_change) {
364 if (!message_status_change || !*message_status_change) return;
366 free((*message_status_change)->dmID);
367 free((*message_status_change)->time);
368 free((*message_status_change)->dmMessageStatus);
370 zfree(*message_status_change);
374 /* Deallocate struct isds_approval recursively and NULL it */
375 void isds_approval_free(struct isds_approval **approval) {
376 if (!approval || !*approval) return;
378 free((*approval)->refference);
380 zfree(*approval);
384 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
385 * The email string is deallocated too. */
386 void isds_credentials_delivery_free(
387 struct isds_credentials_delivery **credentials_delivery) {
388 if (!credentials_delivery || !*credentials_delivery) return;
390 free((*credentials_delivery)->email);
391 free((*credentials_delivery)->token);
392 free((*credentials_delivery)->new_user_name);
394 zfree(*credentials_delivery);
398 /* Deallocate struct isds_commercial_permission recursively and NULL it */
399 void isds_commercial_permission_free(
400 struct isds_commercial_permission **permission) {
401 if (NULL == permission || NULL == *permission) return;
403 free((*permission)->recipient);
404 free((*permission)->payer);
405 free((*permission)->expiration);
406 free((*permission)->count);
407 free((*permission)->reply_identifier);
409 zfree(*permission);
413 /* Deallocate struct isds_credit_event recursively and NULL it */
414 void isds_credit_event_free(struct isds_credit_event **event) {
415 if (NULL == event || NULL == *event) return;
417 free((*event)->time);
418 switch ((*event)->type) {
419 case ISDS_CREDIT_CHARGED:
420 free((*event)->details.charged.transaction);
421 break;
422 case ISDS_CREDIT_DISCHARGED:
423 free((*event)->details.discharged.transaction);
424 break;
425 case ISDS_CREDIT_MESSAGE_SENT:
426 free((*event)->details.message_sent.recipient);
427 free((*event)->details.message_sent.message_id);
428 break;
429 case ISDS_CREDIT_STORAGE_SET:
430 free((*event)->details.storage_set.new_valid_from);
431 free((*event)->details.storage_set.new_valid_to);
432 free((*event)->details.storage_set.old_capacity);
433 free((*event)->details.storage_set.old_valid_from);
434 free((*event)->details.storage_set.old_valid_to);
435 free((*event)->details.storage_set.initiator);
436 break;
437 case ISDS_CREDIT_EXPIRED:
438 break;
441 zfree(*event);
445 /* Deallocate struct isds_fulltext_result recursively and NULL it */
446 void isds_fulltext_result_free(
447 struct isds_fulltext_result **result) {
448 if (NULL == result || NULL == *result) return;
450 free((*result)->dbID);
451 free((*result)->name);
452 isds_list_free(&((*result)->name_match_start));
453 isds_list_free(&((*result)->name_match_end));
454 free((*result)->address);
455 isds_list_free(&((*result)->address_match_start));
456 isds_list_free(&((*result)->address_match_end));
457 free((*result)->ic);
458 free((*result)->biDate);
460 zfree(*result);
464 /* *DUP_OR_ERROR macros needs error label */
465 #define STRDUP_OR_ERROR(new, template) { \
466 if (!template) { \
467 (new) = NULL; \
468 } else { \
469 (new) = strdup(template); \
470 if (!new) goto error; \
474 #define FLATDUP_OR_ERROR(new, template) { \
475 if (!template) { \
476 (new) = NULL; \
477 } else { \
478 (new) = malloc(sizeof(*(new))); \
479 if (!new) goto error; \
480 memcpy((new), (template), sizeof(*(template))); \
484 /* Copy structure isds_pki_credentials recursively. */
485 struct isds_pki_credentials *isds_pki_credentials_duplicate(
486 const struct isds_pki_credentials *template) {
487 struct isds_pki_credentials *new = NULL;
489 if(!template) return NULL;
491 new = calloc(1, sizeof(*new));
492 if (!new) return NULL;
494 STRDUP_OR_ERROR(new->engine, template->engine);
495 new->certificate_format = template->certificate_format;
496 STRDUP_OR_ERROR(new->certificate, template->certificate);
497 new->key_format = template->key_format;
498 STRDUP_OR_ERROR(new->key, template->key);
499 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
501 return new;
503 error:
504 isds_pki_credentials_free(&new);
505 return NULL;
509 /* Copy structure isds_PersonName recursively */
510 struct isds_PersonName *isds_PersonName_duplicate(
511 const struct isds_PersonName *src) {
512 struct isds_PersonName *new = NULL;
514 if (!src) return NULL;
516 new = calloc(1, sizeof(*new));
517 if (!new) return NULL;
519 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
520 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
521 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
522 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
524 return new;
526 error:
527 isds_PersonName_free(&new);
528 return NULL;
532 /* Copy structure isds_BirthInfo recursively */
533 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
534 const struct isds_BirthInfo *template) {
535 struct isds_BirthInfo *new = NULL;
537 if (!template) return NULL;
539 new = calloc(1, sizeof(*new));
540 if (!new) return NULL;
542 FLATDUP_OR_ERROR(new->biDate, template->biDate);
543 STRDUP_OR_ERROR(new->biCity, template->biCity);
544 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
545 STRDUP_OR_ERROR(new->biState, template->biState);
547 return new;
549 error:
550 isds_BirthInfo_free(&new);
551 return NULL;
555 /* Copy structure isds_Address recursively */
556 struct isds_Address *isds_Address_duplicate(
557 const struct isds_Address *src) {
558 struct isds_Address *new = NULL;
560 if (!src) return NULL;
562 new = calloc(1, sizeof(*new));
563 if (!new) return NULL;
565 FLATDUP_OR_ERROR(new->adCode, src->adCode);
566 STRDUP_OR_ERROR(new->adCity, src->adCity);
567 STRDUP_OR_ERROR(new->adDistrict, src->adDistrict);
568 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
569 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
570 STRDUP_OR_ERROR(new->adNumberInMunicipality,
571 src->adNumberInMunicipality);
572 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
573 STRDUP_OR_ERROR(new->adState, src->adState);
575 return new;
577 error:
578 isds_Address_free(&new);
579 return NULL;
583 /* Copy structure isds_DbOwnerInfo recursively */
584 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
585 const struct isds_DbOwnerInfo *src) {
586 struct isds_DbOwnerInfo *new = NULL;
587 if (!src) return NULL;
589 new = calloc(1, sizeof(*new));
590 if (!new) return NULL;
592 STRDUP_OR_ERROR(new->dbID, src->dbID);
593 FLATDUP_OR_ERROR(new->dbType, src->dbType);
594 STRDUP_OR_ERROR(new->ic, src->ic);
596 if (src->personName) {
597 if (!(new->personName =
598 isds_PersonName_duplicate(src->personName)))
599 goto error;
602 STRDUP_OR_ERROR(new->firmName, src->firmName);
604 if (src->birthInfo) {
605 if (!(new->birthInfo =
606 isds_BirthInfo_duplicate(src->birthInfo)))
607 goto error;
610 if (src->address) {
611 if (!(new->address = isds_Address_duplicate(src->address)))
612 goto error;
615 STRDUP_OR_ERROR(new->nationality, src->nationality);
616 STRDUP_OR_ERROR(new->email, src->email);
617 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
618 STRDUP_OR_ERROR(new->identifier, src->identifier);
619 FLATDUP_OR_ERROR(new->aifoIsds, src->aifoIsds);
620 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
621 FLATDUP_OR_ERROR(new->dbState, src->dbState);
622 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
623 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
625 return new;
627 error:
628 isds_DbOwnerInfo_free(&new);
629 return NULL;
633 /* Copy structure isds_DbUserInfo recursively */
634 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
635 const struct isds_DbUserInfo *src) {
636 struct isds_DbUserInfo *new = NULL;
637 if (!src) return NULL;
639 new = calloc(1, sizeof(*new));
640 if (!new) return NULL;
642 STRDUP_OR_ERROR(new->userID, src->userID);
643 FLATDUP_OR_ERROR(new->userType, src->userType);
644 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
646 if (src->personName) {
647 if (!(new->personName =
648 isds_PersonName_duplicate(src->personName)))
649 goto error;
652 if (src->address) {
653 if (!(new->address = isds_Address_duplicate(src->address)))
654 goto error;
657 FLATDUP_OR_ERROR(new->biDate, src->biDate);
658 STRDUP_OR_ERROR(new->ic, src->ic);
659 STRDUP_OR_ERROR(new->firmName, src->firmName);
660 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
661 STRDUP_OR_ERROR(new->caCity, src->caCity);
662 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
663 STRDUP_OR_ERROR(new->caState, src->caState);
664 STRDUP_OR_ERROR(new->aifo_ticket, src->aifo_ticket);
666 return new;
668 error:
669 isds_DbUserInfo_free(&new);
670 return NULL;
673 #undef FLATDUP_OR_ERROR
674 #undef STRDUP_OR_ERROR
677 /* Logs libxml2 errors. Should be registered to libxml2 library.
678 * @ctx is unused currently
679 * @msg is printf-like formated message from libxml2 (UTF-8?)
680 * @... are variadic arguments for @msg */
681 static void log_xml(void *ctx, const char *msg, ...) {
682 va_list ap;
683 char *text = NULL;
685 /* Silent warning for unused function argument.
686 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
687 (void)ctx;
689 if (!msg) return;
691 va_start(ap, msg);
692 isds_vasprintf(&text, msg, ap);
693 va_end(ap);
695 if (text)
696 isds_log(ILF_XML, ILL_ERR, "%s", text);
697 free(text);
701 /* Initialize ISDS library.
702 * Global function, must be called before other functions.
703 * If it fails you can not use ISDS library and must call isds_cleanup() to
704 * free partially initialized global variables. */
705 isds_error isds_init(void) {
706 /* NULL global variables */
707 log_facilities = ILF_ALL;
708 log_level = ILL_WARNING;
709 log_callback = NULL;
710 log_callback_data = NULL;
712 #if ENABLE_NLS
713 /* Initialize gettext */
714 bindtextdomain(PACKAGE, LOCALEDIR);
715 #endif
717 #if HAVE_LIBCURL
718 /* Initialize CURL */
719 if (curl_global_init(CURL_GLOBAL_ALL)) {
720 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
721 return IE_ERROR;
723 #endif /* HAVE_LIBCURL */
725 /* Initialise cryptographic back-ends. */
726 if (IE_SUCCESS != _isds_init_crypto()) {
727 isds_log(ILF_ISDS, ILL_CRIT,
728 _("Initialization of cryptographic back-end failed\n"));
729 return IE_ERROR;
732 /* This can _exit() current program. Find not so assertive check. */
733 LIBXML_TEST_VERSION;
734 xmlSetGenericErrorFunc(NULL, log_xml);
736 /* Check expat */
737 if (_isds_init_expat(&version_expat)) {
738 isds_log(ILF_ISDS, ILL_CRIT,
739 _("expat library initialization failed\n"));
740 return IE_ERROR;
743 /* Allocate global variables */
746 return IE_SUCCESS;
750 /* Deinitialize ISDS library.
751 * Global function, must be called as last library function. */
752 isds_error isds_cleanup(void) {
753 /* XML */
754 xmlCleanupParser();
756 #if HAVE_LIBCURL
757 /* Curl */
758 curl_global_cleanup();
759 #endif
761 return IE_SUCCESS;
765 /* Return version string of this library. Version of dependencies can be
766 * embedded. Do no try to parse it. You must free it. */
767 char *isds_version(void) {
768 char *buffer = NULL;
770 isds_asprintf(&buffer,
771 #if HAVE_LIBCURL
772 # ifndef USE_OPENSSL_BACKEND
773 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
774 # else
775 _("%s (%s, %s, %s, libxml2 %s)"),
776 # endif
777 #else
778 # ifndef USE_OPENSSL_BACKEND
779 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
780 # else
781 _("%s (%s, %s, libxml2 %s)"),
782 # endif
783 #endif
784 PACKAGE_VERSION,
785 #if HAVE_LIBCURL
786 curl_version(),
787 #endif
788 #ifndef USE_OPENSSL_BACKEND
789 version_gpgme, version_gcrypt,
790 #else
791 version_openssl,
792 #endif
793 version_expat, xmlParserVersion);
794 return buffer;
798 /* Return text description of ISDS error */
799 const char *isds_strerror(const isds_error error) {
800 switch (error) {
801 case IE_SUCCESS:
802 return(_("Success")); break;
803 case IE_ERROR:
804 return(_("Unspecified error")); break;
805 case IE_NOTSUP:
806 return(_("Not supported")); break;
807 case IE_INVAL:
808 return(_("Invalid value")); break;
809 case IE_INVALID_CONTEXT:
810 return(_("Invalid context")); break;
811 case IE_NOT_LOGGED_IN:
812 return(_("Not logged in")); break;
813 case IE_CONNECTION_CLOSED:
814 return(_("Connection closed")); break;
815 case IE_TIMED_OUT:
816 return(_("Timed out")); break;
817 case IE_NOEXIST:
818 return(_("Not exist")); break;
819 case IE_NOMEM:
820 return(_("Out of memory")); break;
821 case IE_NETWORK:
822 return(_("Network problem")); break;
823 case IE_HTTP:
824 return(_("HTTP problem")); break;
825 case IE_SOAP:
826 return(_("SOAP problem")); break;
827 case IE_XML:
828 return(_("XML problem")); break;
829 case IE_ISDS:
830 return(_("ISDS server problem")); break;
831 case IE_ENUM:
832 return(_("Invalid enum value")); break;
833 case IE_DATE:
834 return(_("Invalid date value")); break;
835 case IE_2BIG:
836 return(_("Too big")); break;
837 case IE_2SMALL:
838 return(_("Too small")); break;
839 case IE_NOTUNIQ:
840 return(_("Value not unique")); break;
841 case IE_NOTEQUAL:
842 return(_("Values not equal")); break;
843 case IE_PARTIAL_SUCCESS:
844 return(_("Some suboperations failed")); break;
845 case IE_ABORTED:
846 return(_("Operation aborted")); break;
847 case IE_SECURITY:
848 return(_("Security problem")); break;
849 default:
850 return(_("Unknown error"));
855 /* Create ISDS context.
856 * Each context can be used for different sessions to (possibly) different
857 * ISDS server with different credentials. */
858 struct isds_ctx *isds_ctx_create(void) {
859 struct isds_ctx *context;
860 context = malloc(sizeof(*context));
861 if (context) memset(context, 0, sizeof(*context));
862 return context;
865 #if HAVE_LIBCURL
866 /* Close possibly opened connection to Czech POINT document deposit without
867 * resetting long_message buffer.
868 * XXX: Do not use czp_close_connection() if you do not want to destroy log
869 * message.
870 * @context is Czech POINT session context. */
871 static isds_error czp_do_close_connection(struct isds_ctx *context) {
872 if (!context) return IE_INVALID_CONTEXT;
873 _isds_close_connection(context);
874 return IE_SUCCESS;
878 /* Discard credentials.
879 * @context is ISDS context
880 * @discard_saved_username is true for removing saved username, false for
881 * keeping it.
882 * Only that. It does not cause log out, connection close or similar. */
883 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
884 _Bool discard_saved_username) {
885 if(!context) return IE_INVALID_CONTEXT;
887 if (context->username) {
888 memset(context->username, 0, strlen(context->username));
889 zfree(context->username);
891 if (context->password) {
892 memset(context->password, 0, strlen(context->password));
893 zfree(context->password);
895 isds_pki_credentials_free(&context->pki_credentials);
896 if (discard_saved_username && context->saved_username) {
897 memset(context->saved_username, 0, strlen(context->saved_username));
898 zfree(context->saved_username);
901 return IE_SUCCESS;
903 #endif /* HAVE_LIBCURL */
906 /* Destroy ISDS context and free memory.
907 * @context will be NULLed on success. */
908 isds_error isds_ctx_free(struct isds_ctx **context) {
909 if (!context || !*context) {
910 return IE_INVALID_CONTEXT;
913 #if HAVE_LIBCURL
914 /* Discard credentials and close connection */
915 switch ((*context)->type) {
916 case CTX_TYPE_NONE: break;
917 case CTX_TYPE_ISDS: isds_logout(*context); break;
918 case CTX_TYPE_CZP:
919 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
920 czp_do_close_connection(*context); break;
923 /* For sure */
924 _isds_discard_credentials(*context, 1);
926 /* Free other structures */
927 free((*context)->url);
928 free((*context)->tls_verify_server);
929 free((*context)->tls_ca_file);
930 free((*context)->tls_ca_dir);
931 free((*context)->tls_crl_file);
932 #endif /* HAVE_LIBCURL */
933 free((*context)->long_message);
935 free(*context);
936 *context = NULL;
937 return IE_SUCCESS;
941 /* Return long message text produced by library function, e.g. detailed error
942 * message. Returned pointer is only valid until new library function is
943 * called for the same context. Could be NULL, especially if NULL context is
944 * supplied. Return string is locale encoded. */
945 char *isds_long_message(const struct isds_ctx *context) {
946 if (!context) return NULL;
947 return context->long_message;
951 /* Stores message into context' long_message buffer.
952 * Application can pick the message up using isds_long_message().
953 * NULL @message truncates the buffer but does not deallocate it.
954 * @message is coded in locale encoding */
955 _hidden isds_error isds_log_message(struct isds_ctx *context,
956 const char *message) {
957 char *buffer;
958 size_t length;
960 if (!context) return IE_INVALID_CONTEXT;
962 /* FIXME: Check for integer overflow */
963 length = 1 + ((message) ? strlen(message) : 0);
964 buffer = realloc(context->long_message, length);
965 if (!buffer) return IE_NOMEM;
967 if (message)
968 strcpy(buffer, message);
969 else
970 *buffer = '\0';
972 context->long_message = buffer;
973 return IE_SUCCESS;
977 /* Appends message into context' long_message buffer.
978 * Application can pick the message up using isds_long_message().
979 * NULL message has void effect. */
980 _hidden isds_error isds_append_message(struct isds_ctx *context,
981 const char *message) {
982 char *buffer;
983 size_t old_length, length;
985 if (!context) return IE_INVALID_CONTEXT;
986 if (!message) return IE_SUCCESS;
987 if (!context->long_message)
988 return isds_log_message(context, message);
990 old_length = strlen(context->long_message);
991 /* FIXME: Check for integer overflow */
992 length = 1 + old_length + strlen(message);
993 buffer = realloc(context->long_message, length);
994 if (!buffer) return IE_NOMEM;
996 strcpy(buffer + old_length, message);
998 context->long_message = buffer;
999 return IE_SUCCESS;
1003 /* Stores formatted message into context' long_message buffer.
1004 * Application can pick the message up using isds_long_message(). */
1005 _hidden isds_error isds_printf_message(struct isds_ctx *context,
1006 const char *format, ...) {
1007 va_list ap;
1008 int length;
1010 if (!context) return IE_INVALID_CONTEXT;
1011 va_start(ap, format);
1012 length = isds_vasprintf(&(context->long_message), format, ap);
1013 va_end(ap);
1015 return (length < 0) ? IE_ERROR: IE_SUCCESS;
1019 /* Set logging up.
1020 * @facilities is bit mask of isds_log_facility values,
1021 * @level is verbosity level. */
1022 void isds_set_logging(const unsigned int facilities,
1023 const isds_log_level level) {
1024 log_facilities = facilities;
1025 log_level = level;
1029 /* Register callback function libisds calls when new global log message is
1030 * produced by library. Library logs to stderr by default.
1031 * @callback is function provided by application libisds will call. See type
1032 * definition for @callback argument explanation. Pass NULL to revert logging to
1033 * default behaviour.
1034 * @data is application specific data @callback gets as last argument */
1035 void isds_set_log_callback(isds_log_callback callback, void *data) {
1036 log_callback = callback;
1037 log_callback_data = data;
1041 /* Log @message in class @facility with log @level into global log. @message
1042 * is printf(3) formatting string, variadic arguments may be necessary.
1043 * For debugging purposes. */
1044 _hidden isds_error isds_log(const isds_log_facility facility,
1045 const isds_log_level level, const char *message, ...) {
1046 va_list ap;
1047 char *buffer = NULL;
1048 int length;
1050 if (level > log_level) return IE_SUCCESS;
1051 if (!(log_facilities & facility)) return IE_SUCCESS;
1052 if (!message) return IE_INVAL;
1054 if (log_callback) {
1055 /* Pass message to application supplied callback function */
1056 va_start(ap, message);
1057 length = isds_vasprintf(&buffer, message, ap);
1058 va_end(ap);
1060 if (length == -1) {
1061 return IE_ERROR;
1063 if (length > 0) {
1064 log_callback(facility, level, buffer, length, log_callback_data);
1066 free(buffer);
1067 } else {
1068 /* Default: Log it to stderr */
1069 va_start(ap, message);
1070 vfprintf(stderr, message, ap);
1071 va_end(ap);
1072 /* Line buffered printf is default.
1073 * fflush(stderr);*/
1076 return IE_SUCCESS;
1080 /* Set timeout in milliseconds for each network job like connecting to server
1081 * or sending message. Use 0 to disable timeout limits. */
1082 isds_error isds_set_timeout(struct isds_ctx *context,
1083 const unsigned int timeout) {
1084 if (!context) return IE_INVALID_CONTEXT;
1085 zfree(context->long_message);
1087 #if HAVE_LIBCURL
1088 context->timeout = timeout;
1090 if (context->curl) {
1091 CURLcode curl_err;
1093 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1094 if (!curl_err)
1095 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1096 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1097 context->timeout);
1098 #else
1099 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1100 context->timeout / 1000);
1101 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1102 if (curl_err) return IE_ERROR;
1105 return IE_SUCCESS;
1106 #else /* not HAVE_LIBCURL */
1107 return IE_NOTSUP;
1108 #endif
1112 /* Register callback function libisds calls periodically during HTTP data
1113 * transfer.
1114 * @context is session context
1115 * @callback is function provided by application libisds will call. See type
1116 * definition for @callback argument explanation.
1117 * @data is application specific data @callback gets as last argument */
1118 isds_error isds_set_progress_callback(struct isds_ctx *context,
1119 isds_progress_callback callback, void *data) {
1120 if (!context) return IE_INVALID_CONTEXT;
1121 zfree(context->long_message);
1123 #if HAVE_LIBCURL
1124 context->progress_callback = callback;
1125 context->progress_callback_data = data;
1127 return IE_SUCCESS;
1128 #else /* not HAVE_LIBCURL */
1129 return IE_NOTSUP;
1130 #endif
1134 /* Change context settings.
1135 * @context is context which setting will be applied to
1136 * @option is name of option. It determines the type of last argument. See
1137 * isds_option definition for more info.
1138 * @... is value of new setting. Type is determined by @option
1139 * */
1140 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1141 ...) {
1142 isds_error err = IE_SUCCESS;
1143 va_list ap;
1144 #if HAVE_LIBCURL
1145 char *pointer, *string;
1146 #endif
1148 if (!context) return IE_INVALID_CONTEXT;
1149 zfree(context->long_message);
1151 va_start(ap, option);
1153 #define REPLACE_VA_BOOLEAN(destination) { \
1154 if (!(destination)) { \
1155 (destination) = malloc(sizeof(*(destination))); \
1156 if (!(destination)) { \
1157 err = IE_NOMEM; goto leave; \
1160 *(destination) = (_Bool) !!va_arg(ap, int); \
1163 #define REPLACE_VA_STRING(destination) { \
1164 string = va_arg(ap, char *); \
1165 if (string) { \
1166 pointer = realloc((destination), 1 + strlen(string)); \
1167 if (!pointer) { err = IE_NOMEM; goto leave; } \
1168 strcpy(pointer, string); \
1169 (destination) = pointer; \
1170 } else { \
1171 free(destination); \
1172 (destination) = NULL; \
1176 switch (option) {
1177 case IOPT_TLS_VERIFY_SERVER:
1178 #if HAVE_LIBCURL
1179 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1180 #else
1181 err = IE_NOTSUP; goto leave;
1182 #endif
1183 break;
1184 case IOPT_TLS_CA_FILE:
1185 #if HAVE_LIBCURL
1186 REPLACE_VA_STRING(context->tls_ca_file);
1187 #else
1188 err = IE_NOTSUP; goto leave;
1189 #endif
1190 break;
1191 case IOPT_TLS_CA_DIRECTORY:
1192 #if HAVE_LIBCURL
1193 REPLACE_VA_STRING(context->tls_ca_dir);
1194 #else
1195 err = IE_NOTSUP; goto leave;
1196 #endif
1197 break;
1198 case IOPT_TLS_CRL_FILE:
1199 #if HAVE_LIBCURL
1200 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1201 REPLACE_VA_STRING(context->tls_crl_file);
1202 #else
1203 isds_log_message(context,
1204 _("Curl library does not support CRL definition"));
1205 err = IE_NOTSUP;
1206 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1207 #else
1208 err = IE_NOTSUP; goto leave;
1209 #endif /* not HAVE_LIBCURL */
1210 break;
1211 case IOPT_NORMALIZE_MIME_TYPE:
1212 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1213 break;
1215 default:
1216 err = IE_ENUM; goto leave;
1219 #undef REPLACE_VA_STRING
1220 #undef REPLACE_VA_BOOLEAN
1222 leave:
1223 va_end(ap);
1224 return err;
1228 #if HAVE_LIBCURL
1229 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1230 * Destination for NULL argument will not be touched.
1231 * Destination pointers must be freed before calling this function.
1232 * If @username is @context->saved_username, the saved_username will not be
1233 * replaced. The saved_username is clobbered only if context has set otp
1234 * member.
1235 * Return IE_SUCCESS on success. */
1236 static isds_error _isds_store_credentials(struct isds_ctx *context,
1237 const char *username, const char *password,
1238 const struct isds_pki_credentials *pki_credentials) {
1239 if (NULL == context) return IE_INVALID_CONTEXT;
1241 /* FIXME: mlock password
1242 * (I have a library) */
1244 if (username) {
1245 context->username = strdup(username);
1246 if (context->otp && context->saved_username != username)
1247 context->saved_username = strdup(username);
1249 if (password) {
1250 if (NULL == context->otp_credentials)
1251 context->password = strdup(password);
1252 else
1253 context->password = _isds_astrcat(password,
1254 context->otp_credentials->otp_code);
1256 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1258 if ((NULL != username && NULL == context->username) ||
1259 (NULL != password && NULL == context->password) ||
1260 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1261 (context->otp && NULL != context->username &&
1262 NULL == context->saved_username)) {
1263 return IE_NOMEM;
1266 return IE_SUCCESS;
1268 #endif
1271 /* Connect and log into ISDS server.
1272 * All required arguments will be copied, you do not have to keep them after
1273 * that.
1274 * ISDS supports six different authentication methods. Exact method is
1275 * selected on @username, @password, @pki_credentials, and @otp arguments:
1276 * - If @pki_credentials == NULL, @username and @password must be supplied
1277 * and then
1278 * - If @otp == NULL, simple authentication by username and password will
1279 * be proceeded.
1280 * - If @otp != NULL, authentication by username and password and OTP
1281 * will be used.
1282 * - If @pki_credentials != NULL, then
1283 * - If @username == NULL, only certificate will be used
1284 * - If @username != NULL, then
1285 * - If @password == NULL, then certificate will be used and
1286 * @username shifts meaning to box ID. This is used for hosted
1287 * services.
1288 * - Otherwise all three arguments will be used.
1289 * Please note, that different cases require different certificate type
1290 * (system qualified one or commercial non qualified one). This library
1291 * does not check such political issues. Please see ISDS Specification
1292 * for more details.
1293 * @url is base address of ISDS web service. Pass extern isds_locator
1294 * variable to use production ISDS instance without client certificate
1295 * authentication (or extern isds_cert_locator with client certificate
1296 * authentication or extern isds_otp_locators with OTP authentication).
1297 * Passing NULL has the same effect, autoselection between isds_locator,
1298 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1299 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1300 * isds_otp_testing_locator) variable to select testing instance.
1301 * @username is user name of ISDS user or box ID
1302 * @password is user's secret password
1303 * @pki_credentials defines public key cryptographic material to use in client
1304 * authentication.
1305 * @otp selects one-time password authentication method to use, defines OTP
1306 * code (if known) and returns fine grade resolution of OTP procedure.
1307 * @return:
1308 * IE_SUCCESS if authentication succeeds
1309 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1310 * requested, fine grade reason will be set into @otp->resolution. Error
1311 * message from server can be obtained by isds_long_message() call.
1312 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1313 * server has sent OTP code through side channel. Application is expected to
1314 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1315 * this call to complete second phase of TOTP authentication;
1316 * or other appropriate error. */
1317 isds_error isds_login(struct isds_ctx *context, const char *url,
1318 const char *username, const char *password,
1319 const struct isds_pki_credentials *pki_credentials,
1320 struct isds_otp *otp) {
1321 #if HAVE_LIBCURL
1322 isds_error err = IE_NOT_LOGGED_IN;
1323 isds_error soap_err;
1324 xmlNsPtr isds_ns = NULL;
1325 xmlNodePtr request = NULL;
1326 #endif /* HAVE_LIBCURL */
1328 if (!context) return IE_INVALID_CONTEXT;
1329 zfree(context->long_message);
1331 #if HAVE_LIBCURL
1332 /* Close connection if already logged in */
1333 if (context->curl) {
1334 _isds_close_connection(context);
1337 /* Store configuration */
1338 context->type = CTX_TYPE_ISDS;
1339 zfree(context->url);
1341 /* Mangle base URI according to requested authentication method */
1342 if (NULL == pki_credentials) {
1343 isds_log(ILF_SEC, ILL_INFO,
1344 _("Selected authentication method: no certificate, "
1345 "username and password\n"));
1346 if (!username || !password) {
1347 isds_log_message(context,
1348 _("Both username and password must be supplied"));
1349 return IE_INVAL;
1351 context->otp_credentials = otp;
1352 context->otp = (NULL != context->otp_credentials);
1354 if (!context->otp) {
1355 /* Default locator is official system (without certificate or
1356 * OTP) */
1357 context->url = strdup((NULL != url) ? url : isds_locator);
1358 } else {
1359 const char *authenticator_uri = NULL;
1360 if (!url) url = isds_otp_locator;
1361 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1362 switch (context->otp_credentials->method) {
1363 case OTP_HMAC:
1364 isds_log(ILF_SEC, ILL_INFO,
1365 _("Selected authentication method: "
1366 "HMAC-based one-time password\n"));
1367 authenticator_uri =
1368 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1369 break;
1370 case OTP_TIME:
1371 isds_log(ILF_SEC, ILL_INFO,
1372 _("Selected authentication method: "
1373 "Time-based one-time password\n"));
1374 if (context->otp_credentials->otp_code == NULL) {
1375 isds_log(ILF_SEC, ILL_INFO,
1376 _("OTP code has not been provided by "
1377 "application, requesting server for "
1378 "new one.\n"));
1379 authenticator_uri =
1380 "%1$sas/processLogin?type=totp&sendSms=true&"
1381 "uri=%1$sapps/";
1382 } else {
1383 isds_log(ILF_SEC, ILL_INFO,
1384 _("OTP code has been provided by "
1385 "application, not requesting server "
1386 "for new one.\n"));
1387 authenticator_uri =
1388 "%1$sas/processLogin?type=totp&"
1389 "uri=%1$sapps/";
1391 break;
1392 default:
1393 isds_log_message(context,
1394 _("Unknown one-time password authentication "
1395 "method requested by application"));
1396 return IE_ENUM;
1398 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1399 return IE_NOMEM;
1401 } else {
1402 /* Default locator is official system (with client certificate) */
1403 context->otp = 0;
1404 context->otp_credentials = NULL;
1405 if (!url) url = isds_cert_locator;
1407 if (!username) {
1408 isds_log(ILF_SEC, ILL_INFO,
1409 _("Selected authentication method: system certificate, "
1410 "no username and no password\n"));
1411 password = NULL;
1412 context->url = _isds_astrcat(url, "cert/");
1413 } else {
1414 if (!password) {
1415 isds_log(ILF_SEC, ILL_INFO,
1416 _("Selected authentication method: system certificate, "
1417 "box ID and no password\n"));
1418 context->url = _isds_astrcat(url, "hspis/");
1419 } else {
1420 isds_log(ILF_SEC, ILL_INFO,
1421 _("Selected authentication method: commercial "
1422 "certificate, username and password\n"));
1423 context->url = _isds_astrcat(url, "certds/");
1427 if (!(context->url))
1428 return IE_NOMEM;
1430 /* Prepare CURL handle */
1431 context->curl = curl_easy_init();
1432 if (!(context->curl))
1433 return IE_ERROR;
1435 /* Build log-in request */
1436 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1437 if (!request) {
1438 isds_log_message(context, _("Could not build ISDS log-in request"));
1439 return IE_ERROR;
1441 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1442 if(!isds_ns) {
1443 isds_log_message(context, _("Could not create ISDS name space"));
1444 xmlFreeNode(request);
1445 return IE_ERROR;
1447 xmlSetNs(request, isds_ns);
1449 /* Store credentials */
1450 _isds_discard_credentials(context, 1);
1451 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1452 _isds_discard_credentials(context, 1);
1453 xmlFreeNode(request);
1454 return IE_NOMEM;
1457 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1458 username, url);
1460 /* XXX: ISDS documentation does not specify response body for
1461 * DummyOperation request. However real server sends back
1462 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1463 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1464 * SOAP body content, e.g. the dmStatus element. */
1466 /* Send log-in request */
1467 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1469 if (context->otp) {
1470 /* Revert context URL from OTP authentication service URL to OTP web
1471 * service base URL for subsequent calls. Potenial isds_login() retry
1472 * will re-set context URL again. */
1473 zfree(context->url);
1474 context->url = _isds_astrcat(url, "apps/");
1475 if (context->url == NULL) {
1476 soap_err = IE_NOMEM;
1478 /* Detach pointer to OTP credentials from context */
1479 context->otp_credentials = NULL;
1482 /* Remove credentials */
1483 _isds_discard_credentials(context, 0);
1485 /* Destroy log-in request */
1486 xmlFreeNode(request);
1488 if (soap_err) {
1489 _isds_close_connection(context);
1490 return soap_err;
1493 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1494 * authentication succeeded if soap_err == IE_SUCCESS */
1495 err = IE_SUCCESS;
1497 if (!err)
1498 isds_log(ILF_ISDS, ILL_DEBUG,
1499 _("User %s has been logged into server %s successfully\n"),
1500 username, url);
1501 return err;
1502 #else /* not HAVE_LIBCURL */
1503 return IE_NOTSUP;
1504 #endif
1508 /* Log out from ISDS server discards credentials and connection configuration. */
1509 isds_error isds_logout(struct isds_ctx *context) {
1510 if (!context) return IE_INVALID_CONTEXT;
1511 zfree(context->long_message);
1513 #if HAVE_LIBCURL
1514 if (context->curl) {
1515 if (context->otp) {
1516 isds_error err = _isds_invalidate_otp_cookie(context);
1517 if (err) return err;
1520 /* Close connection */
1521 _isds_close_connection(context);
1523 /* Discard credentials for sure. They should not survive isds_login(),
1524 * even successful .*/
1525 _isds_discard_credentials(context, 1);
1527 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1528 } else {
1529 _isds_discard_credentials(context, 1);
1531 zfree(context->url);
1532 return IE_SUCCESS;
1533 #else /* not HAVE_LIBCURL */
1534 return IE_NOTSUP;
1535 #endif
1539 /* Verify connection to ISDS is alive and server is responding.
1540 * Send dummy request to ISDS and expect dummy response. */
1541 isds_error isds_ping(struct isds_ctx *context) {
1542 #if HAVE_LIBCURL
1543 isds_error soap_err;
1544 xmlNsPtr isds_ns = NULL;
1545 xmlNodePtr request = NULL;
1546 #endif /* HAVE_LIBCURL */
1548 if (!context) return IE_INVALID_CONTEXT;
1549 zfree(context->long_message);
1551 #if HAVE_LIBCURL
1552 /* Check if connection is established */
1553 if (!context->curl) return IE_CONNECTION_CLOSED;
1556 /* Build dummy request */
1557 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1558 if (!request) {
1559 isds_log_message(context, _("Could build ISDS dummy request"));
1560 return IE_ERROR;
1562 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1563 if(!isds_ns) {
1564 isds_log_message(context, _("Could not create ISDS name space"));
1565 xmlFreeNode(request);
1566 return IE_ERROR;
1568 xmlSetNs(request, isds_ns);
1570 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1572 /* XXX: ISDS documentation does not specify response body for
1573 * DummyOperation request. However real server sends back
1574 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1575 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1576 * SOAP body content, e.g. the dmStatus element. */
1578 /* Send dummy request */
1579 soap_err = _isds_soap(context, "DS/dz", request, NULL, NULL, NULL, NULL);
1581 /* Destroy log-in request */
1582 xmlFreeNode(request);
1584 if (soap_err) {
1585 isds_log(ILF_ISDS, ILL_DEBUG,
1586 _("ISDS server could not be contacted\n"));
1587 return soap_err;
1590 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1591 * authentication succeeded if soap_err == IE_SUCCESS */
1594 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1596 return IE_SUCCESS;
1597 #else /* not HAVE_LIBCURL */
1598 return IE_NOTSUP;
1599 #endif
1603 /* Send bogus request to ISDS.
1604 * Just for test purposes */
1605 isds_error isds_bogus_request(struct isds_ctx *context) {
1606 #if HAVE_LIBCURL
1607 isds_error err;
1608 xmlNsPtr isds_ns = NULL;
1609 xmlNodePtr request = NULL;
1610 xmlDocPtr response = NULL;
1611 xmlChar *code = NULL, *message = NULL;
1612 #endif
1614 if (!context) return IE_INVALID_CONTEXT;
1615 zfree(context->long_message);
1617 #if HAVE_LIBCURL
1618 /* Check if connection is established */
1619 if (!context->curl) {
1620 /* Testing printf message */
1621 isds_printf_message(context, "%s", _("I said connection closed"));
1622 return IE_CONNECTION_CLOSED;
1626 /* Build dummy request */
1627 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1628 if (!request) {
1629 isds_log_message(context, _("Could build ISDS bogus request"));
1630 return IE_ERROR;
1632 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1633 if(!isds_ns) {
1634 isds_log_message(context, _("Could not create ISDS name space"));
1635 xmlFreeNode(request);
1636 return IE_ERROR;
1638 xmlSetNs(request, isds_ns);
1640 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1642 /* Sent bogus request */
1643 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1645 /* Destroy request */
1646 xmlFreeNode(request);
1648 if (err) {
1649 isds_log(ILF_ISDS, ILL_DEBUG,
1650 _("Processing ISDS response on bogus request failed\n"));
1651 xmlFreeDoc(response);
1652 return err;
1655 /* Check for response status */
1656 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1657 &code, &message, NULL);
1658 if (err) {
1659 isds_log(ILF_ISDS, ILL_DEBUG,
1660 _("ISDS response on bogus request is missing status\n"));
1661 free(code);
1662 free(message);
1663 xmlFreeDoc(response);
1664 return err;
1666 if (xmlStrcmp(code, BAD_CAST "0000")) {
1667 char *code_locale = _isds_utf82locale((char*)code);
1668 char *message_locale = _isds_utf82locale((char*)message);
1669 isds_log(ILF_ISDS, ILL_DEBUG,
1670 _("Server refused bogus request (code=%s, message=%s)\n"),
1671 code_locale, message_locale);
1672 /* XXX: Literal error messages from ISDS are Czech messages
1673 * (English sometimes) in UTF-8. It's hard to catch them for
1674 * translation. Successfully gettextized would return in locale
1675 * encoding, unsuccessfully translated would pass in UTF-8. */
1676 isds_log_message(context, message_locale);
1677 free(code_locale);
1678 free(message_locale);
1679 free(code);
1680 free(message);
1681 xmlFreeDoc(response);
1682 return IE_ISDS;
1686 free(code);
1687 free(message);
1688 xmlFreeDoc(response);
1690 isds_log(ILF_ISDS, ILL_DEBUG,
1691 _("Bogus message accepted by server. This should not happen.\n"));
1693 return IE_SUCCESS;
1694 #else /* not HAVE_LIBCURL */
1695 return IE_NOTSUP;
1696 #endif
1700 #if HAVE_LIBCURL
1701 /* Serialize XML subtree to buffer preserving XML indentation.
1702 * @context is session context
1703 * @subtree is XML element to be serialized (with children)
1704 * @buffer is automatically reallocated buffer where serialize to
1705 * @length is size of serialized stream in bytes
1706 * @return standard error code, free @buffer in case of error */
1707 static isds_error serialize_subtree(struct isds_ctx *context,
1708 xmlNodePtr subtree, void **buffer, size_t *length) {
1709 isds_error err = IE_SUCCESS;
1710 xmlBufferPtr xml_buffer = NULL;
1711 xmlSaveCtxtPtr save_ctx = NULL;
1712 xmlDocPtr subtree_doc = NULL;
1713 xmlNodePtr subtree_copy;
1714 xmlNsPtr isds_ns;
1715 void *new_buffer;
1717 if (!context) return IE_INVALID_CONTEXT;
1718 if (!buffer) return IE_INVAL;
1719 zfree(*buffer);
1720 if (!subtree || !length) return IE_INVAL;
1722 /* Make temporary XML document with @subtree root element */
1723 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1724 * It can result in not well-formed on invalid XML tree (e.g. name space
1725 * prefix definition can miss. */
1726 /*FIXME */
1728 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1729 if (!subtree_doc) {
1730 isds_log_message(context, _("Could not build temporary document"));
1731 err = IE_ERROR;
1732 goto leave;
1735 /* XXX: Copy subtree and attach the copy to document.
1736 * One node can not bee attached into more document at the same time.
1737 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1738 * automatically.
1739 * XXX: Check xmlSaveTree() too. */
1740 subtree_copy = xmlCopyNodeList(subtree);
1741 if (!subtree_copy) {
1742 isds_log_message(context, _("Could not copy subtree"));
1743 err = IE_ERROR;
1744 goto leave;
1746 xmlDocSetRootElement(subtree_doc, subtree_copy);
1748 /* Only this way we get namespace definition as @xmlns:isds,
1749 * otherwise we get namespace prefix without definition */
1750 /* FIXME: Don't overwrite original default namespace */
1751 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1752 if(!isds_ns) {
1753 isds_log_message(context, _("Could not create ISDS name space"));
1754 err = IE_ERROR;
1755 goto leave;
1757 xmlSetNs(subtree_copy, isds_ns);
1760 /* Serialize the document into buffer */
1761 xml_buffer = xmlBufferCreate();
1762 if (!xml_buffer) {
1763 isds_log_message(context, _("Could not create xmlBuffer"));
1764 err = IE_ERROR;
1765 goto leave;
1767 /* Last argument 0 means to not format the XML tree */
1768 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1769 if (!save_ctx) {
1770 isds_log_message(context, _("Could not create XML serializer"));
1771 err = IE_ERROR;
1772 goto leave;
1774 /* XXX: According LibXML documentation, this function does not return
1775 * meaningful value yet */
1776 xmlSaveDoc(save_ctx, subtree_doc);
1777 if (-1 == xmlSaveFlush(save_ctx)) {
1778 isds_log_message(context,
1779 _("Could not serialize XML subtree"));
1780 err = IE_ERROR;
1781 goto leave;
1783 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1784 * even after xmlSaveFlush(). Thus close it here */
1785 xmlSaveClose(save_ctx); save_ctx = NULL;
1788 /* Store and detach buffer from xml_buffer */
1789 *buffer = xml_buffer->content;
1790 *length = xml_buffer->use;
1791 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1793 /* Shrink buffer */
1794 new_buffer = realloc(*buffer, *length);
1795 if (new_buffer) *buffer = new_buffer;
1797 leave:
1798 if (err) {
1799 zfree(*buffer);
1800 *length = 0;
1803 xmlSaveClose(save_ctx);
1804 xmlBufferFree(xml_buffer);
1805 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1806 return err;
1808 #endif /* HAVE_LIBCURL */
1811 #if 0
1812 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1813 * @context is session context
1814 * @document is original document where @nodeset points to
1815 * @nodeset is XPath node set to dump (recursively)
1816 * @buffer is automatically reallocated buffer where serialize to
1817 * @length is size of serialized stream in bytes
1818 * @return standard error code, free @buffer in case of error */
1819 static isds_error dump_nodeset(struct isds_ctx *context,
1820 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1821 void **buffer, size_t *length) {
1822 isds_error err = IE_SUCCESS;
1823 xmlBufferPtr xml_buffer = NULL;
1824 void *new_buffer;
1826 if (!context) return IE_INVALID_CONTEXT;
1827 if (!buffer) return IE_INVAL;
1828 zfree(*buffer);
1829 if (!document || !nodeset || !length) return IE_INVAL;
1830 *length = 0;
1832 /* Empty node set results into NULL buffer */
1833 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1834 goto leave;
1837 /* Resulting the document into buffer */
1838 xml_buffer = xmlBufferCreate();
1839 if (!xml_buffer) {
1840 isds_log_message(context, _("Could not create xmlBuffer"));
1841 err = IE_ERROR;
1842 goto leave;
1845 /* Iterate over all nodes */
1846 for (int i = 0; i < nodeset->nodeNr; i++) {
1847 /* Serialize node.
1848 * XXX: xmlNodeDump() appends to xml_buffer. */
1849 if (-1 ==
1850 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1851 isds_log_message(context, _("Could not dump XML node"));
1852 err = IE_ERROR;
1853 goto leave;
1857 /* Store and detach buffer from xml_buffer */
1858 *buffer = xml_buffer->content;
1859 *length = xml_buffer->use;
1860 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1862 /* Shrink buffer */
1863 new_buffer = realloc(*buffer, *length);
1864 if (new_buffer) *buffer = new_buffer;
1867 leave:
1868 if (err) {
1869 zfree(*buffer);
1870 *length = 0;
1873 xmlBufferFree(xml_buffer);
1874 return err;
1876 #endif
1878 #if 0
1879 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1880 * @context is session context
1881 * @document is original document where @nodeset points to
1882 * @nodeset is XPath node set to dump (recursively)
1883 * @buffer is automatically reallocated buffer where serialize to
1884 * @length is size of serialized stream in bytes
1885 * @return standard error code, free @buffer in case of error */
1886 static isds_error dump_nodeset(struct isds_ctx *context,
1887 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1888 void **buffer, size_t *length) {
1889 isds_error err = IE_SUCCESS;
1890 xmlBufferPtr xml_buffer = NULL;
1891 xmlSaveCtxtPtr save_ctx = NULL;
1892 void *new_buffer;
1894 if (!context) return IE_INVALID_CONTEXT;
1895 if (!buffer) return IE_INVAL;
1896 zfree(*buffer);
1897 if (!document || !nodeset || !length) return IE_INVAL;
1898 *length = 0;
1900 /* Empty node set results into NULL buffer */
1901 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1902 goto leave;
1905 /* Resulting the document into buffer */
1906 xml_buffer = xmlBufferCreate();
1907 if (!xml_buffer) {
1908 isds_log_message(context, _("Could not create xmlBuffer"));
1909 err = IE_ERROR;
1910 goto leave;
1912 if (xmlSubstituteEntitiesDefault(1)) {
1913 isds_log_message(context, _("Could not disable attribute escaping"));
1914 err = IE_ERROR;
1915 goto leave;
1917 /* Last argument means:
1918 * 0 to not format the XML tree
1919 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1920 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1921 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1922 if (!save_ctx) {
1923 isds_log_message(context, _("Could not create XML serializer"));
1924 err = IE_ERROR;
1925 goto leave;
1927 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1928 isds_log_message(context, _("Could not disable attribute escaping"));
1929 err = IE_ERROR;
1930 goto leave;
1934 /* Iterate over all nodes */
1935 for (int i = 0; i < nodeset->nodeNr; i++) {
1936 /* Serialize node.
1937 * XXX: xmlNodeDump() appends to xml_buffer. */
1938 /*if (-1 ==
1939 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1941 /* XXX: According LibXML documentation, this function does not return
1942 * meaningful value yet */
1943 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1944 if (-1 == xmlSaveFlush(save_ctx)) {
1945 isds_log_message(context,
1946 _("Could not serialize XML subtree"));
1947 err = IE_ERROR;
1948 goto leave;
1952 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1953 * even after xmlSaveFlush(). Thus close it here */
1954 xmlSaveClose(save_ctx); save_ctx = NULL;
1956 /* Store and detach buffer from xml_buffer */
1957 *buffer = xml_buffer->content;
1958 *length = xml_buffer->use;
1959 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1961 /* Shrink buffer */
1962 new_buffer = realloc(*buffer, *length);
1963 if (new_buffer) *buffer = new_buffer;
1965 leave:
1966 if (err) {
1967 zfree(*buffer);
1968 *length = 0;
1971 xmlSaveClose(save_ctx);
1972 xmlBufferFree(xml_buffer);
1973 return err;
1975 #endif
1978 #if HAVE_LIBCURL
1979 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1980 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1981 if (!string || !type) return IE_INVAL;
1983 if (!xmlStrcmp(string, BAD_CAST "FO"))
1984 *type = DBTYPE_FO;
1985 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1986 *type = DBTYPE_PFO;
1987 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1988 *type = DBTYPE_PFO_ADVOK;
1989 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1990 *type = DBTYPE_PFO_DANPOR;
1991 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1992 *type = DBTYPE_PFO_INSSPR;
1993 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1994 *type = DBTYPE_PO;
1995 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1996 *type = DBTYPE_PO_ZAK;
1997 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1998 *type = DBTYPE_PO_REQ;
1999 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
2000 *type = DBTYPE_OVM;
2001 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
2002 *type = DBTYPE_OVM_NOTAR;
2003 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
2004 *type = DBTYPE_OVM_EXEKUT;
2005 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
2006 *type = DBTYPE_OVM_REQ;
2007 else
2008 return IE_ENUM;
2009 return IE_SUCCESS;
2013 /* Convert ISDS dbType enum @type to UTF-8 string.
2014 * @Return pointer to static string, or NULL if unknown enum value */
2015 static const xmlChar *isds_DbType2string(const isds_DbType type) {
2016 switch(type) {
2017 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2018 * of view of generic public SOAP interface. */
2019 case DBTYPE_FO: return(BAD_CAST "FO"); break;
2020 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
2021 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
2022 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
2023 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
2024 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2025 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2026 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2027 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2028 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2029 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2030 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2031 default: return NULL; break;
2036 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2037 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2038 if (!string || !type) return IE_INVAL;
2040 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2041 *type = USERTYPE_PRIMARY;
2042 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2043 *type = USERTYPE_ENTRUSTED;
2044 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2045 *type = USERTYPE_ADMINISTRATOR;
2046 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2047 *type = USERTYPE_OFFICIAL;
2048 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2049 *type = USERTYPE_OFFICIAL_CERT;
2050 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2051 *type = USERTYPE_LIQUIDATOR;
2052 else
2053 return IE_ENUM;
2054 return IE_SUCCESS;
2058 /* Convert ISDS userType enum @type to UTF-8 string.
2059 * @Return pointer to static string, or NULL if unknown enum value */
2060 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2061 switch(type) {
2062 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2063 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2064 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2065 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2066 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2067 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2068 default: return NULL; break;
2073 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2074 static isds_error string2isds_sender_type(const xmlChar *string,
2075 isds_sender_type *type) {
2076 if (!string || !type) return IE_INVAL;
2078 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2079 *type = SENDERTYPE_PRIMARY;
2080 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2081 *type = SENDERTYPE_ENTRUSTED;
2082 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2083 *type = SENDERTYPE_ADMINISTRATOR;
2084 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2085 *type = SENDERTYPE_OFFICIAL;
2086 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2087 *type = SENDERTYPE_VIRTUAL;
2088 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2089 *type = SENDERTYPE_OFFICIAL_CERT;
2090 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2091 *type = SENDERTYPE_LIQUIDATOR;
2092 else
2093 return IE_ENUM;
2094 return IE_SUCCESS;
2098 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2099 static isds_error string2isds_payment_type(const xmlChar *string,
2100 isds_payment_type *type) {
2101 if (!string || !type) return IE_INVAL;
2103 if (!xmlStrcmp(string, BAD_CAST "K"))
2104 *type = PAYMENT_SENDER;
2105 else if (!xmlStrcmp(string, BAD_CAST "O"))
2106 *type = PAYMENT_RESPONSE;
2107 else if (!xmlStrcmp(string, BAD_CAST "G"))
2108 *type = PAYMENT_SPONSOR;
2109 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2110 *type = PAYMENT_SPONSOR_LIMITED;
2111 else if (!xmlStrcmp(string, BAD_CAST "D"))
2112 *type = PAYMENT_SPONSOR_EXTERNAL;
2113 else if (!xmlStrcmp(string, BAD_CAST "E"))
2114 *type = PAYMENT_STAMP;
2115 else
2116 return IE_ENUM;
2117 return IE_SUCCESS;
2121 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2122 * ciEventType is integer but we convert it from string representation
2123 * directly. */
2124 static isds_error string2isds_credit_event_type(const xmlChar *string,
2125 isds_credit_event_type *type) {
2126 if (!string || !type) return IE_INVAL;
2128 if (!xmlStrcmp(string, BAD_CAST "1"))
2129 *type = ISDS_CREDIT_CHARGED;
2130 else if (!xmlStrcmp(string, BAD_CAST "2"))
2131 *type = ISDS_CREDIT_DISCHARGED;
2132 else if (!xmlStrcmp(string, BAD_CAST "3"))
2133 *type = ISDS_CREDIT_MESSAGE_SENT;
2134 else if (!xmlStrcmp(string, BAD_CAST "4"))
2135 *type = ISDS_CREDIT_STORAGE_SET;
2136 else if (!xmlStrcmp(string, BAD_CAST "5"))
2137 *type = ISDS_CREDIT_EXPIRED;
2138 else
2139 return IE_ENUM;
2140 return IE_SUCCESS;
2144 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2145 * @Return pointer to static string, or NULL if unknown enum value */
2146 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2147 switch(type) {
2148 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2149 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2150 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2151 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2152 default: return NULL; break;
2157 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2158 * ISDSSearch2/searchType value.
2159 * @Return pointer to static string, or NULL if unknown enum value */
2160 static const xmlChar *isds_fulltext_target2string(
2161 const isds_fulltext_target type) {
2162 switch(type) {
2163 case FULLTEXT_ALL: return(BAD_CAST "GENERAL"); break;
2164 case FULLTEXT_ADDRESS: return(BAD_CAST "ADDRESS"); break;
2165 case FULLTEXT_IC: return(BAD_CAST "ICO"); break;
2166 case FULLTEXT_BOX_ID: return(BAD_CAST "DBID"); break;
2167 default: return NULL; break;
2170 #endif /* HAVE_LIBCURL */
2173 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2174 * @Return IE_ENUM if @string is not valid enum member */
2175 static isds_error string2isds_FileMetaType(const xmlChar *string,
2176 isds_FileMetaType *type) {
2177 if (!string || !type) return IE_INVAL;
2179 if (!xmlStrcmp(string, BAD_CAST "main"))
2180 *type = FILEMETATYPE_MAIN;
2181 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2182 *type = FILEMETATYPE_ENCLOSURE;
2183 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2184 *type = FILEMETATYPE_SIGNATURE;
2185 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2186 *type = FILEMETATYPE_META;
2187 else
2188 return IE_ENUM;
2189 return IE_SUCCESS;
2193 /* Convert UTF-8 @string to ISDS hash @algorithm.
2194 * @Return IE_ENUM if @string is not valid enum member */
2195 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2196 isds_hash_algorithm *algorithm) {
2197 if (!string || !algorithm) return IE_INVAL;
2199 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2200 *algorithm = HASH_ALGORITHM_MD5;
2201 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2202 *algorithm = HASH_ALGORITHM_SHA_1;
2203 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2204 *algorithm = HASH_ALGORITHM_SHA_224;
2205 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2206 *algorithm = HASH_ALGORITHM_SHA_256;
2207 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2208 *algorithm = HASH_ALGORITHM_SHA_384;
2209 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2210 *algorithm = HASH_ALGORITHM_SHA_512;
2211 else
2212 return IE_ENUM;
2213 return IE_SUCCESS;
2217 #if HAVE_LIBCURL
2218 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2219 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2220 if (!time || !string) return IE_INVAL;
2222 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2223 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2224 return IE_ERROR;
2226 return IE_SUCCESS;
2230 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2231 * respects the @time microseconds too. */
2232 static isds_error timeval2timestring(const struct timeval *time,
2233 xmlChar **string) {
2234 struct tm broken;
2235 time_t seconds_as_time_t;
2237 if (!time || !string) return IE_INVAL;
2239 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2240 * 32-bit long in Microsoft API. Convert value to the type expected by
2241 * gmtime_r(). */
2242 seconds_as_time_t = time->tv_sec;
2243 if (!gmtime_r(&seconds_as_time_t, &broken)) return IE_DATE;
2244 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2246 /* TODO: small negative year should be formatted as "-0012". This is not
2247 * true for glibc "%04d". We should implement it.
2248 * time->tv_usec type is su_seconds_t which is required to be signed
2249 * integer to accomodate values from range [-1, 1000000].
2250 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2251 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2252 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2253 * of the range check above. */
2254 if (-1 == isds_asprintf((char **) string,
2255 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32,
2256 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2257 broken.tm_hour, broken.tm_min, broken.tm_sec,
2258 (int32_t)time->tv_usec))
2259 return IE_ERROR;
2261 return IE_SUCCESS;
2263 #endif /* HAVE_LIBCURL */
2266 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2267 * It respects microseconds too. Microseconds are rounded half up.
2268 * In case of error, @time will be freed. */
2269 static isds_error timestring2timeval(const xmlChar *string,
2270 struct timeval **time) {
2271 struct tm broken;
2272 char *offset, *delim, *endptr;
2273 const int subsecond_resolution = 6;
2274 char subseconds[subsecond_resolution + 1];
2275 _Bool round_up = 0;
2276 int offset_hours, offset_minutes;
2277 int i;
2278 long int long_number;
2279 #ifdef _WIN32
2280 int tmp;
2281 #endif
2283 if (!time) return IE_INVAL;
2284 if (!string) {
2285 zfree(*time);
2286 return IE_INVAL;
2289 memset(&broken, 0, sizeof(broken));
2291 if (!*time) {
2292 *time = calloc(1, sizeof(**time));
2293 if (!*time) return IE_NOMEM;
2294 } else {
2295 memset(*time, 0, sizeof(**time));
2299 /* xsd:date is ISO 8601 string, thus ASCII */
2300 /*TODO: negative year */
2302 #ifdef _WIN32
2303 i = 0;
2304 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2305 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2306 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2307 &i)) < 6) {
2308 zfree(*time);
2309 return IE_DATE;
2312 broken.tm_year -= 1900;
2313 broken.tm_mon--;
2314 broken.tm_isdst = -1;
2315 offset = (char*)string + i;
2316 #else
2317 /* Parse date and time without subseconds and offset */
2318 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2319 if (!offset) {
2320 zfree(*time);
2321 return IE_DATE;
2323 #endif
2325 /* Get subseconds */
2326 if (*offset == '.' ) {
2327 offset++;
2329 /* Copy first 6 digits, pad it with zeros.
2330 * Current server implementation uses only millisecond resolution. */
2331 /* TODO: isdigit() is locale sensitive */
2332 for (i = 0;
2333 i < subsecond_resolution && isdigit(*offset);
2334 i++, offset++) {
2335 subseconds[i] = *offset;
2337 if (subsecond_resolution == i && isdigit(*offset)) {
2338 /* Check 7th digit for rounding */
2339 if (*offset >= '5') round_up = 1;
2340 offset++;
2342 for (; i < subsecond_resolution; i++) {
2343 subseconds[i] = '0';
2345 subseconds[subsecond_resolution] = '\0';
2347 /* Convert it into integer */
2348 long_number = strtol(subseconds, &endptr, 10);
2349 if (*endptr != '\0' || long_number == LONG_MIN ||
2350 long_number == LONG_MAX) {
2351 zfree(*time);
2352 return IE_DATE;
2354 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2355 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2356 * microseconds" and "the type shall be a signed integer capable of
2357 * storing values at least in the range [-1, 1000000]. */
2358 if (long_number < -1 || long_number >= 1000000) {
2359 zfree(*time);
2360 return IE_DATE;
2362 (*time)->tv_usec = long_number;
2364 /* Round the subseconds */
2365 if (round_up) {
2366 if (999999 == (*time)->tv_usec) {
2367 (*time)->tv_usec = 0;
2368 broken.tm_sec++;
2369 } else {
2370 (*time)->tv_usec++;
2374 /* move to the zone offset delimiter or signal NULL*/
2375 delim = strchr(offset, '-');
2376 if (!delim)
2377 delim = strchr(offset, '+');
2378 if (!delim)
2379 delim = strchr(offset, 'Z');
2380 offset = delim;
2383 /* Get zone offset */
2384 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2385 * "" equals to "Z" and it means UTC zone. */
2386 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2387 * colon separator */
2388 if (offset && (*offset == '-' || *offset == '+')) {
2389 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2390 zfree(*time);
2391 return IE_DATE;
2393 if (*offset == '+') {
2394 broken.tm_hour -= offset_hours;
2395 broken.tm_min -= offset_minutes;
2396 } else {
2397 broken.tm_hour += offset_hours;
2398 broken.tm_min += offset_minutes;
2402 /* Convert to time_t */
2403 (*time)->tv_sec = _isds_timegm(&broken);
2404 if ((*time)->tv_sec == (time_t) -1) {
2405 zfree(*time);
2406 return IE_DATE;
2409 return IE_SUCCESS;
2413 /* Convert unsigned int into isds_message_status.
2414 * @context is session context
2415 * @number is pointer to number value. NULL will be treated as invalid value.
2416 * @status is automatically reallocated status
2417 * @return IE_SUCCESS, or error code and free status */
2418 static isds_error uint2isds_message_status(struct isds_ctx *context,
2419 const unsigned long int *number, isds_message_status **status) {
2420 if (!context) return IE_INVALID_CONTEXT;
2421 if (!status) return IE_INVAL;
2423 free(*status); *status = NULL;
2424 if (!number) return IE_INVAL;
2426 if (*number < 1 || *number > 10) {
2427 isds_printf_message(context, _("Invalid message status value: %lu"),
2428 *number);
2429 return IE_ENUM;
2432 *status = malloc(sizeof(**status));
2433 if (!*status) return IE_NOMEM;
2435 **status = 1 << *number;
2436 return IE_SUCCESS;
2440 /* Convert event description string into isds_event members type and
2441 * description
2442 * @string is raw event description starting with event prefix
2443 * @event is structure where to store type and stripped description to
2444 * @return standard error code, unknown prefix is not classified as an error.
2445 * */
2446 static isds_error eventstring2event(const xmlChar *string,
2447 struct isds_event* event) {
2448 const xmlChar *known_prefixes[] = {
2449 BAD_CAST "EV0:",
2450 BAD_CAST "EV1:",
2451 BAD_CAST "EV2:",
2452 BAD_CAST "EV3:",
2453 BAD_CAST "EV4:",
2454 BAD_CAST "EV5:",
2455 BAD_CAST "EV11:",
2456 BAD_CAST "EV12:",
2457 BAD_CAST "EV13:"
2459 const isds_event_type types[] = {
2460 EVENT_ENTERED_SYSTEM,
2461 EVENT_ACCEPTED_BY_RECIPIENT,
2462 EVENT_ACCEPTED_BY_FICTION,
2463 EVENT_UNDELIVERABLE,
2464 EVENT_COMMERCIAL_ACCEPTED,
2465 EVENT_DELIVERED,
2466 EVENT_PRIMARY_LOGIN,
2467 EVENT_ENTRUSTED_LOGIN,
2468 EVENT_SYSCERT_LOGIN
2470 unsigned int index;
2471 size_t length;
2473 if (!string || !event) return IE_INVAL;
2475 if (!event->type) {
2476 event->type = malloc(sizeof(*event->type));
2477 if (!(event->type)) return IE_NOMEM;
2479 zfree(event->description);
2481 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2482 index++) {
2483 length = xmlUTF8Strlen(known_prefixes[index]);
2485 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2486 /* Prefix is known */
2487 *event->type = types[index];
2489 /* Strip prefix from description and spaces */
2490 /* TODO: Recognize all white spaces from UCS blank class and
2491 * operate on UTF-8 chars. */
2492 for (; string[length] != '\0' && string[length] == ' '; length++);
2493 event->description = strdup((char *) (string + length));
2494 if (!(event->description)) return IE_NOMEM;
2496 return IE_SUCCESS;
2500 /* Unknown event prefix.
2501 * XSD allows any string */
2502 char *string_locale = _isds_utf82locale((char *) string);
2503 isds_log(ILF_ISDS, ILL_WARNING,
2504 _("Unknown delivery info event prefix: %s\n"), string_locale);
2505 free(string_locale);
2507 *event->type = EVENT_UKNOWN;
2508 event->description = strdup((char *) string);
2509 if (!(event->description)) return IE_NOMEM;
2511 return IE_SUCCESS;
2515 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2516 * and leave label */
2517 #define EXTRACT_STRING(element, string) { \
2518 xmlXPathFreeObject(result); \
2519 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2520 if (NULL == (result)) { \
2521 err = IE_ERROR; \
2522 goto leave; \
2524 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2525 if (result->nodesetval->nodeNr > 1) { \
2526 isds_printf_message(context, _("Multiple %s element"), element); \
2527 err = IE_ERROR; \
2528 goto leave; \
2530 (string) = (char *) \
2531 xmlXPathCastNodeSetToString(result->nodesetval); \
2532 if (NULL == (string)) { \
2533 err = IE_ERROR; \
2534 goto leave; \
2539 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2541 char *string = NULL; \
2542 EXTRACT_STRING(element, string); \
2544 if (string) { \
2545 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2546 if (!(booleanPtr)) { \
2547 free(string); \
2548 err = IE_NOMEM; \
2549 goto leave; \
2552 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2553 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2554 *(booleanPtr) = 1; \
2555 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2556 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2557 *(booleanPtr) = 0; \
2558 else { \
2559 char *string_locale = _isds_utf82locale((char*)string); \
2560 isds_printf_message(context, \
2561 _("%s value is not valid boolean: %s"), \
2562 element, string_locale); \
2563 free(string_locale); \
2564 free(string); \
2565 err = IE_ERROR; \
2566 goto leave; \
2569 free(string); \
2573 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2575 char *string = NULL; \
2576 EXTRACT_STRING(element, string); \
2578 if (NULL == string) { \
2579 isds_printf_message(context, _("%s element is empty"), element); \
2580 err = IE_ERROR; \
2581 goto leave; \
2583 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2584 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2585 (boolean) = 1; \
2586 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2587 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2588 (boolean) = 0; \
2589 else { \
2590 char *string_locale = _isds_utf82locale((char*)string); \
2591 isds_printf_message(context, \
2592 _("%s value is not valid boolean: %s"), \
2593 element, string_locale); \
2594 free(string_locale); \
2595 free(string); \
2596 err = IE_ERROR; \
2597 goto leave; \
2600 free(string); \
2603 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2605 char *string = NULL; \
2606 EXTRACT_STRING(element, string); \
2607 if (string) { \
2608 long int number; \
2609 char *endptr; \
2611 number = strtol((char*)string, &endptr, 10); \
2613 if (*endptr != '\0') { \
2614 char *string_locale = _isds_utf82locale((char *)string); \
2615 isds_printf_message(context, \
2616 _("%s is not valid integer: %s"), \
2617 element, string_locale); \
2618 free(string_locale); \
2619 free(string); \
2620 err = IE_ISDS; \
2621 goto leave; \
2624 if (number == LONG_MIN || number == LONG_MAX) { \
2625 char *string_locale = _isds_utf82locale((char *)string); \
2626 isds_printf_message(context, \
2627 _("%s value out of range of long int: %s"), \
2628 element, string_locale); \
2629 free(string_locale); \
2630 free(string); \
2631 err = IE_ERROR; \
2632 goto leave; \
2635 free(string); string = NULL; \
2637 if (!(preallocated)) { \
2638 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2639 if (!(longintPtr)) { \
2640 err = IE_NOMEM; \
2641 goto leave; \
2644 *(longintPtr) = number; \
2648 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2650 char *string = NULL; \
2651 EXTRACT_STRING(element, string); \
2652 if (string) { \
2653 long int number; \
2654 char *endptr; \
2656 number = strtol((char*)string, &endptr, 10); \
2658 if (*endptr != '\0') { \
2659 char *string_locale = _isds_utf82locale((char *)string); \
2660 isds_printf_message(context, \
2661 _("%s is not valid integer: %s"), \
2662 element, string_locale); \
2663 free(string_locale); \
2664 free(string); \
2665 err = IE_ISDS; \
2666 goto leave; \
2669 if (number == LONG_MIN || number == LONG_MAX) { \
2670 char *string_locale = _isds_utf82locale((char *)string); \
2671 isds_printf_message(context, \
2672 _("%s value out of range of long int: %s"), \
2673 element, string_locale); \
2674 free(string_locale); \
2675 free(string); \
2676 err = IE_ERROR; \
2677 goto leave; \
2680 free(string); string = NULL; \
2681 if (number < 0) { \
2682 isds_printf_message(context, \
2683 _("%s value is negative: %ld"), element, number); \
2684 err = IE_ERROR; \
2685 goto leave; \
2688 if (!(preallocated)) { \
2689 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2690 if (!(ulongintPtr)) { \
2691 err = IE_NOMEM; \
2692 goto leave; \
2695 *(ulongintPtr) = number; \
2699 #define EXTRACT_DATE(element, tmPtr) { \
2700 char *string = NULL; \
2701 EXTRACT_STRING(element, string); \
2702 if (NULL != string) { \
2703 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2704 if (NULL == (tmPtr)) { \
2705 free(string); \
2706 err = IE_NOMEM; \
2707 goto leave; \
2709 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2710 if (err) { \
2711 if (err == IE_NOTSUP) { \
2712 err = IE_ISDS; \
2713 char *string_locale = _isds_utf82locale(string); \
2714 char *element_locale = _isds_utf82locale(element); \
2715 isds_printf_message(context, _("Invalid %s value: %s"), \
2716 element_locale, string_locale); \
2717 free(string_locale); \
2718 free(element_locale); \
2720 free(string); \
2721 goto leave; \
2723 free(string); \
2727 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2728 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2729 NULL); \
2730 if ((required) && (!string)) { \
2731 char *attribute_locale = _isds_utf82locale(attribute); \
2732 char *element_locale = \
2733 _isds_utf82locale((char *)xpath_ctx->node->name); \
2734 isds_printf_message(context, \
2735 _("Could not extract required %s attribute value from " \
2736 "%s element"), attribute_locale, element_locale); \
2737 free(element_locale); \
2738 free(attribute_locale); \
2739 err = IE_ERROR; \
2740 goto leave; \
2745 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2747 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2748 (xmlChar *) (string)); \
2749 if (!node) { \
2750 isds_printf_message(context, \
2751 _("Could not add %s child to %s element"), \
2752 element, (parent)->name); \
2753 err = IE_ERROR; \
2754 goto leave; \
2758 #define INSERT_STRING(parent, element, string) \
2759 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2761 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2763 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2764 else { INSERT_STRING(parent, element, "false"); } \
2767 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2769 if (booleanPtr) { \
2770 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2771 } else { \
2772 INSERT_STRING(parent, element, NULL); \
2776 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2777 if ((longintPtr)) { \
2778 /* FIXME: locale sensitive */ \
2779 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2780 err = IE_NOMEM; \
2781 goto leave; \
2783 INSERT_STRING(parent, element, buffer) \
2784 free(buffer); (buffer) = NULL; \
2785 } else { INSERT_STRING(parent, element, NULL) } \
2788 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2789 if ((ulongintPtr)) { \
2790 /* FIXME: locale sensitive */ \
2791 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2792 err = IE_NOMEM; \
2793 goto leave; \
2795 INSERT_STRING(parent, element, buffer) \
2796 free(buffer); (buffer) = NULL; \
2797 } else { INSERT_STRING(parent, element, NULL) } \
2800 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2802 /* FIXME: locale sensitive */ \
2803 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2804 err = IE_NOMEM; \
2805 goto leave; \
2807 INSERT_STRING(parent, element, buffer) \
2808 free(buffer); (buffer) = NULL; \
2811 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2812 * new attribute. */
2813 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2815 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2816 (xmlChar *) (string)); \
2817 if (!attribute_node) { \
2818 isds_printf_message(context, _("Could not add %s " \
2819 "attribute to %s element"), \
2820 (attribute), (parent)->name); \
2821 err = IE_ERROR; \
2822 goto leave; \
2826 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2827 if (string) { \
2828 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2829 if (length > (maximum)) { \
2830 isds_printf_message(context, \
2831 ngettext("%s has more than %d characters", \
2832 "%s has more than %d characters", (maximum)), \
2833 (name), (maximum)); \
2834 err = IE_2BIG; \
2835 goto leave; \
2837 if (length < (minimum)) { \
2838 isds_printf_message(context, \
2839 ngettext("%s has less than %d characters", \
2840 "%s has less than %d characters", (minimum)), \
2841 (name), (minimum)); \
2842 err = IE_2SMALL; \
2843 goto leave; \
2848 #define INSERT_ELEMENT(child, parent, element) \
2850 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2851 if (!(child)) { \
2852 isds_printf_message(context, \
2853 _("Could not add %s child to %s element"), \
2854 (element), (parent)->name); \
2855 err = IE_ERROR; \
2856 goto leave; \
2861 /* Find child element by name in given XPath context and switch context onto
2862 * it. The child must be uniq and must exist. Otherwise fails.
2863 * @context is ISDS context
2864 * @child is child element name
2865 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2866 * into it child. In error case, the @xpath_ctx keeps original value. */
2867 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2868 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2869 isds_error err = IE_SUCCESS;
2870 xmlXPathObjectPtr result = NULL;
2872 if (!context) return IE_INVALID_CONTEXT;
2873 if (!child || !xpath_ctx) return IE_INVAL;
2875 /* Find child */
2876 result = xmlXPathEvalExpression(child, xpath_ctx);
2877 if (!result) {
2878 err = IE_XML;
2879 goto leave;
2882 /* No match */
2883 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2884 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2885 char *child_locale = _isds_utf82locale((char*) child);
2886 isds_printf_message(context,
2887 _("%s element does not contain %s child"),
2888 parent_locale, child_locale);
2889 free(child_locale);
2890 free(parent_locale);
2891 err = IE_NOEXIST;
2892 goto leave;
2895 /* More matches */
2896 if (result->nodesetval->nodeNr > 1) {
2897 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2898 char *child_locale = _isds_utf82locale((char*) child);
2899 isds_printf_message(context,
2900 _("%s element contains multiple %s children"),
2901 parent_locale, child_locale);
2902 free(child_locale);
2903 free(parent_locale);
2904 err = IE_NOTUNIQ;
2905 goto leave;
2908 /* Switch context */
2909 xpath_ctx->node = result->nodesetval->nodeTab[0];
2911 leave:
2912 xmlXPathFreeObject(result);
2913 return err;
2918 #if HAVE_LIBCURL
2919 /* Find and convert XSD:gPersonName group in current node into structure
2920 * @context is ISDS context
2921 * @personName is automatically reallocated person name structure. If no member
2922 * value is found, will be freed.
2923 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2924 * elements
2925 * In case of error @personName will be freed. */
2926 static isds_error extract_gPersonName(struct isds_ctx *context,
2927 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2928 isds_error err = IE_SUCCESS;
2929 xmlXPathObjectPtr result = NULL;
2931 if (!context) return IE_INVALID_CONTEXT;
2932 if (!personName) return IE_INVAL;
2933 isds_PersonName_free(personName);
2934 if (!xpath_ctx) return IE_INVAL;
2937 *personName = calloc(1, sizeof(**personName));
2938 if (!*personName) {
2939 err = IE_NOMEM;
2940 goto leave;
2943 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2944 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2945 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2946 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2948 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2949 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2950 isds_PersonName_free(personName);
2952 leave:
2953 if (err) isds_PersonName_free(personName);
2954 xmlXPathFreeObject(result);
2955 return err;
2959 /* Find and convert XSD:gAddress group extended with relevant
2960 * tdbPersonalOwnerinfo members in current node into structure
2961 * @context is ISDS context
2962 * @address is automatically reallocated address structure. If no member
2963 * value is found, will be freed.
2964 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2965 * elements
2966 * In case of error @address will be freed. */
2967 static isds_error extract_gAddress(struct isds_ctx *context,
2968 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2969 isds_error err = IE_SUCCESS;
2970 xmlXPathObjectPtr result = NULL;
2972 if (!context) return IE_INVALID_CONTEXT;
2973 if (!address) return IE_INVAL;
2974 isds_Address_free(address);
2975 if (!xpath_ctx) return IE_INVAL;
2978 *address = calloc(1, sizeof(**address));
2979 if (!*address) {
2980 err = IE_NOMEM;
2981 goto leave;
2984 EXTRACT_LONGINT("isds:adCode", (*address)->adCode, 0);
2985 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2986 EXTRACT_STRING("isds:adDistrict", (*address)->adDistrict);
2987 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2988 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2989 EXTRACT_STRING("isds:adNumberInMunicipality",
2990 (*address)->adNumberInMunicipality);
2991 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2992 EXTRACT_STRING("isds:adState", (*address)->adState);
2994 if (!(*address)->adCity && !(*address)->adStreet &&
2995 !(*address)->adNumberInStreet &&
2996 !(*address)->adNumberInMunicipality &&
2997 !(*address)->adZipCode && !(*address)->adState)
2998 isds_Address_free(address);
3000 leave:
3001 if (err) isds_Address_free(address);
3002 xmlXPathFreeObject(result);
3003 return err;
3007 /* Find and convert isds:biDate element in current node into structure
3008 * @context is ISDS context
3009 * @biDate is automatically reallocated birth date structure. If no member
3010 * value is found, will be freed.
3011 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3012 * element
3013 * In case of error @biDate will be freed. */
3014 static isds_error extract_BiDate(struct isds_ctx *context,
3015 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
3016 isds_error err = IE_SUCCESS;
3017 xmlXPathObjectPtr result = NULL;
3018 char *string = NULL;
3020 if (!context) return IE_INVALID_CONTEXT;
3021 if (!biDate) return IE_INVAL;
3022 zfree(*biDate);
3023 if (!xpath_ctx) return IE_INVAL;
3025 EXTRACT_STRING("isds:biDate", string);
3026 if (string) {
3027 *biDate = calloc(1, sizeof(**biDate));
3028 if (!*biDate) {
3029 err = IE_NOMEM;
3030 goto leave;
3032 err = _isds_datestring2tm((xmlChar *)string, *biDate);
3033 if (err) {
3034 if (err == IE_NOTSUP) {
3035 err = IE_ISDS;
3036 char *string_locale = _isds_utf82locale(string);
3037 isds_printf_message(context,
3038 _("Invalid isds:biDate value: %s"), string_locale);
3039 free(string_locale);
3041 goto leave;
3045 leave:
3046 if (err) zfree(*biDate);
3047 free(string);
3048 xmlXPathFreeObject(result);
3049 return err;
3053 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3054 * @context is ISDS context
3055 * @db_owner_info is automatically reallocated box owner info structure
3056 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3057 * XSD:tdbPersonalOwenerInfo element
3058 * In case of error @db_owner_info will be freed. */
3059 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
3060 struct isds_DbOwnerInfo **db_owner_info,
3061 xmlXPathContextPtr xpath_ctx) {
3062 isds_error err = IE_SUCCESS;
3063 xmlXPathObjectPtr result = NULL;
3064 char *string = NULL;
3066 if (!context) return IE_INVALID_CONTEXT;
3067 if (!db_owner_info) return IE_INVAL;
3068 isds_DbOwnerInfo_free(db_owner_info);
3069 if (!xpath_ctx) return IE_INVAL;
3072 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3073 if (!*db_owner_info) {
3074 err = IE_NOMEM;
3075 goto leave;
3078 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
3080 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info)->aifoIsds);
3082 EXTRACT_STRING("isds:dbType", string);
3083 if (string) {
3084 (*db_owner_info)->dbType =
3085 calloc(1, sizeof(*((*db_owner_info)->dbType)));
3086 if (!(*db_owner_info)->dbType) {
3087 err = IE_NOMEM;
3088 goto leave;
3090 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
3091 if (err) {
3092 zfree((*db_owner_info)->dbType);
3093 if (err == IE_ENUM) {
3094 err = IE_ISDS;
3095 char *string_locale = _isds_utf82locale(string);
3096 isds_printf_message(context, _("Unknown isds:dbType: %s"),
3097 string_locale);
3098 free(string_locale);
3100 goto leave;
3102 zfree(string);
3105 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
3107 err = extract_gPersonName(context, &(*db_owner_info)->personName,
3108 xpath_ctx);
3109 if (err) goto leave;
3111 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3113 (*db_owner_info)->birthInfo =
3114 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3115 if (!(*db_owner_info)->birthInfo) {
3116 err = IE_NOMEM;
3117 goto leave;
3119 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3120 xpath_ctx);
3121 if (err) goto leave;
3122 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3123 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3124 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3125 if (!(*db_owner_info)->birthInfo->biDate &&
3126 !(*db_owner_info)->birthInfo->biCity &&
3127 !(*db_owner_info)->birthInfo->biCounty &&
3128 !(*db_owner_info)->birthInfo->biState)
3129 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3131 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3132 if (err) goto leave;
3134 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3135 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3136 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3137 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3138 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3140 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3142 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3143 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3144 (*db_owner_info)->dbOpenAddressing);
3146 leave:
3147 if (err) isds_DbOwnerInfo_free(db_owner_info);
3148 free(string);
3149 xmlXPathFreeObject(result);
3150 return err;
3154 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3155 * @context is session context
3156 * @owner is libisds structure with box description.
3157 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3158 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3159 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3160 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3161 * ignored.
3162 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3163 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3164 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3165 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3166 * type. */
3167 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3168 const struct isds_DbOwnerInfo *owner, _Bool pfo_subtype,
3169 xmlNodePtr db_owner_info) {
3171 isds_error err = IE_SUCCESS;
3172 xmlNodePtr node;
3173 xmlChar *string = NULL;
3174 const xmlChar *type_string = NULL;
3176 if (!context) return IE_INVALID_CONTEXT;
3177 if (!owner || !db_owner_info) return IE_INVAL;
3180 /* XXX: All the elements except email and telNumber are mandatory. */
3181 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3182 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3184 if (pfo_subtype) {
3185 INSERT_BOOLEAN(db_owner_info, "aifoIsds", owner->aifoIsds);
3188 if (!pfo_subtype) {
3189 /* dbType */
3190 if (owner->dbType) {
3191 type_string = isds_DbType2string(*(owner->dbType));
3192 if (!type_string) {
3193 isds_printf_message(context, _("Invalid dbType value: %d"),
3194 *(owner->dbType));
3195 err = IE_ENUM;
3196 goto leave;
3199 INSERT_STRING(db_owner_info, "dbType", type_string);
3201 INSERT_STRING(db_owner_info, "ic", owner->ic);
3204 INSERT_STRING(db_owner_info, "pnFirstName",
3205 (NULL == owner->personName) ? NULL: owner->personName->pnFirstName);
3206 INSERT_STRING(db_owner_info, "pnMiddleName",
3207 (NULL == owner->personName) ? NULL: owner->personName->pnMiddleName);
3208 INSERT_STRING(db_owner_info, "pnLastName",
3209 (NULL == owner->personName) ? NULL: owner->personName->pnLastName);
3210 if (!pfo_subtype) {
3211 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3212 (NULL == owner->personName) ? NULL:
3213 owner->personName->pnLastNameAtBirth);
3215 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3218 if (NULL != owner->birthInfo && NULL != owner->birthInfo->biDate) {
3219 err = tm2datestring(owner->birthInfo->biDate, &string);
3220 if (err) goto leave;
3222 INSERT_STRING(db_owner_info, "biDate", string);
3223 zfree(string);
3225 INSERT_STRING(db_owner_info, "biCity",
3226 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCity);
3227 INSERT_STRING(db_owner_info, "biCounty",
3228 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biCounty);
3229 INSERT_STRING(db_owner_info, "biState",
3230 (NULL == owner->birthInfo) ? NULL: owner->birthInfo->biState);
3232 if (pfo_subtype) {
3233 INSERT_LONGINT(db_owner_info, "adCode",
3234 (NULL == owner->address) ? NULL : owner->address->adCode,
3235 string);
3237 INSERT_STRING(db_owner_info, "adCity",
3238 (NULL == owner->address) ? NULL: owner->address->adCity);
3239 if (pfo_subtype) {
3240 INSERT_STRING(db_owner_info, "adDistrict",
3241 (NULL == owner->address) ? NULL: owner->address->adDistrict);
3243 INSERT_STRING(db_owner_info, "adStreet",
3244 (NULL == owner->address) ? NULL: owner->address->adStreet);
3245 INSERT_STRING(db_owner_info, "adNumberInStreet",
3246 (NULL == owner->address) ? NULL: owner->address->adNumberInStreet);
3247 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3248 (NULL == owner->address) ? NULL: owner->address->adNumberInMunicipality);
3249 INSERT_STRING(db_owner_info, "adZipCode",
3250 (NULL == owner->address) ? NULL: owner->address->adZipCode);
3251 INSERT_STRING(db_owner_info, "adState",
3252 (NULL == owner->address) ? NULL: owner->address->adState);
3254 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3256 if (!pfo_subtype) {
3257 INSERT_STRING(db_owner_info, "email", owner->email);
3258 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3260 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3261 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3263 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3264 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3266 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3268 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3269 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3270 owner->dbOpenAddressing);
3273 leave:
3274 free(string);
3275 return err;
3279 /* Convert XSD:tDbUserInfo XML tree into structure
3280 * @context is ISDS context
3281 * @db_user_info is automatically reallocated user info structure
3282 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3283 * In case of error @db_user_info will be freed. */
3284 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3285 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3286 isds_error err = IE_SUCCESS;
3287 xmlXPathObjectPtr result = NULL;
3288 char *string = NULL;
3290 if (!context) return IE_INVALID_CONTEXT;
3291 if (!db_user_info) return IE_INVAL;
3292 isds_DbUserInfo_free(db_user_info);
3293 if (!xpath_ctx) return IE_INVAL;
3296 *db_user_info = calloc(1, sizeof(**db_user_info));
3297 if (!*db_user_info) {
3298 err = IE_NOMEM;
3299 goto leave;
3302 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info)->aifo_ticket, 0);
3304 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3306 EXTRACT_STRING("isds:userType", string);
3307 if (string) {
3308 (*db_user_info)->userType =
3309 calloc(1, sizeof(*((*db_user_info)->userType)));
3310 if (!(*db_user_info)->userType) {
3311 err = IE_NOMEM;
3312 goto leave;
3314 err = string2isds_UserType((xmlChar *)string,
3315 (*db_user_info)->userType);
3316 if (err) {
3317 zfree((*db_user_info)->userType);
3318 if (err == IE_ENUM) {
3319 err = IE_ISDS;
3320 char *string_locale = _isds_utf82locale(string);
3321 isds_printf_message(context,
3322 _("Unknown isds:userType value: %s"), string_locale);
3323 free(string_locale);
3325 goto leave;
3327 zfree(string);
3330 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3332 (*db_user_info)->personName =
3333 calloc(1, sizeof(*((*db_user_info)->personName)));
3334 if (!(*db_user_info)->personName) {
3335 err = IE_NOMEM;
3336 goto leave;
3339 err = extract_gPersonName(context, &(*db_user_info)->personName,
3340 xpath_ctx);
3341 if (err) goto leave;
3343 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3344 if (err) goto leave;
3346 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3347 if (err) goto leave;
3349 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3350 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3352 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3353 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3354 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3356 /* ???: Default value is "CZ" according specification. Should we provide
3357 * it? */
3358 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3360 leave:
3361 if (err) isds_DbUserInfo_free(db_user_info);
3362 free(string);
3363 xmlXPathFreeObject(result);
3364 return err;
3368 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3369 * @context is session context
3370 * @user is libisds structure with user description
3371 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3372 * @db_user_info is XML element of XSD:tDbUserInfo */
3373 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3374 const struct isds_DbUserInfo *user, _Bool honor_aifo_ticket,
3375 xmlNodePtr db_user_info) {
3377 isds_error err = IE_SUCCESS;
3378 xmlNodePtr node;
3379 xmlAttrPtr attribute_node;
3380 xmlChar *string = NULL;
3382 if (!context) return IE_INVALID_CONTEXT;
3383 if (!user || !db_user_info) return IE_INVAL;
3385 /* Build XSD:tDbUserInfo */
3387 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3388 * allow it everywhere. */
3389 if (honor_aifo_ticket && user->aifo_ticket) {
3390 INSERT_STRING_ATTRIBUTE(db_user_info, "AIFOTicket", user->aifo_ticket);
3393 if (user->personName) {
3394 INSERT_STRING(db_user_info, "pnFirstName",
3395 user->personName->pnFirstName);
3396 INSERT_STRING(db_user_info, "pnMiddleName",
3397 user->personName->pnMiddleName);
3398 INSERT_STRING(db_user_info, "pnLastName",
3399 user->personName->pnLastName);
3400 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3401 user->personName->pnLastNameAtBirth);
3403 if (user->address) {
3404 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3405 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3406 INSERT_STRING(db_user_info, "adNumberInStreet",
3407 user->address->adNumberInStreet);
3408 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3409 user->address->adNumberInMunicipality);
3410 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3411 INSERT_STRING(db_user_info, "adState", user->address->adState);
3413 if (user->biDate) {
3414 if (!tm2datestring(user->biDate, &string))
3415 INSERT_STRING(db_user_info, "biDate", string);
3416 zfree(string);
3418 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3419 INSERT_STRING(db_user_info, "userID", user->userID);
3421 /* userType */
3422 if (user->userType) {
3423 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3424 if (!type_string) {
3425 isds_printf_message(context, _("Invalid userType value: %d"),
3426 *(user->userType));
3427 err = IE_ENUM;
3428 goto leave;
3430 INSERT_STRING(db_user_info, "userType", type_string);
3433 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3434 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3435 INSERT_STRING(db_user_info, "ic", user->ic);
3436 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3437 INSERT_STRING(db_user_info, "firmName", user->firmName);
3438 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3439 INSERT_STRING(db_user_info, "caCity", user->caCity);
3440 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3441 INSERT_STRING(db_user_info, "caState", user->caState);
3443 leave:
3444 free(string);
3445 return err;
3449 /* Convert XSD:tPDZRec XML tree into structure
3450 * @context is ISDS context
3451 * @permission is automatically reallocated commercial permission structure
3452 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3453 * In case of error @permission will be freed. */
3454 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3455 struct isds_commercial_permission **permission,
3456 xmlXPathContextPtr xpath_ctx) {
3457 isds_error err = IE_SUCCESS;
3458 xmlXPathObjectPtr result = NULL;
3459 char *string = NULL;
3461 if (!context) return IE_INVALID_CONTEXT;
3462 if (!permission) return IE_INVAL;
3463 isds_commercial_permission_free(permission);
3464 if (!xpath_ctx) return IE_INVAL;
3467 *permission = calloc(1, sizeof(**permission));
3468 if (!*permission) {
3469 err = IE_NOMEM;
3470 goto leave;
3473 EXTRACT_STRING("isds:PDZType", string);
3474 if (string) {
3475 err = string2isds_payment_type((xmlChar *)string,
3476 &(*permission)->type);
3477 if (err) {
3478 if (err == IE_ENUM) {
3479 err = IE_ISDS;
3480 char *string_locale = _isds_utf82locale(string);
3481 isds_printf_message(context,
3482 _("Unknown isds:PDZType value: %s"), string_locale);
3483 free(string_locale);
3485 goto leave;
3487 zfree(string);
3490 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3491 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3493 EXTRACT_STRING("isds:PDZExpire", string);
3494 if (string) {
3495 err = timestring2timeval((xmlChar *) string,
3496 &((*permission)->expiration));
3497 if (err) {
3498 char *string_locale = _isds_utf82locale(string);
3499 if (err == IE_DATE) err = IE_ISDS;
3500 isds_printf_message(context,
3501 _("Could not convert PDZExpire as ISO time: %s"),
3502 string_locale);
3503 free(string_locale);
3504 goto leave;
3506 zfree(string);
3509 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3510 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3512 leave:
3513 if (err) isds_commercial_permission_free(permission);
3514 free(string);
3515 xmlXPathFreeObject(result);
3516 return err;
3520 /* Convert XSD:tCiRecord XML tree into structure
3521 * @context is ISDS context
3522 * @event is automatically reallocated commercial credit event structure
3523 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3524 * In case of error @event will be freed. */
3525 static isds_error extract_CiRecord(struct isds_ctx *context,
3526 struct isds_credit_event **event,
3527 xmlXPathContextPtr xpath_ctx) {
3528 isds_error err = IE_SUCCESS;
3529 xmlXPathObjectPtr result = NULL;
3530 char *string = NULL;
3531 long int *number_ptr;
3533 if (!context) return IE_INVALID_CONTEXT;
3534 if (!event) return IE_INVAL;
3535 isds_credit_event_free(event);
3536 if (!xpath_ctx) return IE_INVAL;
3539 *event = calloc(1, sizeof(**event));
3540 if (!*event) {
3541 err = IE_NOMEM;
3542 goto leave;
3545 EXTRACT_STRING("isds:ciEventTime", string);
3546 if (string) {
3547 err = timestring2timeval((xmlChar *) string,
3548 &(*event)->time);
3549 if (err) {
3550 char *string_locale = _isds_utf82locale(string);
3551 if (err == IE_DATE) err = IE_ISDS;
3552 isds_printf_message(context,
3553 _("Could not convert ciEventTime as ISO time: %s"),
3554 string_locale);
3555 free(string_locale);
3556 goto leave;
3558 zfree(string);
3561 EXTRACT_STRING("isds:ciEventType", string);
3562 if (string) {
3563 err = string2isds_credit_event_type((xmlChar *)string,
3564 &(*event)->type);
3565 if (err) {
3566 if (err == IE_ENUM) {
3567 err = IE_ISDS;
3568 char *string_locale = _isds_utf82locale(string);
3569 isds_printf_message(context,
3570 _("Unknown isds:ciEventType value: %s"), string_locale);
3571 free(string_locale);
3573 goto leave;
3575 zfree(string);
3578 number_ptr = &((*event)->credit_change);
3579 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3580 number_ptr = &(*event)->new_credit;
3581 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3583 switch((*event)->type) {
3584 case ISDS_CREDIT_CHARGED:
3585 EXTRACT_STRING("isds:ciTransID",
3586 (*event)->details.charged.transaction);
3587 break;
3588 case ISDS_CREDIT_DISCHARGED:
3589 EXTRACT_STRING("isds:ciTransID",
3590 (*event)->details.discharged.transaction);
3591 break;
3592 case ISDS_CREDIT_MESSAGE_SENT:
3593 EXTRACT_STRING("isds:ciRecipientID",
3594 (*event)->details.message_sent.recipient);
3595 EXTRACT_STRING("isds:ciPDZID",
3596 (*event)->details.message_sent.message_id);
3597 break;
3598 case ISDS_CREDIT_STORAGE_SET:
3599 number_ptr = &((*event)->details.storage_set.new_capacity);
3600 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3601 EXTRACT_DATE("isds:ciNewFrom",
3602 (*event)->details.storage_set.new_valid_from);
3603 EXTRACT_DATE("isds:ciNewTo",
3604 (*event)->details.storage_set.new_valid_to);
3605 EXTRACT_LONGINT("isds:ciOldCapacity",
3606 (*event)->details.storage_set.old_capacity, 0);
3607 EXTRACT_DATE("isds:ciOldFrom",
3608 (*event)->details.storage_set.old_valid_from);
3609 EXTRACT_DATE("isds:ciOldTo",
3610 (*event)->details.storage_set.old_valid_to);
3611 EXTRACT_STRING("isds:ciDoneBy",
3612 (*event)->details.storage_set.initiator);
3613 break;
3614 case ISDS_CREDIT_EXPIRED:
3615 break;
3618 leave:
3619 if (err) isds_credit_event_free(event);
3620 free(string);
3621 xmlXPathFreeObject(result);
3622 return err;
3626 #endif /* HAVE_LIBCURL */
3629 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3630 * isds_envelope structure. The envelope is automatically allocated but not
3631 * reallocated. The date are just appended into envelope structure.
3632 * @context is ISDS context
3633 * @envelope is automatically allocated message envelope structure
3634 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3635 * In case of error @envelope will be freed. */
3636 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3637 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3638 isds_error err = IE_SUCCESS;
3639 xmlXPathObjectPtr result = NULL;
3641 if (!context) return IE_INVALID_CONTEXT;
3642 if (!envelope) return IE_INVAL;
3643 if (!xpath_ctx) return IE_INVAL;
3646 if (!*envelope) {
3647 /* Allocate envelope */
3648 *envelope = calloc(1, sizeof(**envelope));
3649 if (!*envelope) {
3650 err = IE_NOMEM;
3651 goto leave;
3653 } else {
3654 /* Else free former data */
3655 zfree((*envelope)->dmSenderOrgUnit);
3656 zfree((*envelope)->dmSenderOrgUnitNum);
3657 zfree((*envelope)->dbIDRecipient);
3658 zfree((*envelope)->dmRecipientOrgUnit);
3659 zfree((*envelope)->dmRecipientOrgUnitNum);
3660 zfree((*envelope)->dmToHands);
3661 zfree((*envelope)->dmAnnotation);
3662 zfree((*envelope)->dmRecipientRefNumber);
3663 zfree((*envelope)->dmSenderRefNumber);
3664 zfree((*envelope)->dmRecipientIdent);
3665 zfree((*envelope)->dmSenderIdent);
3666 zfree((*envelope)->dmLegalTitleLaw);
3667 zfree((*envelope)->dmLegalTitleYear);
3668 zfree((*envelope)->dmLegalTitleSect);
3669 zfree((*envelope)->dmLegalTitlePar);
3670 zfree((*envelope)->dmLegalTitlePoint);
3671 zfree((*envelope)->dmPersonalDelivery);
3672 zfree((*envelope)->dmAllowSubstDelivery);
3675 /* Extract envelope elements added by sender or ISDS
3676 * (XSD: gMessageEnvelopeSub type) */
3677 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3678 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3679 (*envelope)->dmSenderOrgUnitNum, 0);
3680 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3681 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3682 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3683 (*envelope)->dmRecipientOrgUnitNum, 0);
3684 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3685 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3686 EXTRACT_STRING("isds:dmRecipientRefNumber",
3687 (*envelope)->dmRecipientRefNumber);
3688 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3689 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3690 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3692 /* Extract envelope elements regarding law reference */
3693 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3694 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3695 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3696 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3697 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3699 /* Extract envelope other elements */
3700 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3701 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3702 (*envelope)->dmAllowSubstDelivery);
3704 leave:
3705 if (err) isds_envelope_free(envelope);
3706 xmlXPathFreeObject(result);
3707 return err;
3712 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3713 * isds_envelope structure. The envelope is automatically allocated but not
3714 * reallocated. The date are just appended into envelope structure.
3715 * @context is ISDS context
3716 * @envelope is automatically allocated message envelope structure
3717 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3718 * In case of error @envelope will be freed. */
3719 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3720 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3721 isds_error err = IE_SUCCESS;
3722 xmlXPathObjectPtr result = NULL;
3724 if (!context) return IE_INVALID_CONTEXT;
3725 if (!envelope) return IE_INVAL;
3726 if (!xpath_ctx) return IE_INVAL;
3729 if (!*envelope) {
3730 /* Allocate envelope */
3731 *envelope = calloc(1, sizeof(**envelope));
3732 if (!*envelope) {
3733 err = IE_NOMEM;
3734 goto leave;
3736 } else {
3737 /* Else free former data */
3738 zfree((*envelope)->dmID);
3739 zfree((*envelope)->dbIDSender);
3740 zfree((*envelope)->dmSender);
3741 zfree((*envelope)->dmSenderAddress);
3742 zfree((*envelope)->dmSenderType);
3743 zfree((*envelope)->dmRecipient);
3744 zfree((*envelope)->dmRecipientAddress);
3745 zfree((*envelope)->dmAmbiguousRecipient);
3748 /* Extract envelope elements added by ISDS
3749 * (XSD: gMessageEnvelope type) */
3750 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3751 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3752 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3753 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3754 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3755 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3756 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3757 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3758 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3759 (*envelope)->dmAmbiguousRecipient);
3761 /* Extract envelope elements added by sender and ISDS
3762 * (XSD: gMessageEnvelope type) */
3763 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3764 if (err) goto leave;
3766 leave:
3767 if (err) isds_envelope_free(envelope);
3768 xmlXPathFreeObject(result);
3769 return err;
3773 /* Convert other envelope elements from XML tree into isds_envelope structure:
3774 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3775 * The envelope is automatically allocated but not reallocated.
3776 * The data are just appended into envelope structure.
3777 * @context is ISDS context
3778 * @envelope is automatically allocated message envelope structure
3779 * @xpath_ctx is XPath context with current node as parent desired elements
3780 * In case of error @envelope will be freed. */
3781 static isds_error append_status_size_times(struct isds_ctx *context,
3782 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3783 isds_error err = IE_SUCCESS;
3784 xmlXPathObjectPtr result = NULL;
3785 char *string = NULL;
3786 unsigned long int *unumber = NULL;
3788 if (!context) return IE_INVALID_CONTEXT;
3789 if (!envelope) return IE_INVAL;
3790 if (!xpath_ctx) return IE_INVAL;
3793 if (!*envelope) {
3794 /* Allocate new */
3795 *envelope = calloc(1, sizeof(**envelope));
3796 if (!*envelope) {
3797 err = IE_NOMEM;
3798 goto leave;
3800 } else {
3801 /* Free old data */
3802 zfree((*envelope)->dmMessageStatus);
3803 zfree((*envelope)->dmAttachmentSize);
3804 zfree((*envelope)->dmDeliveryTime);
3805 zfree((*envelope)->dmAcceptanceTime);
3809 /* dmMessageStatus element is mandatory */
3810 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3811 if (!unumber) {
3812 isds_log_message(context,
3813 _("Missing mandatory sisds:dmMessageStatus integer"));
3814 err = IE_ISDS;
3815 goto leave;
3817 err = uint2isds_message_status(context, unumber,
3818 &((*envelope)->dmMessageStatus));
3819 if (err) {
3820 if (err == IE_ENUM) err = IE_ISDS;
3821 goto leave;
3823 free(unumber); unumber = NULL;
3825 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3828 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3829 if (string) {
3830 err = timestring2timeval((xmlChar *) string,
3831 &((*envelope)->dmDeliveryTime));
3832 if (err) {
3833 char *string_locale = _isds_utf82locale(string);
3834 if (err == IE_DATE) err = IE_ISDS;
3835 isds_printf_message(context,
3836 _("Could not convert dmDeliveryTime as ISO time: %s"),
3837 string_locale);
3838 free(string_locale);
3839 goto leave;
3841 zfree(string);
3844 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3845 if (string) {
3846 err = timestring2timeval((xmlChar *) string,
3847 &((*envelope)->dmAcceptanceTime));
3848 if (err) {
3849 char *string_locale = _isds_utf82locale(string);
3850 if (err == IE_DATE) err = IE_ISDS;
3851 isds_printf_message(context,
3852 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3853 string_locale);
3854 free(string_locale);
3855 goto leave;
3857 zfree(string);
3860 leave:
3861 if (err) isds_envelope_free(envelope);
3862 free(unumber);
3863 free(string);
3864 xmlXPathFreeObject(result);
3865 return err;
3869 /* Convert message type attribute of current element into isds_envelope
3870 * structure.
3871 * TODO: This function can be incorporated into append_status_size_times() as
3872 * they are called always together.
3873 * The envelope is automatically allocated but not reallocated.
3874 * The data are just appended into envelope structure.
3875 * @context is ISDS context
3876 * @envelope is automatically allocated message envelope structure
3877 * @xpath_ctx is XPath context with current node as parent of attribute
3878 * carrying message type
3879 * In case of error @envelope will be freed. */
3880 static isds_error append_message_type(struct isds_ctx *context,
3881 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3882 isds_error err = IE_SUCCESS;
3884 if (!context) return IE_INVALID_CONTEXT;
3885 if (!envelope) return IE_INVAL;
3886 if (!xpath_ctx) return IE_INVAL;
3889 if (!*envelope) {
3890 /* Allocate new */
3891 *envelope = calloc(1, sizeof(**envelope));
3892 if (!*envelope) {
3893 err = IE_NOMEM;
3894 goto leave;
3896 } else {
3897 /* Free old data */
3898 zfree((*envelope)->dmType);
3902 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3904 if (!(*envelope)->dmType) {
3905 /* Use default value */
3906 (*envelope)->dmType = strdup("V");
3907 if (!(*envelope)->dmType) {
3908 err = IE_NOMEM;
3909 goto leave;
3911 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3912 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3913 isds_printf_message(context,
3914 _("Message type in dmType attribute is not 1 character long: "
3915 "%s"),
3916 type_locale);
3917 free(type_locale);
3918 err = IE_ISDS;
3919 goto leave;
3922 leave:
3923 if (err) isds_envelope_free(envelope);
3924 return err;
3928 #if HAVE_LIBCURL
3929 /* Convert dmType isds_envelope member into XML attribute and append it to
3930 * current node.
3931 * @context is ISDS context
3932 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3933 * @dm_envelope is XML element the resulting attribute will be appended to.
3934 * @return error code, in case of error context' message is filled. */
3935 static isds_error insert_message_type(struct isds_ctx *context,
3936 const char *type, xmlNodePtr dm_envelope) {
3937 isds_error err = IE_SUCCESS;
3938 xmlAttrPtr attribute_node;
3940 if (!context) return IE_INVALID_CONTEXT;
3941 if (!dm_envelope) return IE_INVAL;
3943 /* Insert optional message type */
3944 if (type) {
3945 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3946 char *type_locale = _isds_utf82locale(type);
3947 isds_printf_message(context,
3948 _("Message type in envelope is not 1 character long: %s"),
3949 type_locale);
3950 free(type_locale);
3951 err = IE_INVAL;
3952 goto leave;
3954 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3957 leave:
3958 return err;
3960 #endif /* HAVE_LIBCURL */
3963 /* Extract message document into reallocated document structure
3964 * @context is ISDS context
3965 * @document is automatically reallocated message documents structure
3966 * @xpath_ctx is XPath context with current node as isds:dmFile
3967 * In case of error @document will be freed. */
3968 static isds_error extract_document(struct isds_ctx *context,
3969 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3970 isds_error err = IE_SUCCESS;
3971 xmlXPathObjectPtr result = NULL;
3972 xmlNodePtr file_node;
3973 char *string = NULL;
3975 if (!context) return IE_INVALID_CONTEXT;
3976 if (!document) return IE_INVAL;
3977 isds_document_free(document);
3978 if (!xpath_ctx) return IE_INVAL;
3979 file_node = xpath_ctx->node;
3981 *document = calloc(1, sizeof(**document));
3982 if (!*document) {
3983 err = IE_NOMEM;
3984 goto leave;
3987 /* Extract document meta data */
3988 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3989 if (context->normalize_mime_type) {
3990 const char *normalized_type =
3991 isds_normalize_mime_type((*document)->dmMimeType);
3992 if (NULL != normalized_type &&
3993 normalized_type != (*document)->dmMimeType) {
3994 char *new_type = strdup(normalized_type);
3995 if (NULL == new_type) {
3996 isds_printf_message(context,
3997 _("Not enough memory to normalize document MIME type"));
3998 err = IE_NOMEM;
3999 goto leave;
4001 free((*document)->dmMimeType);
4002 (*document)->dmMimeType = new_type;
4006 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
4007 err = string2isds_FileMetaType((xmlChar*)string,
4008 &((*document)->dmFileMetaType));
4009 if (err) {
4010 char *meta_type_locale = _isds_utf82locale(string);
4011 isds_printf_message(context,
4012 _("Document has invalid dmFileMetaType attribute value: %s"),
4013 meta_type_locale);
4014 free(meta_type_locale);
4015 err = IE_ISDS;
4016 goto leave;
4018 zfree(string);
4020 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
4021 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
4022 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
4023 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
4026 /* Extract document data.
4027 * Base64 encoded blob or XML subtree must be presented. */
4029 /* Check for dmEncodedContent */
4030 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
4031 xpath_ctx);
4032 if (!result) {
4033 err = IE_XML;
4034 goto leave;
4037 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4038 /* Here we have Base64 blob */
4039 (*document)->is_xml = 0;
4041 if (result->nodesetval->nodeNr > 1) {
4042 isds_printf_message(context,
4043 _("Document has more dmEncodedContent elements"));
4044 err = IE_ISDS;
4045 goto leave;
4048 xmlXPathFreeObject(result); result = NULL;
4049 EXTRACT_STRING("isds:dmEncodedContent", string);
4051 /* Decode non-empty document */
4052 if (string && string[0] != '\0') {
4053 (*document)->data_length =
4054 _isds_b64decode(string, &((*document)->data));
4055 if ((*document)->data_length == (size_t) -1) {
4056 isds_printf_message(context,
4057 _("Error while Base64-decoding document content"));
4058 err = IE_ERROR;
4059 goto leave;
4062 } else {
4063 /* No Base64 blob, try XML document */
4064 xmlXPathFreeObject(result); result = NULL;
4065 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
4066 xpath_ctx);
4067 if (!result) {
4068 err = IE_XML;
4069 goto leave;
4072 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4073 /* Here we have XML document */
4074 (*document)->is_xml = 1;
4076 if (result->nodesetval->nodeNr > 1) {
4077 isds_printf_message(context,
4078 _("Document has more dmXMLContent elements"));
4079 err = IE_ISDS;
4080 goto leave;
4083 /* XXX: We cannot serialize the content simply because:
4084 * - XML document may point out of its scope (e.g. to message
4085 * envelope)
4086 * - isds:dmXMLContent can contain more elements, no element,
4087 * a text node only
4088 * - it's not the XML way
4089 * Thus we provide the only right solution: XML DOM. Let's
4090 * application to cope with this hot potato :) */
4091 (*document)->xml_node_list =
4092 result->nodesetval->nodeTab[0]->children;
4093 } else {
4094 /* No base64 blob, nor XML document */
4095 isds_printf_message(context,
4096 _("Document has no dmEncodedContent, nor dmXMLContent "
4097 "element"));
4098 err = IE_ISDS;
4099 goto leave;
4104 leave:
4105 if (err) isds_document_free(document);
4106 free(string);
4107 xmlXPathFreeObject(result);
4108 xpath_ctx->node = file_node;
4109 return err;
4114 /* Extract message documents into reallocated list of documents
4115 * @context is ISDS context
4116 * @documents is automatically reallocated message documents list structure
4117 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4118 * In case of error @documents will be freed. */
4119 static isds_error extract_documents(struct isds_ctx *context,
4120 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
4121 isds_error err = IE_SUCCESS;
4122 xmlXPathObjectPtr result = NULL;
4123 xmlNodePtr files_node;
4124 struct isds_list *document, *prev_document = NULL;
4126 if (!context) return IE_INVALID_CONTEXT;
4127 if (!documents) return IE_INVAL;
4128 isds_list_free(documents);
4129 if (!xpath_ctx) return IE_INVAL;
4130 files_node = xpath_ctx->node;
4132 /* Find documents */
4133 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
4134 if (!result) {
4135 err = IE_XML;
4136 goto leave;
4139 /* No match */
4140 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4141 isds_printf_message(context,
4142 _("Message does not contain any document"));
4143 err = IE_ISDS;
4144 goto leave;
4148 /* Iterate over documents */
4149 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4151 /* Allocate and append list item */
4152 document = calloc(1, sizeof(*document));
4153 if (!document) {
4154 err = IE_NOMEM;
4155 goto leave;
4157 document->destructor = (void (*)(void **))isds_document_free;
4158 if (i == 0) *documents = document;
4159 else prev_document->next = document;
4160 prev_document = document;
4162 /* Extract document */
4163 xpath_ctx->node = result->nodesetval->nodeTab[i];
4164 err = extract_document(context,
4165 (struct isds_document **) &(document->data), xpath_ctx);
4166 if (err) goto leave;
4170 leave:
4171 if (err) isds_list_free(documents);
4172 xmlXPathFreeObject(result);
4173 xpath_ctx->node = files_node;
4174 return err;
4178 #if HAVE_LIBCURL
4179 /* Convert isds:dmRecord XML tree into structure
4180 * @context is ISDS context
4181 * @envelope is automatically reallocated message envelope structure
4182 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4183 * In case of error @envelope will be freed. */
4184 static isds_error extract_DmRecord(struct isds_ctx *context,
4185 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4186 isds_error err = IE_SUCCESS;
4187 xmlXPathObjectPtr result = NULL;
4189 if (!context) return IE_INVALID_CONTEXT;
4190 if (!envelope) return IE_INVAL;
4191 isds_envelope_free(envelope);
4192 if (!xpath_ctx) return IE_INVAL;
4195 *envelope = calloc(1, sizeof(**envelope));
4196 if (!*envelope) {
4197 err = IE_NOMEM;
4198 goto leave;
4202 /* Extract tRecord data */
4203 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4205 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4206 * dmAcceptanceTime. */
4207 err = append_status_size_times(context, envelope, xpath_ctx);
4208 if (err) goto leave;
4210 /* Extract envelope elements added by sender and ISDS
4211 * (XSD: gMessageEnvelope type) */
4212 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4213 if (err) goto leave;
4215 /* Get message type */
4216 err = append_message_type(context, envelope, xpath_ctx);
4217 if (err) goto leave;
4220 leave:
4221 if (err) isds_envelope_free(envelope);
4222 xmlXPathFreeObject(result);
4223 return err;
4227 /* Convert XSD:tStateChangesRecord type XML tree into structure
4228 * @context is ISDS context
4229 * @changed_status is automatically reallocated message state change structure
4230 * @xpath_ctx is XPath context with current node as element of
4231 * XSD:tStateChangesRecord type
4232 * In case of error @changed_status will be freed. */
4233 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4234 struct isds_message_status_change **changed_status,
4235 xmlXPathContextPtr xpath_ctx) {
4236 isds_error err = IE_SUCCESS;
4237 xmlXPathObjectPtr result = NULL;
4238 unsigned long int *unumber = NULL;
4239 char *string = NULL;
4241 if (!context) return IE_INVALID_CONTEXT;
4242 if (!changed_status) return IE_INVAL;
4243 isds_message_status_change_free(changed_status);
4244 if (!xpath_ctx) return IE_INVAL;
4247 *changed_status = calloc(1, sizeof(**changed_status));
4248 if (!*changed_status) {
4249 err = IE_NOMEM;
4250 goto leave;
4254 /* Extract tGetStateChangesInput data */
4255 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4257 /* dmEventTime is mandatory */
4258 EXTRACT_STRING("isds:dmEventTime", string);
4259 if (string) {
4260 err = timestring2timeval((xmlChar *) string,
4261 &((*changed_status)->time));
4262 if (err) {
4263 char *string_locale = _isds_utf82locale(string);
4264 if (err == IE_DATE) err = IE_ISDS;
4265 isds_printf_message(context,
4266 _("Could not convert dmEventTime as ISO time: %s"),
4267 string_locale);
4268 free(string_locale);
4269 goto leave;
4271 zfree(string);
4274 /* dmMessageStatus element is mandatory */
4275 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4276 if (!unumber) {
4277 isds_log_message(context,
4278 _("Missing mandatory isds:dmMessageStatus integer"));
4279 err = IE_ISDS;
4280 goto leave;
4282 err = uint2isds_message_status(context, unumber,
4283 &((*changed_status)->dmMessageStatus));
4284 if (err) {
4285 if (err == IE_ENUM) err = IE_ISDS;
4286 goto leave;
4288 zfree(unumber);
4291 leave:
4292 free(unumber);
4293 free(string);
4294 if (err) isds_message_status_change_free(changed_status);
4295 xmlXPathFreeObject(result);
4296 return err;
4298 #endif /* HAVE_LIBCURL */
4301 /* Find and convert isds:dmHash XML tree into structure
4302 * @context is ISDS context
4303 * @envelope is automatically reallocated message hash structure
4304 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4305 * In case of error @hash will be freed. */
4306 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4307 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4308 isds_error err = IE_SUCCESS;
4309 xmlNodePtr old_ctx_node;
4310 xmlXPathObjectPtr result = NULL;
4311 char *string = NULL;
4313 if (!context) return IE_INVALID_CONTEXT;
4314 if (!hash) return IE_INVAL;
4315 isds_hash_free(hash);
4316 if (!xpath_ctx) return IE_INVAL;
4318 old_ctx_node = xpath_ctx->node;
4320 *hash = calloc(1, sizeof(**hash));
4321 if (!*hash) {
4322 err = IE_NOMEM;
4323 goto leave;
4326 /* Locate dmHash */
4327 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4328 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4329 err = IE_ISDS;
4330 goto leave;
4332 if (err) {
4333 err = IE_ERROR;
4334 goto leave;
4337 /* Get hash algorithm */
4338 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4339 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4340 if (err) {
4341 if (err == IE_ENUM) {
4342 char *string_locale = _isds_utf82locale(string);
4343 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4344 string_locale);
4345 free(string_locale);
4347 goto leave;
4349 zfree(string);
4351 /* Get hash value */
4352 EXTRACT_STRING(".", string);
4353 if (!string) {
4354 isds_printf_message(context,
4355 _("sisds:dmHash element is missing hash value"));
4356 err = IE_ISDS;
4357 goto leave;
4359 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4360 if ((*hash)->length == (size_t) -1) {
4361 isds_printf_message(context,
4362 _("Error while Base64-decoding hash value"));
4363 err = IE_ERROR;
4364 goto leave;
4367 leave:
4368 if (err) isds_hash_free(hash);
4369 free(string);
4370 xmlXPathFreeObject(result);
4371 xpath_ctx->node = old_ctx_node;
4372 return err;
4376 /* Find and append isds:dmQTimestamp XML tree into envelope.
4377 * Because one service is allowed to miss time-stamp content, and we think
4378 * other could too (flaw in specification), this function is deliberated and
4379 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4380 * @context is ISDS context
4381 * @envelope is automatically allocated envelope structure
4382 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4383 * child
4384 * In case of error @envelope will be freed. */
4385 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4386 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4387 isds_error err = IE_SUCCESS;
4388 xmlXPathObjectPtr result = NULL;
4389 char *string = NULL;
4391 if (!context) return IE_INVALID_CONTEXT;
4392 if (!envelope) return IE_INVAL;
4393 if (!xpath_ctx) {
4394 isds_envelope_free(envelope);
4395 return IE_INVAL;
4398 if (!*envelope) {
4399 *envelope = calloc(1, sizeof(**envelope));
4400 if (!*envelope) {
4401 err = IE_NOMEM;
4402 goto leave;
4404 } else {
4405 zfree((*envelope)->timestamp);
4406 (*envelope)->timestamp_length = 0;
4409 /* Get dmQTimestamp */
4410 EXTRACT_STRING("sisds:dmQTimestamp", string);
4411 if (!string) {
4412 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4413 goto leave;
4415 (*envelope)->timestamp_length =
4416 _isds_b64decode(string, &((*envelope)->timestamp));
4417 if ((*envelope)->timestamp_length == (size_t) -1) {
4418 isds_printf_message(context,
4419 _("Error while Base64-decoding time stamp value"));
4420 err = IE_ERROR;
4421 goto leave;
4424 leave:
4425 if (err) isds_envelope_free(envelope);
4426 free(string);
4427 xmlXPathFreeObject(result);
4428 return err;
4432 /* Convert XSD tReturnedMessage XML tree into message structure.
4433 * It does not store serialized XML tree into message->raw.
4434 * It does store (pointer to) parsed XML tree into message->xml if needed.
4435 * @context is ISDS context
4436 * @include_documents Use true if documents must be extracted
4437 * (tReturnedMessage XSD type), use false if documents shall be omitted
4438 * (tReturnedMessageEnvelope).
4439 * @message is automatically reallocated message structure
4440 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4441 * type
4442 * In case of error @message will be freed. */
4443 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4444 const _Bool include_documents, struct isds_message **message,
4445 xmlXPathContextPtr xpath_ctx) {
4446 isds_error err = IE_SUCCESS;
4447 xmlNodePtr message_node;
4449 if (!context) return IE_INVALID_CONTEXT;
4450 if (!message) return IE_INVAL;
4451 isds_message_free(message);
4452 if (!xpath_ctx) return IE_INVAL;
4455 *message = calloc(1, sizeof(**message));
4456 if (!*message) {
4457 err = IE_NOMEM;
4458 goto leave;
4461 /* Save message XPATH context node */
4462 message_node = xpath_ctx->node;
4465 /* Extract dmDM */
4466 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4467 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4468 if (err) { err = IE_ERROR; goto leave; }
4469 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4470 if (err) goto leave;
4472 if (include_documents) {
4473 struct isds_list *item;
4475 /* Extract dmFiles */
4476 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4477 xpath_ctx);
4478 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4479 err = IE_ISDS; goto leave;
4481 if (err) { err = IE_ERROR; goto leave; }
4482 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4483 if (err) goto leave;
4485 /* Store xmlDoc of this message if needed */
4486 /* Only if we got a XML document in all the documents. */
4487 for (item = (*message)->documents; item; item = item->next) {
4488 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4489 (*message)->xml = xpath_ctx->doc;
4490 break;
4496 /* Restore context to message */
4497 xpath_ctx->node = message_node;
4499 /* Extract dmHash */
4500 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4501 xpath_ctx);
4502 if (err) goto leave;
4504 /* Extract dmQTimestamp, */
4505 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4506 xpath_ctx);
4507 if (err) goto leave;
4509 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4510 * dmAcceptanceTime. */
4511 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4512 if (err) goto leave;
4514 /* Get message type */
4515 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4516 if (err) goto leave;
4518 leave:
4519 if (err) isds_message_free(message);
4520 return err;
4524 /* Extract message event into reallocated isds_event structure
4525 * @context is ISDS context
4526 * @event is automatically reallocated message event structure
4527 * @xpath_ctx is XPath context with current node as isds:dmEvent
4528 * In case of error @event will be freed. */
4529 static isds_error extract_event(struct isds_ctx *context,
4530 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4531 isds_error err = IE_SUCCESS;
4532 xmlXPathObjectPtr result = NULL;
4533 xmlNodePtr event_node;
4534 char *string = NULL;
4536 if (!context) return IE_INVALID_CONTEXT;
4537 if (!event) return IE_INVAL;
4538 isds_event_free(event);
4539 if (!xpath_ctx) return IE_INVAL;
4540 event_node = xpath_ctx->node;
4542 *event = calloc(1, sizeof(**event));
4543 if (!*event) {
4544 err = IE_NOMEM;
4545 goto leave;
4548 /* Extract event data.
4549 * All elements are optional according XSD. That's funny. */
4550 EXTRACT_STRING("sisds:dmEventTime", string);
4551 if (string) {
4552 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4553 if (err) {
4554 char *string_locale = _isds_utf82locale(string);
4555 if (err == IE_DATE) err = IE_ISDS;
4556 isds_printf_message(context,
4557 _("Could not convert dmEventTime as ISO time: %s"),
4558 string_locale);
4559 free(string_locale);
4560 goto leave;
4562 zfree(string);
4565 /* dmEventDescr element has prefix and the rest */
4566 EXTRACT_STRING("sisds:dmEventDescr", string);
4567 if (string) {
4568 err = eventstring2event((xmlChar *) string, *event);
4569 if (err) goto leave;
4570 zfree(string);
4573 leave:
4574 if (err) isds_event_free(event);
4575 free(string);
4576 xmlXPathFreeObject(result);
4577 xpath_ctx->node = event_node;
4578 return err;
4582 /* Convert element of XSD tEventsArray type from XML tree into
4583 * isds_list of isds_event's structure. The list is automatically reallocated.
4584 * @context is ISDS context
4585 * @events is automatically reallocated list of event structures
4586 * @xpath_ctx is XPath context with current node as tEventsArray
4587 * In case of error @events will be freed. */
4588 static isds_error extract_events(struct isds_ctx *context,
4589 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4590 isds_error err = IE_SUCCESS;
4591 xmlXPathObjectPtr result = NULL;
4592 xmlNodePtr events_node;
4593 struct isds_list *event, *prev_event = NULL;
4595 if (!context) return IE_INVALID_CONTEXT;
4596 if (!events) return IE_INVAL;
4597 if (!xpath_ctx) return IE_INVAL;
4598 events_node = xpath_ctx->node;
4600 /* Free old list */
4601 isds_list_free(events);
4603 /* Find events */
4604 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4605 if (!result) {
4606 err = IE_XML;
4607 goto leave;
4610 /* No match */
4611 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4612 isds_printf_message(context,
4613 _("Delivery info does not contain any event"));
4614 err = IE_ISDS;
4615 goto leave;
4619 /* Iterate over events */
4620 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4622 /* Allocate and append list item */
4623 event = calloc(1, sizeof(*event));
4624 if (!event) {
4625 err = IE_NOMEM;
4626 goto leave;
4628 event->destructor = (void (*)(void **))isds_event_free;
4629 if (i == 0) *events = event;
4630 else prev_event->next = event;
4631 prev_event = event;
4633 /* Extract event */
4634 xpath_ctx->node = result->nodesetval->nodeTab[i];
4635 err = extract_event(context,
4636 (struct isds_event **) &(event->data), xpath_ctx);
4637 if (err) goto leave;
4641 leave:
4642 if (err) isds_list_free(events);
4643 xmlXPathFreeObject(result);
4644 xpath_ctx->node = events_node;
4645 return err;
4649 #if HAVE_LIBCURL
4650 /* Insert Base64 encoded data as element with text child.
4651 * @context is session context
4652 * @parent is XML node to append @element with @data as child
4653 * @ns is XML namespace of @element, use NULL to inherit from @parent
4654 * @element is UTF-8 encoded name of new element
4655 * @data is bit stream to encode into @element
4656 * @length is size of @data in bytes
4657 * @return standard error code and fill long error message if needed */
4658 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4659 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4660 const void *data, size_t length) {
4661 isds_error err = IE_SUCCESS;
4662 xmlNodePtr node;
4664 if (!context) return IE_INVALID_CONTEXT;
4665 if (!data && length > 0) return IE_INVAL;
4666 if (!parent || !element) return IE_INVAL;
4668 xmlChar *base64data = NULL;
4669 base64data = (xmlChar *) _isds_b64encode(data, length);
4670 if (!base64data) {
4671 isds_printf_message(context,
4672 ngettext("Not enough memory to encode %zd byte into Base64",
4673 "Not enough memory to encode %zd bytes into Base64",
4674 length),
4675 length);
4676 err = IE_NOMEM;
4677 goto leave;
4679 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4681 leave:
4682 free(base64data);
4683 return err;
4687 /* Convert isds_document structure into XML tree and append to dmFiles node.
4688 * @context is session context
4689 * @document is ISDS document
4690 * @dm_files is XML element the resulting tree will be appended to as a child.
4691 * @return error code, in case of error context' message is filled. */
4692 static isds_error insert_document(struct isds_ctx *context,
4693 struct isds_document *document, xmlNodePtr dm_files) {
4694 isds_error err = IE_SUCCESS;
4695 xmlNodePtr new_file = NULL, file = NULL, node;
4696 xmlAttrPtr attribute_node;
4698 if (!context) return IE_INVALID_CONTEXT;
4699 if (!document || !dm_files) return IE_INVAL;
4701 /* Allocate new dmFile */
4702 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4703 if (!new_file) {
4704 isds_printf_message(context, _("Could not allocate main dmFile"));
4705 err = IE_ERROR;
4706 goto leave;
4708 /* Append the new dmFile.
4709 * XXX: Main document must go first */
4710 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4711 file = xmlAddPrevSibling(dm_files->children, new_file);
4712 else
4713 file = xmlAddChild(dm_files, new_file);
4715 if (!file) {
4716 xmlFreeNode(new_file); new_file = NULL;
4717 isds_printf_message(context, _("Could not add dmFile child to "
4718 "%s element"), dm_files->name);
4719 err = IE_ERROR;
4720 goto leave;
4723 /* @dmMimeType is required */
4724 if (!document->dmMimeType) {
4725 isds_log_message(context,
4726 _("Document is missing mandatory MIME type definition"));
4727 err = IE_INVAL;
4728 goto leave;
4730 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4732 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4733 if (!string) {
4734 isds_printf_message(context,
4735 _("Document has unknown dmFileMetaType: %ld"),
4736 document->dmFileMetaType);
4737 err = IE_ENUM;
4738 goto leave;
4740 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4742 if (document->dmFileGuid) {
4743 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4745 if (document->dmUpFileGuid) {
4746 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4749 /* @dmFileDescr is required */
4750 if (!document->dmFileDescr) {
4751 isds_log_message(context,
4752 _("Document is missing mandatory description (title)"));
4753 err = IE_INVAL;
4754 goto leave;
4756 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4758 if (document->dmFormat) {
4759 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4763 /* Insert content (body) of the document. */
4764 if (document->is_xml) {
4765 /* XML document requested */
4767 /* Allocate new dmXMLContent */
4768 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4769 if (!xmlcontent) {
4770 isds_printf_message(context,
4771 _("Could not allocate dmXMLContent element"));
4772 err = IE_ERROR;
4773 goto leave;
4775 /* Append it */
4776 node = xmlAddChild(file, xmlcontent);
4777 if (!node) {
4778 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4779 isds_printf_message(context,
4780 _("Could not add dmXMLContent child to %s element"),
4781 file->name);
4782 err = IE_ERROR;
4783 goto leave;
4786 /* Copy non-empty node list */
4787 if (document->xml_node_list) {
4788 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4789 document->xml_node_list);
4790 if (!content) {
4791 isds_printf_message(context,
4792 _("Not enough memory to copy XML document"));
4793 err = IE_NOMEM;
4794 goto leave;
4797 if (!xmlAddChildList(node, content)) {
4798 xmlFreeNodeList(content);
4799 isds_printf_message(context,
4800 _("Error while adding XML document into dmXMLContent"));
4801 err = IE_XML;
4802 goto leave;
4804 /* XXX: We cannot free the content here because it's part of node's
4805 * document since now. It will be freed with it automatically. */
4807 } else {
4808 /* Binary document requested */
4809 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4810 document->data, document->data_length);
4811 if (err) goto leave;
4814 leave:
4815 return err;
4819 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4820 * The copy must be preallocated, the date are just appended into structure.
4821 * @context is ISDS context
4822 * @copy is message copy structure
4823 * @xpath_ctx is XPath context with current node as tMStatus */
4824 static isds_error append_TMStatus(struct isds_ctx *context,
4825 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4826 isds_error err = IE_SUCCESS;
4827 xmlXPathObjectPtr result = NULL;
4828 char *code = NULL, *message = NULL;
4830 if (!context) return IE_INVALID_CONTEXT;
4831 if (!copy || !xpath_ctx) return IE_INVAL;
4833 /* Free old values */
4834 zfree(copy->dmStatus);
4835 zfree(copy->dmID);
4837 /* Get error specific to this copy */
4838 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4839 if (!code) {
4840 isds_log_message(context,
4841 _("Missing isds:dmStatusCode under "
4842 "XSD:tMStatus type element"));
4843 err = IE_ISDS;
4844 goto leave;
4847 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4848 /* This copy failed */
4849 copy->error = IE_ISDS;
4850 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4851 if (message) {
4852 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4853 if (!copy->dmStatus) {
4854 copy->dmStatus = code;
4855 code = NULL;
4857 } else {
4858 copy->dmStatus = code;
4859 code = NULL;
4861 } else {
4862 /* This copy succeeded. In this case only, message ID is valid */
4863 copy->error = IE_SUCCESS;
4865 EXTRACT_STRING("isds:dmID", copy->dmID);
4866 if (!copy->dmID) {
4867 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4868 "but did not returned assigned message ID\n"));
4869 err = IE_ISDS;
4873 leave:
4874 free(code);
4875 free(message);
4876 xmlXPathFreeObject(result);
4877 return err;
4881 /* Insert struct isds_approval data (box approval) into XML tree
4882 * @context is session context
4883 * @approval is libisds structure with approval description. NULL is
4884 * acceptable.
4885 * @parent is XML element to append @approval to */
4886 static isds_error insert_GExtApproval(struct isds_ctx *context,
4887 const struct isds_approval *approval, xmlNodePtr parent) {
4889 isds_error err = IE_SUCCESS;
4890 xmlNodePtr node;
4892 if (!context) return IE_INVALID_CONTEXT;
4893 if (!parent) return IE_INVAL;
4895 if (!approval) return IE_SUCCESS;
4897 /* Build XSD:gExtApproval */
4898 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4899 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4901 leave:
4902 return err;
4906 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4907 * code
4908 * @context is session context
4909 * @service_name is name of SERVICE_DB_ACCESS
4910 * @response is reallocated server SOAP body response as XML document
4911 * @raw_response is reallocated bit stream with response body. Use
4912 * NULL if you don't care
4913 * @raw_response_length is size of @raw_response in bytes
4914 * @code is reallocated ISDS status code
4915 * @status_message is reallocated ISDS status message
4916 * @return error coded from lower layer, context message will be set up
4917 * appropriately. */
4918 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4919 const xmlChar *service_name,
4920 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4921 xmlChar **code, xmlChar **status_message) {
4923 isds_error err = IE_SUCCESS;
4924 char *service_name_locale = NULL;
4925 xmlNodePtr request = NULL, node;
4926 xmlNsPtr isds_ns = NULL;
4928 if (!context) return IE_INVALID_CONTEXT;
4929 if (!service_name) return IE_INVAL;
4930 if (!response || !code || !status_message) return IE_INVAL;
4931 if (!raw_response_length && raw_response) return IE_INVAL;
4933 /* Free output argument */
4934 xmlFreeDoc(*response); *response = NULL;
4935 if (raw_response) zfree(*raw_response);
4936 zfree(*code);
4937 zfree(*status_message);
4940 /* Check if connection is established
4941 * TODO: This check should be done downstairs. */
4942 if (!context->curl) return IE_CONNECTION_CLOSED;
4944 service_name_locale = _isds_utf82locale((char*)service_name);
4945 if (!service_name_locale) {
4946 err = IE_NOMEM;
4947 goto leave;
4950 /* Build request */
4951 request = xmlNewNode(NULL, service_name);
4952 if (!request) {
4953 isds_printf_message(context,
4954 _("Could not build %s request"), service_name_locale);
4955 err = IE_ERROR;
4956 goto leave;
4958 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4959 if(!isds_ns) {
4960 isds_log_message(context, _("Could not create ISDS name space"));
4961 err = IE_ERROR;
4962 goto leave;
4964 xmlSetNs(request, isds_ns);
4967 /* Add XSD:tDummyInput child */
4968 INSERT_STRING(request, "dbDummy", NULL);
4971 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4972 service_name_locale);
4974 /* Send request */
4975 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4976 raw_response, raw_response_length);
4977 xmlFreeNode(request); request = NULL;
4979 if (err) {
4980 isds_log(ILF_ISDS, ILL_DEBUG,
4981 _("Processing ISDS response on %s request failed\n"),
4982 service_name_locale);
4983 goto leave;
4986 /* Check for response status */
4987 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4988 code, status_message, NULL);
4989 if (err) {
4990 isds_log(ILF_ISDS, ILL_DEBUG,
4991 _("ISDS response on %s request is missing status\n"),
4992 service_name_locale);
4993 goto leave;
4996 /* Request processed, but nothing found */
4997 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4998 char *code_locale = _isds_utf82locale((char*) *code);
4999 char *status_message_locale =
5000 _isds_utf82locale((char*) *status_message);
5001 isds_log(ILF_ISDS, ILL_DEBUG,
5002 _("Server refused %s request (code=%s, message=%s)\n"),
5003 service_name_locale, code_locale, status_message_locale);
5004 isds_log_message(context, status_message_locale);
5005 free(code_locale);
5006 free(status_message_locale);
5007 err = IE_ISDS;
5008 goto leave;
5011 leave:
5012 free(service_name_locale);
5013 xmlFreeNode(request);
5014 return err;
5016 #endif
5019 /* Get data about logged in user and his box.
5020 * @context is session context
5021 * @db_owner_info is reallocated box owner description. It will be freed on
5022 * error.
5023 * @return error code from lower layer, context message will be set up
5024 * appropriately. */
5025 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
5026 struct isds_DbOwnerInfo **db_owner_info) {
5027 isds_error err = IE_SUCCESS;
5028 #if HAVE_LIBCURL
5029 xmlDocPtr response = NULL;
5030 xmlChar *code = NULL, *message = NULL;
5031 xmlXPathContextPtr xpath_ctx = NULL;
5032 xmlXPathObjectPtr result = NULL;
5033 char *string = NULL;
5034 #endif
5036 if (!context) return IE_INVALID_CONTEXT;
5037 zfree(context->long_message);
5038 if (!db_owner_info) return IE_INVAL;
5039 isds_DbOwnerInfo_free(db_owner_info);
5041 #if HAVE_LIBCURL
5042 /* Check if connection is established */
5043 if (!context->curl) return IE_CONNECTION_CLOSED;
5046 /* Do request and check for success */
5047 err = build_send_check_dbdummy_request(context,
5048 BAD_CAST "GetOwnerInfoFromLogin",
5049 &response, NULL, NULL, &code, &message);
5050 if (err) goto leave;
5053 /* Extract data */
5054 /* Prepare structure */
5055 *db_owner_info = calloc(1, sizeof(**db_owner_info));
5056 if (!*db_owner_info) {
5057 err = IE_NOMEM;
5058 goto leave;
5060 xpath_ctx = xmlXPathNewContext(response);
5061 if (!xpath_ctx) {
5062 err = IE_ERROR;
5063 goto leave;
5065 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5066 err = IE_ERROR;
5067 goto leave;
5070 /* Set context node */
5071 result = xmlXPathEvalExpression(BAD_CAST
5072 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
5073 if (!result) {
5074 err = IE_ERROR;
5075 goto leave;
5077 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5078 isds_log_message(context, _("Missing dbOwnerInfo element"));
5079 err = IE_ISDS;
5080 goto leave;
5082 if (result->nodesetval->nodeNr > 1) {
5083 isds_log_message(context, _("Multiple dbOwnerInfo element"));
5084 err = IE_ISDS;
5085 goto leave;
5087 xpath_ctx->node = result->nodesetval->nodeTab[0];
5088 xmlXPathFreeObject(result); result = NULL;
5090 /* Extract it */
5091 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
5094 leave:
5095 if (err) {
5096 isds_DbOwnerInfo_free(db_owner_info);
5099 free(string);
5100 xmlXPathFreeObject(result);
5101 xmlXPathFreeContext(xpath_ctx);
5103 free(code);
5104 free(message);
5105 xmlFreeDoc(response);
5107 if (!err)
5108 isds_log(ILF_ISDS, ILL_DEBUG,
5109 _("GetOwnerInfoFromLogin request processed by server "
5110 "successfully.\n"));
5111 #else /* not HAVE_LIBCURL */
5112 err = IE_NOTSUP;
5113 #endif
5115 return err;
5119 /* Get data about logged in user. */
5120 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
5121 struct isds_DbUserInfo **db_user_info) {
5122 isds_error err = IE_SUCCESS;
5123 #if HAVE_LIBCURL
5124 xmlDocPtr response = NULL;
5125 xmlChar *code = NULL, *message = NULL;
5126 xmlXPathContextPtr xpath_ctx = NULL;
5127 xmlXPathObjectPtr result = NULL;
5128 #endif
5130 if (!context) return IE_INVALID_CONTEXT;
5131 zfree(context->long_message);
5132 if (!db_user_info) return IE_INVAL;
5133 isds_DbUserInfo_free(db_user_info);
5135 #if HAVE_LIBCURL
5136 /* Check if connection is established */
5137 if (!context->curl) return IE_CONNECTION_CLOSED;
5140 /* Do request and check for success */
5141 err = build_send_check_dbdummy_request(context,
5142 BAD_CAST "GetUserInfoFromLogin",
5143 &response, NULL, NULL, &code, &message);
5144 if (err) goto leave;
5147 /* Extract data */
5148 /* Prepare structure */
5149 *db_user_info = calloc(1, sizeof(**db_user_info));
5150 if (!*db_user_info) {
5151 err = IE_NOMEM;
5152 goto leave;
5154 xpath_ctx = xmlXPathNewContext(response);
5155 if (!xpath_ctx) {
5156 err = IE_ERROR;
5157 goto leave;
5159 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5160 err = IE_ERROR;
5161 goto leave;
5164 /* Set context node */
5165 result = xmlXPathEvalExpression(BAD_CAST
5166 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
5167 if (!result) {
5168 err = IE_ERROR;
5169 goto leave;
5171 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5172 isds_log_message(context, _("Missing dbUserInfo element"));
5173 err = IE_ISDS;
5174 goto leave;
5176 if (result->nodesetval->nodeNr > 1) {
5177 isds_log_message(context, _("Multiple dbUserInfo element"));
5178 err = IE_ISDS;
5179 goto leave;
5181 xpath_ctx->node = result->nodesetval->nodeTab[0];
5182 xmlXPathFreeObject(result); result = NULL;
5184 /* Extract it */
5185 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5187 leave:
5188 if (err) {
5189 isds_DbUserInfo_free(db_user_info);
5192 xmlXPathFreeObject(result);
5193 xmlXPathFreeContext(xpath_ctx);
5195 free(code);
5196 free(message);
5197 xmlFreeDoc(response);
5199 if (!err)
5200 isds_log(ILF_ISDS, ILL_DEBUG,
5201 _("GetUserInfoFromLogin request processed by server "
5202 "successfully.\n"));
5203 #else /* not HAVE_LIBCURL */
5204 err = IE_NOTSUP;
5205 #endif
5207 return err;
5211 /* Get expiration time of current password
5212 * @context is session context
5213 * @expiration is automatically reallocated time when password expires. If
5214 * password expiration is disabled, NULL will be returned. In case of error
5215 * it will be nulled too. */
5216 isds_error isds_get_password_expiration(struct isds_ctx *context,
5217 struct timeval **expiration) {
5218 isds_error err = IE_SUCCESS;
5219 #if HAVE_LIBCURL
5220 xmlDocPtr response = NULL;
5221 xmlChar *code = NULL, *message = NULL;
5222 xmlXPathContextPtr xpath_ctx = NULL;
5223 xmlXPathObjectPtr result = NULL;
5224 char *string = NULL;
5225 #endif
5227 if (!context) return IE_INVALID_CONTEXT;
5228 zfree(context->long_message);
5229 if (!expiration) return IE_INVAL;
5230 zfree(*expiration);
5232 #if HAVE_LIBCURL
5233 /* Check if connection is established */
5234 if (!context->curl) return IE_CONNECTION_CLOSED;
5237 /* Do request and check for success */
5238 err = build_send_check_dbdummy_request(context,
5239 BAD_CAST "GetPasswordInfo",
5240 &response, NULL, NULL, &code, &message);
5241 if (err) goto leave;
5244 /* Extract data */
5245 xpath_ctx = xmlXPathNewContext(response);
5246 if (!xpath_ctx) {
5247 err = IE_ERROR;
5248 goto leave;
5250 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5251 err = IE_ERROR;
5252 goto leave;
5255 /* Set context node */
5256 result = xmlXPathEvalExpression(BAD_CAST
5257 "/isds:GetPasswordInfoResponse", xpath_ctx);
5258 if (!result) {
5259 err = IE_ERROR;
5260 goto leave;
5262 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5263 isds_log_message(context,
5264 _("Missing GetPasswordInfoResponse element"));
5265 err = IE_ISDS;
5266 goto leave;
5268 if (result->nodesetval->nodeNr > 1) {
5269 isds_log_message(context,
5270 _("Multiple GetPasswordInfoResponse element"));
5271 err = IE_ISDS;
5272 goto leave;
5274 xpath_ctx->node = result->nodesetval->nodeTab[0];
5275 xmlXPathFreeObject(result); result = NULL;
5277 /* Extract expiration date */
5278 EXTRACT_STRING("isds:pswExpDate", string);
5279 if (string) {
5280 /* And convert it if any returned. Otherwise expiration is disabled. */
5281 err = timestring2timeval((xmlChar *) string, expiration);
5282 if (err) {
5283 char *string_locale = _isds_utf82locale(string);
5284 if (err == IE_DATE) err = IE_ISDS;
5285 isds_printf_message(context,
5286 _("Could not convert pswExpDate as ISO time: %s"),
5287 string_locale);
5288 free(string_locale);
5289 goto leave;
5293 leave:
5294 if (err) {
5295 if (*expiration) {
5296 zfree(*expiration);
5300 free(string);
5301 xmlXPathFreeObject(result);
5302 xmlXPathFreeContext(xpath_ctx);
5304 free(code);
5305 free(message);
5306 xmlFreeDoc(response);
5308 if (!err)
5309 isds_log(ILF_ISDS, ILL_DEBUG,
5310 _("GetPasswordInfo request processed by server "
5311 "successfully.\n"));
5312 #else /* not HAVE_LIBCURL */
5313 err = IE_NOTSUP;
5314 #endif
5316 return err;
5320 #if HAVE_LIBCURL
5321 /* Request delivering new TOTP code from ISDS through side channel before
5322 * changing password.
5323 * @context is session context
5324 * @password is current password.
5325 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5326 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5327 * function for more details.
5328 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5329 * NULL, if you don't care.
5330 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5331 * error code. */
5332 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5333 const char *password, struct isds_otp *otp, char **refnumber) {
5334 isds_error err = IE_SUCCESS;
5335 char *saved_url = NULL; /* No copy */
5336 #if HAVE_CURL_REAUTHORIZATION_BUG
5337 CURL *saved_curl = NULL; /* No copy */
5338 #endif
5339 xmlNsPtr isds_ns = NULL;
5340 xmlNodePtr request = NULL;
5341 xmlDocPtr response = NULL;
5342 xmlChar *code = NULL, *message = NULL;
5343 const xmlChar *codes[] = {
5344 BAD_CAST "2300",
5345 BAD_CAST "2301",
5346 BAD_CAST "2302"
5348 const char *meanings[] = {
5349 N_("Unexpected error"),
5350 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5351 N_("One-time code could not been sent. Try later again.")
5353 const isds_otp_resolution resolutions[] = {
5354 OTP_RESOLUTION_UNKNOWN,
5355 OTP_RESOLUTION_TO_FAST,
5356 OTP_RESOLUTION_TOTP_NOT_SENT
5359 if (NULL == context) return IE_INVALID_CONTEXT;
5360 zfree(context->long_message);
5361 if (NULL == password) {
5362 isds_log_message(context,
5363 _("Second argument (password) of isds_change_password() "
5364 "is NULL"));
5365 return IE_INVAL;
5368 /* Check if connection is established
5369 * TODO: This check should be done downstairs. */
5370 if (!context->curl) return IE_CONNECTION_CLOSED;
5372 if (!context->otp) {
5373 isds_log_message(context, _("This function requires OTP-authenticated "
5374 "context"));
5375 return IE_INVALID_CONTEXT;
5377 if (NULL == otp) {
5378 isds_log_message(context, _("If one-time password authentication "
5379 "method is in use, requesting new OTP code requires "
5380 "one-time credentials argument either"));
5381 return IE_INVAL;
5383 if (otp->method != OTP_TIME) {
5384 isds_log_message(context, _("Requesting new time-based OTP code from "
5385 "server requires one-time password authentication "
5386 "method"));
5387 return IE_INVAL;
5389 if (otp->otp_code != NULL) {
5390 isds_log_message(context, _("Requesting new time-based OTP code from "
5391 "server requires undefined OTP code member in "
5392 "one-time credentials argument"));
5393 return IE_INVAL;
5397 /* Build request */
5398 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5399 if (!request) {
5400 isds_log_message(context, _("Could not build SendSMSCode request"));
5401 return IE_ERROR;
5403 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5404 if(!isds_ns) {
5405 isds_log_message(context, _("Could not create ISDS name space"));
5406 xmlFreeNode(request);
5407 return IE_ERROR;
5409 xmlSetNs(request, isds_ns);
5411 /* Change URL temporarily for sending this request only */
5413 char *new_url = NULL;
5414 if ((err = _isds_build_url_from_context(context,
5415 "%1$.*2$sasws/changePassword", &new_url))) {
5416 goto leave;
5418 saved_url = context->url;
5419 context->url = new_url;
5422 /* Store credentials for sending this request only */
5423 context->otp_credentials = otp;
5424 _isds_discard_credentials(context, 0);
5425 if ((err = _isds_store_credentials(context, context->saved_username,
5426 password, NULL))) {
5427 _isds_discard_credentials(context, 0);
5428 goto leave;
5430 #if HAVE_CURL_REAUTHORIZATION_BUG
5431 saved_curl = context->curl;
5432 context->curl = curl_easy_init();
5433 if (NULL == context->curl) {
5434 err = IE_ERROR;
5435 goto leave;
5437 if (context->timeout) {
5438 err = isds_set_timeout(context, context->timeout);
5439 if (err) goto leave;
5441 #endif
5443 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5445 /* Sent request */
5446 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5448 /* Remove temporal credentials */
5449 _isds_discard_credentials(context, 0);
5450 /* Detach pointer to OTP credentials from context */
5451 context->otp_credentials = NULL;
5452 /* Keep context->otp true to keep signaling this is OTP session */
5454 /* Destroy request */
5455 xmlFreeNode(request); request = NULL;
5457 if (err) {
5458 isds_log(ILF_ISDS, ILL_DEBUG,
5459 _("Processing ISDS response on SendSMSCode request failed\n"));
5460 goto leave;
5463 /* Check for response status */
5464 err = isds_response_status(context, SERVICE_ASWS, response,
5465 &code, &message, (xmlChar **)refnumber);
5466 if (err) {
5467 isds_log(ILF_ISDS, ILL_DEBUG,
5468 _("ISDS response on SendSMSCode request is missing "
5469 "status\n"));
5470 goto leave;
5473 /* Check for error */
5474 if (xmlStrcmp(code, BAD_CAST "0000")) {
5475 char *code_locale = _isds_utf82locale((char*)code);
5476 char *message_locale = _isds_utf82locale((char*)message);
5477 size_t i;
5478 isds_log(ILF_ISDS, ILL_DEBUG,
5479 _("Server refused to send new code on SendSMSCode "
5480 "request (code=%s, message=%s)\n"),
5481 code_locale, message_locale);
5483 /* Check for known error codes */
5484 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5485 if (!xmlStrcmp(code, codes[i])) break;
5487 if (i < sizeof(codes)/sizeof(*codes)) {
5488 isds_log_message(context, _(meanings[i]));
5489 /* Mimic otp->resolution according to the code, specification does
5490 * prescribe OTP header to be available. */
5491 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5492 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5493 otp->resolution = resolutions[i];
5494 } else
5495 isds_log_message(context, message_locale);
5497 free(code_locale);
5498 free(message_locale);
5500 err = IE_ISDS;
5501 goto leave;
5504 /* Otherwise new code sent successfully */
5505 /* Mimic otp->resolution according to the code, specification does
5506 * prescribe OTP header to be available. */
5507 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5508 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5510 leave:
5511 if (NULL != saved_url) {
5512 /* Revert URL to original one */
5513 zfree(context->url);
5514 context->url = saved_url;
5516 #if HAVE_CURL_REAUTHORIZATION_BUG
5517 if (NULL != saved_curl) {
5518 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5519 context->curl = saved_curl;
5521 #endif
5523 free(code);
5524 free(message);
5525 xmlFreeDoc(response);
5526 xmlFreeNode(request);
5528 if (!err)
5529 isds_log(ILF_ISDS, ILL_DEBUG,
5530 _("New OTP code has been sent successfully on SendSMSCode "
5531 "request.\n"));
5532 return err;
5536 /* Convert response status code to isds_error code and set long message
5537 * @context is context to save long message to
5538 * @map is mapping from codes to errors and messages. Pass NULL for generic
5539 * handling.
5540 * @code is status code to translate
5541 * @message is non-localized status message to put into long message in case
5542 * of uknown error. It can be NULL if server did not provide any.
5543 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5544 * invalid invocation. */
5545 static isds_error statuscode2isds_error(struct isds_ctx *context,
5546 const struct code_map_isds_error *map,
5547 const xmlChar *code, const xmlChar *message) {
5548 if (NULL == code) {
5549 isds_log_message(context,
5550 _("NULL status code passed to statuscode2isds_error()"));
5551 return IE_INVAL;
5554 if (NULL != map) {
5555 /* Check for known error codes */
5556 for (int i=0; map->codes[i] != NULL; i++) {
5557 if (!xmlStrcmp(code, map->codes[i])) {
5558 isds_log_message(context, _(map->meanings[i]));
5559 return map->errors[i];
5564 /* Other error */
5565 if (xmlStrcmp(code, BAD_CAST "0000")) {
5566 char *message_locale = _isds_utf82locale((char*)message);
5567 if (NULL == message_locale)
5568 isds_log_message(context, _("ISDS server returned unknown error"));
5569 else
5570 isds_log_message(context, message_locale);
5571 free(message_locale);
5572 return IE_ISDS;
5575 return IE_SUCCESS;
5577 #endif
5580 /* Change user password in ISDS.
5581 * User must supply old password, new password will takes effect after some
5582 * time, current session can continue. Password must fulfill some constraints.
5583 * @context is session context
5584 * @old_password is current password.
5585 * @new_password is requested new password
5586 * @otp auxiliary data required if one-time password authentication is in use,
5587 * defines OTP code (if known) and returns fine grade resolution of OTP
5588 * procedure. Pass NULL, if one-time password authentication is not needed.
5589 * Please note the @otp argument must match OTP method used at log-in time. See
5590 * isds_login() function for more details.
5591 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5592 * NULL, if you don't care.
5593 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5594 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5595 * awaiting OTP code that has been delivered by side channel to the user. */
5596 isds_error isds_change_password(struct isds_ctx *context,
5597 const char *old_password, const char *new_password,
5598 struct isds_otp *otp, char **refnumber) {
5599 isds_error err = IE_SUCCESS;
5600 #if HAVE_LIBCURL
5601 char *saved_url = NULL; /* No copy */
5602 #if HAVE_CURL_REAUTHORIZATION_BUG
5603 CURL *saved_curl = NULL; /* No copy */
5604 #endif
5605 xmlNsPtr isds_ns = NULL;
5606 xmlNodePtr request = NULL, node;
5607 xmlDocPtr response = NULL;
5608 xmlChar *code = NULL, *message = NULL;
5609 const xmlChar *codes[] = {
5610 BAD_CAST "1066",
5611 BAD_CAST "1067",
5612 BAD_CAST "1079",
5613 BAD_CAST "1080",
5614 BAD_CAST "1081",
5615 BAD_CAST "1082",
5616 BAD_CAST "1083",
5617 BAD_CAST "1090",
5618 BAD_CAST "1091",
5619 BAD_CAST "2300",
5620 BAD_CAST "9204"
5622 const char *meanings[] = {
5623 N_("Password length must be between 8 and 32 characters"),
5624 N_("Password cannot be reused"), /* Server does not distinguish 1067
5625 and 1091 on ChangePasswordOTP */
5626 N_("Password contains forbidden character"),
5627 N_("Password must contain at least one upper-case letter, "
5628 "one lower-case, and one digit"),
5629 N_("Password cannot contain sequence of three identical characters"),
5630 N_("Password cannot contain user identifier"),
5631 N_("Password is too simmple"),
5632 N_("Old password is not valid"),
5633 N_("Password cannot be reused"),
5634 N_("Unexpected error"),
5635 N_("LDAP update error")
5637 #endif
5639 if (!context) return IE_INVALID_CONTEXT;
5640 zfree(context->long_message);
5641 if (NULL != refnumber)
5642 zfree(*refnumber);
5643 if (NULL == old_password) {
5644 isds_log_message(context,
5645 _("Second argument (old password) of isds_change_password() "
5646 "is NULL"));
5647 return IE_INVAL;
5649 if (NULL == otp && NULL == new_password) {
5650 isds_log_message(context,
5651 _("Third argument (new password) of isds_change_password() "
5652 "is NULL"));
5653 return IE_INVAL;
5656 #if HAVE_LIBCURL
5657 /* Check if connection is established
5658 * TODO: This check should be done downstairs. */
5659 if (!context->curl) return IE_CONNECTION_CLOSED;
5661 if (context->otp && NULL == otp) {
5662 isds_log_message(context, _("If one-time password authentication "
5663 "method is in use, changing password requires one-time "
5664 "credentials either"));
5665 return IE_INVAL;
5668 /* Build ChangeISDSPassword request */
5669 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5670 BAD_CAST "ChangePasswordOTP");
5671 if (!request) {
5672 isds_log_message(context, (NULL == otp) ?
5673 _("Could not build ChangeISDSPassword request") :
5674 _("Could not build ChangePasswordOTP request"));
5675 return IE_ERROR;
5677 isds_ns = xmlNewNs(request,
5678 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5679 NULL);
5680 if(!isds_ns) {
5681 isds_log_message(context, _("Could not create ISDS name space"));
5682 xmlFreeNode(request);
5683 return IE_ERROR;
5685 xmlSetNs(request, isds_ns);
5687 INSERT_STRING(request, "dbOldPassword", old_password);
5688 INSERT_STRING(request, "dbNewPassword", new_password);
5690 if (NULL != otp) {
5691 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5692 switch (otp->method) {
5693 case OTP_HMAC:
5694 isds_log(ILF_SEC, ILL_INFO,
5695 _("Selected authentication method: "
5696 "HMAC-based one-time password\n"));
5697 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5698 break;
5699 case OTP_TIME:
5700 isds_log(ILF_SEC, ILL_INFO,
5701 _("Selected authentication method: "
5702 "Time-based one-time password\n"));
5703 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5704 if (otp->otp_code == NULL) {
5705 isds_log(ILF_SEC, ILL_INFO,
5706 _("OTP code has not been provided by "
5707 "application, requesting server for "
5708 "new one.\n"));
5709 err = _isds_request_totp_code(context, old_password, otp,
5710 refnumber);
5711 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5712 goto leave;
5714 } else {
5715 isds_log(ILF_SEC, ILL_INFO,
5716 _("OTP code has been provided by "
5717 "application, not requesting server "
5718 "for new one.\n"));
5720 break;
5721 default:
5722 isds_log_message(context,
5723 _("Unknown one-time password authentication "
5724 "method requested by application"));
5725 err = IE_ENUM;
5726 goto leave;
5729 /* Change URL temporarily for sending this request only */
5731 char *new_url = NULL;
5732 if ((err = _isds_build_url_from_context(context,
5733 "%1$.*2$sasws/changePassword", &new_url))) {
5734 goto leave;
5736 saved_url = context->url;
5737 context->url = new_url;
5740 /* Store credentials for sending this request only */
5741 context->otp_credentials = otp;
5742 _isds_discard_credentials(context, 0);
5743 if ((err = _isds_store_credentials(context, context->saved_username,
5744 old_password, NULL))) {
5745 _isds_discard_credentials(context, 0);
5746 goto leave;
5748 #if HAVE_CURL_REAUTHORIZATION_BUG
5749 saved_curl = context->curl;
5750 context->curl = curl_easy_init();
5751 if (NULL == context->curl) {
5752 err = IE_ERROR;
5753 goto leave;
5755 if (context->timeout) {
5756 err = isds_set_timeout(context, context->timeout);
5757 if (err) goto leave;
5759 #endif
5762 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5763 _("Sending ChangeISDSPassword request to ISDS\n") :
5764 _("Sending ChangePasswordOTP request to ISDS\n"));
5766 /* Sent request */
5767 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5768 request, &response, NULL, NULL);
5770 if (otp) {
5771 /* Remove temporal credentials */
5772 _isds_discard_credentials(context, 0);
5773 /* Detach pointer to OTP credentials from context */
5774 context->otp_credentials = NULL;
5775 /* Keep context->otp true to keep signaling this is OTP session */
5778 /* Destroy request */
5779 xmlFreeNode(request); request = NULL;
5781 if (err) {
5782 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5783 _("Processing ISDS response on ChangeISDSPassword "
5784 "request failed\n") :
5785 _("Processing ISDS response on ChangePasswordOTP "
5786 "request failed\n"));
5787 goto leave;
5790 /* Check for response status */
5791 err = isds_response_status(context,
5792 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5793 &code, &message, (xmlChar **)refnumber);
5794 if (err) {
5795 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5796 _("ISDS response on ChangeISDSPassword request is missing "
5797 "status\n") :
5798 _("ISDS response on ChangePasswordOTP request is missing "
5799 "status\n"));
5800 goto leave;
5803 /* Check for known error codes */
5804 for (size_t i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5805 if (!xmlStrcmp(code, codes[i])) {
5806 char *code_locale = _isds_utf82locale((char*)code);
5807 char *message_locale = _isds_utf82locale((char*)message);
5808 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5809 _("Server refused to change password on ChangeISDSPassword "
5810 "request (code=%s, message=%s)\n") :
5811 _("Server refused to change password on ChangePasswordOTP "
5812 "request (code=%s, message=%s)\n"),
5813 code_locale, message_locale);
5814 free(code_locale);
5815 free(message_locale);
5816 isds_log_message(context, _(meanings[i]));
5817 err = IE_INVAL;
5818 goto leave;
5822 /* Other error */
5823 if (xmlStrcmp(code, BAD_CAST "0000")) {
5824 char *code_locale = _isds_utf82locale((char*)code);
5825 char *message_locale = _isds_utf82locale((char*)message);
5826 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5827 _("Server refused to change password on ChangeISDSPassword "
5828 "request (code=%s, message=%s)\n") :
5829 _("Server refused to change password on ChangePasswordOTP "
5830 "request (code=%s, message=%s)\n"),
5831 code_locale, message_locale);
5832 isds_log_message(context, message_locale);
5833 free(code_locale);
5834 free(message_locale);
5835 err = IE_ISDS;
5836 goto leave;
5839 /* Otherwise password changed successfully */
5841 leave:
5842 if (NULL != saved_url) {
5843 /* Revert URL to original one */
5844 zfree(context->url);
5845 context->url = saved_url;
5847 #if HAVE_CURL_REAUTHORIZATION_BUG
5848 if (NULL != saved_curl) {
5849 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5850 context->curl = saved_curl;
5852 #endif
5854 free(code);
5855 free(message);
5856 xmlFreeDoc(response);
5857 xmlFreeNode(request);
5859 if (!err)
5860 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5861 _("Password changed successfully on ChangeISDSPassword "
5862 "request.\n") :
5863 _("Password changed successfully on ChangePasswordOTP "
5864 "request.\n"));
5865 #else /* not HAVE_LIBCURL */
5866 err = IE_NOTSUP;
5867 #endif
5869 return err;
5873 #if HAVE_LIBCURL
5874 /* Generic middle part with request sending and response check.
5875 * It sends prepared request and checks for error code.
5876 * @context is ISDS session context.
5877 * @service is ISDS service handler
5878 * @service_name is name in scope of given @service
5879 * @request is XML tree with request. Will be freed to save memory.
5880 * @response is XML document outputting ISDS response.
5881 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5882 * @map is mapping from status code to library error. Pass NULL if no special
5883 * handling is requested.
5884 * NULL, if you don't care. */
5885 static isds_error send_destroy_request_check_response(
5886 struct isds_ctx *context,
5887 const isds_service service, const xmlChar *service_name,
5888 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5889 const struct code_map_isds_error *map) {
5890 isds_error err = IE_SUCCESS;
5891 char *service_name_locale = NULL;
5892 xmlChar *code = NULL, *message = NULL;
5895 if (!context) return IE_INVALID_CONTEXT;
5896 if (!service_name || *service_name == '\0' || !request || !*request ||
5897 !response)
5898 return IE_INVAL;
5900 /* Check if connection is established
5901 * TODO: This check should be done downstairs. */
5902 if (!context->curl) return IE_CONNECTION_CLOSED;
5904 service_name_locale = _isds_utf82locale((char*) service_name);
5905 if (!service_name_locale) {
5906 err = IE_NOMEM;
5907 goto leave;
5910 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5911 service_name_locale);
5913 /* Send request */
5914 err = _isds(context, service, *request, response, NULL, NULL);
5915 xmlFreeNode(*request); *request = NULL;
5917 if (err) {
5918 isds_log(ILF_ISDS, ILL_DEBUG,
5919 _("Processing ISDS response on %s request failed\n"),
5920 service_name_locale);
5921 goto leave;
5924 /* Check for response status */
5925 err = isds_response_status(context, service, *response,
5926 &code, &message, refnumber);
5927 if (err) {
5928 isds_log(ILF_ISDS, ILL_DEBUG,
5929 _("ISDS response on %s request is missing status\n"),
5930 service_name_locale);
5931 goto leave;
5934 err = statuscode2isds_error(context, map, code, message);
5936 /* Request processed, but server failed */
5937 if (xmlStrcmp(code, BAD_CAST "0000")) {
5938 char *code_locale = _isds_utf82locale((char*) code);
5939 char *message_locale = _isds_utf82locale((char*) message);
5940 isds_log(ILF_ISDS, ILL_DEBUG,
5941 _("Server refused %s request (code=%s, message=%s)\n"),
5942 service_name_locale, code_locale, message_locale);
5943 free(code_locale);
5944 free(message_locale);
5945 goto leave;
5949 leave:
5950 free(code);
5951 free(message);
5952 if (err && *response) {
5953 xmlFreeDoc(*response);
5954 *response = NULL;
5956 if (*request) {
5957 xmlFreeNode(*request);
5958 *request = NULL;
5960 free(service_name_locale);
5962 return err;
5966 /* Generic bottom half with request sending.
5967 * It sends prepared request, checks for error code, destroys response and
5968 * request and log success or failure.
5969 * @context is ISDS session context.
5970 * @service is ISDS service handler
5971 * @service_name is name in scope of given @service
5972 * @request is XML tree with request. Will be freed to save memory.
5973 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5974 * NULL, if you don't care. */
5975 static isds_error send_request_check_drop_response(
5976 struct isds_ctx *context,
5977 const isds_service service, const xmlChar *service_name,
5978 xmlNodePtr *request, xmlChar **refnumber) {
5979 isds_error err = IE_SUCCESS;
5980 xmlDocPtr response = NULL;
5983 if (!context) return IE_INVALID_CONTEXT;
5984 if (!service_name || *service_name == '\0' || !request || !*request)
5985 return IE_INVAL;
5987 /* Send request and check response*/
5988 err = send_destroy_request_check_response(context,
5989 service, service_name, request, &response, refnumber, NULL);
5991 xmlFreeDoc(response);
5993 if (*request) {
5994 xmlFreeNode(*request);
5995 *request = NULL;
5998 if (!err) {
5999 char *service_name_locale = _isds_utf82locale((char *) service_name);
6000 isds_log(ILF_ISDS, ILL_DEBUG,
6001 _("%s request processed by server successfully.\n"),
6002 service_name_locale);
6003 free(service_name_locale);
6006 return err;
6010 /* Insert isds_credentials_delivery structure into XML request if not NULL
6011 * @context is session context
6012 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6013 * credentials delivery. The email field is passed.
6014 * @parent is XML element where to insert */
6015 static isds_error insert_credentials_delivery(struct isds_ctx *context,
6016 const struct isds_credentials_delivery *credentials_delivery,
6017 xmlNodePtr parent) {
6018 isds_error err = IE_SUCCESS;
6019 xmlNodePtr node;
6021 if (!context) return IE_INVALID_CONTEXT;
6022 if (!parent) return IE_INVAL;
6024 if (credentials_delivery) {
6025 /* Following elements are valid only for services:
6026 * NewAccessData, AddDataBoxUser, CreateDataBox */
6027 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
6028 INSERT_STRING(parent, "email", credentials_delivery->email);
6031 leave:
6032 return err;
6036 /* Extract credentials delivery from ISDS response.
6037 * @context is session context
6038 * @credentials_delivery is pointer to valid structure to fill in returned
6039 * user's password (and new log-in name). If NULL, do not extract the data.
6040 * @response is pointer to XML document with ISDS response
6041 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6042 * @return IE_SUCCESS even if new user name has not been found because it's not
6043 * clear whether it's returned always. */
6044 static isds_error extract_credentials_delivery(struct isds_ctx *context,
6045 struct isds_credentials_delivery *credentials_delivery,
6046 xmlDocPtr response, const char *request_name) {
6047 isds_error err = IE_SUCCESS;
6048 xmlXPathContextPtr xpath_ctx = NULL;
6049 xmlXPathObjectPtr result = NULL;
6050 char *xpath_query = NULL;
6052 if (!context) return IE_INVALID_CONTEXT;
6053 if (credentials_delivery) {
6054 zfree(credentials_delivery->token);
6055 zfree(credentials_delivery->new_user_name);
6057 if (!response || !request_name || !*request_name) return IE_INVAL;
6060 /* Extract optional token */
6061 if (credentials_delivery) {
6062 xpath_ctx = xmlXPathNewContext(response);
6063 if (!xpath_ctx) {
6064 err = IE_ERROR;
6065 goto leave;
6067 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6068 err = IE_ERROR;
6069 goto leave;
6072 /* Verify root element */
6073 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
6074 request_name)) {
6075 err = IE_NOMEM;
6076 goto leave;
6078 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
6079 if (!result) {
6080 err = IE_ERROR;
6081 goto leave;
6083 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6084 char *request_name_locale = _isds_utf82locale(request_name);
6085 isds_log(ILF_ISDS, ILL_WARNING,
6086 _("Wrong element in ISDS response for %s request "
6087 "while extracting credentials delivery details\n"),
6088 request_name_locale);
6089 free(request_name_locale);
6090 err = IE_ERROR;
6091 goto leave;
6093 xpath_ctx->node = result->nodesetval->nodeTab[0];
6096 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6097 * optional. */
6098 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
6100 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
6101 if (!credentials_delivery->token) {
6102 char *request_name_locale = _isds_utf82locale(request_name);
6103 isds_log(ILF_ISDS, ILL_ERR,
6104 _("ISDS did not return token on %s request "
6105 "even if requested\n"), request_name_locale);
6106 free(request_name_locale);
6107 err = IE_ERROR;
6111 leave:
6112 free(xpath_query);
6113 xmlXPathFreeObject(result);
6114 xmlXPathFreeContext(xpath_ctx);
6116 return err;
6120 /* Build XSD:tCreateDBInput request type for box creating.
6121 * @context is session context
6122 * @request outputs built XML tree
6123 * @service_name is request name of SERVICE_DB_MANIPULATION service
6124 * @box is box description to create including single primary user (in case of
6125 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6126 * ignored.
6127 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6128 * box, or contact address of PFO box owner)
6129 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6130 * @upper_box_id is optional ID of supper box if currently created box is
6131 * subordinated.
6132 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6133 * don't care.
6134 * @credentials_delivery is valid pointer if ISDS should return token that box
6135 * owner can use to obtain his new credentials in on-line way. Then valid email
6136 * member value should be supplied.
6137 * @approval is optional external approval of box manipulation */
6138 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
6139 xmlNodePtr *request, const xmlChar *service_name,
6140 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6141 const xmlChar *former_names, const xmlChar *upper_box_id,
6142 const xmlChar *ceo_label,
6143 const struct isds_credentials_delivery *credentials_delivery,
6144 const struct isds_approval *approval) {
6145 isds_error err = IE_SUCCESS;
6146 xmlNsPtr isds_ns = NULL;
6147 xmlNodePtr node, dbPrimaryUsers;
6148 xmlChar *string = NULL;
6149 const struct isds_list *item;
6152 if (!context) return IE_INVALID_CONTEXT;
6153 if (!request || !service_name || service_name[0] == '\0' || !box)
6154 return IE_INVAL;
6157 /* Build CreateDataBox-similar request */
6158 *request = xmlNewNode(NULL, service_name);
6159 if (!*request) {
6160 char *service_name_locale = _isds_utf82locale((char*) service_name);
6161 isds_printf_message(context, _("Could build %s request"),
6162 service_name_locale);
6163 free(service_name_locale);
6164 return IE_ERROR;
6166 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
6167 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
6168 if (!isds_ns) {
6169 isds_log_message(context, _("Could not create ISDS1 name space"));
6170 xmlFreeNode(*request);
6171 return IE_ERROR;
6173 } else {
6174 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6175 if (!isds_ns) {
6176 isds_log_message(context, _("Could not create ISDS name space"));
6177 xmlFreeNode(*request);
6178 return IE_ERROR;
6181 xmlSetNs(*request, isds_ns);
6183 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6184 err = insert_DbOwnerInfo(context, box, 0, node);
6185 if (err) goto leave;
6187 /* Insert users */
6188 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6189 * verbose documentation allows none dbUserInfo */
6190 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6191 for (item = users; item; item = item->next) {
6192 if (item->data) {
6193 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6194 err = insert_DbUserInfo(context,
6195 (struct isds_DbUserInfo *) item->data, 1, node);
6196 if (err) goto leave;
6200 INSERT_STRING(*request, "dbFormerNames", former_names);
6201 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6202 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6204 err = insert_credentials_delivery(context, credentials_delivery, *request);
6205 if (err) goto leave;
6207 err = insert_GExtApproval(context, approval, *request);
6208 if (err) goto leave;
6210 leave:
6211 if (err) {
6212 xmlFreeNode(*request);
6213 *request = NULL;
6215 free(string);
6216 return err;
6218 #endif /* HAVE_LIBCURL */
6221 /* Create new box.
6222 * @context is session context
6223 * @box is box description to create including single primary user (in case of
6224 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6225 * ignored. It outputs box ID assigned by ISDS in dbID element.
6226 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6227 * box, or contact address of PFO box owner)
6228 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6229 * @upper_box_id is optional ID of supper box if currently created box is
6230 * subordinated.
6231 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6232 * @credentials_delivery is NULL if new password should be delivered off-line
6233 * to box owner. It is valid pointer if owner should obtain new password on-line
6234 * on dedicated web server. Then input @credentials_delivery.email value is
6235 * his e-mail address he must provide to dedicated web server together
6236 * with output reallocated @credentials_delivery.token member. Output
6237 * member @credentials_delivery.new_user_name is unused up on this call.
6238 * @approval is optional external approval of box manipulation
6239 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6240 * NULL, if you don't care.*/
6241 isds_error isds_add_box(struct isds_ctx *context,
6242 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6243 const char *former_names, const char *upper_box_id,
6244 const char *ceo_label,
6245 struct isds_credentials_delivery *credentials_delivery,
6246 const struct isds_approval *approval, char **refnumber) {
6247 isds_error err = IE_SUCCESS;
6248 #if HAVE_LIBCURL
6249 xmlNodePtr request = NULL;
6250 xmlDocPtr response = NULL;
6251 xmlXPathContextPtr xpath_ctx = NULL;
6252 xmlXPathObjectPtr result = NULL;
6253 #endif
6256 if (!context) return IE_INVALID_CONTEXT;
6257 zfree(context->long_message);
6258 if (credentials_delivery) {
6259 zfree(credentials_delivery->token);
6260 zfree(credentials_delivery->new_user_name);
6262 if (!box) return IE_INVAL;
6264 #if HAVE_LIBCURL
6265 /* Scratch box ID */
6266 zfree(box->dbID);
6268 /* Build CreateDataBox request */
6269 err = build_CreateDBInput_request(context,
6270 &request, BAD_CAST "CreateDataBox",
6271 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6272 (xmlChar *) ceo_label, credentials_delivery, approval);
6273 if (err) goto leave;
6275 /* Send it to server and process response */
6276 err = send_destroy_request_check_response(context,
6277 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6278 &response, (xmlChar **) refnumber, NULL);
6280 /* Extract box ID */
6281 xpath_ctx = xmlXPathNewContext(response);
6282 if (!xpath_ctx) {
6283 err = IE_ERROR;
6284 goto leave;
6286 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6287 err = IE_ERROR;
6288 goto leave;
6290 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6292 /* Extract optional token */
6293 err = extract_credentials_delivery(context, credentials_delivery, response,
6294 "CreateDataBox");
6296 leave:
6297 xmlXPathFreeObject(result);
6298 xmlXPathFreeContext(xpath_ctx);
6299 xmlFreeDoc(response);
6300 xmlFreeNode(request);
6302 if (!err) {
6303 isds_log(ILF_ISDS, ILL_DEBUG,
6304 _("CreateDataBox request processed by server successfully.\n"));
6306 #else /* not HAVE_LIBCURL */
6307 err = IE_NOTSUP;
6308 #endif
6310 return err;
6314 /* Notify ISDS about new PFO entity.
6315 * This function has no real effect.
6316 * @context is session context
6317 * @box is PFO description including single primary user. aifoIsds,
6318 * address->adCode, address->adDistrict members are ignored.
6319 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6320 * @former_names is optional undocumented string. Pass NULL if you don't care.
6321 * @upper_box_id is optional ID of supper box if currently created box is
6322 * subordinated.
6323 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6324 * @approval is optional external approval of box manipulation
6325 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6326 * NULL, if you don't care.*/
6327 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6328 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6329 const char *former_names, const char *upper_box_id,
6330 const char *ceo_label, const struct isds_approval *approval,
6331 char **refnumber) {
6332 isds_error err = IE_SUCCESS;
6333 #if HAVE_LIBCURL
6334 xmlNodePtr request = NULL;
6335 #endif
6337 if (!context) return IE_INVALID_CONTEXT;
6338 zfree(context->long_message);
6339 if (!box) return IE_INVAL;
6341 #if HAVE_LIBCURL
6342 /* Build CreateDataBoxPFOInfo request */
6343 err = build_CreateDBInput_request(context,
6344 &request, BAD_CAST "CreateDataBoxPFOInfo",
6345 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6346 (xmlChar *) ceo_label, NULL, approval);
6347 if (err) goto leave;
6349 /* Send it to server and process response */
6350 err = send_request_check_drop_response(context,
6351 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6352 (xmlChar **) refnumber);
6353 /* XXX: XML Schema names output dbID element but textual documentation
6354 * states no box identifier is returned. */
6355 leave:
6356 xmlFreeNode(request);
6357 #else /* not HAVE_LIBCURL */
6358 err = IE_NOTSUP;
6359 #endif
6360 return err;
6364 /* Common implementation for removing given box.
6365 * @context is session context
6366 * @service_name is UTF-8 encoded name fo ISDS service
6367 * @box is box description to delete. aifoIsds, address->adCode,
6368 * address->adDistrict members are ignored.
6369 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6370 * carry sane value. If NULL, do not inject this information into request.
6371 * @approval is optional external approval of box manipulation
6372 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6373 * NULL, if you don't care.*/
6374 static isds_error _isds_delete_box_common(struct isds_ctx *context,
6375 const xmlChar *service_name,
6376 const struct isds_DbOwnerInfo *box, const struct tm *since,
6377 const struct isds_approval *approval, char **refnumber) {
6378 isds_error err = IE_SUCCESS;
6379 #if HAVE_LIBCURL
6380 xmlNsPtr isds_ns = NULL;
6381 xmlNodePtr request = NULL;
6382 xmlNodePtr node;
6383 xmlChar *string = NULL;
6384 #endif
6387 if (!context) return IE_INVALID_CONTEXT;
6388 zfree(context->long_message);
6389 if (!service_name || !*service_name || !box) return IE_INVAL;
6392 #if HAVE_LIBCURL
6393 /* Build DeleteDataBox(Promptly) request */
6394 request = xmlNewNode(NULL, service_name);
6395 if (!request) {
6396 char *service_name_locale = _isds_utf82locale((char*)service_name);
6397 isds_printf_message(context,
6398 _("Could build %s request"), service_name_locale);
6399 free(service_name_locale);
6400 return IE_ERROR;
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;
6408 xmlSetNs(request, isds_ns);
6410 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6411 err = insert_DbOwnerInfo(context, box, 0, node);
6412 if (err) goto leave;
6414 if (since) {
6415 err = tm2datestring(since, &string);
6416 if (err) {
6417 isds_log_message(context,
6418 _("Could not convert `since' argument to ISO date string"));
6419 goto leave;
6421 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6422 zfree(string);
6425 err = insert_GExtApproval(context, approval, request);
6426 if (err) goto leave;
6429 /* Send it to server and process response */
6430 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6431 service_name, &request, (xmlChar **) refnumber);
6433 leave:
6434 xmlFreeNode(request);
6435 free(string);
6436 #else /* not HAVE_LIBCURL */
6437 err = IE_NOTSUP;
6438 #endif
6439 return err;
6443 /* Remove given box permanently.
6444 * @context is session context
6445 * @box is box description to delete. aifoIsds, address->adCode,
6446 * address->adDistrict members are ignored.
6447 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6448 * carry sane value.
6449 * @approval is optional external approval of box manipulation
6450 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6451 * NULL, if you don't care.*/
6452 isds_error isds_delete_box(struct isds_ctx *context,
6453 const struct isds_DbOwnerInfo *box, const struct tm *since,
6454 const struct isds_approval *approval, char **refnumber) {
6455 if (!context) return IE_INVALID_CONTEXT;
6456 zfree(context->long_message);
6457 if (!box || !since) return IE_INVAL;
6459 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6460 box, since, approval, refnumber);
6464 /* Undocumented function.
6465 * @context is session context
6466 * @box is box description to delete. aifoIsds, address->adCode,
6467 * address->adDistrict members are ignored.
6468 * @approval is optional external approval of box manipulation
6469 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6470 * NULL, if you don't care.*/
6471 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6472 const struct isds_DbOwnerInfo *box,
6473 const struct isds_approval *approval, char **refnumber) {
6474 if (!context) return IE_INVALID_CONTEXT;
6475 zfree(context->long_message);
6476 if (!box) return IE_INVAL;
6478 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6479 box, NULL, approval, refnumber);
6483 /* Update data about given box.
6484 * @context is session context
6485 * @old_box current box description. aifoIsds, address->adCode,
6486 * address->adDistrict members are ignored.
6487 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6488 * address->adDistrict members are ignored.
6489 * @approval is optional external approval of box manipulation
6490 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6491 * NULL, if you don't care.*/
6492 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6493 const struct isds_DbOwnerInfo *old_box,
6494 const struct isds_DbOwnerInfo *new_box,
6495 const struct isds_approval *approval, char **refnumber) {
6496 isds_error err = IE_SUCCESS;
6497 #if HAVE_LIBCURL
6498 xmlNsPtr isds_ns = NULL;
6499 xmlNodePtr request = NULL;
6500 xmlNodePtr node;
6501 #endif
6504 if (!context) return IE_INVALID_CONTEXT;
6505 zfree(context->long_message);
6506 if (!old_box || !new_box) return IE_INVAL;
6509 #if HAVE_LIBCURL
6510 /* Build UpdateDataBoxDescr request */
6511 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6512 if (!request) {
6513 isds_log_message(context,
6514 _("Could build UpdateDataBoxDescr request"));
6515 return IE_ERROR;
6517 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6518 if(!isds_ns) {
6519 isds_log_message(context, _("Could not create ISDS name space"));
6520 xmlFreeNode(request);
6521 return IE_ERROR;
6523 xmlSetNs(request, isds_ns);
6525 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6526 err = insert_DbOwnerInfo(context, old_box, 0, node);
6527 if (err) goto leave;
6529 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6530 err = insert_DbOwnerInfo(context, new_box, 0, node);
6531 if (err) goto leave;
6533 err = insert_GExtApproval(context, approval, request);
6534 if (err) goto leave;
6537 /* Send it to server and process response */
6538 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6539 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6541 leave:
6542 xmlFreeNode(request);
6543 #else /* not HAVE_LIBCURL */
6544 err = IE_NOTSUP;
6545 #endif
6547 return err;
6551 #if HAVE_LIBCURL
6552 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6553 * code
6554 * @context is session context
6555 * @service is SOAP service
6556 * @service_name is name of request in @service
6557 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6558 * @box_id is box ID of interest
6559 * @approval is optional external approval of box manipulation
6560 * @response is server SOAP body response as XML document
6561 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6562 * NULL, if you don't care.
6563 * @return error coded from lower layer, context message will be set up
6564 * appropriately. */
6565 static isds_error build_send_dbid_request_check_response(
6566 struct isds_ctx *context, const isds_service service,
6567 const xmlChar *service_name, const xmlChar *box_id_element,
6568 const xmlChar *box_id, const struct isds_approval *approval,
6569 xmlDocPtr *response, xmlChar **refnumber) {
6571 isds_error err = IE_SUCCESS;
6572 char *service_name_locale = NULL, *box_id_locale = NULL;
6573 xmlNodePtr request = NULL, node;
6574 xmlNsPtr isds_ns = NULL;
6576 if (!context) return IE_INVALID_CONTEXT;
6577 if (!service_name || !box_id) return IE_INVAL;
6578 if (!response) return IE_INVAL;
6580 /* Free output argument */
6581 xmlFreeDoc(*response); *response = NULL;
6583 /* Prepare strings */
6584 service_name_locale = _isds_utf82locale((char*)service_name);
6585 if (!service_name_locale) {
6586 err = IE_NOMEM;
6587 goto leave;
6589 box_id_locale = _isds_utf82locale((char*)box_id);
6590 if (!box_id_locale) {
6591 err = IE_NOMEM;
6592 goto leave;
6595 /* Build request */
6596 request = xmlNewNode(NULL, service_name);
6597 if (!request) {
6598 isds_printf_message(context,
6599 _("Could not build %s request for %s box"), service_name_locale,
6600 box_id_locale);
6601 err = IE_ERROR;
6602 goto leave;
6604 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6605 if(!isds_ns) {
6606 isds_log_message(context, _("Could not create ISDS name space"));
6607 err = IE_ERROR;
6608 goto leave;
6610 xmlSetNs(request, isds_ns);
6612 /* Add XSD:tIdDbInput children */
6613 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6614 INSERT_STRING(request, box_id_element, box_id);
6615 err = insert_GExtApproval(context, approval, request);
6616 if (err) goto leave;
6618 /* Send request and check response*/
6619 err = send_destroy_request_check_response(context,
6620 service, service_name, &request, response, refnumber, NULL);
6622 leave:
6623 free(service_name_locale);
6624 free(box_id_locale);
6625 xmlFreeNode(request);
6626 return err;
6628 #endif /* HAVE_LIBCURL */
6631 /* Get data about all users assigned to given box.
6632 * @context is session context
6633 * @box_id is box ID
6634 * @users is automatically reallocated list of struct isds_DbUserInfo */
6635 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6636 struct isds_list **users) {
6637 isds_error err = IE_SUCCESS;
6638 #if HAVE_LIBCURL
6639 xmlDocPtr response = NULL;
6640 xmlXPathContextPtr xpath_ctx = NULL;
6641 xmlXPathObjectPtr result = NULL;
6642 int i;
6643 struct isds_list *item, *prev_item = NULL;
6644 #endif
6646 if (!context) return IE_INVALID_CONTEXT;
6647 zfree(context->long_message);
6648 if (!users || !box_id) return IE_INVAL;
6649 isds_list_free(users);
6652 #if HAVE_LIBCURL
6653 /* Do request and check for success */
6654 err = build_send_dbid_request_check_response(context,
6655 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6656 BAD_CAST box_id, NULL, &response, NULL);
6657 if (err) goto leave;
6660 /* Extract data */
6661 /* Prepare structure */
6662 xpath_ctx = xmlXPathNewContext(response);
6663 if (!xpath_ctx) {
6664 err = IE_ERROR;
6665 goto leave;
6667 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6668 err = IE_ERROR;
6669 goto leave;
6672 /* Set context node */
6673 result = xmlXPathEvalExpression(BAD_CAST
6674 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6675 xpath_ctx);
6676 if (!result) {
6677 err = IE_ERROR;
6678 goto leave;
6680 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6681 /* Iterate over all users */
6682 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6684 /* Prepare structure */
6685 item = calloc(1, sizeof(*item));
6686 if (!item) {
6687 err = IE_NOMEM;
6688 goto leave;
6690 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6691 if (i == 0) *users = item;
6692 else prev_item->next = item;
6693 prev_item = item;
6695 /* Extract it */
6696 xpath_ctx->node = result->nodesetval->nodeTab[i];
6697 err = extract_DbUserInfo(context,
6698 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6699 if (err) goto leave;
6703 leave:
6704 if (err) {
6705 isds_list_free(users);
6708 xmlXPathFreeObject(result);
6709 xmlXPathFreeContext(xpath_ctx);
6710 xmlFreeDoc(response);
6712 if (!err)
6713 isds_log(ILF_ISDS, ILL_DEBUG,
6714 _("GetDataBoxUsers request processed by server "
6715 "successfully.\n"));
6716 #else /* not HAVE_LIBCURL */
6717 err = IE_NOTSUP;
6718 #endif
6720 return err;
6724 /* Update data about user assigned to given box.
6725 * @context is session context
6726 * @box is box identification. aifoIsds, address->adCode,
6727 * address->adDistrict members are ignored.
6728 * @old_user identifies user to update, aifo_ticket member is ignored
6729 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6730 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6731 * NULL, if you don't care.*/
6732 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6733 const struct isds_DbOwnerInfo *box,
6734 const struct isds_DbUserInfo *old_user,
6735 const struct isds_DbUserInfo *new_user,
6736 char **refnumber) {
6737 isds_error err = IE_SUCCESS;
6738 #if HAVE_LIBCURL
6739 xmlNsPtr isds_ns = NULL;
6740 xmlNodePtr request = NULL;
6741 xmlNodePtr node;
6742 #endif
6745 if (!context) return IE_INVALID_CONTEXT;
6746 zfree(context->long_message);
6747 if (!box || !old_user || !new_user) return IE_INVAL;
6750 #if HAVE_LIBCURL
6751 /* Build UpdateDataBoxUser request */
6752 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6753 if (!request) {
6754 isds_log_message(context,
6755 _("Could build UpdateDataBoxUser request"));
6756 return IE_ERROR;
6758 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6759 if(!isds_ns) {
6760 isds_log_message(context, _("Could not create ISDS name space"));
6761 xmlFreeNode(request);
6762 return IE_ERROR;
6764 xmlSetNs(request, isds_ns);
6766 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6767 err = insert_DbOwnerInfo(context, box, 0, node);
6768 if (err) goto leave;
6770 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6771 err = insert_DbUserInfo(context, old_user, 0, node);
6772 if (err) goto leave;
6774 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6775 err = insert_DbUserInfo(context, new_user, 0, node);
6776 if (err) goto leave;
6778 /* Send it to server and process response */
6779 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6780 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6782 leave:
6783 xmlFreeNode(request);
6784 #else /* not HAVE_LIBCURL */
6785 err = IE_NOTSUP;
6786 #endif
6788 return err;
6792 /* Undocumented function.
6793 * @context is session context
6794 * @box_id is UTF-8 encoded box identifier
6795 * @token is UTF-8 encoded temporary password
6796 * @user_id outputs UTF-8 encoded reallocated user identifier
6797 * @password outpus UTF-8 encoded reallocated user password
6798 * Output arguments will be nulled in case of error */
6799 isds_error isds_activate(struct isds_ctx *context,
6800 const char *box_id, const char *token,
6801 char **user_id, char **password) {
6802 isds_error err = IE_SUCCESS;
6803 #if HAVE_LIBCURL
6804 xmlNsPtr isds_ns = NULL;
6805 xmlNodePtr request = NULL, node;
6806 xmlDocPtr response = NULL;
6807 xmlXPathContextPtr xpath_ctx = NULL;
6808 xmlXPathObjectPtr result = NULL;
6809 #endif
6812 if (!context) return IE_INVALID_CONTEXT;
6813 zfree(context->long_message);
6815 if (user_id) zfree(*user_id);
6816 if (password) zfree(*password);
6818 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6821 #if HAVE_LIBCURL
6822 /* Build Activate request */
6823 request = xmlNewNode(NULL, BAD_CAST "Activate");
6824 if (!request) {
6825 isds_log_message(context, _("Could build Activate request"));
6826 return IE_ERROR;
6828 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6829 if(!isds_ns) {
6830 isds_log_message(context, _("Could not create ISDS name space"));
6831 xmlFreeNode(request);
6832 return IE_ERROR;
6834 xmlSetNs(request, isds_ns);
6836 INSERT_STRING(request, "dbAccessDataId", token);
6837 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6838 INSERT_STRING(request, "dbID", box_id);
6841 /* Send request and check response*/
6842 err = send_destroy_request_check_response(context,
6843 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6844 &response, NULL, NULL);
6845 if (err) goto leave;
6848 /* Extract data */
6849 xpath_ctx = xmlXPathNewContext(response);
6850 if (!xpath_ctx) {
6851 err = IE_ERROR;
6852 goto leave;
6854 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6855 err = IE_ERROR;
6856 goto leave;
6858 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6859 xpath_ctx);
6860 if (!result) {
6861 err = IE_ERROR;
6862 goto leave;
6864 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6865 isds_log_message(context, _("Missing ActivateResponse element"));
6866 err = IE_ISDS;
6867 goto leave;
6869 if (result->nodesetval->nodeNr > 1) {
6870 isds_log_message(context, _("Multiple ActivateResponse element"));
6871 err = IE_ISDS;
6872 goto leave;
6874 xpath_ctx->node = result->nodesetval->nodeTab[0];
6875 xmlXPathFreeObject(result); result = NULL;
6877 EXTRACT_STRING("isds:userId", *user_id);
6878 if (!*user_id)
6879 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6880 "but did not return `userId' element.\n"));
6882 EXTRACT_STRING("isds:password", *password);
6883 if (!*password)
6884 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6885 "but did not return `password' element.\n"));
6887 leave:
6888 xmlXPathFreeObject(result);
6889 xmlXPathFreeContext(xpath_ctx);
6890 xmlFreeDoc(response);
6891 xmlFreeNode(request);
6893 if (!err)
6894 isds_log(ILF_ISDS, ILL_DEBUG,
6895 _("Activate request processed by server successfully.\n"));
6896 #else /* not HAVE_LIBCURL */
6897 err = IE_NOTSUP;
6898 #endif
6900 return err;
6904 /* Reset credentials of user assigned to given box.
6905 * @context is session context
6906 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6907 * members are ignored.
6908 * @user identifies user to reset password, aifo_ticket member is ignored
6909 * @fee_paid is true if fee has been paid, false otherwise
6910 * @approval is optional external approval of box manipulation
6911 * @credentials_delivery is NULL if new password should be delivered off-line
6912 * to the user. It is valid pointer if user should obtain new password on-line
6913 * on dedicated web server. Then input @credentials_delivery.email value is
6914 * user's e-mail address user must provide to dedicated web server together
6915 * with @credentials_delivery.token. The output reallocated token user needs
6916 * to use to authorize on the web server to view his new password. Output
6917 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6918 * ISDS changed up on this call. (No reason why server could change the name
6919 * is known now.)
6920 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6921 * NULL, if you don't care.*/
6922 isds_error isds_reset_password(struct isds_ctx *context,
6923 const struct isds_DbOwnerInfo *box,
6924 const struct isds_DbUserInfo *user,
6925 const _Bool fee_paid, const struct isds_approval *approval,
6926 struct isds_credentials_delivery *credentials_delivery,
6927 char **refnumber) {
6928 isds_error err = IE_SUCCESS;
6929 #if HAVE_LIBCURL
6930 xmlNsPtr isds_ns = NULL;
6931 xmlNodePtr request = NULL, node;
6932 xmlDocPtr response = NULL;
6933 #endif
6936 if (!context) return IE_INVALID_CONTEXT;
6937 zfree(context->long_message);
6939 if (credentials_delivery) {
6940 zfree(credentials_delivery->token);
6941 zfree(credentials_delivery->new_user_name);
6943 if (!box || !user) return IE_INVAL;
6946 #if HAVE_LIBCURL
6947 /* Build NewAccessData request */
6948 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6949 if (!request) {
6950 isds_log_message(context,
6951 _("Could build NewAccessData request"));
6952 return IE_ERROR;
6954 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6955 if(!isds_ns) {
6956 isds_log_message(context, _("Could not create ISDS name space"));
6957 xmlFreeNode(request);
6958 return IE_ERROR;
6960 xmlSetNs(request, isds_ns);
6962 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6963 err = insert_DbOwnerInfo(context, box, 0, node);
6964 if (err) goto leave;
6966 INSERT_ELEMENT(node, request, "dbUserInfo");
6967 err = insert_DbUserInfo(context, user, 0, node);
6968 if (err) goto leave;
6970 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6972 err = insert_credentials_delivery(context, credentials_delivery, request);
6973 if (err) goto leave;
6975 err = insert_GExtApproval(context, approval, request);
6976 if (err) goto leave;
6978 /* Send request and check response*/
6979 err = send_destroy_request_check_response(context,
6980 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6981 &response, (xmlChar **) refnumber, NULL);
6982 if (err) goto leave;
6985 /* Extract optional token */
6986 err = extract_credentials_delivery(context, credentials_delivery,
6987 response, "NewAccessData");
6989 leave:
6990 xmlFreeDoc(response);
6991 xmlFreeNode(request);
6993 if (!err)
6994 isds_log(ILF_ISDS, ILL_DEBUG,
6995 _("NewAccessData request processed by server "
6996 "successfully.\n"));
6997 #else /* not HAVE_LIBCURL */
6998 err = IE_NOTSUP;
6999 #endif
7001 return err;
7005 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7006 * code, destroy response and log success.
7007 * @context is ISDS session context.
7008 * @service_name is name of SERVICE_DB_MANIPULATION service
7009 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7010 * members are ignored.
7011 * @user identifies user to remove
7012 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7013 * @credentials_delivery is NULL if new user's password should be delivered
7014 * off-line to the user. It is valid pointer if user should obtain new
7015 * password on-line on dedicated web server. Then input
7016 * @credentials_delivery.email value is user's e-mail address user must
7017 * provide to dedicated web server together with @credentials_delivery.token.
7018 * The output reallocated token user needs to use to authorize on the web
7019 * server to view his new password. Output reallocated
7020 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7021 * assingned or changed up on this call.
7022 * @approval is optional external approval of box manipulation
7023 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7024 * NULL, if you don't care. */
7025 static isds_error build_send_manipulationboxuser_request_check_drop_response(
7026 struct isds_ctx *context, const xmlChar *service_name,
7027 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7028 _Bool honor_aifo_ticket,
7029 struct isds_credentials_delivery *credentials_delivery,
7030 const struct isds_approval *approval, xmlChar **refnumber) {
7031 isds_error err = IE_SUCCESS;
7032 #if HAVE_LIBCURL
7033 xmlNsPtr isds_ns = NULL;
7034 xmlNodePtr request = NULL, node;
7035 xmlDocPtr response = NULL;
7036 #endif
7039 if (!context) return IE_INVALID_CONTEXT;
7040 zfree(context->long_message);
7041 if (credentials_delivery) {
7042 zfree(credentials_delivery->token);
7043 zfree(credentials_delivery->new_user_name);
7045 if (!service_name || service_name[0] == '\0' || !box || !user)
7046 return IE_INVAL;
7049 #if HAVE_LIBCURL
7050 /* Build NewAccessData or similar request */
7051 request = xmlNewNode(NULL, service_name);
7052 if (!request) {
7053 char *service_name_locale = _isds_utf82locale((char *) service_name);
7054 isds_printf_message(context, _("Could not build %s request"),
7055 service_name_locale);
7056 free(service_name_locale);
7057 return IE_ERROR;
7059 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7060 if(!isds_ns) {
7061 isds_log_message(context, _("Could not create ISDS name space"));
7062 xmlFreeNode(request);
7063 return IE_ERROR;
7065 xmlSetNs(request, isds_ns);
7067 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7068 err = insert_DbOwnerInfo(context, box, 0, node);
7069 if (err) goto leave;
7071 INSERT_ELEMENT(node, request, "dbUserInfo");
7072 err = insert_DbUserInfo(context, user, honor_aifo_ticket, node);
7073 if (err) goto leave;
7075 err = insert_credentials_delivery(context, credentials_delivery, request);
7076 if (err) goto leave;
7078 err = insert_GExtApproval(context, approval, request);
7079 if (err) goto leave;
7082 /* Send request and check response*/
7083 err = send_destroy_request_check_response(context,
7084 SERVICE_DB_MANIPULATION, service_name, &request, &response,
7085 refnumber, NULL);
7087 xmlFreeNode(request);
7088 request = NULL;
7090 /* Pick up credentials_delivery if requested */
7091 err = extract_credentials_delivery(context, credentials_delivery, response,
7092 (char *)service_name);
7094 leave:
7095 xmlFreeDoc(response);
7096 if (request) xmlFreeNode(request);
7098 if (!err) {
7099 char *service_name_locale = _isds_utf82locale((char *) service_name);
7100 isds_log(ILF_ISDS, ILL_DEBUG,
7101 _("%s request processed by server successfully.\n"),
7102 service_name_locale);
7103 free(service_name_locale);
7105 #else /* not HAVE_LIBCURL */
7106 err = IE_NOTSUP;
7107 #endif
7109 return err;
7113 /* Assign new user to given box.
7114 * @context is session context
7115 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7116 * members are ignored.
7117 * @user defines new user to add
7118 * @credentials_delivery is NULL if new user's password should be delivered
7119 * off-line to the user. It is valid pointer if user should obtain new
7120 * password on-line on dedicated web server. Then input
7121 * @credentials_delivery.email value is user's e-mail address user must
7122 * provide to dedicated web server together with @credentials_delivery.token.
7123 * The output reallocated token user needs to use to authorize on the web
7124 * server to view his new password. Output reallocated
7125 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7126 * assingned up on this call.
7127 * @approval is optional external approval of box manipulation
7128 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7129 * NULL, if you don't care.*/
7130 isds_error isds_add_user(struct isds_ctx *context,
7131 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7132 struct isds_credentials_delivery *credentials_delivery,
7133 const struct isds_approval *approval, char **refnumber) {
7134 return build_send_manipulationboxuser_request_check_drop_response(context,
7135 BAD_CAST "AddDataBoxUser", box, user, 1, credentials_delivery,
7136 approval, (xmlChar **) refnumber);
7140 /* Remove user assigned to given box.
7141 * @context is session context
7142 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7143 * members are ignored.
7144 * @user identifies user to remove, aifo_ticket member is ignored
7145 * @approval is optional external approval of box manipulation
7146 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7147 * NULL, if you don't care.*/
7148 isds_error isds_delete_user(struct isds_ctx *context,
7149 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
7150 const struct isds_approval *approval, char **refnumber) {
7151 return build_send_manipulationboxuser_request_check_drop_response(context,
7152 BAD_CAST "DeleteDataBoxUser", box, user, 0, NULL, approval,
7153 (xmlChar **) refnumber);
7157 /* Get list of boxes in ZIP archive.
7158 * @context is session context
7159 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7160 * System recognizes following values currently: ALL (all boxes), UPG
7161 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7162 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7163 * commercial messages). This argument is a string because specification
7164 * states new values can appear in the future. Not all list types are
7165 * available to all users.
7166 * @buffer is automatically reallocated memory to store the list of boxes. The
7167 * list is zipped CSV file.
7168 * @buffer_length is size of @buffer data in bytes.
7169 * In case of error @buffer will be freed and @buffer_length will be
7170 * undefined.*/
7171 isds_error isds_get_box_list_archive(struct isds_ctx *context,
7172 const char *list_identifier, void **buffer, size_t *buffer_length) {
7173 isds_error err = IE_SUCCESS;
7174 #if HAVE_LIBCURL
7175 xmlNsPtr isds_ns = NULL;
7176 xmlNodePtr request = NULL, node;
7177 xmlDocPtr response = NULL;
7178 xmlXPathContextPtr xpath_ctx = NULL;
7179 xmlXPathObjectPtr result = NULL;
7180 char *string = NULL;
7181 #endif
7184 if (!context) return IE_INVALID_CONTEXT;
7185 zfree(context->long_message);
7186 if (buffer) zfree(*buffer);
7187 if (!buffer || !buffer_length) return IE_INVAL;
7190 #if HAVE_LIBCURL
7191 /* Check if connection is established
7192 * TODO: This check should be done downstairs. */
7193 if (!context->curl) return IE_CONNECTION_CLOSED;
7196 /* Build AuthenticateMessage request */
7197 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7198 if (!request) {
7199 isds_log_message(context,
7200 _("Could not build GetDataBoxList request"));
7201 return IE_ERROR;
7203 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7204 if(!isds_ns) {
7205 isds_log_message(context, _("Could not create ISDS name space"));
7206 xmlFreeNode(request);
7207 return IE_ERROR;
7209 xmlSetNs(request, isds_ns);
7210 INSERT_STRING(request, "dblType", list_identifier);
7212 /* Send request to server and process response */
7213 err = send_destroy_request_check_response(context,
7214 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7215 &response, NULL, NULL);
7216 if (err) goto leave;
7219 /* Extract Base-64 encoded ZIP file */
7220 xpath_ctx = xmlXPathNewContext(response);
7221 if (!xpath_ctx) {
7222 err = IE_ERROR;
7223 goto leave;
7225 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7226 err = IE_ERROR;
7227 goto leave;
7229 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7231 /* Decode non-empty archive */
7232 if (string && string[0] != '\0') {
7233 *buffer_length = _isds_b64decode(string, buffer);
7234 if (*buffer_length == (size_t) -1) {
7235 isds_printf_message(context,
7236 _("Error while Base64-decoding box list archive"));
7237 err = IE_ERROR;
7238 goto leave;
7243 leave:
7244 free(string);
7245 xmlXPathFreeObject(result);
7246 xmlXPathFreeContext(xpath_ctx);
7247 xmlFreeDoc(response);
7248 xmlFreeNode(request);
7250 if (!err) {
7251 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7252 "processed by server successfully.\n"));
7254 #else /* not HAVE_LIBCURL */
7255 err = IE_NOTSUP;
7256 #endif
7258 return err;
7262 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7263 * send it, check for error code, extract list of results, destroy response
7264 * and log success.
7265 * @context is ISDS session context.
7266 * @service_name is name of SERVICE_DB_SEARCH service
7267 * @pfo_service is false if tDbOwnerInfo request should be built from
7268 * @criteria and corresponding result extracted. It is true if
7269 * tDbPersonalOwnerInfoRequest request should be built. The request and
7270 * response differ subset of significant isds_DbOwnerInfo structure members.
7271 * @criteria is filter. You should fill in at least some members.
7272 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7273 * members will be ignored. If @pfo_service is true, dbType, ic,
7274 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7275 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7276 * ignored.
7277 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7278 * possibly empty. Input NULL or valid old structure. The same memebers as
7279 * in described for @criteria argument will be NULL according to @pfo_service
7280 * switch.
7281 * @return:
7282 * IE_SUCCESS if search succeeded, @boxes contains useful data
7283 * IE_NOEXIST if no such box exists, @boxes will be NULL
7284 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7285 * contains still valid data
7286 * other code if something bad happens. @boxes will be NULL. */
7287 static isds_error build_send_findbox_request_check_parse_drop_response(
7288 struct isds_ctx *context, const xmlChar *service_name,
7289 _Bool pfo_service, const struct isds_DbOwnerInfo *criteria,
7290 struct isds_list **boxes) {
7291 isds_error err = IE_SUCCESS;
7292 #if HAVE_LIBCURL
7293 char *service_name_locale = NULL;
7294 _Bool truncated = 0;
7295 xmlNsPtr isds_ns = NULL;
7296 xmlNodePtr request = NULL;
7297 xmlDocPtr response = NULL;
7298 xmlChar *code = NULL, *message = NULL;
7299 xmlNodePtr db_owner_info;
7300 xmlXPathContextPtr xpath_ctx = NULL;
7301 xmlXPathObjectPtr result = NULL;
7302 xmlChar *string = NULL;
7303 #endif
7306 if (!context) return IE_INVALID_CONTEXT;
7307 zfree(context->long_message);
7308 if (!boxes) return IE_INVAL;
7309 isds_list_free(boxes);
7311 if (!criteria) {
7312 return IE_INVAL;
7315 #if HAVE_LIBCURL
7316 /* Check if connection is established
7317 * TODO: This check should be done downstairs. */
7318 if (!context->curl) return IE_CONNECTION_CLOSED;
7319 service_name_locale = _isds_utf82locale((char *) service_name);
7321 /* Build request */
7322 request = xmlNewNode(NULL, service_name);
7323 if (!request) {
7324 isds_printf_message(context, _("Could not build %s request"),
7325 service_name_locale);
7326 free(service_name_locale);
7327 return IE_ERROR;
7329 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7330 if(!isds_ns) {
7331 isds_log_message(context, _("Could not create ISDS name space"));
7332 free(service_name_locale);
7333 xmlFreeNode(request);
7334 return IE_ERROR;
7336 xmlSetNs(request, isds_ns);
7337 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7338 if (!db_owner_info) {
7339 isds_printf_message(context,
7340 _("Could not add dbOwnerInfo child to %s element"),
7341 service_name_locale);
7342 free(service_name_locale);
7343 xmlFreeNode(request);
7344 return IE_ERROR;
7347 err = insert_DbOwnerInfo(context, criteria, pfo_service, db_owner_info);
7348 if (err) goto leave;
7351 /* Send request */
7352 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
7353 service_name_locale);
7354 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7356 /* Destroy request */
7357 xmlFreeNode(request); request = NULL;
7359 if (err) {
7360 isds_log(ILF_ISDS, ILL_DEBUG,
7361 _("Processing ISDS response on %s request failed\n"),
7362 service_name_locale);
7363 goto leave;
7366 /* Check for response status */
7367 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7368 &code, &message, NULL);
7369 if (err) {
7370 isds_log(ILF_ISDS, ILL_DEBUG,
7371 _("ISDS response on %s request is missing status\n"),
7372 service_name_locale);
7373 goto leave;
7376 /* Request processed, but nothing found */
7377 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7378 !xmlStrcmp(code, BAD_CAST "5001")) {
7379 char *code_locale = _isds_utf82locale((char*)code);
7380 char *message_locale = _isds_utf82locale((char*)message);
7381 isds_log(ILF_ISDS, ILL_DEBUG,
7382 _("Server did not find any box on %s request "
7383 "(code=%s, message=%s)\n"), service_name_locale,
7384 code_locale, message_locale);
7385 isds_log_message(context, message_locale);
7386 free(code_locale);
7387 free(message_locale);
7388 err = IE_NOEXIST;
7389 goto leave;
7392 /* Warning, not an error */
7393 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7394 char *code_locale = _isds_utf82locale((char*)code);
7395 char *message_locale = _isds_utf82locale((char*)message);
7396 isds_log(ILF_ISDS, ILL_DEBUG,
7397 _("Server truncated response on %s request "
7398 "(code=%s, message=%s)\n"), service_name_locale,
7399 code_locale, message_locale);
7400 isds_log_message(context, message_locale);
7401 free(code_locale);
7402 free(message_locale);
7403 truncated = 1;
7406 /* Other error */
7407 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7408 char *code_locale = _isds_utf82locale((char*)code);
7409 char *message_locale = _isds_utf82locale((char*)message);
7410 isds_log(ILF_ISDS, ILL_DEBUG,
7411 _("Server refused %s request (code=%s, message=%s)\n"),
7412 service_name_locale, code_locale, message_locale);
7413 isds_log_message(context, message_locale);
7414 free(code_locale);
7415 free(message_locale);
7416 err = IE_ISDS;
7417 goto leave;
7420 xpath_ctx = xmlXPathNewContext(response);
7421 if (!xpath_ctx) {
7422 err = IE_ERROR;
7423 goto leave;
7425 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7426 err = IE_ERROR;
7427 goto leave;
7430 /* Extract boxes if they present */
7431 if (-1 == isds_asprintf((char **)&string,
7432 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7433 service_name)) {
7434 err = IE_NOMEM;
7435 goto leave;
7437 result = xmlXPathEvalExpression(string, xpath_ctx);
7438 zfree(string);
7439 if (!result) {
7440 err = IE_ERROR;
7441 goto leave;
7443 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7444 struct isds_list *item, *prev_item = NULL;
7445 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7446 item = calloc(1, sizeof(*item));
7447 if (!item) {
7448 err = IE_NOMEM;
7449 goto leave;
7452 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7453 if (i == 0) *boxes = item;
7454 else prev_item->next = item;
7455 prev_item = item;
7457 xpath_ctx->node = result->nodesetval->nodeTab[i];
7458 err = extract_DbOwnerInfo(context,
7459 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7460 if (err) goto leave;
7464 leave:
7465 if (err) {
7466 isds_list_free(boxes);
7467 } else {
7468 if (truncated) err = IE_2BIG;
7471 free(string);
7472 xmlFreeNode(request);
7473 xmlXPathFreeObject(result);
7474 xmlXPathFreeContext(xpath_ctx);
7476 free(code);
7477 free(message);
7478 xmlFreeDoc(response);
7480 if (!err)
7481 isds_log(ILF_ISDS, ILL_DEBUG,
7482 _("%s request processed by server successfully.\n"),
7483 service_name_locale);
7484 free(service_name_locale);
7485 #else /* not HAVE_LIBCURL */
7486 err = IE_NOTSUP;
7487 #endif
7489 return err;
7493 /* Find boxes suiting given criteria.
7494 * @criteria is filter. You should fill in at least some members. aifoIsds,
7495 * address->adCode, address->adDistrict members are ignored.
7496 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7497 * possibly empty. Input NULL or valid old structure.
7498 * @return:
7499 * IE_SUCCESS if search succeeded, @boxes contains useful data
7500 * IE_NOEXIST if no such box exists, @boxes will be NULL
7501 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7502 * contains still valid data
7503 * other code if something bad happens. @boxes will be NULL. */
7504 isds_error isds_FindDataBox(struct isds_ctx *context,
7505 const struct isds_DbOwnerInfo *criteria,
7506 struct isds_list **boxes) {
7507 return build_send_findbox_request_check_parse_drop_response(context,
7508 BAD_CAST "FindDataBox", 0, criteria, boxes);
7512 /* Find accessible FO-type boxes suiting given criteria.
7513 * @criteria is filter. You should fill in at least some members. dbType, ic,
7514 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7515 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7516 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7517 * possibly empty. Input NULL or valid old structure.
7518 * @return:
7519 * IE_SUCCESS if search succeeded, @boxes contains useful data
7520 * IE_NOEXIST if no such box exists, @boxes will be NULL
7521 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7522 * contains still valid data
7523 * other code if something bad happens. @boxes will be NULL. */
7524 isds_error isds_FindPersonalDataBox(struct isds_ctx *context,
7525 const struct isds_DbOwnerInfo *criteria,
7526 struct isds_list **boxes) {
7527 return build_send_findbox_request_check_parse_drop_response(context,
7528 BAD_CAST "FindPersonalDataBox", 1, criteria, boxes);
7532 #if HAVE_LIBCURL
7533 /* Convert a string with match markers into a plain string with list of
7534 * pointers to the matches
7535 * @string is an UTF-8 encoded non-constant string with match markers
7536 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7537 * The markers will be removed from the string.
7538 * @starts is a reallocated list of static pointers into the @string pointing
7539 * to places where match start markers occured.
7540 * @ends is a reallocated list of static pointers into the @string pointing
7541 * to places where match end markers occured.
7542 * @return IE_SUCCESS in case of no failure. */
7543 static isds_error interpret_matches(xmlChar *string,
7544 struct isds_list **starts, struct isds_list **ends) {
7545 isds_error err = IE_SUCCESS;
7546 xmlChar *pointer, *destination, *source;
7547 struct isds_list *item, *prev_start = NULL, *prev_end = NULL;
7549 isds_list_free(starts);
7550 isds_list_free(ends);
7551 if (NULL == starts || NULL == ends) return IE_INVAL;
7552 if (NULL == string) return IE_SUCCESS;
7554 for (pointer = string; *pointer != '\0';) {
7555 if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_START*$|", 14)) {
7556 /* Remove the start marker */
7557 for (source = pointer + 14, destination = pointer;
7558 *source != '\0'; source++, destination++) {
7559 *destination = *source;
7561 *destination = '\0';
7562 /* Append the pointer into the list */
7563 item = calloc(1, sizeof(*item));
7564 if (!item) {
7565 err = IE_NOMEM;
7566 goto leave;
7568 item->destructor = (void (*)(void **))NULL;
7569 item->data = pointer;
7570 if (NULL == prev_start) *starts = item;
7571 else prev_start->next = item;
7572 prev_start = item;
7573 } else if (!xmlStrncmp(pointer, BAD_CAST "|$*HL_END*$|", 12)) {
7574 /* Remove the end marker */
7575 for (source = pointer + 12, destination = pointer;
7576 *source != '\0'; source++, destination++) {
7577 *destination = *source;
7579 *destination = '\0';
7580 /* Append the pointer into the list */
7581 item = calloc(1, sizeof(*item));
7582 if (!item) {
7583 err = IE_NOMEM;
7584 goto leave;
7586 item->destructor = (void (*)(void **))NULL;
7587 item->data = pointer;
7588 if (NULL == prev_end) *ends = item;
7589 else prev_end->next = item;
7590 prev_end = item;
7591 } else {
7592 pointer++;
7596 leave:
7597 if (err) {
7598 isds_list_free(starts);
7599 isds_list_free(ends);
7601 return err;
7605 /* Convert isds:dbResult XML tree into structure
7606 * @context is ISDS context.
7607 * @fulltext_result is automatically reallocated found box structure.
7608 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7609 * @collect_matches is true to interpret match markers.
7610 * In case of error @result will be freed. */
7611 static isds_error extract_dbResult(struct isds_ctx *context,
7612 struct isds_fulltext_result **fulltext_result,
7613 xmlXPathContextPtr xpath_ctx, _Bool collect_matches) {
7614 isds_error err = IE_SUCCESS;
7615 xmlXPathObjectPtr result = NULL;
7616 char *string = NULL;
7618 if (NULL == context) return IE_INVALID_CONTEXT;
7619 if (NULL == fulltext_result) return IE_INVAL;
7620 isds_fulltext_result_free(fulltext_result);
7621 if (!xpath_ctx) return IE_INVAL;
7624 *fulltext_result = calloc(1, sizeof(**fulltext_result));
7625 if (NULL == *fulltext_result) {
7626 err = IE_NOMEM;
7627 goto leave;
7630 /* Extract data */
7631 EXTRACT_STRING("isds:dbID", (*fulltext_result)->dbID);
7633 EXTRACT_STRING("isds:dbType", string);
7634 if (NULL == string) {
7635 err = IE_ISDS;
7636 isds_log_message(context, _("Empty isds:dbType element"));
7637 goto leave;
7639 err = string2isds_DbType((xmlChar *)string, &(*fulltext_result)->dbType);
7640 if (err) {
7641 if (err == IE_ENUM) {
7642 err = IE_ISDS;
7643 char *string_locale = _isds_utf82locale(string);
7644 isds_printf_message(context, _("Unknown isds:dbType: %s"),
7645 string_locale);
7646 free(string_locale);
7648 goto leave;
7650 zfree(string);
7652 EXTRACT_STRING("isds:dbName", (*fulltext_result)->name);
7653 EXTRACT_STRING("isds:dbAddress", (*fulltext_result)->address);
7655 err = extract_BiDate(context, &(*fulltext_result)->biDate, xpath_ctx);
7656 if (err) goto leave;
7658 EXTRACT_STRING("isds:dbICO", (*fulltext_result)->ic);
7659 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7660 (*fulltext_result)->dbEffectiveOVM);
7662 EXTRACT_STRING("isds:dbSendOptions", string);
7663 if (NULL == string) {
7664 err = IE_ISDS;
7665 isds_log_message(context, _("Empty isds:dbSendOptions element"));
7666 goto leave;
7668 if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DZ")) {
7669 (*fulltext_result)->active = 1;
7670 (*fulltext_result)->public_sending = 1;
7671 (*fulltext_result)->commercial_sending = 0;
7672 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "ALL")) {
7673 (*fulltext_result)->active = 1;
7674 (*fulltext_result)->public_sending = 1;
7675 (*fulltext_result)->commercial_sending = 1;
7676 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "PDZ")) {
7677 (*fulltext_result)->active = 1;
7678 (*fulltext_result)->public_sending = 0;
7679 (*fulltext_result)->commercial_sending = 1;
7680 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "NONE")) {
7681 (*fulltext_result)->active = 1;
7682 (*fulltext_result)->public_sending = 0;
7683 (*fulltext_result)->commercial_sending = 0;
7684 } else if (!xmlStrcmp(BAD_CAST string, BAD_CAST "DISABLED")) {
7685 (*fulltext_result)->active = 0;
7686 (*fulltext_result)->public_sending = 0;
7687 (*fulltext_result)->commercial_sending = 0;
7688 } else {
7689 err = IE_ISDS;
7690 char *string_locale = _isds_utf82locale(string);
7691 isds_printf_message(context, _("Unknown isds:dbSendOptions value: %s"),
7692 string_locale);
7693 free(string_locale);
7694 goto leave;
7696 zfree(string);
7698 /* Interpret match marks */
7699 if (collect_matches) {
7700 err = interpret_matches(BAD_CAST (*fulltext_result)->name,
7701 &((*fulltext_result)->name_match_start),
7702 &((*fulltext_result)->name_match_end));
7703 if (err) goto leave;
7704 err = interpret_matches(BAD_CAST (*fulltext_result)->address,
7705 &((*fulltext_result)->address_match_start),
7706 &((*fulltext_result)->address_match_end));
7707 if (err) goto leave;
7710 leave:
7711 if (err) isds_fulltext_result_free(fulltext_result);
7712 free(string);
7713 xmlXPathFreeObject(result);
7714 return err;
7716 #endif /* HAVE_LIBCURL */
7719 /* Find boxes matching a given full-text criteria.
7720 * @context is a session context
7721 * @query is a non-empty string which consists of words to search
7722 * @target selects box attributes to search for @query words. Pass NULL if you
7723 * don't care.
7724 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7725 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7726 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7727 * which is DBTYPE_SYSTEM.
7728 * @page_size defines count of boxes to constitute a response page. It counts
7729 * from zero. Pass NULL to let server to use a default value (50 now).
7730 * @page_number defines ordinar number of the response page to return. It
7731 * counts from zero. Pass NULL to let server to use a default value (0 now).
7732 * @track_matches points to true for marking @query words found in the box
7733 * attributes. It points to false for not marking. Pass NULL to let the server
7734 * to use default value (false now).
7735 * @total_matching_boxes outputs reallocated number of all boxes matching the
7736 * query. Will be pointer to NULL if server did not provide the value.
7737 * Pass NULL if you don't care.
7738 * @current_page_beginning outputs reallocated ordinar number of the first box
7739 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7740 * server did not provide the value. Pass NULL if you don't care.
7741 * @current_page_size outputs reallocated count of boxes in the this @boxes
7742 * page. It will be pointer to NULL if the server did not provide the value.
7743 * Pass NULL if you don't care.
7744 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7745 * is the last one, false if more boxes match, NULL if the server did not
7746 * provude the value. Pass NULL if you don't care.
7747 * @boxes outputs reallocated list of isds_fulltext_result structures,
7748 * possibly empty.
7749 * @return:
7750 * IE_SUCCESS if search succeeded
7751 * IE_2BIG if @page_size is too large
7752 * other code if something bad happens; output arguments will be NULL. */
7753 isds_error isds_find_box_by_fulltext(struct isds_ctx *context,
7754 const char *query,
7755 const isds_fulltext_target *target,
7756 const isds_DbType *box_type,
7757 const unsigned long int *page_size,
7758 const unsigned long int *page_number,
7759 const _Bool *track_matches,
7760 unsigned long int **total_matching_boxes,
7761 unsigned long int **current_page_beginning,
7762 unsigned long int **current_page_size,
7763 _Bool **last_page,
7764 struct isds_list **boxes) {
7765 isds_error err = IE_SUCCESS;
7766 #if HAVE_LIBCURL
7767 xmlNsPtr isds_ns = NULL;
7768 xmlNodePtr request = NULL;
7769 xmlDocPtr response = NULL;
7770 xmlNodePtr node;
7771 xmlXPathContextPtr xpath_ctx = NULL;
7772 xmlXPathObjectPtr result = NULL;
7773 const xmlChar *static_string = NULL;
7774 xmlChar *string = NULL;
7776 const xmlChar *codes[] = {
7777 BAD_CAST "1004",
7778 BAD_CAST "1152",
7779 BAD_CAST "1153",
7780 BAD_CAST "1154",
7781 BAD_CAST "1155",
7782 BAD_CAST "1156",
7783 BAD_CAST "9002",
7784 NULL
7786 const char *meanings[] = {
7787 N_("You are not allowed to perform the search"),
7788 N_("The query string is empty"),
7789 N_("Searched box ID is malformed"),
7790 N_("Searched organization ID is malformed"),
7791 N_("Invalid input"),
7792 N_("Requested page size is too large"),
7793 N_("Search engine internal error")
7795 const isds_error errors[] = {
7796 IE_ISDS,
7797 IE_INVAL,
7798 IE_INVAL,
7799 IE_INVAL,
7800 IE_INVAL,
7801 IE_2BIG,
7802 IE_ISDS
7804 struct code_map_isds_error map = {
7805 .codes = codes,
7806 .meanings = meanings,
7807 .errors = errors
7809 #endif
7812 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7813 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7814 if (NULL != current_page_size) zfree(*current_page_size);
7815 if (NULL != last_page) zfree(*last_page);
7816 isds_list_free(boxes);
7818 if (NULL == context) return IE_INVALID_CONTEXT;
7819 zfree(context->long_message);
7821 if (NULL == boxes) return IE_INVAL;
7823 if (NULL == query || !xmlStrcmp(BAD_CAST query, BAD_CAST "")) {
7824 isds_log_message(context, _("Query string must be non-empty"));
7825 return IE_INVAL;
7828 #if HAVE_LIBCURL
7829 /* Check if connection is established
7830 * TODO: This check should be done downstairs. */
7831 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7833 /* Build FindDataBox request */
7834 request = xmlNewNode(NULL, BAD_CAST "ISDSSearch2");
7835 if (NULL == request) {
7836 isds_log_message(context,
7837 _("Could not build ISDSSearch2 request"));
7838 return IE_ERROR;
7840 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7841 if(NULL == isds_ns) {
7842 isds_log_message(context, _("Could not create ISDS name space"));
7843 xmlFreeNode(request);
7844 return IE_ERROR;
7846 xmlSetNs(request, isds_ns);
7848 INSERT_STRING(request, "searchText", query);
7850 if (NULL != target) {
7851 static_string = isds_fulltext_target2string(*(target));
7852 if (NULL == static_string) {
7853 isds_printf_message(context, _("Invalid target value: %d"),
7854 *(target));
7855 err = IE_ENUM;
7856 goto leave;
7859 INSERT_STRING(request, "searchType", static_string);
7860 static_string = NULL;
7862 if (NULL != box_type) {
7863 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7864 if (DBTYPE_SYSTEM == *box_type) {
7865 static_string = BAD_CAST "ALL";
7866 } else if (DBTYPE_OVM_MAIN == *box_type) {
7867 static_string = BAD_CAST "OVM_MAIN";
7868 } else {
7869 static_string = isds_DbType2string(*(box_type));
7870 if (NULL == static_string) {
7871 isds_printf_message(context, _("Invalid box type value: %d"),
7872 *(box_type));
7873 err = IE_ENUM;
7874 goto leave;
7878 INSERT_STRING(request, "searchScope", static_string);
7879 static_string = NULL;
7881 INSERT_ULONGINT(request, "page", page_number, string);
7882 INSERT_ULONGINT(request, "pageSize", page_size, string);
7883 INSERT_BOOLEAN(request, "highlighting", track_matches);
7885 /* Send request and check response */
7886 err = send_destroy_request_check_response(context,
7887 SERVICE_DB_SEARCH, BAD_CAST "ISDSSearch2",
7888 &request, &response, NULL, &map);
7889 if (err) goto leave;
7891 /* Parse response */
7892 xpath_ctx = xmlXPathNewContext(response);
7893 if (NULL == xpath_ctx) {
7894 err = IE_ERROR;
7895 goto leave;
7897 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7898 err = IE_ERROR;
7899 goto leave;
7901 result = xmlXPathEvalExpression(BAD_CAST "/isds:ISDSSearch2Response",
7902 xpath_ctx);
7903 if (!result) {
7904 err = IE_ERROR;
7905 goto leave;
7907 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7908 isds_log_message(context, _("Missing ISDSSearch2 element"));
7909 err = IE_ISDS;
7910 goto leave;
7912 if (result->nodesetval->nodeNr > 1) {
7913 isds_log_message(context, _("Multiple ISDSSearch2 element"));
7914 err = IE_ISDS;
7915 goto leave;
7917 xpath_ctx->node = result->nodesetval->nodeTab[0];
7918 xmlXPathFreeObject(result); result = NULL;
7921 /* Extract counters */
7922 if (NULL != total_matching_boxes) {
7923 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes, 0);
7925 if (NULL != current_page_size) {
7926 EXTRACT_ULONGINT("isds:currentCount", *current_page_size, 0);
7928 if (NULL != current_page_beginning) {
7929 EXTRACT_ULONGINT("isds:position", *current_page_beginning, 0);
7931 if (NULL != last_page) {
7932 EXTRACT_BOOLEAN("isds:lastPage", *last_page);
7934 xmlXPathFreeObject(result); result = NULL;
7936 /* Extract boxes if they present */
7937 result = xmlXPathEvalExpression(BAD_CAST
7938 "isds:dbResults/isds:dbResult", xpath_ctx);
7939 if (NULL == result) {
7940 err = IE_ERROR;
7941 goto leave;
7943 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7944 struct isds_list *item, *prev_item = NULL;
7945 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7946 item = calloc(1, sizeof(*item));
7947 if (!item) {
7948 err = IE_NOMEM;
7949 goto leave;
7952 item->destructor = (void (*)(void **))isds_fulltext_result_free;
7953 if (i == 0) *boxes = item;
7954 else prev_item->next = item;
7955 prev_item = item;
7957 xpath_ctx->node = result->nodesetval->nodeTab[i];
7958 err = extract_dbResult(context,
7959 (struct isds_fulltext_result **) &(item->data), xpath_ctx,
7960 (NULL == track_matches) ? 0 : *track_matches);
7961 if (err) goto leave;
7965 leave:
7966 if (err) {
7967 if (NULL != total_matching_boxes) zfree(*total_matching_boxes);
7968 if (NULL != current_page_beginning) zfree(*current_page_beginning);
7969 if (NULL != current_page_size) zfree(*current_page_size);
7970 if (NULL != last_page) zfree(*last_page);
7971 isds_list_free(boxes);
7974 free(string);
7975 xmlFreeNode(request);
7976 xmlXPathFreeObject(result);
7977 xmlXPathFreeContext(xpath_ctx);
7978 xmlFreeDoc(response);
7980 if (!err)
7981 isds_log(ILF_ISDS, ILL_DEBUG,
7982 _("ISDSSearch2 request processed by server successfully.\n"));
7983 #else /* not HAVE_LIBCURL */
7984 err = IE_NOTSUP;
7985 #endif
7987 return err;
7991 /* Get status of a box.
7992 * @context is ISDS session context.
7993 * @box_id is UTF-8 encoded box identifier as zero terminated string
7994 * @box_status is return value of box status.
7995 * @return:
7996 * IE_SUCCESS if box has been found and its status retrieved
7997 * IE_NOEXIST if box is not known to ISDS server
7998 * or other appropriate error.
7999 * You can use isds_DbState to enumerate box status. However out of enum
8000 * range value can be returned too. This is feature because ISDS
8001 * specification leaves the set of values open.
8002 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8003 * the box has been deleted, but ISDS still lists its former existence. */
8004 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
8005 long int *box_status) {
8006 isds_error err = IE_SUCCESS;
8007 #if HAVE_LIBCURL
8008 xmlNsPtr isds_ns = NULL;
8009 xmlNodePtr request = NULL, db_id;
8010 xmlDocPtr response = NULL;
8011 xmlXPathContextPtr xpath_ctx = NULL;
8012 xmlXPathObjectPtr result = NULL;
8013 xmlChar *string = NULL;
8015 const xmlChar *codes[] = {
8016 BAD_CAST "5001",
8017 BAD_CAST "1007",
8018 BAD_CAST "2011",
8019 NULL
8021 const char *meanings[] = {
8022 "The box does not exist",
8023 "Box ID is malformed",
8024 "Box ID malformed",
8026 const isds_error errors[] = {
8027 IE_NOEXIST,
8028 IE_INVAL,
8029 IE_INVAL,
8031 struct code_map_isds_error map = {
8032 .codes = codes,
8033 .meanings = meanings,
8034 .errors = errors
8036 #endif
8038 if (!context) return IE_INVALID_CONTEXT;
8039 zfree(context->long_message);
8040 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
8042 #if HAVE_LIBCURL
8043 /* Check if connection is established
8044 * TODO: This check should be done downstairs. */
8045 if (!context->curl) return IE_CONNECTION_CLOSED;
8048 /* Build CheckDataBox request */
8049 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
8050 if (!request) {
8051 isds_log_message(context,
8052 _("Could build CheckDataBox request"));
8053 return IE_ERROR;
8055 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8056 if(!isds_ns) {
8057 isds_log_message(context, _("Could not create ISDS name space"));
8058 xmlFreeNode(request);
8059 return IE_ERROR;
8061 xmlSetNs(request, isds_ns);
8062 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
8063 if (!db_id) {
8064 isds_log_message(context, _("Could not add dbID child to "
8065 "CheckDataBox element"));
8066 xmlFreeNode(request);
8067 return IE_ERROR;
8071 /* Send request and check response*/
8072 err = send_destroy_request_check_response(context,
8073 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
8074 &request, &response, NULL, &map);
8075 if (err) goto leave;
8078 /* Extract data */
8079 xpath_ctx = xmlXPathNewContext(response);
8080 if (!xpath_ctx) {
8081 err = IE_ERROR;
8082 goto leave;
8084 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8085 err = IE_ERROR;
8086 goto leave;
8088 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
8089 xpath_ctx);
8090 if (!result) {
8091 err = IE_ERROR;
8092 goto leave;
8094 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8095 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
8096 err = IE_ISDS;
8097 goto leave;
8099 if (result->nodesetval->nodeNr > 1) {
8100 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
8101 err = IE_ISDS;
8102 goto leave;
8104 xpath_ctx->node = result->nodesetval->nodeTab[0];
8105 xmlXPathFreeObject(result); result = NULL;
8107 EXTRACT_LONGINT("isds:dbState", box_status, 1);
8110 leave:
8111 free(string);
8112 xmlXPathFreeObject(result);
8113 xmlXPathFreeContext(xpath_ctx);
8115 xmlFreeDoc(response);
8117 if (!err)
8118 isds_log(ILF_ISDS, ILL_DEBUG,
8119 _("CheckDataBox request processed by server successfully.\n"));
8120 #else /* not HAVE_LIBCURL */
8121 err = IE_NOTSUP;
8122 #endif
8124 return err;
8128 /* Get list of permissions to send commercial messages.
8129 * @context is ISDS session context.
8130 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8131 * @permissions is a reallocated list of permissions (struct
8132 * isds_commercial_permission*) to send commercial messages from @box_id. The
8133 * order of permissions is significant as the server applies the permissions
8134 * and associated pre-paid credits in the order. Empty list means no
8135 * permission.
8136 * @return:
8137 * IE_SUCCESS if the list has been obtained correctly,
8138 * or other appropriate error. */
8139 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
8140 const char *box_id, struct isds_list **permissions) {
8141 isds_error err = IE_SUCCESS;
8142 #if HAVE_LIBCURL
8143 xmlDocPtr response = NULL;
8144 xmlXPathContextPtr xpath_ctx = NULL;
8145 xmlXPathObjectPtr result = NULL;
8146 #endif
8148 if (!context) return IE_INVALID_CONTEXT;
8149 zfree(context->long_message);
8150 if (NULL == permissions) return IE_INVAL;
8151 isds_list_free(permissions);
8152 if (NULL == box_id) return IE_INVAL;
8154 #if HAVE_LIBCURL
8155 /* Check if connection is established */
8156 if (!context->curl) return IE_CONNECTION_CLOSED;
8158 /* Do request and check for success */
8159 err = build_send_dbid_request_check_response(context,
8160 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
8161 BAD_CAST box_id, NULL, &response, NULL);
8162 if (!err) {
8163 isds_log(ILF_ISDS, ILL_DEBUG,
8164 _("PDZInfo request processed by server successfully.\n"));
8167 /* Extract data */
8168 /* Prepare structure */
8169 xpath_ctx = xmlXPathNewContext(response);
8170 if (!xpath_ctx) {
8171 err = IE_ERROR;
8172 goto leave;
8174 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8175 err = IE_ERROR;
8176 goto leave;
8179 /* Set context node */
8180 result = xmlXPathEvalExpression(BAD_CAST
8181 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8182 xpath_ctx);
8183 if (!result) {
8184 err = IE_ERROR;
8185 goto leave;
8187 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8188 struct isds_list *prev_item = NULL;
8190 /* Iterate over all permission records */
8191 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8192 struct isds_list *item;
8194 /* Prepare structure */
8195 item = calloc(1, sizeof(*item));
8196 if (!item) {
8197 err = IE_NOMEM;
8198 goto leave;
8200 item->destructor = (void(*)(void**))isds_commercial_permission_free;
8201 if (i == 0) *permissions = item;
8202 else prev_item->next = item;
8203 prev_item = item;
8205 /* Extract it */
8206 xpath_ctx->node = result->nodesetval->nodeTab[i];
8207 err = extract_DbPDZRecord(context,
8208 (struct isds_commercial_permission **) (&item->data),
8209 xpath_ctx);
8210 if (err) goto leave;
8214 leave:
8215 if (err) {
8216 isds_list_free(permissions);
8219 xmlXPathFreeObject(result);
8220 xmlXPathFreeContext(xpath_ctx);
8221 xmlFreeDoc(response);
8223 #else /* not HAVE_LIBCURL */
8224 err = IE_NOTSUP;
8225 #endif
8227 return err;
8231 /* Get details about credit for sending pre-paid commercial messages.
8232 * @context is ISDS session context.
8233 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8234 * @from_date is first day of credit history to return in @history. Only
8235 * tm_year, tm_mon and tm_mday carry sane value.
8236 * @to_date is last day of credit history to return in @history. Only
8237 * tm_year, tm_mon and tm_mday carry sane value.
8238 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8239 * if you don't care. This and all other credit values are integers in
8240 * hundredths of Czech Crowns.
8241 * @email outputs notification e-mail address where notifications about credit
8242 * are sent. This is automatically reallocated string. Pass NULL if you don't
8243 * care. It can return NULL if no address is defined.
8244 * @history outputs auto-reallocated list of pointers to struct
8245 * isds_credit_event. Events in closed interval @from_time to @to_time are
8246 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8247 * are sorted by time.
8248 * @return:
8249 * IE_SUCCESS if the credit details have been obtained correctly,
8250 * or other appropriate error. Please note that server allows to retrieve
8251 * only limited history of events. */
8252 isds_error isds_get_commercial_credit(struct isds_ctx *context,
8253 const char *box_id,
8254 const struct tm *from_date, const struct tm *to_date,
8255 long int *credit, char **email, struct isds_list **history) {
8256 isds_error err = IE_SUCCESS;
8257 #if HAVE_LIBCURL
8258 char *box_id_locale = NULL;
8259 xmlNodePtr request = NULL, node;
8260 xmlNsPtr isds_ns = NULL;
8261 xmlChar *string = NULL;
8263 xmlDocPtr response = NULL;
8264 xmlXPathContextPtr xpath_ctx = NULL;
8265 xmlXPathObjectPtr result = NULL;
8267 const xmlChar *codes[] = {
8268 BAD_CAST "1004",
8269 BAD_CAST "2011",
8270 BAD_CAST "1093",
8271 BAD_CAST "1137",
8272 BAD_CAST "1058",
8273 NULL
8275 const char *meanings[] = {
8276 "Insufficient priviledges for the box",
8277 "The box does not exist",
8278 "Date is too long (history is not available after 15 months)",
8279 "Interval is too long (limit is 3 months)",
8280 "Invalid date"
8282 const isds_error errors[] = {
8283 IE_ISDS,
8284 IE_NOEXIST,
8285 IE_DATE,
8286 IE_DATE,
8287 IE_DATE,
8289 struct code_map_isds_error map = {
8290 .codes = codes,
8291 .meanings = meanings,
8292 .errors = errors
8294 #endif
8296 if (!context) return IE_INVALID_CONTEXT;
8297 zfree(context->long_message);
8299 /* Free output argument */
8300 if (NULL != credit) *credit = 0;
8301 if (NULL != email) zfree(*email);
8302 isds_list_free(history);
8304 if (NULL == box_id) return IE_INVAL;
8306 #if HAVE_LIBCURL
8307 /* Check if connection is established */
8308 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
8310 box_id_locale = _isds_utf82locale((char*)box_id);
8311 if (NULL == box_id_locale) {
8312 err = IE_NOMEM;
8313 goto leave;
8316 /* Build request */
8317 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
8318 if (NULL == request) {
8319 isds_printf_message(context,
8320 _("Could not build DataBoxCreditInfo request for %s box"),
8321 box_id_locale);
8322 err = IE_ERROR;
8323 goto leave;
8325 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8326 if(!isds_ns) {
8327 isds_log_message(context, _("Could not create ISDS name space"));
8328 err = IE_ERROR;
8329 goto leave;
8331 xmlSetNs(request, isds_ns);
8333 /* Add mandatory XSD:tIdDbInput child */
8334 INSERT_STRING(request, BAD_CAST "dbID", box_id);
8335 /* Add mandatory dates elements with optional values */
8336 if (from_date) {
8337 err = tm2datestring(from_date, &string);
8338 if (err) {
8339 isds_log_message(context,
8340 _("Could not convert `from_date' argument to ISO date "
8341 "string"));
8342 goto leave;
8344 INSERT_STRING(request, "ciFromDate", string);
8345 zfree(string);
8346 } else {
8347 INSERT_STRING(request, "ciFromDate", NULL);
8349 if (to_date) {
8350 err = tm2datestring(to_date, &string);
8351 if (err) {
8352 isds_log_message(context,
8353 _("Could not convert `to_date' argument to ISO date "
8354 "string"));
8355 goto leave;
8357 INSERT_STRING(request, "ciTodate", string);
8358 zfree(string);
8359 } else {
8360 INSERT_STRING(request, "ciTodate", NULL);
8363 /* Send request and check response*/
8364 err = send_destroy_request_check_response(context,
8365 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
8366 &request, &response, NULL, &map);
8367 if (err) goto leave;
8370 /* Extract data */
8371 /* Set context to the root */
8372 xpath_ctx = xmlXPathNewContext(response);
8373 if (!xpath_ctx) {
8374 err = IE_ERROR;
8375 goto leave;
8377 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8378 err = IE_ERROR;
8379 goto leave;
8381 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
8382 xpath_ctx);
8383 if (!result) {
8384 err = IE_ERROR;
8385 goto leave;
8387 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8388 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
8389 err = IE_ISDS;
8390 goto leave;
8392 if (result->nodesetval->nodeNr > 1) {
8393 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
8394 err = IE_ISDS;
8395 goto leave;
8397 xpath_ctx->node = result->nodesetval->nodeTab[0];
8398 xmlXPathFreeObject(result); result = NULL;
8400 /* Extract common data */
8401 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
8402 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
8404 /* Extract records */
8405 if (NULL == history) goto leave;
8406 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
8407 xpath_ctx);
8408 if (!result) {
8409 err = IE_ERROR;
8410 goto leave;
8412 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8413 struct isds_list *prev_item = NULL;
8415 /* Iterate over all records */
8416 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
8417 struct isds_list *item;
8419 /* Prepare structure */
8420 item = calloc(1, sizeof(*item));
8421 if (!item) {
8422 err = IE_NOMEM;
8423 goto leave;
8425 item->destructor = (void(*)(void**))isds_credit_event_free;
8426 if (i == 0) *history = item;
8427 else prev_item->next = item;
8428 prev_item = item;
8430 /* Extract it */
8431 xpath_ctx->node = result->nodesetval->nodeTab[i];
8432 err = extract_CiRecord(context,
8433 (struct isds_credit_event **) (&item->data),
8434 xpath_ctx);
8435 if (err) goto leave;
8439 leave:
8440 if (!err) {
8441 isds_log(ILF_ISDS, ILL_DEBUG,
8442 _("DataBoxCreditInfo request processed by server successfully.\n"));
8444 if (err) {
8445 isds_list_free(history);
8446 if (NULL != email) zfree(*email)
8449 free(box_id_locale);
8450 xmlXPathFreeObject(result);
8451 xmlXPathFreeContext(xpath_ctx);
8452 xmlFreeDoc(response);
8454 #else /* not HAVE_LIBCURL */
8455 err = IE_NOTSUP;
8456 #endif
8458 return err;
8462 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8463 * code, destroy response and log success.
8464 * @context is ISDS session context.
8465 * @service_name is name of SERVICE_DB_MANIPULATION service
8466 * @box_id is UTF-8 encoded box identifier as zero terminated string
8467 * @approval is optional external approval of box manipulation
8468 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8469 * NULL, if you don't care. */
8470 static isds_error build_send_manipulationdbid_request_check_drop_response(
8471 struct isds_ctx *context, const xmlChar *service_name,
8472 const xmlChar *box_id, const struct isds_approval *approval,
8473 xmlChar **refnumber) {
8474 isds_error err = IE_SUCCESS;
8475 #if HAVE_LIBCURL
8476 xmlDocPtr response = NULL;
8477 #endif
8479 if (!context) return IE_INVALID_CONTEXT;
8480 zfree(context->long_message);
8481 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
8483 #if HAVE_LIBCURL
8484 /* Check if connection is established */
8485 if (!context->curl) return IE_CONNECTION_CLOSED;
8487 /* Do request and check for success */
8488 err = build_send_dbid_request_check_response(context,
8489 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
8490 &response, refnumber);
8491 xmlFreeDoc(response);
8493 if (!err) {
8494 char *service_name_locale = _isds_utf82locale((char *) service_name);
8495 isds_log(ILF_ISDS, ILL_DEBUG,
8496 _("%s request processed by server successfully.\n"),
8497 service_name_locale);
8498 free(service_name_locale);
8500 #else /* not HAVE_LIBCURL */
8501 err = IE_NOTSUP;
8502 #endif
8504 return err;
8508 /* Switch box into state where box can receive commercial messages (off by
8509 * default)
8510 * @context is ISDS session context.
8511 * @box_id is UTF-8 encoded box identifier as zero terminated string
8512 * @allow is true for enable, false for disable commercial messages income
8513 * @approval is optional external approval of box manipulation
8514 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8515 * NULL, if you don't care. */
8516 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
8517 const char *box_id, const _Bool allow,
8518 const struct isds_approval *approval, char **refnumber) {
8519 return build_send_manipulationdbid_request_check_drop_response(context,
8520 (allow) ? BAD_CAST "SetOpenAddressing" :
8521 BAD_CAST "ClearOpenAddressing",
8522 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8526 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8527 * message acceptance). This is just a box permission. Sender must apply
8528 * such role by sending each message.
8529 * @context is ISDS session context.
8530 * @box_id is UTF-8 encoded box identifier as zero terminated string
8531 * @allow is true for enable, false for disable OVM role permission
8532 * @approval is optional external approval of box manipulation
8533 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8534 * NULL, if you don't care. */
8535 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
8536 const char *box_id, const _Bool allow,
8537 const struct isds_approval *approval, char **refnumber) {
8538 return build_send_manipulationdbid_request_check_drop_response(context,
8539 (allow) ? BAD_CAST "SetEffectiveOVM" :
8540 BAD_CAST "ClearEffectiveOVM",
8541 BAD_CAST box_id, approval, (xmlChar **) refnumber);
8545 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8546 * code, destroy response and log success.
8547 * @context is ISDS session context.
8548 * @service_name is name of SERVICE_DB_MANIPULATION service
8549 * @owner is structure describing box. aifoIsds, address->adCode,
8550 * address->adDistrict members are ignored.
8551 * @approval is optional external approval of box manipulation
8552 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8553 * NULL, if you don't care. */
8554 static isds_error build_send_manipulationdbowner_request_check_drop_response(
8555 struct isds_ctx *context, const xmlChar *service_name,
8556 const struct isds_DbOwnerInfo *owner,
8557 const struct isds_approval *approval, xmlChar **refnumber) {
8558 isds_error err = IE_SUCCESS;
8559 #if HAVE_LIBCURL
8560 char *service_name_locale = NULL;
8561 xmlNodePtr request = NULL, db_owner_info;
8562 xmlNsPtr isds_ns = NULL;
8563 #endif
8566 if (!context) return IE_INVALID_CONTEXT;
8567 zfree(context->long_message);
8568 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
8570 #if HAVE_LIBCURL
8571 service_name_locale = _isds_utf82locale((char*)service_name);
8572 if (!service_name_locale) {
8573 err = IE_NOMEM;
8574 goto leave;
8577 /* Build request */
8578 request = xmlNewNode(NULL, service_name);
8579 if (!request) {
8580 isds_printf_message(context,
8581 _("Could not build %s request"), service_name_locale);
8582 err = IE_ERROR;
8583 goto leave;
8585 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8586 if(!isds_ns) {
8587 isds_log_message(context, _("Could not create ISDS name space"));
8588 err = IE_ERROR;
8589 goto leave;
8591 xmlSetNs(request, isds_ns);
8594 /* Add XSD:tOwnerInfoInput child*/
8595 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
8596 err = insert_DbOwnerInfo(context, owner, 0, db_owner_info);
8597 if (err) goto leave;
8599 /* Add XSD:gExtApproval*/
8600 err = insert_GExtApproval(context, approval, request);
8601 if (err) goto leave;
8603 /* Send it to server and process response */
8604 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8605 service_name, &request, refnumber);
8607 leave:
8608 xmlFreeNode(request);
8609 free(service_name_locale);
8610 #else /* not HAVE_LIBCURL */
8611 err = IE_NOTSUP;
8612 #endif
8614 return err;
8618 /* Switch box accessibility state on request of box owner.
8619 * Despite the name, owner must do the request off-line. This function is
8620 * designed for such off-line meeting points (e.g. Czech POINT).
8621 * @context is ISDS session context.
8622 * @box identifies box to switch accessibility state. aifoIsds,
8623 * address->adCode, address->adDistrict members are ignored.
8624 * @allow is true for making accessible, false to disallow access.
8625 * @approval is optional external approval of box manipulation
8626 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8627 * NULL, if you don't care. */
8628 isds_error isds_switch_box_accessibility_on_owner_request(
8629 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8630 const _Bool allow, const struct isds_approval *approval,
8631 char **refnumber) {
8632 return build_send_manipulationdbowner_request_check_drop_response(context,
8633 (allow) ? BAD_CAST "EnableOwnDataBox" :
8634 BAD_CAST "DisableOwnDataBox",
8635 box, approval, (xmlChar **) refnumber);
8639 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8640 * date.
8641 * @context is ISDS session context.
8642 * @box identifies box to switch accessibility state. aifoIsds,
8643 * address->adCode, address->adDistrict members are ignored.
8644 * @since is date since accessibility has been denied. This can be past too.
8645 * Only tm_year, tm_mon and tm_mday carry sane value.
8646 * @approval is optional external approval of box manipulation
8647 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8648 * NULL, if you don't care. */
8649 isds_error isds_disable_box_accessibility_externaly(
8650 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
8651 const struct tm *since, const struct isds_approval *approval,
8652 char **refnumber) {
8653 isds_error err = IE_SUCCESS;
8654 #if HAVE_LIBCURL
8655 char *service_name_locale = NULL;
8656 xmlNodePtr request = NULL, node;
8657 xmlNsPtr isds_ns = NULL;
8658 xmlChar *string = NULL;
8659 #endif
8662 if (!context) return IE_INVALID_CONTEXT;
8663 zfree(context->long_message);
8664 if (!box || !since) return IE_INVAL;
8666 #if HAVE_LIBCURL
8667 /* Build request */
8668 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
8669 if (!request) {
8670 isds_printf_message(context,
8671 _("Could not build %s request"), "DisableDataBoxExternally");
8672 err = IE_ERROR;
8673 goto leave;
8675 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8676 if(!isds_ns) {
8677 isds_log_message(context, _("Could not create ISDS name space"));
8678 err = IE_ERROR;
8679 goto leave;
8681 xmlSetNs(request, isds_ns);
8684 /* Add @box identification */
8685 INSERT_ELEMENT(node, request, "dbOwnerInfo");
8686 err = insert_DbOwnerInfo(context, box, 0, node);
8687 if (err) goto leave;
8689 /* Add @since date */
8690 err = tm2datestring(since, &string);
8691 if(err) {
8692 isds_log_message(context,
8693 _("Could not convert `since' argument to ISO date string"));
8694 goto leave;
8696 INSERT_STRING(request, "dbOwnerDisableDate", string);
8697 zfree(string);
8699 /* Add @approval */
8700 err = insert_GExtApproval(context, approval, request);
8701 if (err) goto leave;
8703 /* Send it to server and process response */
8704 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
8705 BAD_CAST "DisableDataBoxExternally", &request,
8706 (xmlChar **) refnumber);
8708 leave:
8709 free(string);
8710 xmlFreeNode(request);
8711 free(service_name_locale);
8712 #else /* not HAVE_LIBCURL */
8713 err = IE_NOTSUP;
8714 #endif
8716 return err;
8720 #if HAVE_LIBCURL
8721 /* Insert struct isds_message data (envelope (recipient data optional) and
8722 * documents into XML tree
8723 * @context is session context
8724 * @outgoing_message is libisds structure with message data
8725 * @create_message is XML CreateMessage or CreateMultipleMessage element
8726 * @process_recipient true for recipient data serialization, false for no
8727 * serialization */
8728 static isds_error insert_envelope_files(struct isds_ctx *context,
8729 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8730 const _Bool process_recipient) {
8732 isds_error err = IE_SUCCESS;
8733 xmlNodePtr envelope, dm_files, node;
8734 xmlChar *string = NULL;
8736 if (!context) return IE_INVALID_CONTEXT;
8737 if (!outgoing_message || !create_message) return IE_INVAL;
8740 /* Build envelope */
8741 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8742 if (!envelope) {
8743 isds_printf_message(context, _("Could not add dmEnvelope child to "
8744 "%s element"), create_message->name);
8745 return IE_ERROR;
8748 if (!outgoing_message->envelope) {
8749 isds_log_message(context, _("Outgoing message is missing envelope"));
8750 err = IE_INVAL;
8751 goto leave;
8754 /* Insert optional message type */
8755 err = insert_message_type(context, outgoing_message->envelope->dmType,
8756 envelope);
8757 if (err) goto leave;
8759 INSERT_STRING(envelope, "dmSenderOrgUnit",
8760 outgoing_message->envelope->dmSenderOrgUnit);
8761 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8762 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8764 if (process_recipient) {
8765 if (!outgoing_message->envelope->dbIDRecipient) {
8766 isds_log_message(context,
8767 _("Outgoing message is missing recipient box identifier"));
8768 err = IE_INVAL;
8769 goto leave;
8771 INSERT_STRING(envelope, "dbIDRecipient",
8772 outgoing_message->envelope->dbIDRecipient);
8774 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8775 outgoing_message->envelope->dmRecipientOrgUnit);
8776 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8777 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8778 INSERT_STRING(envelope, "dmToHands",
8779 outgoing_message->envelope->dmToHands);
8782 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8783 "dmAnnotation");
8784 INSERT_STRING(envelope, "dmAnnotation",
8785 outgoing_message->envelope->dmAnnotation);
8787 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8788 0, 50, "dmRecipientRefNumber");
8789 INSERT_STRING(envelope, "dmRecipientRefNumber",
8790 outgoing_message->envelope->dmRecipientRefNumber);
8792 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8793 0, 50, "dmSenderRefNumber");
8794 INSERT_STRING(envelope, "dmSenderRefNumber",
8795 outgoing_message->envelope->dmSenderRefNumber);
8797 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8798 0, 50, "dmRecipientIdent");
8799 INSERT_STRING(envelope, "dmRecipientIdent",
8800 outgoing_message->envelope->dmRecipientIdent);
8802 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8803 0, 50, "dmSenderIdent");
8804 INSERT_STRING(envelope, "dmSenderIdent",
8805 outgoing_message->envelope->dmSenderIdent);
8807 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8808 outgoing_message->envelope->dmLegalTitleLaw, string);
8809 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8810 outgoing_message->envelope->dmLegalTitleYear, string);
8811 INSERT_STRING(envelope, "dmLegalTitleSect",
8812 outgoing_message->envelope->dmLegalTitleSect);
8813 INSERT_STRING(envelope, "dmLegalTitlePar",
8814 outgoing_message->envelope->dmLegalTitlePar);
8815 INSERT_STRING(envelope, "dmLegalTitlePoint",
8816 outgoing_message->envelope->dmLegalTitlePoint);
8818 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8819 outgoing_message->envelope->dmPersonalDelivery);
8820 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8821 outgoing_message->envelope->dmAllowSubstDelivery);
8823 /* ???: Should we require value for dbEffectiveOVM sender?
8824 * ISDS has default as true */
8825 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8826 INSERT_BOOLEAN(envelope, "dmOVM",
8827 outgoing_message->envelope->dmPublishOwnID);
8830 /* Append dmFiles */
8831 if (!outgoing_message->documents) {
8832 isds_log_message(context,
8833 _("Outgoing message is missing list of documents"));
8834 err = IE_INVAL;
8835 goto leave;
8837 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8838 if (!dm_files) {
8839 isds_printf_message(context, _("Could not add dmFiles child to "
8840 "%s element"), create_message->name);
8841 err = IE_ERROR;
8842 goto leave;
8845 /* Check for document hierarchy */
8846 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8847 if (err) goto leave;
8849 /* Process each document */
8850 for (struct isds_list *item =
8851 (struct isds_list *) outgoing_message->documents;
8852 item; item = item->next) {
8853 if (!item->data) {
8854 isds_log_message(context,
8855 _("List of documents contains empty item"));
8856 err = IE_INVAL;
8857 goto leave;
8859 /* FIXME: Check for dmFileMetaType and for document references.
8860 * Only first document can be of MAIN type */
8861 err = insert_document(context, (struct isds_document*) item->data,
8862 dm_files);
8864 if (err) goto leave;
8867 leave:
8868 free(string);
8869 return err;
8871 #endif /* HAVE_LIBCURL */
8874 /* Send a message via ISDS to a recipient
8875 * @context is session context
8876 * @outgoing_message is message to send; Some members are mandatory (like
8877 * dbIDRecipient), some are optional and some are irrelevant (especially data
8878 * about sender). Included pointer to isds_list documents must contain at
8879 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8880 * members will be filled with valid data from ISDS. Exact list of write
8881 * members is subject to change. Currently dmID is changed.
8882 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8883 isds_error isds_send_message(struct isds_ctx *context,
8884 struct isds_message *outgoing_message) {
8886 isds_error err = IE_SUCCESS;
8887 #if HAVE_LIBCURL
8888 xmlNsPtr isds_ns = NULL;
8889 xmlNodePtr request = NULL;
8890 xmlDocPtr response = NULL;
8891 xmlChar *code = NULL, *message = NULL;
8892 xmlXPathContextPtr xpath_ctx = NULL;
8893 xmlXPathObjectPtr result = NULL;
8894 /*_Bool message_is_complete = 0;*/
8895 #endif
8897 if (!context) return IE_INVALID_CONTEXT;
8898 zfree(context->long_message);
8899 if (!outgoing_message) return IE_INVAL;
8901 #if HAVE_LIBCURL
8902 /* Check if connection is established
8903 * TODO: This check should be done downstairs. */
8904 if (!context->curl) return IE_CONNECTION_CLOSED;
8907 /* Build CreateMessage request */
8908 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8909 if (!request) {
8910 isds_log_message(context,
8911 _("Could not build CreateMessage request"));
8912 return IE_ERROR;
8914 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8915 if(!isds_ns) {
8916 isds_log_message(context, _("Could not create ISDS name space"));
8917 xmlFreeNode(request);
8918 return IE_ERROR;
8920 xmlSetNs(request, isds_ns);
8922 /* Append envelope and files */
8923 err = insert_envelope_files(context, outgoing_message, request, 1);
8924 if (err) goto leave;
8927 /* Signal we can serialize message since now */
8928 /*message_is_complete = 1;*/
8931 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8933 /* Sent request */
8934 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8936 /* Don't' destroy request, we want to provide it to application later */
8938 if (err) {
8939 isds_log(ILF_ISDS, ILL_DEBUG,
8940 _("Processing ISDS response on CreateMessage "
8941 "request failed\n"));
8942 goto leave;
8945 /* Check for response status */
8946 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8947 &code, &message, NULL);
8948 if (err) {
8949 isds_log(ILF_ISDS, ILL_DEBUG,
8950 _("ISDS response on CreateMessage request "
8951 "is missing status\n"));
8952 goto leave;
8955 /* Request processed, but refused by server or server failed */
8956 if (xmlStrcmp(code, BAD_CAST "0000")) {
8957 char *box_id_locale =
8958 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8959 char *code_locale = _isds_utf82locale((char*)code);
8960 char *message_locale = _isds_utf82locale((char*)message);
8961 isds_log(ILF_ISDS, ILL_DEBUG,
8962 _("Server did not accept message for %s on CreateMessage "
8963 "request (code=%s, message=%s)\n"),
8964 box_id_locale, code_locale, message_locale);
8965 isds_log_message(context, message_locale);
8966 free(box_id_locale);
8967 free(code_locale);
8968 free(message_locale);
8969 err = IE_ISDS;
8970 goto leave;
8974 /* Extract data */
8975 xpath_ctx = xmlXPathNewContext(response);
8976 if (!xpath_ctx) {
8977 err = IE_ERROR;
8978 goto leave;
8980 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8981 err = IE_ERROR;
8982 goto leave;
8984 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8985 xpath_ctx);
8986 if (!result) {
8987 err = IE_ERROR;
8988 goto leave;
8990 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8991 isds_log_message(context, _("Missing CreateMessageResponse element"));
8992 err = IE_ISDS;
8993 goto leave;
8995 if (result->nodesetval->nodeNr > 1) {
8996 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8997 err = IE_ISDS;
8998 goto leave;
9000 xpath_ctx->node = result->nodesetval->nodeTab[0];
9001 xmlXPathFreeObject(result); result = NULL;
9003 if (outgoing_message->envelope->dmID) {
9004 free(outgoing_message->envelope->dmID);
9005 outgoing_message->envelope->dmID = NULL;
9007 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
9008 if (!outgoing_message->envelope->dmID) {
9009 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
9010 "but did not return assigned message ID\n"));
9013 leave:
9014 /* TODO: Serialize message into structure member raw */
9015 /* XXX: Each web service transport message in different format.
9016 * Therefore it's not possible to save them directly.
9017 * To save them, one must figure out common format.
9018 * We can leave it on application, or we can implement the ESS format. */
9019 /*if (message_is_complete) {
9020 if (outgoing_message->envelope->dmID) {
9022 /* Add assigned message ID as first child*/
9023 /*xmlNodePtr dmid_text = xmlNewText(
9024 (xmlChar *) outgoing_message->envelope->dmID);
9025 if (!dmid_text) goto serialization_failed;
9027 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9028 BAD_CAST "dmID");
9029 if (!dmid_element) {
9030 xmlFreeNode(dmid_text);
9031 goto serialization_failed;
9034 xmlNodePtr dmid_element_with_text =
9035 xmlAddChild(dmid_element, dmid_text);
9036 if (!dmid_element_with_text) {
9037 xmlFreeNode(dmid_element);
9038 xmlFreeNode(dmid_text);
9039 goto serialization_failed;
9042 node = xmlAddPrevSibling(envelope->childern,
9043 dmid_element_with_text);
9044 if (!node) {
9045 xmlFreeNodeList(dmid_element_with_text);
9046 goto serialization_failed;
9050 /* Serialize message with ID into raw */
9051 /*buffer = serialize_element(envelope)*/
9052 /* }
9054 serialization_failed:
9058 /* Clean up */
9059 xmlXPathFreeObject(result);
9060 xmlXPathFreeContext(xpath_ctx);
9062 free(code);
9063 free(message);
9064 xmlFreeDoc(response);
9065 xmlFreeNode(request);
9067 if (!err)
9068 isds_log(ILF_ISDS, ILL_DEBUG,
9069 _("CreateMessage request processed by server "
9070 "successfully.\n"));
9071 #else /* not HAVE_LIBCURL */
9072 err = IE_NOTSUP;
9073 #endif
9075 return err;
9079 /* Send a message via ISDS to a multiple recipients
9080 * @context is session context
9081 * @outgoing_message is message to send; Some members are mandatory,
9082 * some are optional and some are irrelevant (especially data
9083 * about sender). Data about recipient will be substituted by ISDS from
9084 * @copies. Included pointer to isds_list documents must
9085 * contain at least one document of FILEMETATYPE_MAIN.
9086 * @copies is list of isds_message_copy structures addressing all desired
9087 * recipients. This is read-write structure, some members will be filled with
9088 * valid data from ISDS (message IDs, error codes, error descriptions).
9089 * @return
9090 * ISDS_SUCCESS if all messages have been sent
9091 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9092 * succeeded messages can be identified by copies->data->error),
9093 * or other error code if something other goes wrong. */
9094 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
9095 const struct isds_message *outgoing_message,
9096 struct isds_list *copies) {
9098 isds_error err = IE_SUCCESS;
9099 #if HAVE_LIBCURL
9100 isds_error append_err;
9101 xmlNsPtr isds_ns = NULL;
9102 xmlNodePtr request = NULL, recipients, recipient, node;
9103 struct isds_list *item;
9104 struct isds_message_copy *copy;
9105 xmlDocPtr response = NULL;
9106 xmlChar *code = NULL, *message = NULL;
9107 xmlXPathContextPtr xpath_ctx = NULL;
9108 xmlXPathObjectPtr result = NULL;
9109 xmlChar *string = NULL;
9110 int i;
9111 #endif
9113 if (!context) return IE_INVALID_CONTEXT;
9114 zfree(context->long_message);
9115 if (!outgoing_message || !copies) return IE_INVAL;
9117 #if HAVE_LIBCURL
9118 /* Check if connection is established
9119 * TODO: This check should be done downstairs. */
9120 if (!context->curl) return IE_CONNECTION_CLOSED;
9123 /* Build CreateMultipleMessage request */
9124 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
9125 if (!request) {
9126 isds_log_message(context,
9127 _("Could not build CreateMultipleMessage request"));
9128 return IE_ERROR;
9130 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9131 if(!isds_ns) {
9132 isds_log_message(context, _("Could not create ISDS name space"));
9133 xmlFreeNode(request);
9134 return IE_ERROR;
9136 xmlSetNs(request, isds_ns);
9139 /* Build recipients */
9140 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
9141 if (!recipients) {
9142 isds_log_message(context, _("Could not add dmRecipients child to "
9143 "CreateMultipleMessage element"));
9144 xmlFreeNode(request);
9145 return IE_ERROR;
9148 /* Insert each recipient */
9149 for (item = copies; item; item = item->next) {
9150 copy = (struct isds_message_copy *) item->data;
9151 if (!copy) {
9152 isds_log_message(context,
9153 _("`copies' list item contains empty data"));
9154 err = IE_INVAL;
9155 goto leave;
9158 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
9159 if (!recipient) {
9160 isds_log_message(context, _("Could not add dmRecipient child to "
9161 "dmRecipients element"));
9162 err = IE_ERROR;
9163 goto leave;
9166 if (!copy->dbIDRecipient) {
9167 isds_log_message(context,
9168 _("Message copy is missing recipient box identifier"));
9169 err = IE_INVAL;
9170 goto leave;
9172 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
9173 INSERT_STRING(recipient, "dmRecipientOrgUnit",
9174 copy->dmRecipientOrgUnit);
9175 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
9176 copy->dmRecipientOrgUnitNum, string);
9177 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
9180 /* Append envelope and files */
9181 err = insert_envelope_files(context, outgoing_message, request, 0);
9182 if (err) goto leave;
9185 isds_log(ILF_ISDS, ILL_DEBUG,
9186 _("Sending CreateMultipleMessage request to ISDS\n"));
9188 /* Sent request */
9189 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
9190 if (err) {
9191 isds_log(ILF_ISDS, ILL_DEBUG,
9192 _("Processing ISDS response on CreateMultipleMessage "
9193 "request failed\n"));
9194 goto leave;
9197 /* Check for response status */
9198 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
9199 &code, &message, NULL);
9200 if (err) {
9201 isds_log(ILF_ISDS, ILL_DEBUG,
9202 _("ISDS response on CreateMultipleMessage request "
9203 "is missing status\n"));
9204 goto leave;
9207 /* Request processed, but some copies failed */
9208 if (!xmlStrcmp(code, BAD_CAST "0004")) {
9209 char *box_id_locale =
9210 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9211 char *code_locale = _isds_utf82locale((char*)code);
9212 char *message_locale = _isds_utf82locale((char*)message);
9213 isds_log(ILF_ISDS, ILL_DEBUG,
9214 _("Server did accept message for multiple recipients "
9215 "on CreateMultipleMessage request but delivery to "
9216 "some of them failed (code=%s, message=%s)\n"),
9217 box_id_locale, code_locale, message_locale);
9218 isds_log_message(context, message_locale);
9219 free(box_id_locale);
9220 free(code_locale);
9221 free(message_locale);
9222 err = IE_PARTIAL_SUCCESS;
9225 /* Request refused by server as whole */
9226 else if (xmlStrcmp(code, BAD_CAST "0000")) {
9227 char *box_id_locale =
9228 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
9229 char *code_locale = _isds_utf82locale((char*)code);
9230 char *message_locale = _isds_utf82locale((char*)message);
9231 isds_log(ILF_ISDS, ILL_DEBUG,
9232 _("Server did not accept message for multiple recipients "
9233 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9234 box_id_locale, code_locale, message_locale);
9235 isds_log_message(context, message_locale);
9236 free(box_id_locale);
9237 free(code_locale);
9238 free(message_locale);
9239 err = IE_ISDS;
9240 goto leave;
9244 /* Extract data */
9245 xpath_ctx = xmlXPathNewContext(response);
9246 if (!xpath_ctx) {
9247 err = IE_ERROR;
9248 goto leave;
9250 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9251 err = IE_ERROR;
9252 goto leave;
9254 result = xmlXPathEvalExpression(
9255 BAD_CAST "/isds:CreateMultipleMessageResponse"
9256 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9257 xpath_ctx);
9258 if (!result) {
9259 err = IE_ERROR;
9260 goto leave;
9262 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9263 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
9264 err = IE_ISDS;
9265 goto leave;
9268 /* Extract message ID and delivery status for each copy */
9269 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
9270 item = item->next, i++) {
9271 copy = (struct isds_message_copy *) item->data;
9272 xpath_ctx->node = result->nodesetval->nodeTab[i];
9274 append_err = append_TMStatus(context, copy, xpath_ctx);
9275 if (append_err) {
9276 err = append_err;
9277 goto leave;
9280 if (item || i < result->nodesetval->nodeNr) {
9281 isds_printf_message(context, _("ISDS returned unexpected number of "
9282 "message copy delivery states: %d"),
9283 result->nodesetval->nodeNr);
9284 err = IE_ISDS;
9285 goto leave;
9289 leave:
9290 /* Clean up */
9291 free(string);
9292 xmlXPathFreeObject(result);
9293 xmlXPathFreeContext(xpath_ctx);
9295 free(code);
9296 free(message);
9297 xmlFreeDoc(response);
9298 xmlFreeNode(request);
9300 if (!err)
9301 isds_log(ILF_ISDS, ILL_DEBUG,
9302 _("CreateMultipleMessageResponse request processed by server "
9303 "successfully.\n"));
9304 #else /* not HAVE_LIBCURL */
9305 err = IE_NOTSUP;
9306 #endif
9308 return err;
9312 /* Get list of messages. This is common core for getting sent or received
9313 * messages.
9314 * Any criterion argument can be NULL, if you don't care about it.
9315 * @context is session context. Must not be NULL.
9316 * @outgoing_direction is true if you want list of outgoing messages,
9317 * it's false if you want incoming messages.
9318 * @from_time is minimal time and date of message sending inclusive.
9319 * @to_time is maximal time and date of message sending inclusive
9320 * @organization_unit_number is number of sender/recipient respectively.
9321 * @status_filter is bit field of isds_message_status values. Use special
9322 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9323 * all values, you can use bit-wise arithmetic if you want.)
9324 * @offset is index of first message we are interested in. First message is 1.
9325 * Set to 0 (or 1) if you don't care.
9326 * @number is maximal length of list you want to get as input value, outputs
9327 * number of messages matching these criteria. Can be NULL if you don't care
9328 * (applies to output value either).
9329 * @messages is automatically reallocated list of isds_message's. Be ware that
9330 * it returns only brief overview (envelope and some other fields) about each
9331 * message, not the complete message. FIXME: Specify exact fields.
9332 * The list is sorted by delivery time in ascending order.
9333 * Use NULL if you don't care about don't need the data (useful if you want to
9334 * know only the @number). If you provide &NULL, list will be allocated on
9335 * heap, if you provide pointer to non-NULL, list will be freed automatically
9336 * at first. Also in case of error the list will be NULLed.
9337 * @return IE_SUCCESS or appropriate error code. */
9338 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
9339 _Bool outgoing_direction,
9340 const struct timeval *from_time, const struct timeval *to_time,
9341 const long int *organization_unit_number,
9342 const unsigned int status_filter,
9343 const unsigned long int offset, unsigned long int *number,
9344 struct isds_list **messages) {
9346 isds_error err = IE_SUCCESS;
9347 #if HAVE_LIBCURL
9348 xmlNsPtr isds_ns = NULL;
9349 xmlNodePtr request = NULL, node;
9350 xmlDocPtr response = NULL;
9351 xmlChar *code = NULL, *message = NULL;
9352 xmlXPathContextPtr xpath_ctx = NULL;
9353 xmlXPathObjectPtr result = NULL;
9354 xmlChar *string = NULL;
9355 int count = 0;
9356 #endif
9358 if (!context) return IE_INVALID_CONTEXT;
9359 zfree(context->long_message);
9361 /* Free former message list if any */
9362 if (messages) isds_list_free(messages);
9364 #if HAVE_LIBCURL
9365 /* Check if connection is established
9366 * TODO: This check should be done downstairs. */
9367 if (!context->curl) return IE_CONNECTION_CLOSED;
9369 /* Build GetListOf*Messages request */
9370 request = xmlNewNode(NULL,
9371 (outgoing_direction) ?
9372 BAD_CAST "GetListOfSentMessages" :
9373 BAD_CAST "GetListOfReceivedMessages"
9375 if (!request) {
9376 isds_log_message(context,
9377 (outgoing_direction) ?
9378 _("Could not build GetListOfSentMessages request") :
9379 _("Could not build GetListOfReceivedMessages request")
9381 return IE_ERROR;
9383 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9384 if(!isds_ns) {
9385 isds_log_message(context, _("Could not create ISDS name space"));
9386 xmlFreeNode(request);
9387 return IE_ERROR;
9389 xmlSetNs(request, isds_ns);
9392 if (from_time) {
9393 err = timeval2timestring(from_time, &string);
9394 if (err) goto leave;
9396 INSERT_STRING(request, "dmFromTime", string);
9397 free(string); string = NULL;
9399 if (to_time) {
9400 err = timeval2timestring(to_time, &string);
9401 if (err) goto leave;
9403 INSERT_STRING(request, "dmToTime", string);
9404 free(string); string = NULL;
9406 if (outgoing_direction) {
9407 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
9408 organization_unit_number, string);
9409 } else {
9410 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
9411 organization_unit_number, string);
9414 if (status_filter > MESSAGESTATE_ANY) {
9415 isds_printf_message(context,
9416 _("Invalid message state filter value: %ld"), status_filter);
9417 err = IE_INVAL;
9418 goto leave;
9420 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
9422 if (offset > 0 ) {
9423 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
9424 } else {
9425 INSERT_STRING(request, "dmOffset", "1");
9428 /* number 0 means no limit */
9429 if (number && *number == 0) {
9430 INSERT_STRING(request, "dmLimit", NULL);
9431 } else {
9432 INSERT_ULONGINT(request, "dmLimit", number, string);
9436 isds_log(ILF_ISDS, ILL_DEBUG,
9437 (outgoing_direction) ?
9438 _("Sending GetListOfSentMessages request to ISDS\n") :
9439 _("Sending GetListOfReceivedMessages request to ISDS\n")
9442 /* Sent request */
9443 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
9444 xmlFreeNode(request); request = NULL;
9446 if (err) {
9447 isds_log(ILF_ISDS, ILL_DEBUG,
9448 (outgoing_direction) ?
9449 _("Processing ISDS response on GetListOfSentMessages "
9450 "request failed\n") :
9451 _("Processing ISDS response on GetListOfReceivedMessages "
9452 "request failed\n")
9454 goto leave;
9457 /* Check for response status */
9458 err = isds_response_status(context, SERVICE_DM_INFO, response,
9459 &code, &message, NULL);
9460 if (err) {
9461 isds_log(ILF_ISDS, ILL_DEBUG,
9462 (outgoing_direction) ?
9463 _("ISDS response on GetListOfSentMessages request "
9464 "is missing status\n") :
9465 _("ISDS response on GetListOfReceivedMessages request "
9466 "is missing status\n")
9468 goto leave;
9471 /* Request processed, but nothing found */
9472 if (xmlStrcmp(code, BAD_CAST "0000")) {
9473 char *code_locale = _isds_utf82locale((char*)code);
9474 char *message_locale = _isds_utf82locale((char*)message);
9475 isds_log(ILF_ISDS, ILL_DEBUG,
9476 (outgoing_direction) ?
9477 _("Server refused GetListOfSentMessages request "
9478 "(code=%s, message=%s)\n") :
9479 _("Server refused GetListOfReceivedMessages request "
9480 "(code=%s, message=%s)\n"),
9481 code_locale, message_locale);
9482 isds_log_message(context, message_locale);
9483 free(code_locale);
9484 free(message_locale);
9485 err = IE_ISDS;
9486 goto leave;
9490 /* Extract data */
9491 xpath_ctx = xmlXPathNewContext(response);
9492 if (!xpath_ctx) {
9493 err = IE_ERROR;
9494 goto leave;
9496 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9497 err = IE_ERROR;
9498 goto leave;
9500 result = xmlXPathEvalExpression(
9501 (outgoing_direction) ?
9502 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
9503 "isds:dmRecords/isds:dmRecord" :
9504 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
9505 "isds:dmRecords/isds:dmRecord",
9506 xpath_ctx);
9507 if (!result) {
9508 err = IE_ERROR;
9509 goto leave;
9512 /* Fill output arguments in */
9513 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9514 struct isds_envelope *envelope;
9515 struct isds_list *item = NULL, *last_item = NULL;
9517 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9518 /* Create new message */
9519 item = calloc(1, sizeof(*item));
9520 if (!item) {
9521 err = IE_NOMEM;
9522 goto leave;
9524 item->destructor = (void(*)(void**)) &isds_message_free;
9525 item->data = calloc(1, sizeof(struct isds_message));
9526 if (!item->data) {
9527 isds_list_free(&item);
9528 err = IE_NOMEM;
9529 goto leave;
9532 /* Extract envelope data */
9533 xpath_ctx->node = result->nodesetval->nodeTab[count];
9534 envelope = NULL;
9535 err = extract_DmRecord(context, &envelope, xpath_ctx);
9536 if (err) {
9537 isds_list_free(&item);
9538 goto leave;
9541 /* Attach extracted envelope */
9542 ((struct isds_message *) item->data)->envelope = envelope;
9544 /* Append new message into the list */
9545 if (!*messages) {
9546 *messages = last_item = item;
9547 } else {
9548 last_item->next = item;
9549 last_item = item;
9553 if (number) *number = count;
9555 leave:
9556 if (err) {
9557 isds_list_free(messages);
9560 free(string);
9561 xmlXPathFreeObject(result);
9562 xmlXPathFreeContext(xpath_ctx);
9564 free(code);
9565 free(message);
9566 xmlFreeDoc(response);
9567 xmlFreeNode(request);
9569 if (!err)
9570 isds_log(ILF_ISDS, ILL_DEBUG,
9571 (outgoing_direction) ?
9572 _("GetListOfSentMessages request processed by server "
9573 "successfully.\n") :
9574 _("GetListOfReceivedMessages request processed by server "
9575 "successfully.\n")
9577 #else /* not HAVE_LIBCURL */
9578 err = IE_NOTSUP;
9579 #endif
9580 return err;
9584 /* Get list of outgoing (already sent) messages.
9585 * Any criterion argument can be NULL, if you don't care about it.
9586 * @context is session context. Must not be NULL.
9587 * @from_time is minimal time and date of message sending inclusive.
9588 * @to_time is maximal time and date of message sending inclusive
9589 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9590 * @status_filter is bit field of isds_message_status values. Use special
9591 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9592 * all values, you can use bit-wise arithmetic if you want.)
9593 * @offset is index of first message we are interested in. First message is 1.
9594 * Set to 0 (or 1) if you don't care.
9595 * @number is maximal length of list you want to get as input value, outputs
9596 * number of messages matching these criteria. Can be NULL if you don't care
9597 * (applies to output value either).
9598 * @messages is automatically reallocated list of isds_message's. Be ware that
9599 * it returns only brief overview (envelope and some other fields) about each
9600 * message, not the complete message. FIXME: Specify exact fields.
9601 * The list is sorted by delivery time in ascending order.
9602 * Use NULL if you don't care about the meta data (useful if you want to know
9603 * only the @number). If you provide &NULL, list will be allocated on heap,
9604 * if you provide pointer to non-NULL, list will be freed automatically at
9605 * first. Also in case of error the list will be NULLed.
9606 * @return IE_SUCCESS or appropriate error code. */
9607 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
9608 const struct timeval *from_time, const struct timeval *to_time,
9609 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
9610 const unsigned long int offset, unsigned long int *number,
9611 struct isds_list **messages) {
9613 return isds_get_list_of_messages(
9614 context, 1,
9615 from_time, to_time, dmSenderOrgUnitNum, status_filter,
9616 offset, number,
9617 messages);
9621 /* Get list of incoming (addressed to you) messages.
9622 * Any criterion argument can be NULL, if you don't care about it.
9623 * @context is session context. Must not be NULL.
9624 * @from_time is minimal time and date of message sending inclusive.
9625 * @to_time is maximal time and date of message sending inclusive
9626 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9627 * @status_filter is bit field of isds_message_status values. Use special
9628 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9629 * all values, you can use bit-wise arithmetic if you want.)
9630 * @offset is index of first message we are interested in. First message is 1.
9631 * Set to 0 (or 1) if you don't care.
9632 * @number is maximal length of list you want to get as input value, outputs
9633 * number of messages matching these criteria. Can be NULL if you don't care
9634 * (applies to output value either).
9635 * @messages is automatically reallocated list of isds_message's. Be ware that
9636 * it returns only brief overview (envelope and some other fields) about each
9637 * message, not the complete message. FIXME: Specify exact fields.
9638 * Use NULL if you don't care about the meta data (useful if you want to know
9639 * only the @number). If you provide &NULL, list will be allocated on heap,
9640 * if you provide pointer to non-NULL, list will be freed automatically at
9641 * first. Also in case of error the list will be NULLed.
9642 * @return IE_SUCCESS or appropriate error code. */
9643 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
9644 const struct timeval *from_time, const struct timeval *to_time,
9645 const long int *dmRecipientOrgUnitNum,
9646 const unsigned int status_filter,
9647 const unsigned long int offset, unsigned long int *number,
9648 struct isds_list **messages) {
9650 return isds_get_list_of_messages(
9651 context, 0,
9652 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
9653 offset, number,
9654 messages);
9658 /* Get list of sent message state changes.
9659 * Any criterion argument can be NULL, if you don't care about it.
9660 * @context is session context. Must not be NULL.
9661 * @from_time is minimal time and date of status changes inclusive
9662 * @to_time is maximal time and date of status changes inclusive
9663 * @changed_states is automatically reallocated list of
9664 * isds_message_status_change's. If you provide &NULL, list will be allocated
9665 * on heap, if you provide pointer to non-NULL, list will be freed
9666 * automatically at first. Also in case of error the list will be NULLed.
9667 * XXX: The list item ordering is not specified.
9668 * XXX: Server provides only `recent' changes.
9669 * @return IE_SUCCESS or appropriate error code. */
9670 isds_error isds_get_list_of_sent_message_state_changes(
9671 struct isds_ctx *context,
9672 const struct timeval *from_time, const struct timeval *to_time,
9673 struct isds_list **changed_states) {
9675 isds_error err = IE_SUCCESS;
9676 #if HAVE_LIBCURL
9677 xmlNsPtr isds_ns = NULL;
9678 xmlNodePtr request = NULL, node;
9679 xmlDocPtr response = NULL;
9680 xmlXPathContextPtr xpath_ctx = NULL;
9681 xmlXPathObjectPtr result = NULL;
9682 xmlChar *string = NULL;
9683 int count = 0;
9684 #endif
9686 if (!context) return IE_INVALID_CONTEXT;
9687 zfree(context->long_message);
9689 /* Free former message list if any */
9690 isds_list_free(changed_states);
9692 #if HAVE_LIBCURL
9693 /* Check if connection is established
9694 * TODO: This check should be done downstairs. */
9695 if (!context->curl) return IE_CONNECTION_CLOSED;
9697 /* Build GetMessageStateChanges request */
9698 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
9699 if (!request) {
9700 isds_log_message(context,
9701 _("Could not build GetMessageStateChanges request"));
9702 return IE_ERROR;
9704 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9705 if(!isds_ns) {
9706 isds_log_message(context, _("Could not create ISDS name space"));
9707 xmlFreeNode(request);
9708 return IE_ERROR;
9710 xmlSetNs(request, isds_ns);
9713 if (from_time) {
9714 err = timeval2timestring(from_time, &string);
9715 if (err) goto leave;
9717 INSERT_STRING(request, "dmFromTime", string);
9718 zfree(string);
9720 if (to_time) {
9721 err = timeval2timestring(to_time, &string);
9722 if (err) goto leave;
9724 INSERT_STRING(request, "dmToTime", string);
9725 zfree(string);
9728 /* Sent request */
9729 err = send_destroy_request_check_response(context,
9730 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9731 &response, NULL, NULL);
9732 if (err) goto leave;
9735 /* Extract data */
9736 xpath_ctx = xmlXPathNewContext(response);
9737 if (!xpath_ctx) {
9738 err = IE_ERROR;
9739 goto leave;
9741 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9742 err = IE_ERROR;
9743 goto leave;
9745 result = xmlXPathEvalExpression(
9746 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9747 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9748 if (!result) {
9749 err = IE_ERROR;
9750 goto leave;
9753 /* Fill output arguments in */
9754 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9755 struct isds_list *item = NULL, *last_item = NULL;
9757 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9758 /* Create new status change */
9759 item = calloc(1, sizeof(*item));
9760 if (!item) {
9761 err = IE_NOMEM;
9762 goto leave;
9764 item->destructor =
9765 (void(*)(void**)) &isds_message_status_change_free;
9767 /* Extract message status change */
9768 xpath_ctx->node = result->nodesetval->nodeTab[count];
9769 err = extract_StateChangesRecord(context,
9770 (struct isds_message_status_change **) &item->data,
9771 xpath_ctx);
9772 if (err) {
9773 isds_list_free(&item);
9774 goto leave;
9777 /* Append new message status change into the list */
9778 if (!*changed_states) {
9779 *changed_states = last_item = item;
9780 } else {
9781 last_item->next = item;
9782 last_item = item;
9787 leave:
9788 if (err) {
9789 isds_list_free(changed_states);
9792 free(string);
9793 xmlXPathFreeObject(result);
9794 xmlXPathFreeContext(xpath_ctx);
9795 xmlFreeDoc(response);
9796 xmlFreeNode(request);
9798 if (!err)
9799 isds_log(ILF_ISDS, ILL_DEBUG,
9800 _("GetMessageStateChanges request processed by server "
9801 "successfully.\n"));
9802 #else /* not HAVE_LIBCURL */
9803 err = IE_NOTSUP;
9804 #endif
9805 return err;
9809 #if HAVE_LIBCURL
9810 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9811 * code
9812 * @context is session context
9813 * @service is ISDS WS service handler
9814 * @service_name is name of SERVICE_DM_OPERATIONS
9815 * @message_id is message ID to send as service argument to ISDS
9816 * @response is reallocated server SOAP body response as XML document
9817 * @raw_response is reallocated bit stream with response body. Use
9818 * NULL if you don't care
9819 * @raw_response_length is size of @raw_response in bytes
9820 * @code is reallocated ISDS status code
9821 * @status_message is reallocated ISDS status message
9822 * @return error coded from lower layer, context message will be set up
9823 * appropriately. */
9824 static isds_error build_send_check_message_request(struct isds_ctx *context,
9825 const isds_service service, const xmlChar *service_name,
9826 const char *message_id,
9827 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9828 xmlChar **code, xmlChar **status_message) {
9830 isds_error err = IE_SUCCESS;
9831 char *service_name_locale = NULL, *message_id_locale = NULL;
9832 xmlNodePtr request = NULL, node;
9833 xmlNsPtr isds_ns = NULL;
9835 if (!context) return IE_INVALID_CONTEXT;
9836 if (!service_name || !message_id) return IE_INVAL;
9837 if (!response || !code || !status_message) return IE_INVAL;
9838 if (!raw_response_length && raw_response) return IE_INVAL;
9840 /* Free output argument */
9841 xmlFreeDoc(*response); *response = NULL;
9842 if (raw_response) zfree(*raw_response);
9843 zfree(*code);
9844 zfree(*status_message);
9847 /* Check if connection is established
9848 * TODO: This check should be done downstairs. */
9849 if (!context->curl) return IE_CONNECTION_CLOSED;
9851 service_name_locale = _isds_utf82locale((char*)service_name);
9852 message_id_locale = _isds_utf82locale(message_id);
9853 if (!service_name_locale || !message_id_locale) {
9854 err = IE_NOMEM;
9855 goto leave;
9858 /* Build request */
9859 request = xmlNewNode(NULL, service_name);
9860 if (!request) {
9861 isds_printf_message(context,
9862 _("Could not build %s request for %s message ID"),
9863 service_name_locale, message_id_locale);
9864 err = IE_ERROR;
9865 goto leave;
9867 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9868 if(!isds_ns) {
9869 isds_log_message(context, _("Could not create ISDS name space"));
9870 err = IE_ERROR;
9871 goto leave;
9873 xmlSetNs(request, isds_ns);
9876 /* Add requested ID */
9877 err = validate_message_id_length(context, (xmlChar *) message_id);
9878 if (err) goto leave;
9879 INSERT_STRING(request, "dmID", message_id);
9882 isds_log(ILF_ISDS, ILL_DEBUG,
9883 _("Sending %s request for %s message ID to ISDS\n"),
9884 service_name_locale, message_id_locale);
9886 /* Send request */
9887 err = _isds(context, service, request, response,
9888 raw_response, raw_response_length);
9889 xmlFreeNode(request); request = NULL;
9891 if (err) {
9892 isds_log(ILF_ISDS, ILL_DEBUG,
9893 _("Processing ISDS response on %s request failed\n"),
9894 service_name_locale);
9895 goto leave;
9898 /* Check for response status */
9899 err = isds_response_status(context, service, *response,
9900 code, status_message, NULL);
9901 if (err) {
9902 isds_log(ILF_ISDS, ILL_DEBUG,
9903 _("ISDS response on %s request is missing status\n"),
9904 service_name_locale);
9905 goto leave;
9908 /* Request processed, but nothing found */
9909 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9910 char *code_locale = _isds_utf82locale((char*) *code);
9911 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9912 isds_log(ILF_ISDS, ILL_DEBUG,
9913 _("Server refused %s request for %s message ID "
9914 "(code=%s, message=%s)\n"),
9915 service_name_locale, message_id_locale,
9916 code_locale, status_message_locale);
9917 isds_log_message(context, status_message_locale);
9918 free(code_locale);
9919 free(status_message_locale);
9920 err = IE_ISDS;
9921 goto leave;
9924 leave:
9925 free(message_id_locale);
9926 free(service_name_locale);
9927 xmlFreeNode(request);
9928 return err;
9932 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9933 * signed data and free ISDS response.
9934 * @context is session context
9935 * @message_id is UTF-8 encoded message ID for logging purpose
9936 * @response is parsed XML document. It will be freed and NULLed in the middle
9937 * of function run to save memory. This is not guaranteed in case of error.
9938 * @request_name is name of ISDS request used to construct response root
9939 * element name and for logging purpose.
9940 * @raw is reallocated output buffer with DER encoded CMS data
9941 * @raw_length is size of @raw buffer in bytes
9942 * @returns standard error codes, in case of error, @raw will be freed and
9943 * NULLed, @response sometimes. */
9944 static isds_error find_extract_signed_data_free_response(
9945 struct isds_ctx *context, const xmlChar *message_id,
9946 xmlDocPtr *response, const xmlChar *request_name,
9947 void **raw, size_t *raw_length) {
9949 isds_error err = IE_SUCCESS;
9950 char *xpath_expression = NULL;
9951 xmlXPathContextPtr xpath_ctx = NULL;
9952 xmlXPathObjectPtr result = NULL;
9953 char *encoded_structure = NULL;
9955 if (!context) return IE_INVALID_CONTEXT;
9956 if (!raw) return IE_INVAL;
9957 zfree(*raw);
9958 if (!message_id || !response || !*response || !request_name || !raw_length)
9959 return IE_INVAL;
9961 /* Build XPath expression */
9962 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9963 "Response/isds:dmSignature");
9964 if (!xpath_expression) return IE_NOMEM;
9966 /* Extract data */
9967 xpath_ctx = xmlXPathNewContext(*response);
9968 if (!xpath_ctx) {
9969 err = IE_ERROR;
9970 goto leave;
9972 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9973 err = IE_ERROR;
9974 goto leave;
9976 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9977 if (!result) {
9978 err = IE_ERROR;
9979 goto leave;
9981 /* Empty response */
9982 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9983 char *message_id_locale = _isds_utf82locale((char*) message_id);
9984 isds_printf_message(context,
9985 _("Server did not return any signed data for message ID `%s' "
9986 "on %s request"),
9987 message_id_locale, request_name);
9988 free(message_id_locale);
9989 err = IE_ISDS;
9990 goto leave;
9992 /* More responses */
9993 if (result->nodesetval->nodeNr > 1) {
9994 char *message_id_locale = _isds_utf82locale((char*) message_id);
9995 isds_printf_message(context,
9996 _("Server did return more signed data for message ID `%s' "
9997 "on %s request"),
9998 message_id_locale, request_name);
9999 free(message_id_locale);
10000 err = IE_ISDS;
10001 goto leave;
10003 /* One response */
10004 xpath_ctx->node = result->nodesetval->nodeTab[0];
10006 /* Extract PKCS#7 structure */
10007 EXTRACT_STRING(".", encoded_structure);
10008 if (!encoded_structure) {
10009 isds_log_message(context, _("dmSignature element is empty"));
10012 /* Here we have delivery info as standalone CMS in encoded_structure.
10013 * We don't need any other data, free them: */
10014 xmlXPathFreeObject(result); result = NULL;
10015 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
10016 xmlFreeDoc(*response); *response = NULL;
10019 /* Decode PKCS#7 to DER format */
10020 *raw_length = _isds_b64decode(encoded_structure, raw);
10021 if (*raw_length == (size_t) -1) {
10022 isds_log_message(context,
10023 _("Error while Base64-decoding PKCS#7 structure"));
10024 err = IE_ERROR;
10025 goto leave;
10028 leave:
10029 if (err) {
10030 zfree(*raw);
10031 raw_length = 0;
10034 free(encoded_structure);
10035 xmlXPathFreeObject(result);
10036 xmlXPathFreeContext(xpath_ctx);
10037 free(xpath_expression);
10039 return err;
10041 #endif /* HAVE_LIBCURL */
10044 /* Download incoming message envelope identified by ID.
10045 * @context is session context
10046 * @message_id is message identifier (you can get them from
10047 * isds_get_list_of_received_messages())
10048 * @message is automatically reallocated message retrieved from ISDS.
10049 * It will miss documents per se. Use isds_get_received_message(), if you are
10050 * interested in documents (content) too.
10051 * Returned hash and timestamp require documents to be verifiable. */
10052 isds_error isds_get_received_envelope(struct isds_ctx *context,
10053 const char *message_id, struct isds_message **message) {
10055 isds_error err = IE_SUCCESS;
10056 #if HAVE_LIBCURL
10057 xmlDocPtr response = NULL;
10058 xmlChar *code = NULL, *status_message = NULL;
10059 xmlXPathContextPtr xpath_ctx = NULL;
10060 xmlXPathObjectPtr result = NULL;
10061 #endif
10063 if (!context) return IE_INVALID_CONTEXT;
10064 zfree(context->long_message);
10066 /* Free former message if any */
10067 if (!message) return IE_INVAL;
10068 isds_message_free(message);
10070 #if HAVE_LIBCURL
10071 /* Do request and check for success */
10072 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10073 BAD_CAST "MessageEnvelopeDownload", message_id,
10074 &response, NULL, NULL, &code, &status_message);
10075 if (err) goto leave;
10077 /* Extract data */
10078 xpath_ctx = xmlXPathNewContext(response);
10079 if (!xpath_ctx) {
10080 err = IE_ERROR;
10081 goto leave;
10083 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10084 err = IE_ERROR;
10085 goto leave;
10087 result = xmlXPathEvalExpression(
10088 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
10089 "isds:dmReturnedMessageEnvelope",
10090 xpath_ctx);
10091 if (!result) {
10092 err = IE_ERROR;
10093 goto leave;
10095 /* Empty response */
10096 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10097 char *message_id_locale = _isds_utf82locale((char*) message_id);
10098 isds_printf_message(context,
10099 _("Server did not return any envelope for ID `%s' "
10100 "on MessageEnvelopeDownload request"), message_id_locale);
10101 free(message_id_locale);
10102 err = IE_ISDS;
10103 goto leave;
10105 /* More envelops */
10106 if (result->nodesetval->nodeNr > 1) {
10107 char *message_id_locale = _isds_utf82locale((char*) message_id);
10108 isds_printf_message(context,
10109 _("Server did return more envelopes for ID `%s' "
10110 "on MessageEnvelopeDownload request"), message_id_locale);
10111 free(message_id_locale);
10112 err = IE_ISDS;
10113 goto leave;
10115 /* One message */
10116 xpath_ctx->node = result->nodesetval->nodeTab[0];
10118 /* Extract the envelope (= message without documents, hence 0) */
10119 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10120 if (err) goto leave;
10122 /* Save XML blob */
10123 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10124 &(*message)->raw_length);
10126 leave:
10127 if (err) {
10128 isds_message_free(message);
10131 xmlXPathFreeObject(result);
10132 xmlXPathFreeContext(xpath_ctx);
10134 free(code);
10135 free(status_message);
10136 if (!*message || !(*message)->xml) {
10137 xmlFreeDoc(response);
10140 if (!err)
10141 isds_log(ILF_ISDS, ILL_DEBUG,
10142 _("MessageEnvelopeDownload request processed by server "
10143 "successfully.\n")
10145 #else /* not HAVE_LIBCURL */
10146 err = IE_NOTSUP;
10147 #endif
10148 return err;
10152 /* Load delivery info of any format from buffer.
10153 * @context is session context
10154 * @raw_type advertises format of @buffer content. Only delivery info types
10155 * are accepted.
10156 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10157 * retrieve such data from message->raw after calling
10158 * isds_get_signed_delivery_info().
10159 * @length is length of buffer in bytes.
10160 * @message is automatically reallocated message parsed from @buffer.
10161 * @strategy selects how buffer will be attached into raw isds_message member.
10162 * */
10163 isds_error isds_load_delivery_info(struct isds_ctx *context,
10164 const isds_raw_type raw_type,
10165 const void *buffer, const size_t length,
10166 struct isds_message **message, const isds_buffer_strategy strategy) {
10168 isds_error err = IE_SUCCESS;
10169 message_ns_type message_ns;
10170 xmlDocPtr message_doc = NULL;
10171 xmlXPathContextPtr xpath_ctx = NULL;
10172 xmlXPathObjectPtr result = NULL;
10173 void *xml_stream = NULL;
10174 size_t xml_stream_length = 0;
10176 if (!context) return IE_INVALID_CONTEXT;
10177 zfree(context->long_message);
10178 if (!message) return IE_INVAL;
10179 isds_message_free(message);
10180 if (!buffer) return IE_INVAL;
10183 /* Select buffer format and extract XML from CMS*/
10184 switch (raw_type) {
10185 case RAWTYPE_DELIVERYINFO:
10186 message_ns = MESSAGE_NS_UNSIGNED;
10187 xml_stream = (void *) buffer;
10188 xml_stream_length = length;
10189 break;
10191 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
10192 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10193 xml_stream = (void *) buffer;
10194 xml_stream_length = length;
10195 break;
10197 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
10198 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
10199 err = _isds_extract_cms_data(context, buffer, length,
10200 &xml_stream, &xml_stream_length);
10201 if (err) goto leave;
10202 break;
10204 default:
10205 isds_log_message(context, _("Bad raw delivery representation type"));
10206 return IE_INVAL;
10207 break;
10210 isds_log(ILF_ISDS, ILL_DEBUG,
10211 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10212 xml_stream_length, xml_stream);
10214 /* Convert delivery info XML stream into XPath context */
10215 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10216 if (!message_doc) {
10217 err = IE_XML;
10218 goto leave;
10220 xpath_ctx = xmlXPathNewContext(message_doc);
10221 if (!xpath_ctx) {
10222 err = IE_ERROR;
10223 goto leave;
10225 /* XXX: Name spaces mangled for signed delivery info:
10226 * http://isds.czechpoint.cz/v20/delivery:
10228 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10229 * <q:dmDelivery>
10230 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10231 * <p:dmID>170272</p:dmID>
10232 * ...
10233 * </p:dmDm>
10234 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10235 * ...
10236 * </q:dmEvents>...</q:dmEvents>
10237 * </q:dmDelivery>
10238 * </q:GetDeliveryInfoResponse>
10239 * */
10240 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10241 err = IE_ERROR;
10242 goto leave;
10244 result = xmlXPathEvalExpression(
10245 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10246 xpath_ctx);
10247 if (!result) {
10248 err = IE_ERROR;
10249 goto leave;
10251 /* Empty delivery info */
10252 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10253 isds_printf_message(context,
10254 _("XML document is not sisds:dmDelivery document"));
10255 err = IE_ISDS;
10256 goto leave;
10258 /* More delivery info's */
10259 if (result->nodesetval->nodeNr > 1) {
10260 isds_printf_message(context,
10261 _("XML document has more sisds:dmDelivery elements"));
10262 err = IE_ISDS;
10263 goto leave;
10265 /* One delivery info */
10266 xpath_ctx->node = result->nodesetval->nodeTab[0];
10268 /* Extract the envelope (= message without documents, hence 0).
10269 * XXX: extract_TReturnedMessage() can obtain attachments size,
10270 * but delivery info carries none. It's coded as option elements,
10271 * so it should work. */
10272 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
10273 if (err) goto leave;
10275 /* Extract events */
10276 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
10277 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
10278 if (err) { err = IE_ERROR; goto leave; }
10279 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
10280 if (err) goto leave;
10282 /* Append raw CMS structure into message */
10283 (*message)->raw_type = raw_type;
10284 switch (strategy) {
10285 case BUFFER_DONT_STORE:
10286 break;
10287 case BUFFER_COPY:
10288 (*message)->raw = malloc(length);
10289 if (!(*message)->raw) {
10290 err = IE_NOMEM;
10291 goto leave;
10293 memcpy((*message)->raw, buffer, length);
10294 (*message)->raw_length = length;
10295 break;
10296 case BUFFER_MOVE:
10297 (*message)->raw = (void *) buffer;
10298 (*message)->raw_length = length;
10299 break;
10300 default:
10301 err = IE_ENUM;
10302 goto leave;
10305 leave:
10306 if (err) {
10307 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10308 isds_message_free(message);
10311 xmlXPathFreeObject(result);
10312 xmlXPathFreeContext(xpath_ctx);
10313 if (!*message || !(*message)->xml) {
10314 xmlFreeDoc(message_doc);
10316 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10318 if (!err)
10319 isds_log(ILF_ISDS, ILL_DEBUG,
10320 _("Delivery info loaded successfully.\n"));
10321 return err;
10325 /* Download signed delivery info-sheet of given message identified by ID.
10326 * @context is session context
10327 * @message_id is message identifier (you can get them from
10328 * isds_get_list_of_{sent,received}_messages())
10329 * @message is automatically reallocated message retrieved from ISDS.
10330 * It will miss documents per se. Use isds_get_signed_received_message(),
10331 * if you are interested in documents (content). OTOH, only this function
10332 * can get list events message has gone through. */
10333 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
10334 const char *message_id, struct isds_message **message) {
10336 isds_error err = IE_SUCCESS;
10337 #if HAVE_LIBCURL
10338 xmlDocPtr response = NULL;
10339 xmlChar *code = NULL, *status_message = NULL;
10340 void *raw = NULL;
10341 size_t raw_length = 0;
10342 #endif
10344 if (!context) return IE_INVALID_CONTEXT;
10345 zfree(context->long_message);
10347 /* Free former message if any */
10348 if (!message) return IE_INVAL;
10349 isds_message_free(message);
10351 #if HAVE_LIBCURL
10352 /* Do request and check for success */
10353 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10354 BAD_CAST "GetSignedDeliveryInfo", message_id,
10355 &response, NULL, NULL, &code, &status_message);
10356 if (err) goto leave;
10358 /* Find signed delivery info, extract it into raw and maybe free
10359 * response */
10360 err = find_extract_signed_data_free_response(context,
10361 (xmlChar *)message_id, &response,
10362 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
10363 if (err) goto leave;
10365 /* Parse delivery info */
10366 err = isds_load_delivery_info(context,
10367 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
10368 message, BUFFER_MOVE);
10369 if (err) goto leave;
10371 raw = NULL;
10373 leave:
10374 if (err) {
10375 isds_message_free(message);
10378 free(raw);
10379 free(code);
10380 free(status_message);
10381 xmlFreeDoc(response);
10383 if (!err)
10384 isds_log(ILF_ISDS, ILL_DEBUG,
10385 _("GetSignedDeliveryInfo request processed by server "
10386 "successfully.\n")
10388 #else /* not HAVE_LIBCURL */
10389 err = IE_NOTSUP;
10390 #endif
10391 return err;
10395 /* Download delivery info-sheet of given message identified by ID.
10396 * @context is session context
10397 * @message_id is message identifier (you can get them from
10398 * isds_get_list_of_{sent,received}_messages())
10399 * @message is automatically reallocated message retrieved from ISDS.
10400 * It will miss documents per se. Use isds_get_received_message(), if you are
10401 * interested in documents (content). OTOH, only this function can get list
10402 * of events message has gone through. */
10403 isds_error isds_get_delivery_info(struct isds_ctx *context,
10404 const char *message_id, struct isds_message **message) {
10406 isds_error err = IE_SUCCESS;
10407 #if HAVE_LIBCURL
10408 xmlDocPtr response = NULL;
10409 xmlChar *code = NULL, *status_message = NULL;
10410 xmlNodePtr delivery_node = NULL;
10411 void *raw = NULL;
10412 size_t raw_length = 0;
10413 #endif
10415 if (!context) return IE_INVALID_CONTEXT;
10416 zfree(context->long_message);
10418 /* Free former message if any */
10419 if (!message) return IE_INVAL;
10420 isds_message_free(message);
10422 #if HAVE_LIBCURL
10423 /* Do request and check for success */
10424 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10425 BAD_CAST "GetDeliveryInfo", message_id,
10426 &response, NULL, NULL, &code, &status_message);
10427 if (err) goto leave;
10430 /* Serialize delivery info */
10431 delivery_node = xmlDocGetRootElement(response);
10432 if (!delivery_node) {
10433 char *message_id_locale = _isds_utf82locale((char*) message_id);
10434 isds_printf_message(context,
10435 _("Server did not return any delivery info for ID `%s' "
10436 "on GetDeliveryInfo request"), message_id_locale);
10437 free(message_id_locale);
10438 err = IE_ISDS;
10439 goto leave;
10441 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
10442 if (err) goto leave;
10444 /* Parse delivery info */
10445 /* TODO: Here we parse the response second time. We could single delivery
10446 * parser from isds_load_delivery_info() to make things faster. */
10447 err = isds_load_delivery_info(context,
10448 RAWTYPE_DELIVERYINFO, raw, raw_length,
10449 message, BUFFER_MOVE);
10450 if (err) goto leave;
10452 raw = NULL;
10455 leave:
10456 if (err) {
10457 isds_message_free(message);
10460 free(raw);
10461 free(code);
10462 free(status_message);
10463 xmlFreeDoc(response);
10465 if (!err)
10466 isds_log(ILF_ISDS, ILL_DEBUG,
10467 _("GetDeliveryInfo request processed by server "
10468 "successfully.\n")
10470 #else /* not HAVE_LIBCURL */
10471 err = IE_NOTSUP;
10472 #endif
10473 return err;
10477 /* Download incoming message identified by ID.
10478 * @context is session context
10479 * @message_id is message identifier (you can get them from
10480 * isds_get_list_of_received_messages())
10481 * @message is automatically reallocated message retrieved from ISDS */
10482 isds_error isds_get_received_message(struct isds_ctx *context,
10483 const char *message_id, struct isds_message **message) {
10485 isds_error err = IE_SUCCESS;
10486 #if HAVE_LIBCURL
10487 xmlDocPtr response = NULL;
10488 void *xml_stream = NULL;
10489 size_t xml_stream_length;
10490 xmlChar *code = NULL, *status_message = NULL;
10491 xmlXPathContextPtr xpath_ctx = NULL;
10492 xmlXPathObjectPtr result = NULL;
10493 char *phys_path = NULL;
10494 size_t phys_start, phys_end;
10495 #endif
10497 if (!context) return IE_INVALID_CONTEXT;
10498 zfree(context->long_message);
10500 /* Free former message if any */
10501 if (NULL == message) return IE_INVAL;
10502 if (message) isds_message_free(message);
10504 #if HAVE_LIBCURL
10505 /* Do request and check for success */
10506 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10507 BAD_CAST "MessageDownload", message_id,
10508 &response, &xml_stream, &xml_stream_length,
10509 &code, &status_message);
10510 if (err) goto leave;
10512 /* Extract data */
10513 xpath_ctx = xmlXPathNewContext(response);
10514 if (!xpath_ctx) {
10515 err = IE_ERROR;
10516 goto leave;
10518 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10519 err = IE_ERROR;
10520 goto leave;
10522 result = xmlXPathEvalExpression(
10523 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10524 xpath_ctx);
10525 if (!result) {
10526 err = IE_ERROR;
10527 goto leave;
10529 /* Empty response */
10530 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10531 char *message_id_locale = _isds_utf82locale((char*) message_id);
10532 isds_printf_message(context,
10533 _("Server did not return any message for ID `%s' "
10534 "on MessageDownload request"), message_id_locale);
10535 free(message_id_locale);
10536 err = IE_ISDS;
10537 goto leave;
10539 /* More messages */
10540 if (result->nodesetval->nodeNr > 1) {
10541 char *message_id_locale = _isds_utf82locale((char*) message_id);
10542 isds_printf_message(context,
10543 _("Server did return more messages for ID `%s' "
10544 "on MessageDownload request"), message_id_locale);
10545 free(message_id_locale);
10546 err = IE_ISDS;
10547 goto leave;
10549 /* One message */
10550 xpath_ctx->node = result->nodesetval->nodeTab[0];
10552 /* Extract the message */
10553 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10554 if (err) goto leave;
10556 /* Locate raw XML blob */
10557 phys_path = strdup(
10558 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
10559 PHYSXML_ELEMENT_SEPARATOR
10560 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
10561 PHYSXML_ELEMENT_SEPARATOR
10562 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10564 if (!phys_path) {
10565 err = IE_NOMEM;
10566 goto leave;
10568 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10569 phys_path, &phys_start, &phys_end);
10570 zfree(phys_path);
10571 if (err) {
10572 isds_log_message(context,
10573 _("Substring with isds:MessageDownloadResponse element "
10574 "could not be located in raw SOAP message"));
10575 goto leave;
10577 /* Save XML blob */
10578 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10579 &(*message)->raw_length);*/
10580 /* TODO: Store name space declarations from ancestors */
10581 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10582 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
10583 (*message)->raw_length = phys_end - phys_start + 1;
10584 (*message)->raw = malloc((*message)->raw_length);
10585 if (!(*message)->raw) {
10586 err = IE_NOMEM;
10587 goto leave;
10589 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
10592 leave:
10593 if (err) {
10594 isds_message_free(message);
10597 free(phys_path);
10599 xmlXPathFreeObject(result);
10600 xmlXPathFreeContext(xpath_ctx);
10602 free(code);
10603 free(status_message);
10604 free(xml_stream);
10605 if (!*message || !(*message)->xml) {
10606 xmlFreeDoc(response);
10609 if (!err)
10610 isds_log(ILF_ISDS, ILL_DEBUG,
10611 _("MessageDownload request processed by server "
10612 "successfully.\n")
10614 #else /* not HAVE_LIBCURL */
10615 err = IE_NOTSUP;
10616 #endif
10617 return err;
10621 /* Load message of any type from buffer.
10622 * @context is session context
10623 * @raw_type defines content type of @buffer. Only message types are allowed.
10624 * @buffer is message raw representation. Format (CMS, plain signed,
10625 * message direction) is defined in @raw_type. You can retrieve such data
10626 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10627 * @length is length of buffer in bytes.
10628 * @message is automatically reallocated message parsed from @buffer.
10629 * @strategy selects how buffer will be attached into raw isds_message member.
10630 * */
10631 isds_error isds_load_message(struct isds_ctx *context,
10632 const isds_raw_type raw_type, const void *buffer, const size_t length,
10633 struct isds_message **message, const isds_buffer_strategy strategy) {
10635 isds_error err = IE_SUCCESS;
10636 void *xml_stream = NULL;
10637 size_t xml_stream_length = 0;
10638 message_ns_type message_ns;
10639 xmlDocPtr message_doc = NULL;
10640 xmlXPathContextPtr xpath_ctx = NULL;
10641 xmlXPathObjectPtr result = NULL;
10643 if (!context) return IE_INVALID_CONTEXT;
10644 zfree(context->long_message);
10645 if (!message) return IE_INVAL;
10646 isds_message_free(message);
10647 if (!buffer) return IE_INVAL;
10650 /* Select buffer format and extract XML from CMS*/
10651 switch (raw_type) {
10652 case RAWTYPE_INCOMING_MESSAGE:
10653 message_ns = MESSAGE_NS_UNSIGNED;
10654 xml_stream = (void *) buffer;
10655 xml_stream_length = length;
10656 break;
10658 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10659 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10660 xml_stream = (void *) buffer;
10661 xml_stream_length = length;
10662 break;
10664 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10665 message_ns = MESSAGE_NS_SIGNED_INCOMING;
10666 err = _isds_extract_cms_data(context, buffer, length,
10667 &xml_stream, &xml_stream_length);
10668 if (err) goto leave;
10669 break;
10671 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10672 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10673 xml_stream = (void *) buffer;
10674 xml_stream_length = length;
10675 break;
10677 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10678 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
10679 err = _isds_extract_cms_data(context, buffer, length,
10680 &xml_stream, &xml_stream_length);
10681 if (err) goto leave;
10682 break;
10684 default:
10685 isds_log_message(context, _("Bad raw message representation type"));
10686 return IE_INVAL;
10687 break;
10690 isds_log(ILF_ISDS, ILL_DEBUG,
10691 _("Loading message:\n%.*s\nEnd of message\n"),
10692 xml_stream_length, xml_stream);
10694 /* Convert messages XML stream into XPath context */
10695 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
10696 if (!message_doc) {
10697 err = IE_XML;
10698 goto leave;
10700 xpath_ctx = xmlXPathNewContext(message_doc);
10701 if (!xpath_ctx) {
10702 err = IE_ERROR;
10703 goto leave;
10705 /* XXX: Standard name space for unsigned incoming direction:
10706 * http://isds.czechpoint.cz/v20/
10708 * XXX: Name spaces mangled for signed outgoing direction:
10709 * http://isds.czechpoint.cz/v20/SentMessage:
10711 * <q:MessageDownloadResponse
10712 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10713 * <q:dmReturnedMessage>
10714 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10715 * <p:dmID>151916</p:dmID>
10716 * ...
10717 * </p:dmDm>
10718 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10719 * ...
10720 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10721 * </q:dmReturnedMessage>
10722 * </q:MessageDownloadResponse>
10724 * XXX: Name spaces mangled for signed incoming direction:
10725 * http://isds.czechpoint.cz/v20/message:
10727 * <q:MessageDownloadResponse
10728 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10729 * <q:dmReturnedMessage>
10730 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10731 * <p:dmID>151916</p:dmID>
10732 * ...
10733 * </p:dmDm>
10734 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10735 * ...
10736 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10737 * </q:dmReturnedMessage>
10738 * </q:MessageDownloadResponse>
10740 * Stupidity of ISDS developers is unlimited */
10741 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10742 err = IE_ERROR;
10743 goto leave;
10745 result = xmlXPathEvalExpression(
10746 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10747 xpath_ctx);
10748 if (!result) {
10749 err = IE_ERROR;
10750 goto leave;
10752 /* Empty message */
10753 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10754 isds_printf_message(context,
10755 _("XML document does not contain "
10756 "sisds:dmReturnedMessage element"));
10757 err = IE_ISDS;
10758 goto leave;
10760 /* More messages */
10761 if (result->nodesetval->nodeNr > 1) {
10762 isds_printf_message(context,
10763 _("XML document has more sisds:dmReturnedMessage elements"));
10764 err = IE_ISDS;
10765 goto leave;
10767 /* One message */
10768 xpath_ctx->node = result->nodesetval->nodeTab[0];
10770 /* Extract the message */
10771 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10772 if (err) goto leave;
10774 /* Append raw buffer into message */
10775 (*message)->raw_type = raw_type;
10776 switch (strategy) {
10777 case BUFFER_DONT_STORE:
10778 break;
10779 case BUFFER_COPY:
10780 (*message)->raw = malloc(length);
10781 if (!(*message)->raw) {
10782 err = IE_NOMEM;
10783 goto leave;
10785 memcpy((*message)->raw, buffer, length);
10786 (*message)->raw_length = length;
10787 break;
10788 case BUFFER_MOVE:
10789 (*message)->raw = (void *) buffer;
10790 (*message)->raw_length = length;
10791 break;
10792 default:
10793 err = IE_ENUM;
10794 goto leave;
10798 leave:
10799 if (err) {
10800 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10801 isds_message_free(message);
10804 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10805 xmlXPathFreeObject(result);
10806 xmlXPathFreeContext(xpath_ctx);
10807 if (!*message || !(*message)->xml) {
10808 xmlFreeDoc(message_doc);
10811 if (!err)
10812 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10813 return err;
10817 /* Determine type of raw message or delivery info according some heuristics.
10818 * It does not validate the raw blob.
10819 * @context is session context
10820 * @raw_type returns content type of @buffer. Valid only if exit code of this
10821 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10822 * reallocated memory.
10823 * @buffer is message raw representation.
10824 * @length is length of buffer in bytes. */
10825 isds_error isds_guess_raw_type(struct isds_ctx *context,
10826 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10827 isds_error err;
10828 void *xml_stream = NULL;
10829 size_t xml_stream_length = 0;
10830 xmlDocPtr document = NULL;
10831 xmlNodePtr root = NULL;
10833 if (!context) return IE_INVALID_CONTEXT;
10834 zfree(context->long_message);
10835 if (length == 0 || !buffer) return IE_INVAL;
10836 if (!raw_type) return IE_INVAL;
10838 /* Try CMS */
10839 err = _isds_extract_cms_data(context, buffer, length,
10840 &xml_stream, &xml_stream_length);
10841 if (err) {
10842 xml_stream = (void *) buffer;
10843 xml_stream_length = (size_t) length;
10844 err = IE_SUCCESS;
10847 /* Try XML */
10848 document = xmlParseMemory(xml_stream, xml_stream_length);
10849 if (!document) {
10850 isds_printf_message(context,
10851 _("Could not parse data as XML document"));
10852 err = IE_NOTSUP;
10853 goto leave;
10856 /* Get root element */
10857 root = xmlDocGetRootElement(document);
10858 if (!root) {
10859 isds_printf_message(context,
10860 _("XML document is missing root element"));
10861 err = IE_XML;
10862 goto leave;
10865 if (!root->ns || !root->ns->href) {
10866 isds_printf_message(context,
10867 _("Root element does not belong to any name space"));
10868 err = IE_NOTSUP;
10869 goto leave;
10872 /* Test name space */
10873 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10874 if (xml_stream == buffer)
10875 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10876 else
10877 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10878 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10879 if (xml_stream == buffer)
10880 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10881 else
10882 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10883 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10884 if (xml_stream == buffer)
10885 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10886 else
10887 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10888 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10889 if (xml_stream != buffer) {
10890 isds_printf_message(context,
10891 _("Document in ISDS name space is encapsulated into CMS" ));
10892 err = IE_NOTSUP;
10893 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10894 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10895 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10896 *raw_type = RAWTYPE_DELIVERYINFO;
10897 else {
10898 isds_printf_message(context,
10899 _("Unknown root element in ISDS name space"));
10900 err = IE_NOTSUP;
10902 } else {
10903 isds_printf_message(context,
10904 _("Unknown name space"));
10905 err = IE_NOTSUP;
10908 leave:
10909 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10910 xmlFreeDoc(document);
10911 return err;
10915 /* Download signed incoming/outgoing message identified by ID.
10916 * @context is session context
10917 * @output is true for outgoing message, false for incoming message
10918 * @message_id is message identifier (you can get them from
10919 * isds_get_list_of_{sent,received}_messages())
10920 * @message is automatically reallocated message retrieved from ISDS. The raw
10921 * member will be filled with PKCS#7 structure in DER format. */
10922 static isds_error isds_get_signed_message(struct isds_ctx *context,
10923 const _Bool outgoing, const char *message_id,
10924 struct isds_message **message) {
10926 isds_error err = IE_SUCCESS;
10927 #if HAVE_LIBCURL
10928 xmlDocPtr response = NULL;
10929 xmlChar *code = NULL, *status_message = NULL;
10930 xmlXPathContextPtr xpath_ctx = NULL;
10931 xmlXPathObjectPtr result = NULL;
10932 char *encoded_structure = NULL;
10933 void *raw = NULL;
10934 size_t raw_length = 0;
10935 #endif
10937 if (!context) return IE_INVALID_CONTEXT;
10938 zfree(context->long_message);
10939 if (!message) return IE_INVAL;
10940 isds_message_free(message);
10942 #if HAVE_LIBCURL
10943 /* Do request and check for success */
10944 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10945 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10946 BAD_CAST "SignedMessageDownload",
10947 message_id, &response, NULL, NULL, &code, &status_message);
10948 if (err) goto leave;
10950 /* Find signed message, extract it into raw and maybe free
10951 * response */
10952 err = find_extract_signed_data_free_response(context,
10953 (xmlChar *)message_id, &response,
10954 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10955 BAD_CAST "SignedMessageDownload",
10956 &raw, &raw_length);
10957 if (err) goto leave;
10959 /* Parse message */
10960 err = isds_load_message(context,
10961 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10962 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10963 raw, raw_length, message, BUFFER_MOVE);
10964 if (err) goto leave;
10966 raw = NULL;
10968 leave:
10969 if (err) {
10970 isds_message_free(message);
10973 free(encoded_structure);
10974 xmlXPathFreeObject(result);
10975 xmlXPathFreeContext(xpath_ctx);
10976 free(raw);
10978 free(code);
10979 free(status_message);
10980 xmlFreeDoc(response);
10982 if (!err)
10983 isds_log(ILF_ISDS, ILL_DEBUG,
10984 (outgoing) ?
10985 _("SignedSentMessageDownload request processed by server "
10986 "successfully.\n") :
10987 _("SignedMessageDownload request processed by server "
10988 "successfully.\n")
10990 #else /* not HAVE_LIBCURL */
10991 err = IE_NOTSUP;
10992 #endif
10993 return err;
10997 /* Download signed incoming message identified by ID.
10998 * @context is session context
10999 * @message_id is message identifier (you can get them from
11000 * isds_get_list_of_received_messages())
11001 * @message is automatically reallocated message retrieved from ISDS. The raw
11002 * member will be filled with PKCS#7 structure in DER format. */
11003 isds_error isds_get_signed_received_message(struct isds_ctx *context,
11004 const char *message_id, struct isds_message **message) {
11005 return isds_get_signed_message(context, 0, message_id, message);
11009 /* Download signed outgoing message identified by ID.
11010 * @context is session context
11011 * @message_id is message identifier (you can get them from
11012 * isds_get_list_of_sent_messages())
11013 * @message is automatically reallocated message retrieved from ISDS. The raw
11014 * member will be filled with PKCS#7 structure in DER format. */
11015 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
11016 const char *message_id, struct isds_message **message) {
11017 return isds_get_signed_message(context, 1, message_id, message);
11021 /* Get type and name of user who sent a message identified by ID.
11022 * @context is session context
11023 * @message_id is message identifier
11024 * @sender_type is pointer to automatically allocated type of sender detected
11025 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11026 * library or to the server, NULL will be returned. Pass NULL if you don't
11027 * care about it.
11028 * @raw_sender_type is automatically reallocated UTF-8 string describing
11029 * sender type or NULL if not known to server. Pass NULL if you don't care.
11030 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11031 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11032 isds_error isds_get_message_sender(struct isds_ctx *context,
11033 const char *message_id, isds_sender_type **sender_type,
11034 char **raw_sender_type, char **sender_name) {
11035 isds_error err = IE_SUCCESS;
11036 #if HAVE_LIBCURL
11037 xmlDocPtr response = NULL;
11038 xmlChar *code = NULL, *status_message = NULL;
11039 xmlXPathContextPtr xpath_ctx = NULL;
11040 xmlXPathObjectPtr result = NULL;
11041 char *type_string = NULL;
11042 #endif
11044 if (!context) return IE_INVALID_CONTEXT;
11045 zfree(context->long_message);
11046 if (sender_type) zfree(*sender_type);
11047 if (raw_sender_type) zfree(*raw_sender_type);
11048 if (sender_name) zfree(*sender_name);
11049 if (!message_id) return IE_INVAL;
11051 #if HAVE_LIBCURL
11052 /* Do request and check for success */
11053 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11054 BAD_CAST "GetMessageAuthor",
11055 message_id, &response, NULL, NULL, &code, &status_message);
11056 if (err) goto leave;
11058 /* Extract data */
11059 xpath_ctx = xmlXPathNewContext(response);
11060 if (!xpath_ctx) {
11061 err = IE_ERROR;
11062 goto leave;
11064 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11065 err = IE_ERROR;
11066 goto leave;
11068 result = xmlXPathEvalExpression(
11069 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
11070 if (!result) {
11071 err = IE_ERROR;
11072 goto leave;
11074 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11075 isds_log_message(context,
11076 _("Missing GetMessageAuthorResponse element"));
11077 err = IE_ISDS;
11078 goto leave;
11080 if (result->nodesetval->nodeNr > 1) {
11081 isds_log_message(context,
11082 _("Multiple GetMessageAuthorResponse element"));
11083 err = IE_ISDS;
11084 goto leave;
11086 xpath_ctx->node = result->nodesetval->nodeTab[0];
11087 xmlXPathFreeObject(result); result = NULL;
11089 /* Fill output arguments in */
11090 EXTRACT_STRING("isds:userType", type_string);
11091 if (NULL != type_string) {
11092 if (NULL != sender_type) {
11093 *sender_type = calloc(1, sizeof(**sender_type));
11094 if (NULL == *sender_type) {
11095 err = IE_NOMEM;
11096 goto leave;
11099 err = string2isds_sender_type((xmlChar *)type_string,
11100 *sender_type);
11101 if (err) {
11102 zfree(*sender_type);
11103 if (err == IE_ENUM) {
11104 err = IE_SUCCESS;
11105 char *type_string_locale = _isds_utf82locale(type_string);
11106 isds_log(ILF_ISDS, ILL_WARNING,
11107 _("Unknown isds:userType value: %s"),
11108 type_string_locale);
11109 free(type_string_locale);
11114 if (NULL != sender_name)
11115 EXTRACT_STRING("isds:authorName", *sender_name);
11117 leave:
11118 if (err) {
11119 if (NULL != sender_type) zfree(*sender_type);
11120 zfree(type_string);
11121 if (NULL != sender_name) zfree(*sender_name);
11123 if (NULL != raw_sender_type) *raw_sender_type = type_string;
11125 xmlXPathFreeObject(result);
11126 xmlXPathFreeContext(xpath_ctx);
11128 free(code);
11129 free(status_message);
11130 xmlFreeDoc(response);
11132 if (!err)
11133 isds_log(ILF_ISDS, ILL_DEBUG,
11134 _("GetMessageAuthor request processed by server "
11135 "successfully.\n"));
11136 #else /* not HAVE_LIBCURL */
11137 err = IE_NOTSUP;
11138 #endif
11139 return err;
11143 /* Retrieve hash of message identified by ID stored in ISDS.
11144 * @context is session context
11145 * @message_id is message identifier
11146 * @hash is automatically reallocated message hash downloaded from ISDS.
11147 * Message must exist in system and must not be deleted. */
11148 isds_error isds_download_message_hash(struct isds_ctx *context,
11149 const char *message_id, struct isds_hash **hash) {
11151 isds_error err = IE_SUCCESS;
11152 #if HAVE_LIBCURL
11153 xmlDocPtr response = NULL;
11154 xmlChar *code = NULL, *status_message = NULL;
11155 xmlXPathContextPtr xpath_ctx = NULL;
11156 xmlXPathObjectPtr result = NULL;
11157 #endif
11159 if (!context) return IE_INVALID_CONTEXT;
11160 zfree(context->long_message);
11162 isds_hash_free(hash);
11164 #if HAVE_LIBCURL
11165 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11166 BAD_CAST "VerifyMessage", message_id,
11167 &response, NULL, NULL, &code, &status_message);
11168 if (err) goto leave;
11171 /* Extract data */
11172 xpath_ctx = xmlXPathNewContext(response);
11173 if (!xpath_ctx) {
11174 err = IE_ERROR;
11175 goto leave;
11177 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11178 err = IE_ERROR;
11179 goto leave;
11181 result = xmlXPathEvalExpression(
11182 BAD_CAST "/isds:VerifyMessageResponse",
11183 xpath_ctx);
11184 if (!result) {
11185 err = IE_ERROR;
11186 goto leave;
11188 /* Empty response */
11189 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11190 char *message_id_locale = _isds_utf82locale((char*) message_id);
11191 isds_printf_message(context,
11192 _("Server did not return any response for ID `%s' "
11193 "on VerifyMessage request"), message_id_locale);
11194 free(message_id_locale);
11195 err = IE_ISDS;
11196 goto leave;
11198 /* More responses */
11199 if (result->nodesetval->nodeNr > 1) {
11200 char *message_id_locale = _isds_utf82locale((char*) message_id);
11201 isds_printf_message(context,
11202 _("Server did return more responses for ID `%s' "
11203 "on VerifyMessage request"), message_id_locale);
11204 free(message_id_locale);
11205 err = IE_ISDS;
11206 goto leave;
11208 /* One response */
11209 xpath_ctx->node = result->nodesetval->nodeTab[0];
11211 /* Extract the hash */
11212 err = find_and_extract_DmHash(context, hash, xpath_ctx);
11214 leave:
11215 if (err) {
11216 isds_hash_free(hash);
11219 xmlXPathFreeObject(result);
11220 xmlXPathFreeContext(xpath_ctx);
11222 free(code);
11223 free(status_message);
11224 xmlFreeDoc(response);
11226 if (!err)
11227 isds_log(ILF_ISDS, ILL_DEBUG,
11228 _("VerifyMessage request processed by server "
11229 "successfully.\n")
11231 #else /* not HAVE_LIBCURL */
11232 err = IE_NOTSUP;
11233 #endif
11234 return err;
11238 /* Erase message specified by @message_id from long term storage. Other
11239 * message cannot be erased on user request.
11240 * @context is session context
11241 * @message_id is message identifier.
11242 * @incoming is true for incoming message, false for outgoing message.
11243 * @return
11244 * IE_SUCCESS if message has ben removed
11245 * IE_INVAL if message does not exist in long term storage or message
11246 * belongs to different box
11247 * TODO: IE_NOEPRM if user has no permission to erase a message */
11248 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
11249 const char *message_id, _Bool incoming) {
11250 isds_error err = IE_SUCCESS;
11251 #if HAVE_LIBCURL
11252 xmlNodePtr request = NULL, node;
11253 xmlNsPtr isds_ns = NULL;
11254 xmlDocPtr response = NULL;
11255 xmlChar *code = NULL, *status_message = NULL;
11256 #endif
11258 if (!context) return IE_INVALID_CONTEXT;
11259 zfree(context->long_message);
11260 if (NULL == message_id) return IE_INVAL;
11262 /* Check if connection is established
11263 * TODO: This check should be done downstairs. */
11264 if (!context->curl) return IE_CONNECTION_CLOSED;
11266 #if HAVE_LIBCURL
11267 /* Build request */
11268 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
11269 if (!request) {
11270 isds_log_message(context,
11271 _("Could build EraseMessage request"));
11272 return IE_ERROR;
11274 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11275 if(!isds_ns) {
11276 isds_log_message(context, _("Could not create ISDS name space"));
11277 xmlFreeNode(request);
11278 return IE_ERROR;
11280 xmlSetNs(request, isds_ns);
11282 err = validate_message_id_length(context, (xmlChar *) message_id);
11283 if (err) goto leave;
11284 INSERT_STRING(request, "dmID", message_id);
11286 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
11289 /* Send request */
11290 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
11291 "message ID %s to ISDS\n"), message_id);
11292 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
11293 xmlFreeNode(request); request = NULL;
11295 if (err) {
11296 isds_log(ILF_ISDS, ILL_DEBUG,
11297 _("Processing ISDS response on EraseMessage request "
11298 "failed\n"));
11299 goto leave;
11302 /* Check for response status */
11303 err = isds_response_status(context, SERVICE_DM_INFO, response,
11304 &code, &status_message, NULL);
11305 if (err) {
11306 isds_log(ILF_ISDS, ILL_DEBUG,
11307 _("ISDS response on EraseMessage request is missing "
11308 "status\n"));
11309 goto leave;
11312 /* Check server status code */
11313 if (!xmlStrcmp(code, BAD_CAST "1211")) {
11314 isds_log_message(context, _("Message to erase belongs to other box"));
11315 err = IE_INVAL;
11316 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
11317 isds_log_message(context, _("Message to erase is not saved in "
11318 "long term storage or the direction does not match"));
11319 err = IE_INVAL;
11320 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
11321 char *code_locale = _isds_utf82locale((char*) code);
11322 char *message_locale = _isds_utf82locale((char*) status_message);
11323 isds_log(ILF_ISDS, ILL_DEBUG,
11324 _("Server refused EraseMessage request "
11325 "(code=%s, message=%s)\n"),
11326 code_locale, message_locale);
11327 isds_log_message(context, message_locale);
11328 free(code_locale);
11329 free(message_locale);
11330 err = IE_ISDS;
11331 goto leave;
11334 leave:
11335 free(code);
11336 free(status_message);
11337 xmlFreeDoc(response);
11338 xmlFreeNode(request);
11340 if (!err)
11341 isds_log(ILF_ISDS, ILL_DEBUG,
11342 _("EraseMessage request processed by server "
11343 "successfully.\n")
11345 #else /* not HAVE_LIBCURL */
11346 err = IE_NOTSUP;
11347 #endif
11348 return err;
11352 /* Mark message as read. This is a transactional commit function to acknowledge
11353 * to ISDS the message has been downloaded and processed by client properly.
11354 * @context is session context
11355 * @message_id is message identifier. */
11356 isds_error isds_mark_message_read(struct isds_ctx *context,
11357 const char *message_id) {
11359 isds_error err = IE_SUCCESS;
11360 #if HAVE_LIBCURL
11361 xmlDocPtr response = NULL;
11362 xmlChar *code = NULL, *status_message = NULL;
11363 #endif
11365 if (!context) return IE_INVALID_CONTEXT;
11366 zfree(context->long_message);
11368 #if HAVE_LIBCURL
11369 /* Do request and check for success */
11370 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11371 BAD_CAST "MarkMessageAsDownloaded", message_id,
11372 &response, NULL, NULL, &code, &status_message);
11374 free(code);
11375 free(status_message);
11376 xmlFreeDoc(response);
11378 if (!err)
11379 isds_log(ILF_ISDS, ILL_DEBUG,
11380 _("MarkMessageAsDownloaded request processed by server "
11381 "successfully.\n")
11383 #else /* not HAVE_LIBCURL */
11384 err = IE_NOTSUP;
11385 #endif
11386 return err;
11390 /* Mark message as received by recipient. This is applicable only to
11391 * commercial message. Use envelope->dmType message member to distinguish
11392 * commercial message from government message. Government message is
11393 * received automatically (by law), commercial message on recipient request.
11394 * @context is session context
11395 * @message_id is message identifier. */
11396 isds_error isds_mark_message_received(struct isds_ctx *context,
11397 const char *message_id) {
11399 isds_error err = IE_SUCCESS;
11400 #if HAVE_LIBCURL
11401 xmlDocPtr response = NULL;
11402 xmlChar *code = NULL, *status_message = NULL;
11403 #endif
11405 if (!context) return IE_INVALID_CONTEXT;
11406 zfree(context->long_message);
11408 #if HAVE_LIBCURL
11409 /* Do request and check for success */
11410 err = build_send_check_message_request(context, SERVICE_DM_INFO,
11411 BAD_CAST "ConfirmDelivery", message_id,
11412 &response, NULL, NULL, &code, &status_message);
11414 free(code);
11415 free(status_message);
11416 xmlFreeDoc(response);
11418 if (!err)
11419 isds_log(ILF_ISDS, ILL_DEBUG,
11420 _("ConfirmDelivery request processed by server "
11421 "successfully.\n")
11423 #else /* not HAVE_LIBCURL */
11424 err = IE_NOTSUP;
11425 #endif
11426 return err;
11430 /* Send document for authorized conversion into Czech POINT system.
11431 * This is public anonymous service, no log-in necessary. Special context is
11432 * used to reuse keep-a-live HTTPS connection.
11433 * @context is Czech POINT session context. DO NOT use context connected to
11434 * ISDS server. Use new context or context used by this function previously.
11435 * @document is document to convert. Only data, data_length, dmFileDescr and
11436 * is_xml members are significant. Be ware that not all document formats can be
11437 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11438 * @id is reallocated identifier assigned by Czech POINT system to
11439 * your document on submit. Use is to tell it to Czech POINT officer.
11440 * @date is reallocated document submit date (submitted documents
11441 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11442 * value. */
11443 isds_error czp_convert_document(struct isds_ctx *context,
11444 const struct isds_document *document,
11445 char **id, struct tm **date) {
11446 isds_error err = IE_SUCCESS;
11447 #if HAVE_LIBCURL
11448 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
11449 xmlNodePtr request = NULL, node;
11450 xmlDocPtr response = NULL;
11452 xmlXPathContextPtr xpath_ctx = NULL;
11453 xmlXPathObjectPtr result = NULL;
11454 long int status = -1;
11455 long int *status_ptr = &status;
11456 char *string = NULL;
11457 #endif
11460 if (!context) return IE_INVALID_CONTEXT;
11461 zfree(context->long_message);
11462 if (!document || !id || !date) return IE_INVAL;
11464 if (document->is_xml) {
11465 isds_log_message(context,
11466 _("XML documents cannot be submitted to conversion"));
11467 return IE_NOTSUP;
11470 /* Free output arguments */
11471 zfree(*id);
11472 zfree(*date);
11474 #if HAVE_LIBCURL
11475 /* Store configuration */
11476 context->type = CTX_TYPE_CZP;
11477 free(context->url);
11478 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
11479 if (!(context->url))
11480 return IE_NOMEM;
11482 /* Prepare CURL handle if not yet connected */
11483 if (!context->curl) {
11484 context->curl = curl_easy_init();
11485 if (!(context->curl))
11486 return IE_ERROR;
11489 /* Build conversion request */
11490 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
11491 if (!request) {
11492 isds_log_message(context,
11493 _("Could not build Czech POINT conversion request"));
11494 return IE_ERROR;
11496 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
11497 if(!deposit_ns) {
11498 isds_log_message(context,
11499 _("Could not create Czech POINT deposit name space"));
11500 xmlFreeNode(request);
11501 return IE_ERROR;
11503 xmlSetNs(request, deposit_ns);
11505 /* Insert children. They are in empty namespace! */
11506 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
11507 if(!empty_ns) {
11508 isds_log_message(context, _("Could not create empty name space"));
11509 err = IE_ERROR;
11510 goto leave;
11512 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
11513 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
11514 document->dmFileDescr);
11516 /* Document encoded in Base64 */
11517 err = insert_base64_encoded_string(context, request, empty_ns, "document",
11518 document->data, document->data_length);
11519 if (err) goto leave;
11521 isds_log(ILF_ISDS, ILL_DEBUG,
11522 _("Submitting document for conversion into Czech POINT deposit"));
11524 /* Send conversion request */
11525 err = _czp_czpdeposit(context, request, &response);
11526 xmlFreeNode(request); request = NULL;
11528 if (err) {
11529 czp_do_close_connection(context);
11530 goto leave;
11534 /* Extract response */
11535 xpath_ctx = xmlXPathNewContext(response);
11536 if (!xpath_ctx) {
11537 err = IE_ERROR;
11538 goto leave;
11540 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11541 err = IE_ERROR;
11542 goto leave;
11544 result = xmlXPathEvalExpression(
11545 BAD_CAST "/deposit:saveDocumentResponse/return",
11546 xpath_ctx);
11547 if (!result) {
11548 err = IE_ERROR;
11549 goto leave;
11551 /* Empty response */
11552 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11553 isds_printf_message(context,
11554 _("Missing `return' element in Czech POINT deposit response"));
11555 err = IE_ISDS;
11556 goto leave;
11558 /* More responses */
11559 if (result->nodesetval->nodeNr > 1) {
11560 isds_printf_message(context,
11561 _("Multiple `return' element in Czech POINT deposit response"));
11562 err = IE_ISDS;
11563 goto leave;
11565 /* One response */
11566 xpath_ctx->node = result->nodesetval->nodeTab[0];
11568 /* Get status */
11569 EXTRACT_LONGINT("status", status_ptr, 1);
11570 if (status) {
11571 EXTRACT_STRING("statusMsg", string);
11572 char *string_locale = _isds_utf82locale(string);
11573 isds_printf_message(context,
11574 _("Czech POINT deposit refused document for conversion "
11575 "(code=%ld, message=%s)"),
11576 status, string_locale);
11577 free(string_locale);
11578 err = IE_ISDS;
11579 goto leave;
11582 /* Get document ID */
11583 EXTRACT_STRING("documentID", *id);
11585 /* Get submit date */
11586 EXTRACT_STRING("dateInserted", string);
11587 if (string) {
11588 *date = calloc(1, sizeof(**date));
11589 if (!*date) {
11590 err = IE_NOMEM;
11591 goto leave;
11593 err = _isds_datestring2tm((xmlChar *)string, *date);
11594 if (err) {
11595 if (err == IE_NOTSUP) {
11596 err = IE_ISDS;
11597 char *string_locale = _isds_utf82locale(string);
11598 isds_printf_message(context,
11599 _("Invalid dateInserted value: %s"), string_locale);
11600 free(string_locale);
11602 goto leave;
11606 leave:
11607 free(string);
11608 xmlXPathFreeObject(result);
11609 xmlXPathFreeContext(xpath_ctx);
11611 xmlFreeDoc(response);
11612 xmlFreeNode(request);
11614 if (!err) {
11615 char *id_locale = _isds_utf82locale((char *) *id);
11616 isds_log(ILF_ISDS, ILL_DEBUG,
11617 _("Document %s has been submitted for conversion "
11618 "to server successfully\n"), id_locale);
11619 free(id_locale);
11621 #else /* not HAVE_LIBCURL */
11622 err = IE_NOTSUP;
11623 #endif
11624 return err;
11628 /* Close possibly opened connection to Czech POINT document deposit.
11629 * @context is Czech POINT session context. */
11630 isds_error czp_close_connection(struct isds_ctx *context) {
11631 if (!context) return IE_INVALID_CONTEXT;
11632 zfree(context->long_message);
11633 #if HAVE_LIBCURL
11634 return czp_do_close_connection(context);
11635 #else
11636 return IE_NOTSUP;
11637 #endif
11641 /* Send request for new box creation in testing ISDS instance.
11642 * It's not possible to request for a production box currently, as it
11643 * communicates via e-mail.
11644 * XXX: This function does not work either. Server complains about invalid
11645 * e-mail address.
11646 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11647 * this function
11648 * @context is special session context for box creation request. DO NOT use
11649 * standard context as it could reveal your password. Use fresh new context or
11650 * context previously used by this function.
11651 * @box is box description to create including single primary user (in case of
11652 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11653 * ignored. It outputs box ID assigned by ISDS in dbID element.
11654 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11655 * box, or contact address of PFO box owner). The email member is mandatory as
11656 * it will be used to deliver credentials.
11657 * @former_names is former name of box owner. Pass NULL if you don't care.
11658 * @approval is optional external approval of box manipulation
11659 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11660 * NULL, if you don't care.*/
11661 isds_error isds_request_new_testing_box(struct isds_ctx *context,
11662 struct isds_DbOwnerInfo *box, const struct isds_list *users,
11663 const char *former_names, const struct isds_approval *approval,
11664 char **refnumber) {
11665 isds_error err = IE_SUCCESS;
11666 #if HAVE_LIBCURL
11667 xmlNodePtr request = NULL;
11668 xmlDocPtr response = NULL;
11669 xmlXPathContextPtr xpath_ctx = NULL;
11670 xmlXPathObjectPtr result = NULL;
11671 #endif
11674 if (!context) return IE_INVALID_CONTEXT;
11675 zfree(context->long_message);
11676 if (!box) return IE_INVAL;
11678 #if HAVE_LIBCURL
11679 if (!box->email || box->email[0] == '\0') {
11680 isds_log_message(context, _("E-mail field is mandatory"));
11681 return IE_INVAL;
11684 /* Scratch box ID */
11685 zfree(box->dbID);
11687 /* Store configuration */
11688 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
11689 free(context->url);
11690 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
11691 if (!(context->url))
11692 return IE_NOMEM;
11694 /* Prepare CURL handle if not yet connected */
11695 if (!context->curl) {
11696 context->curl = curl_easy_init();
11697 if (!(context->curl))
11698 return IE_ERROR;
11701 /* Build CreateDataBox request */
11702 err = build_CreateDBInput_request(context,
11703 &request, BAD_CAST "CreateDataBox",
11704 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
11705 if (err) goto leave;
11707 /* Send it to server and process response */
11708 err = send_destroy_request_check_response(context,
11709 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
11710 &response, (xmlChar **) refnumber, NULL);
11711 if (err) goto leave;
11713 /* Extract box ID */
11714 xpath_ctx = xmlXPathNewContext(response);
11715 if (!xpath_ctx) {
11716 err = IE_ERROR;
11717 goto leave;
11719 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11720 err = IE_ERROR;
11721 goto leave;
11723 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11725 leave:
11726 xmlXPathFreeObject(result);
11727 xmlXPathFreeContext(xpath_ctx);
11728 xmlFreeDoc(response);
11729 xmlFreeNode(request);
11731 if (!err) {
11732 isds_log(ILF_ISDS, ILL_DEBUG,
11733 _("CreateDataBox request processed by server successfully.\n"));
11735 #else /* not HAVE_LIBCURL */
11736 err = IE_NOTSUP;
11737 #endif
11739 return err;
11743 /* Submit CMS signed message to ISDS to verify its originality. This is
11744 * stronger form of isds_verify_message_hash() because ISDS does more checks
11745 * than simple one (potentialy old weak) hash comparison.
11746 * @context is session context
11747 * @message is memory with raw CMS signed message bit stream
11748 * @length is @message size in bytes
11749 * @return
11750 * IE_SUCCESS if message originates in ISDS
11751 * IE_NOTEQUAL if message is unknown to ISDS
11752 * other code for other errors */
11753 isds_error isds_authenticate_message(struct isds_ctx *context,
11754 const void *message, size_t length) {
11755 isds_error err = IE_SUCCESS;
11756 #if HAVE_LIBCURL
11757 xmlNsPtr isds_ns = NULL;
11758 xmlNodePtr request = NULL;
11759 xmlDocPtr response = NULL;
11760 xmlXPathContextPtr xpath_ctx = NULL;
11761 xmlXPathObjectPtr result = NULL;
11762 _Bool *authentic = NULL;
11763 #endif
11765 if (!context) return IE_INVALID_CONTEXT;
11766 zfree(context->long_message);
11767 if (!message || length == 0) return IE_INVAL;
11769 #if HAVE_LIBCURL
11770 /* Check if connection is established
11771 * TODO: This check should be done downstairs. */
11772 if (!context->curl) return IE_CONNECTION_CLOSED;
11775 /* Build AuthenticateMessage request */
11776 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11777 if (!request) {
11778 isds_log_message(context,
11779 _("Could not build AuthenticateMessage request"));
11780 return IE_ERROR;
11782 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11783 if(!isds_ns) {
11784 isds_log_message(context, _("Could not create ISDS name space"));
11785 xmlFreeNode(request);
11786 return IE_ERROR;
11788 xmlSetNs(request, isds_ns);
11790 /* Insert Base64 encoded message */
11791 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11792 message, length);
11793 if (err) goto leave;
11795 /* Send request to server and process response */
11796 err = send_destroy_request_check_response(context,
11797 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11798 &response, NULL, NULL);
11799 if (err) goto leave;
11802 /* ISDS has decided */
11803 xpath_ctx = xmlXPathNewContext(response);
11804 if (!xpath_ctx) {
11805 err = IE_ERROR;
11806 goto leave;
11808 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11809 err = IE_ERROR;
11810 goto leave;
11813 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11815 if (!authentic) {
11816 isds_log_message(context,
11817 _("Server did not return any response on "
11818 "AuthenticateMessage request"));
11819 err = IE_ISDS;
11820 goto leave;
11822 if (*authentic) {
11823 isds_log(ILF_ISDS, ILL_DEBUG,
11824 _("ISDS authenticated the message successfully\n"));
11825 } else {
11826 isds_log_message(context, _("ISDS does not know the message"));
11827 err = IE_NOTEQUAL;
11831 leave:
11832 free(authentic);
11833 xmlXPathFreeObject(result);
11834 xmlXPathFreeContext(xpath_ctx);
11836 xmlFreeDoc(response);
11837 xmlFreeNode(request);
11838 #else /* not HAVE_LIBCURL */
11839 err = IE_NOTSUP;
11840 #endif
11842 return err;
11846 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11847 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11848 * be re-signed.
11849 * @context is session context
11850 * @input_data is memory with raw CMS signed message or delivery info bit
11851 * stream to re-sign
11852 * @input_length is @input_data size in bytes
11853 * @output_data is pointer to auto-allocated memory where to store re-signed
11854 * input data blob. Caller must free it.
11855 * @output_data is pointer where to store @output_data size in bytes
11856 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11857 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11858 * @return
11859 * IE_SUCCESS if CMS blob has been re-signed successfully
11860 * other code for other errors */
11861 isds_error isds_resign_message(struct isds_ctx *context,
11862 const void *input_data, size_t input_length,
11863 void **output_data, size_t *output_length, struct tm **valid_to) {
11864 isds_error err = IE_SUCCESS;
11865 #if HAVE_LIBCURL
11866 xmlNsPtr isds_ns = NULL;
11867 xmlNodePtr request = NULL;
11868 xmlDocPtr response = NULL;
11869 xmlXPathContextPtr xpath_ctx = NULL;
11870 xmlXPathObjectPtr result = NULL;
11871 char *string = NULL;
11872 const xmlChar *codes[] = {
11873 BAD_CAST "2200",
11874 BAD_CAST "2201",
11875 BAD_CAST "2204",
11876 BAD_CAST "2207",
11877 NULL
11879 const char *meanings[] = {
11880 "Message is bad",
11881 "Message is not original",
11882 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11883 "Time stamp could not been generated in time"
11885 const isds_error errors[] = {
11886 IE_INVAL,
11887 IE_NOTUNIQ,
11888 IE_INVAL,
11889 IE_ISDS,
11891 struct code_map_isds_error map = {
11892 .codes = codes,
11893 .meanings = meanings,
11894 .errors = errors
11896 #endif
11898 if (NULL != output_data) *output_data = NULL;
11899 if (NULL != output_length) *output_length = 0;
11900 if (NULL != valid_to) *valid_to = NULL;
11902 if (NULL == context) return IE_INVALID_CONTEXT;
11903 zfree(context->long_message);
11904 if (NULL == input_data || 0 == input_length) {
11905 isds_log_message(context, _("Empty CMS blob on input"));
11906 return IE_INVAL;
11908 if (NULL == output_data || NULL == output_length) {
11909 isds_log_message(context,
11910 _("NULL pointer provided for output CMS blob"));
11911 return IE_INVAL;
11914 #if HAVE_LIBCURL
11915 /* Check if connection is established
11916 * TODO: This check should be done downstairs. */
11917 if (!context->curl) return IE_CONNECTION_CLOSED;
11920 /* Build Re-signISDSDocument request */
11921 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11922 if (!request) {
11923 isds_log_message(context,
11924 _("Could not build Re-signISDSDocument request"));
11925 return IE_ERROR;
11927 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11928 if(!isds_ns) {
11929 isds_log_message(context, _("Could not create ISDS name space"));
11930 xmlFreeNode(request);
11931 return IE_ERROR;
11933 xmlSetNs(request, isds_ns);
11935 /* Insert Base64 encoded CMS blob */
11936 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11937 input_data, input_length);
11938 if (err) goto leave;
11940 /* Send request to server and process response */
11941 err = send_destroy_request_check_response(context,
11942 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11943 &response, NULL, &map);
11944 if (err) goto leave;
11947 /* Extract re-signed data */
11948 xpath_ctx = xmlXPathNewContext(response);
11949 if (!xpath_ctx) {
11950 err = IE_ERROR;
11951 goto leave;
11953 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11954 err = IE_ERROR;
11955 goto leave;
11957 result = xmlXPathEvalExpression(
11958 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11959 if (!result) {
11960 err = IE_ERROR;
11961 goto leave;
11963 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11964 isds_log_message(context,
11965 _("Missing Re-signISDSDocumentResponse element"));
11966 err = IE_ISDS;
11967 goto leave;
11969 if (result->nodesetval->nodeNr > 1) {
11970 isds_log_message(context,
11971 _("Multiple Re-signISDSDocumentResponse element"));
11972 err = IE_ISDS;
11973 goto leave;
11975 xpath_ctx->node = result->nodesetval->nodeTab[0];
11976 xmlXPathFreeObject(result); result = NULL;
11978 EXTRACT_STRING("isds:dmResultDoc", string);
11979 /* Decode non-empty data */
11980 if (NULL != string && string[0] != '\0') {
11981 *output_length = _isds_b64decode(string, output_data);
11982 if (*output_length == (size_t) -1) {
11983 isds_log_message(context,
11984 _("Error while Base64-decoding re-signed data"));
11985 err = IE_ERROR;
11986 goto leave;
11988 } else {
11989 isds_log_message(context, _("Server did not send re-signed data"));
11990 err = IE_ISDS;
11991 goto leave;
11993 zfree(string);
11995 if (NULL != valid_to) {
11996 /* Get time stamp expiration date */
11997 EXTRACT_STRING("isds:dmValidTo", string);
11998 if (NULL != string) {
11999 *valid_to = calloc(1, sizeof(**valid_to));
12000 if (!*valid_to) {
12001 err = IE_NOMEM;
12002 goto leave;
12004 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
12005 if (err) {
12006 if (err == IE_NOTSUP) {
12007 err = IE_ISDS;
12008 char *string_locale = _isds_utf82locale(string);
12009 isds_printf_message(context,
12010 _("Invalid dmValidTo value: %s"), string_locale);
12011 free(string_locale);
12013 goto leave;
12018 leave:
12019 free(string);
12021 xmlXPathFreeObject(result);
12022 xmlXPathFreeContext(xpath_ctx);
12024 xmlFreeDoc(response);
12025 xmlFreeNode(request);
12026 #else /* not HAVE_LIBCURL */
12027 err = IE_NOTSUP;
12028 #endif
12030 return err;
12033 #undef INSERT_ELEMENT
12034 #undef CHECK_FOR_STRING_LENGTH
12035 #undef INSERT_STRING_ATTRIBUTE
12036 #undef INSERT_ULONGINTNOPTR
12037 #undef INSERT_ULONGINT
12038 #undef INSERT_LONGINT
12039 #undef INSERT_BOOLEAN
12040 #undef INSERT_SCALAR_BOOLEAN
12041 #undef INSERT_STRING
12042 #undef INSERT_STRING_WITH_NS
12043 #undef EXTRACT_STRING_ATTRIBUTE
12044 #undef EXTRACT_ULONGINT
12045 #undef EXTRACT_LONGINT
12046 #undef EXTRACT_BOOLEAN
12047 #undef EXTRACT_STRING
12050 /* Compute hash of message from raw representation and store it into envelope.
12051 * Original hash structure will be destroyed in envelope.
12052 * @context is session context
12053 * @message is message carrying raw XML message blob
12054 * @algorithm is desired hash algorithm to use */
12055 isds_error isds_compute_message_hash(struct isds_ctx *context,
12056 struct isds_message *message, const isds_hash_algorithm algorithm) {
12057 isds_error err = IE_SUCCESS;
12058 const char *nsuri;
12059 void *xml_stream = NULL;
12060 size_t xml_stream_length;
12061 size_t phys_start, phys_end;
12062 char *phys_path = NULL;
12063 struct isds_hash *new_hash = NULL;
12066 if (!context) return IE_INVALID_CONTEXT;
12067 zfree(context->long_message);
12068 if (!message) return IE_INVAL;
12070 if (!message->raw) {
12071 isds_log_message(context,
12072 _("Message does not carry raw representation"));
12073 return IE_INVAL;
12076 switch (message->raw_type) {
12077 case RAWTYPE_INCOMING_MESSAGE:
12078 nsuri = ISDS_NS;
12079 xml_stream = message->raw;
12080 xml_stream_length = message->raw_length;
12081 break;
12083 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
12084 nsuri = SISDS_INCOMING_NS;
12085 xml_stream = message->raw;
12086 xml_stream_length = message->raw_length;
12087 break;
12089 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
12090 nsuri = SISDS_INCOMING_NS;
12091 err = _isds_extract_cms_data(context,
12092 message->raw, message->raw_length,
12093 &xml_stream, &xml_stream_length);
12094 if (err) goto leave;
12095 break;
12097 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
12098 nsuri = SISDS_OUTGOING_NS;
12099 xml_stream = message->raw;
12100 xml_stream_length = message->raw_length;
12101 break;
12103 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
12104 nsuri = SISDS_OUTGOING_NS;
12105 err = _isds_extract_cms_data(context,
12106 message->raw, message->raw_length,
12107 &xml_stream, &xml_stream_length);
12108 if (err) goto leave;
12109 break;
12111 default:
12112 isds_log_message(context, _("Bad raw representation type"));
12113 return IE_INVAL;
12114 break;
12118 /* XXX: Hash is computed from original string representing isds:dmDm
12119 * subtree. That means no encoding, white space, xmlns attributes changes.
12120 * In other words, input for hash can be invalid XML stream. */
12121 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
12122 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
12123 PHYSXML_ELEMENT_SEPARATOR,
12124 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
12125 PHYSXML_ELEMENT_SEPARATOR
12126 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
12127 err = IE_NOMEM;
12128 goto leave;
12130 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
12131 phys_path, &phys_start, &phys_end);
12132 zfree(phys_path);
12133 if (err) {
12134 isds_log_message(context,
12135 _("Substring with isds:dmDM element could not be located "
12136 "in raw message"));
12137 goto leave;
12141 /* Compute hash */
12142 new_hash = calloc(1, sizeof(*new_hash));
12143 if (!new_hash) {
12144 err = IE_NOMEM;
12145 goto leave;
12147 new_hash->algorithm = algorithm;
12148 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
12149 new_hash);
12150 if (err) {
12151 isds_log_message(context, _("Could not compute message hash"));
12152 goto leave;
12155 /* Save computed hash */
12156 if (!message->envelope) {
12157 message->envelope = calloc(1, sizeof(*message->envelope));
12158 if (!message->envelope) {
12159 err = IE_NOMEM;
12160 goto leave;
12163 isds_hash_free(&message->envelope->hash);
12164 message->envelope->hash = new_hash;
12166 leave:
12167 if (err) {
12168 isds_hash_free(&new_hash);
12171 free(phys_path);
12172 if (xml_stream != message->raw) free(xml_stream);
12173 return err;
12177 /* Compare two hashes.
12178 * @h1 is first hash
12179 * @h2 is another hash
12180 * @return
12181 * IE_SUCCESS if hashes equal
12182 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12183 * IE_ENUM if not comparable, but both structures defined
12184 * IE_INVAL if some of the structures are undefined (NULL)
12185 * IE_ERROR if internal error occurs */
12186 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
12187 if (h1 == NULL || h2 == NULL) return IE_INVAL;
12188 if (h1->algorithm != h2->algorithm) return IE_ENUM;
12189 if (h1->length != h2->length) return IE_ERROR;
12190 if (h1->length > 0 && !h1->value) return IE_ERROR;
12191 if (h2->length > 0 && !h2->value) return IE_ERROR;
12193 for (size_t i = 0; i < h1->length; i++) {
12194 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
12195 return IE_NOTEQUAL;
12197 return IE_SUCCESS;
12201 /* Check message has gone through ISDS by comparing message hash stored in
12202 * ISDS and locally computed hash. You must provide message with valid raw
12203 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12204 * This is convenient wrapper for isds_download_message_hash(),
12205 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12206 * @context is session context
12207 * @message is message with valid raw and envelope member; envelope->hash
12208 * member will be changed during function run. Use envelope on heap only.
12209 * @return
12210 * IE_SUCCESS if message originates in ISDS
12211 * IE_NOTEQUAL if message is unknown to ISDS
12212 * other code for other errors */
12213 isds_error isds_verify_message_hash(struct isds_ctx *context,
12214 struct isds_message *message) {
12215 isds_error err = IE_SUCCESS;
12216 struct isds_hash *downloaded_hash = NULL;
12218 if (!context) return IE_INVALID_CONTEXT;
12219 zfree(context->long_message);
12220 if (!message) return IE_INVAL;
12222 if (!message->envelope) {
12223 isds_log_message(context,
12224 _("Given message structure is missing envelope"));
12225 return IE_INVAL;
12227 if (!message->raw) {
12228 isds_log_message(context,
12229 _("Given message structure is missing raw representation"));
12230 return IE_INVAL;
12233 err = isds_download_message_hash(context, message->envelope->dmID,
12234 &downloaded_hash);
12235 if (err) goto leave;
12237 err = isds_compute_message_hash(context, message,
12238 downloaded_hash->algorithm);
12239 if (err) goto leave;
12241 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
12243 leave:
12244 isds_hash_free(&downloaded_hash);
12245 return err;
12249 /* Search for document by document ID in list of documents. IDs are compared
12250 * as UTF-8 string.
12251 * @documents is list of isds_documents
12252 * @id is document identifier
12253 * @return first matching document or NULL. */
12254 const struct isds_document *isds_find_document_by_id(
12255 const struct isds_list *documents, const char *id) {
12256 const struct isds_list *item;
12257 const struct isds_document *document;
12259 for (item = documents; item; item = item->next) {
12260 document = (struct isds_document *) item->data;
12261 if (!document) continue;
12263 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
12264 return document;
12267 return NULL;
12271 /* Normalize @mime_type to be proper MIME type.
12272 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12273 * guess regular MIME type (e.g. "application/pdf").
12274 * @mime_type is UTF-8 encoded MIME type to fix
12275 * @return original @mime_type if no better interpretation exists, or
12276 * constant static UTF-8 encoded string with proper MIME type. */
12277 const char *isds_normalize_mime_type(const char *mime_type) {
12278 if (!mime_type) return NULL;
12280 for (size_t offset = 0;
12281 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
12282 offset += 2) {
12283 if (!xmlStrcasecmp((const xmlChar*) mime_type,
12284 extension_map_mime[offset]))
12285 return (const char *) extension_map_mime[offset + 1];
12288 return mime_type;
12292 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12293 struct isds_message **message);
12294 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12295 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12296 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12297 struct isds_address **address);
12299 int isds_message_free(struct isds_message **message);
12300 int isds_address_free(struct isds_address **address);
12304 /* Makes known all relevant namespaces to given XPath context
12305 * @xpath_ctx is XPath context
12306 * @message_ns selects proper message name space. Unsigned and signed
12307 * messages and delivery info's differ in prefix and URI. */
12308 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
12309 const message_ns_type message_ns) {
12310 const xmlChar *message_namespace = NULL;
12312 if (!xpath_ctx) return IE_ERROR;
12314 switch(message_ns) {
12315 case MESSAGE_NS_1:
12316 message_namespace = BAD_CAST ISDS1_NS; break;
12317 case MESSAGE_NS_UNSIGNED:
12318 message_namespace = BAD_CAST ISDS_NS; break;
12319 case MESSAGE_NS_SIGNED_INCOMING:
12320 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
12321 case MESSAGE_NS_SIGNED_OUTGOING:
12322 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
12323 case MESSAGE_NS_SIGNED_DELIVERY:
12324 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
12325 default:
12326 return IE_ENUM;
12329 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
12330 return IE_ERROR;
12331 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
12332 return IE_ERROR;
12333 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
12334 return IE_ERROR;
12335 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
12336 return IE_ERROR;
12337 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
12338 return IE_ERROR;
12339 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
12340 return IE_ERROR;
12341 return IE_SUCCESS;