7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
13 #include "validator.h"
19 * Allocated in isds_init() and deallocated in isds_cleanup(). */
20 unsigned int log_facilities
;
21 isds_log_level log_level
;
22 isds_log_callback log_callback
;
23 void *log_callback_data
;
24 const char *version_gpgme
= N_("n/a");
25 const char *version_gcrypt
= N_("n/a");
26 const char *version_openssl
= N_("n/a");
27 const char *version_expat
= N_("n/a");
30 /* Base URL of production ISDS instance */
31 const char isds_locator
[] = "https://ws1.mojedatovaschranka.cz/";
32 const char isds_cert_locator
[] = "https://ws1c.mojedatovaschranka.cz/";
33 const char isds_otp_locator
[] = "https://www.mojedatovaschranka.cz/";
35 /* Base URL of production ISDS instance */
36 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
37 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
38 const char isds_otp_testing_locator
[] = "https://www.czebox.cz/";
40 /* Extension to MIME type map */
41 static const xmlChar
*extension_map_mime
[] = {
42 BAD_CAST
"cer", BAD_CAST
"application/x-x509-ca-cert",
43 BAD_CAST
"crt", BAD_CAST
"application/x-x509-ca-cert",
44 BAD_CAST
"der", BAD_CAST
"application/x-x509-ca-cert",
45 BAD_CAST
"doc", BAD_CAST
"application/msword",
46 BAD_CAST
"docx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
47 "wordprocessingml.document",
48 BAD_CAST
"dbf", BAD_CAST
"application/octet-stream",
49 BAD_CAST
"prj", BAD_CAST
"application/octet-stream",
50 BAD_CAST
"qix", BAD_CAST
"application/octet-stream",
51 BAD_CAST
"sbn", BAD_CAST
"application/octet-stream",
52 BAD_CAST
"sbx", BAD_CAST
"application/octet-stream",
53 BAD_CAST
"shp", BAD_CAST
"application/octet-stream",
54 BAD_CAST
"shx", BAD_CAST
"application/octet-stream",
55 BAD_CAST
"dgn", BAD_CAST
"application/octet-stream",
56 BAD_CAST
"dwg", BAD_CAST
"image/vnd.dwg",
57 BAD_CAST
"edi", BAD_CAST
"application/edifact",
58 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.form+xml",
59 BAD_CAST
"gfs", BAD_CAST
"application/xml",
60 BAD_CAST
"gml", BAD_CAST
"application/xml",
61 BAD_CAST
"gif", BAD_CAST
"image/gif",
62 BAD_CAST
"htm", BAD_CAST
"text/html",
63 BAD_CAST
"html", BAD_CAST
"text/html",
64 BAD_CAST
"isdoc", BAD_CAST
"text/isdoc",
65 BAD_CAST
"isdocx", BAD_CAST
"text/isdocx",
66 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
67 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
68 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
69 BAD_CAST
"mpeg", BAD_CAST
"video/mpeg",
70 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
71 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg",
72 BAD_CAST
"mpg", BAD_CAST
"video/mpeg",
73 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
74 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
75 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
76 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
77 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
78 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
79 BAD_CAST
"p7b", BAD_CAST
"application/pkcs7-certificates",
80 BAD_CAST
"p7c", BAD_CAST
"application/pkcs7-mime",
81 BAD_CAST
"p7m", BAD_CAST
"application/pkcs7-mime",
82 BAD_CAST
"p7f", BAD_CAST
"application/pkcs7-signature",
83 BAD_CAST
"p7s", BAD_CAST
"application/pkcs7-signature",
84 BAD_CAST
"pk7", BAD_CAST
"application/pkcs7-mime",
85 BAD_CAST
"png", BAD_CAST
"image/png",
86 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
87 BAD_CAST
"pptx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
88 "presentationml.presentation",
89 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
90 BAD_CAST
"tif", BAD_CAST
"image/tiff",
91 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
92 BAD_CAST
"tsr", BAD_CAST
"application/timestamp-reply",
93 BAD_CAST
"tst", BAD_CAST
"application/timestamp-reply",
94 BAD_CAST
"txt", BAD_CAST
"text/plain",
95 BAD_CAST
"wav", BAD_CAST
"audio/wav",
96 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
97 BAD_CAST
"xlsx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
98 "spreadsheetml.sheet",
99 BAD_CAST
"xml", BAD_CAST
"application/xml",
100 BAD_CAST
"xsd", BAD_CAST
"application/xml",
101 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.form-xml-zip"
104 /* Structure type to hold conversion table from status code to isds_error and
106 struct code_map_isds_error
{
107 const xmlChar
**codes
; /* NULL terminated array of status codes */
108 const char **meanings
; /* Mapping to non-localized long messages */
109 const isds_error
*errors
; /* Mapping to isds_error code */
112 /* Deallocate structure isds_pki_credentials and NULL it.
113 * Pass-phrase is discarded.
114 * @pki credentials to to free */
115 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
116 if(!pki
|| !*pki
) return;
118 free((*pki
)->engine
);
119 free((*pki
)->certificate
);
122 if ((*pki
)->passphrase
) {
123 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
124 free((*pki
)->passphrase
);
131 /* Free isds_list with all member data.
132 * @list list to free, on return will be NULL */
133 void isds_list_free(struct isds_list
**list
) {
134 struct isds_list
*item
, *next_item
;
136 if (!list
|| !*list
) return;
138 for(item
= *list
; item
; item
= next_item
) {
139 if (item
->destructor
) (item
->destructor
)(&(item
->data
));
140 next_item
= item
->next
;
148 /* Deallocate structure isds_hash and NULL it.
149 * @hash hash to to free */
150 void isds_hash_free(struct isds_hash
**hash
) {
151 if(!hash
|| !*hash
) return;
152 free((*hash
)->value
);
157 /* Deallocate structure isds_PersonName recursively and NULL it */
158 void isds_PersonName_free(struct isds_PersonName
**person_name
) {
159 if (!person_name
|| !*person_name
) return;
161 free((*person_name
)->pnFirstName
);
162 free((*person_name
)->pnMiddleName
);
163 free((*person_name
)->pnLastName
);
164 free((*person_name
)->pnLastNameAtBirth
);
171 /* Deallocate structure isds_BirthInfo recursively and NULL it */
172 void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
173 if (!birth_info
|| !*birth_info
) return;
175 free((*birth_info
)->biDate
);
176 free((*birth_info
)->biCity
);
177 free((*birth_info
)->biCounty
);
178 free((*birth_info
)->biState
);
185 /* Deallocate structure isds_Address recursively and NULL it */
186 void isds_Address_free(struct isds_Address
**address
) {
187 if (!address
|| !*address
) return;
189 free((*address
)->adCity
);
190 free((*address
)->adStreet
);
191 free((*address
)->adNumberInStreet
);
192 free((*address
)->adNumberInMunicipality
);
193 free((*address
)->adZipCode
);
194 free((*address
)->adState
);
201 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
202 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
203 if (!db_owner_info
|| !*db_owner_info
) return;
205 free((*db_owner_info
)->dbID
);
206 free((*db_owner_info
)->dbType
);
207 free((*db_owner_info
)->ic
);
208 isds_PersonName_free(&((*db_owner_info
)->personName
));
209 free((*db_owner_info
)->firmName
);
210 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
211 isds_Address_free(&((*db_owner_info
)->address
));
212 free((*db_owner_info
)->nationality
);
213 free((*db_owner_info
)->email
);
214 free((*db_owner_info
)->telNumber
);
215 free((*db_owner_info
)->identifier
);
216 free((*db_owner_info
)->registryCode
);
217 free((*db_owner_info
)->dbState
);
218 free((*db_owner_info
)->dbEffectiveOVM
);
219 free((*db_owner_info
)->dbOpenAddressing
);
221 free(*db_owner_info
);
222 *db_owner_info
= NULL
;
225 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
226 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
227 if (!db_user_info
|| !*db_user_info
) return;
229 free((*db_user_info
)->userID
);
230 free((*db_user_info
)->userType
);
231 free((*db_user_info
)->userPrivils
);
232 isds_PersonName_free(&((*db_user_info
)->personName
));
233 isds_Address_free(&((*db_user_info
)->address
));
234 free((*db_user_info
)->biDate
);
235 free((*db_user_info
)->ic
);
236 free((*db_user_info
)->firmName
);
237 free((*db_user_info
)->caStreet
);
238 free((*db_user_info
)->caCity
);
239 free((*db_user_info
)->caZipCode
);
240 free((*db_user_info
)->caState
);
242 zfree(*db_user_info
);
246 /* Deallocate struct isds_event recursively and NULL it */
247 void isds_event_free(struct isds_event
**event
) {
248 if (!event
|| !*event
) return;
250 free((*event
)->time
);
251 free((*event
)->type
);
252 free((*event
)->description
);
257 /* Deallocate struct isds_envelope recursively and NULL it */
258 void isds_envelope_free(struct isds_envelope
**envelope
) {
259 if (!envelope
|| !*envelope
) return;
261 free((*envelope
)->dmID
);
262 free((*envelope
)->dbIDSender
);
263 free((*envelope
)->dmSender
);
264 free((*envelope
)->dmSenderAddress
);
265 free((*envelope
)->dmSenderType
);
266 free((*envelope
)->dmRecipient
);
267 free((*envelope
)->dmRecipientAddress
);
268 free((*envelope
)->dmAmbiguousRecipient
);
269 free((*envelope
)->dmType
);
271 free((*envelope
)->dmOrdinal
);
272 free((*envelope
)->dmMessageStatus
);
273 free((*envelope
)->dmDeliveryTime
);
274 free((*envelope
)->dmAcceptanceTime
);
275 isds_hash_free(&(*envelope
)->hash
);
276 free((*envelope
)->timestamp
);
277 isds_list_free(&(*envelope
)->events
);
279 free((*envelope
)->dmSenderOrgUnit
);
280 free((*envelope
)->dmSenderOrgUnitNum
);
281 free((*envelope
)->dbIDRecipient
);
282 free((*envelope
)->dmRecipientOrgUnit
);
283 free((*envelope
)->dmRecipientOrgUnitNum
);
284 free((*envelope
)->dmToHands
);
285 free((*envelope
)->dmAnnotation
);
286 free((*envelope
)->dmRecipientRefNumber
);
287 free((*envelope
)->dmSenderRefNumber
);
288 free((*envelope
)->dmRecipientIdent
);
289 free((*envelope
)->dmSenderIdent
);
291 free((*envelope
)->dmLegalTitleLaw
);
292 free((*envelope
)->dmLegalTitleYear
);
293 free((*envelope
)->dmLegalTitleSect
);
294 free((*envelope
)->dmLegalTitlePar
);
295 free((*envelope
)->dmLegalTitlePoint
);
297 free((*envelope
)->dmPersonalDelivery
);
298 free((*envelope
)->dmAllowSubstDelivery
);
300 free((*envelope
)->dmOVM
);
301 free((*envelope
)->dmPublishOwnID
);
308 /* Deallocate struct isds_message recursively and NULL it */
309 void isds_message_free(struct isds_message
**message
) {
310 if (!message
|| !*message
) return;
312 free((*message
)->raw
);
313 isds_envelope_free(&((*message
)->envelope
));
314 isds_list_free(&((*message
)->documents
));
315 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
322 /* Deallocate struct isds_document recursively and NULL it */
323 void isds_document_free(struct isds_document
**document
) {
324 if (!document
|| !*document
) return;
326 if (!(*document
)->is_xml
) {
327 free((*document
)->data
);
329 free((*document
)->dmMimeType
);
330 free((*document
)->dmFileGuid
);
331 free((*document
)->dmUpFileGuid
);
332 free((*document
)->dmFileDescr
);
333 free((*document
)->dmFormat
);
340 /* Deallocate struct isds_message_copy recursively and NULL it */
341 void isds_message_copy_free(struct isds_message_copy
**copy
) {
342 if (!copy
|| !*copy
) return;
344 free((*copy
)->dbIDRecipient
);
345 free((*copy
)->dmRecipientOrgUnit
);
346 free((*copy
)->dmRecipientOrgUnitNum
);
347 free((*copy
)->dmToHands
);
349 free((*copy
)->dmStatus
);
356 /* Deallocate struct isds_message_status_change recursively and NULL it */
357 void isds_message_status_change_free(
358 struct isds_message_status_change
**message_status_change
) {
359 if (!message_status_change
|| !*message_status_change
) return;
361 free((*message_status_change
)->dmID
);
362 free((*message_status_change
)->time
);
363 free((*message_status_change
)->dmMessageStatus
);
365 zfree(*message_status_change
);
369 /* Deallocate struct isds_approval recursively and NULL it */
370 void isds_approval_free(struct isds_approval
**approval
) {
371 if (!approval
|| !*approval
) return;
373 free((*approval
)->refference
);
379 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
380 * The email string is deallocated too. */
381 void isds_credentials_delivery_free(
382 struct isds_credentials_delivery
**credentials_delivery
) {
383 if (!credentials_delivery
|| !*credentials_delivery
) return;
385 free((*credentials_delivery
)->email
);
386 free((*credentials_delivery
)->token
);
387 free((*credentials_delivery
)->new_user_name
);
389 zfree(*credentials_delivery
);
393 /* Deallocate struct isds_commercial_permission recursively and NULL it */
394 void isds_commercial_permission_free(
395 struct isds_commercial_permission
**permission
) {
396 if (NULL
== permission
|| NULL
== *permission
) return;
398 free((*permission
)->recipient
);
399 free((*permission
)->payer
);
400 free((*permission
)->expiration
);
401 free((*permission
)->count
);
402 free((*permission
)->reply_identifier
);
408 /* Deallocate struct isds_credit_event recursively and NULL it */
409 void isds_credit_event_free(struct isds_credit_event
**event
) {
410 if (NULL
== event
|| NULL
== *event
) return;
412 free((*event
)->time
);
413 switch ((*event
)->type
) {
414 case ISDS_CREDIT_CHARGED
:
415 free((*event
)->details
.charged
.transaction
);
417 case ISDS_CREDIT_DISCHARGED
:
418 free((*event
)->details
.discharged
.transaction
);
420 case ISDS_CREDIT_MESSAGE_SENT
:
421 free((*event
)->details
.message_sent
.recipient
);
422 free((*event
)->details
.message_sent
.message_id
);
424 case ISDS_CREDIT_STORAGE_SET
:
425 free((*event
)->details
.storage_set
.new_valid_from
);
426 free((*event
)->details
.storage_set
.new_valid_to
);
427 free((*event
)->details
.storage_set
.old_capacity
);
428 free((*event
)->details
.storage_set
.old_valid_from
);
429 free((*event
)->details
.storage_set
.old_valid_to
);
430 free((*event
)->details
.storage_set
.initiator
);
432 case ISDS_CREDIT_EXPIRED
:
440 /* Deallocate struct isds_fulltext_result recursively and NULL it */
441 void isds_fulltext_result_free(
442 struct isds_fulltext_result
**result
) {
443 if (NULL
== result
|| NULL
== *result
) return;
445 free((*result
)->dbID
);
446 free((*result
)->name
);
447 isds_list_free(&((*result
)->name_match_start
));
448 isds_list_free(&((*result
)->name_match_end
));
449 free((*result
)->address
);
450 isds_list_free(&((*result
)->address_match_start
));
451 isds_list_free(&((*result
)->address_match_end
));
453 free((*result
)->biDate
);
459 /* *DUP_OR_ERROR macros needs error label */
460 #define STRDUP_OR_ERROR(new, template) { \
464 (new) = strdup(template); \
465 if (!new) goto error; \
469 #define FLATDUP_OR_ERROR(new, template) { \
473 (new) = malloc(sizeof(*(new))); \
474 if (!new) goto error; \
475 memcpy((new), (template), sizeof(*(template))); \
479 /* Copy structure isds_pki_credentials recursively. */
480 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
481 const struct isds_pki_credentials
*template) {
482 struct isds_pki_credentials
*new = NULL
;
484 if(!template) return NULL
;
486 new = calloc(1, sizeof(*new));
487 if (!new) return NULL
;
489 STRDUP_OR_ERROR(new->engine
, template->engine
);
490 new->certificate_format
= template->certificate_format
;
491 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
492 new->key_format
= template->key_format
;
493 STRDUP_OR_ERROR(new->key
, template->key
);
494 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
499 isds_pki_credentials_free(&new);
504 /* Copy structure isds_PersonName recursively */
505 struct isds_PersonName
*isds_PersonName_duplicate(
506 const struct isds_PersonName
*src
) {
507 struct isds_PersonName
*new = NULL
;
509 if (!src
) return NULL
;
511 new = calloc(1, sizeof(*new));
512 if (!new) return NULL
;
514 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
515 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
516 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
517 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
522 isds_PersonName_free(&new);
527 /* Copy structure isds_BirthInfo recursively */
528 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
529 const struct isds_BirthInfo
*template) {
530 struct isds_BirthInfo
*new = NULL
;
532 if (!template) return NULL
;
534 new = calloc(1, sizeof(*new));
535 if (!new) return NULL
;
537 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
538 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
539 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
540 STRDUP_OR_ERROR(new->biState
, template->biState
);
545 isds_BirthInfo_free(&new);
550 /* Copy structure isds_Address recursively */
551 struct isds_Address
*isds_Address_duplicate(
552 const struct isds_Address
*src
) {
553 struct isds_Address
*new = NULL
;
555 if (!src
) return NULL
;
557 new = calloc(1, sizeof(*new));
558 if (!new) return NULL
;
560 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
561 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
562 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
563 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
564 src
->adNumberInMunicipality
);
565 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
566 STRDUP_OR_ERROR(new->adState
, src
->adState
);
571 isds_Address_free(&new);
576 /* Copy structure isds_DbOwnerInfo recursively */
577 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
578 const struct isds_DbOwnerInfo
*src
) {
579 struct isds_DbOwnerInfo
*new = NULL
;
580 if (!src
) return NULL
;
582 new = calloc(1, sizeof(*new));
583 if (!new) return NULL
;
585 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
586 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
587 STRDUP_OR_ERROR(new->ic
, src
->ic
);
589 if (src
->personName
) {
590 if (!(new->personName
=
591 isds_PersonName_duplicate(src
->personName
)))
595 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
597 if (src
->birthInfo
) {
598 if (!(new->birthInfo
=
599 isds_BirthInfo_duplicate(src
->birthInfo
)))
604 if (!(new->address
= isds_Address_duplicate(src
->address
)))
608 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
609 STRDUP_OR_ERROR(new->email
, src
->email
);
610 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
611 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
612 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
613 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
614 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
615 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
620 isds_DbOwnerInfo_free(&new);
625 /* Copy structure isds_DbUserInfo recursively */
626 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
627 const struct isds_DbUserInfo
*src
) {
628 struct isds_DbUserInfo
*new = NULL
;
629 if (!src
) return NULL
;
631 new = calloc(1, sizeof(*new));
632 if (!new) return NULL
;
634 STRDUP_OR_ERROR(new->userID
, src
->userID
);
635 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
636 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
638 if (src
->personName
) {
639 if (!(new->personName
=
640 isds_PersonName_duplicate(src
->personName
)))
645 if (!(new->address
= isds_Address_duplicate(src
->address
)))
649 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
650 STRDUP_OR_ERROR(new->ic
, src
->ic
);
651 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
652 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
653 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
654 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
655 STRDUP_OR_ERROR(new->caState
, src
->caState
);
660 isds_DbUserInfo_free(&new);
664 #undef FLATDUP_OR_ERROR
665 #undef STRDUP_OR_ERROR
668 /* Logs libxml2 errors. Should be registered to libxml2 library.
669 * @ctx is unused currently
670 * @msg is printf-like formated message from libxml2 (UTF-8?)
671 * @... are variadic arguments for @msg */
672 static void log_xml(void *ctx
, const char *msg
, ...) {
676 /* Silent warning for unused function argument.
677 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
683 isds_vasprintf(&text
, msg
, ap
);
687 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
692 /* Initialize ISDS library.
693 * Global function, must be called before other functions.
694 * If it fails you can not use ISDS library and must call isds_cleanup() to
695 * free partially initialized global variables. */
696 isds_error
isds_init(void) {
697 /* NULL global variables */
698 log_facilities
= ILF_ALL
;
699 log_level
= ILL_WARNING
;
701 log_callback_data
= NULL
;
704 /* Initialize gettext */
705 bindtextdomain(PACKAGE
, LOCALEDIR
);
709 /* Initialize CURL */
710 if (curl_global_init(CURL_GLOBAL_ALL
)) {
711 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
714 #endif /* HAVE_LIBCURL */
716 /* Initialise cryptographic back-ends. */
717 if (IE_SUCCESS
!= _isds_init_crypto()) {
718 isds_log(ILF_ISDS
, ILL_CRIT
,
719 _("Initialization of cryptographic back-end failed\n"));
723 /* This can _exit() current program. Find not so assertive check. */
725 xmlSetGenericErrorFunc(NULL
, log_xml
);
728 if (_isds_init_expat(&version_expat
)) {
729 isds_log(ILF_ISDS
, ILL_CRIT
,
730 _("expat library initialization failed\n"));
734 /* Allocate global variables */
741 /* Deinitialize ISDS library.
742 * Global function, must be called as last library function. */
743 isds_error
isds_cleanup(void) {
749 curl_global_cleanup();
756 /* Return version string of this library. Version of dependencies can be
757 * embedded. Do no try to parse it. You must free it. */
758 char *isds_version(void) {
761 isds_asprintf(&buffer
,
763 # ifndef USE_OPENSSL_BACKEND
764 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
766 _("%s (%s, %s, %s, libxml2 %s)"),
769 # ifndef USE_OPENSSL_BACKEND
770 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
772 _("%s (%s, %s, libxml2 %s)"),
779 #ifndef USE_OPENSSL_BACKEND
780 version_gpgme
, version_gcrypt
,
784 version_expat
, xmlParserVersion
);
789 /* Return text description of ISDS error */
790 const char *isds_strerror(const isds_error error
) {
793 return(_("Success")); break;
795 return(_("Unspecified error")); break;
797 return(_("Not supported")); break;
799 return(_("Invalid value")); break;
800 case IE_INVALID_CONTEXT
:
801 return(_("Invalid context")); break;
802 case IE_NOT_LOGGED_IN
:
803 return(_("Not logged in")); break;
804 case IE_CONNECTION_CLOSED
:
805 return(_("Connection closed")); break;
807 return(_("Timed out")); break;
809 return(_("Not exist")); break;
811 return(_("Out of memory")); break;
813 return(_("Network problem")); break;
815 return(_("HTTP problem")); break;
817 return(_("SOAP problem")); break;
819 return(_("XML problem")); break;
821 return(_("ISDS server problem")); break;
823 return(_("Invalid enum value")); break;
825 return(_("Invalid date value")); break;
827 return(_("Too big")); break;
829 return(_("Too small")); break;
831 return(_("Value not unique")); break;
833 return(_("Values not equal")); break;
834 case IE_PARTIAL_SUCCESS
:
835 return(_("Some suboperations failed")); break;
837 return(_("Operation aborted")); break;
839 return(_("Security problem")); break;
841 return(_("Unknown error"));
846 /* Create ISDS context.
847 * Each context can be used for different sessions to (possibly) different
848 * ISDS server with different credentials. */
849 struct isds_ctx
*isds_ctx_create(void) {
850 struct isds_ctx
*context
;
851 context
= malloc(sizeof(*context
));
852 if (context
) memset(context
, 0, sizeof(*context
));
857 /* Close possibly opened connection to Czech POINT document deposit without
858 * resetting long_message buffer.
859 * XXX: Do not use czp_close_connection() if you do not want to destroy log
861 * @context is Czech POINT session context. */
862 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
863 if (!context
) return IE_INVALID_CONTEXT
;
864 _isds_close_connection(context
);
869 /* Discard credentials.
870 * @context is ISDS context
871 * @discard_saved_username is true for removing saved username, false for
873 * Only that. It does not cause log out, connection close or similar. */
874 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
875 _Bool discard_saved_username
) {
876 if(!context
) return IE_INVALID_CONTEXT
;
878 if (context
->username
) {
879 memset(context
->username
, 0, strlen(context
->username
));
880 zfree(context
->username
);
882 if (context
->password
) {
883 memset(context
->password
, 0, strlen(context
->password
));
884 zfree(context
->password
);
886 isds_pki_credentials_free(&context
->pki_credentials
);
887 if (discard_saved_username
&& context
->saved_username
) {
888 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
889 zfree(context
->saved_username
);
894 #endif /* HAVE_LIBCURL */
897 /* Destroy ISDS context and free memory.
898 * @context will be NULLed on success. */
899 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
900 if (!context
|| !*context
) {
901 return IE_INVALID_CONTEXT
;
905 /* Discard credentials and close connection */
906 switch ((*context
)->type
) {
907 case CTX_TYPE_NONE
: break;
908 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
910 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
911 czp_do_close_connection(*context
); break;
915 _isds_discard_credentials(*context
, 1);
917 /* Free other structures */
918 free((*context
)->url
);
919 free((*context
)->tls_verify_server
);
920 free((*context
)->tls_ca_file
);
921 free((*context
)->tls_ca_dir
);
922 free((*context
)->tls_crl_file
);
923 #endif /* HAVE_LIBCURL */
924 free((*context
)->long_message
);
932 /* Return long message text produced by library function, e.g. detailed error
933 * message. Returned pointer is only valid until new library function is
934 * called for the same context. Could be NULL, especially if NULL context is
935 * supplied. Return string is locale encoded. */
936 char *isds_long_message(const struct isds_ctx
*context
) {
937 if (!context
) return NULL
;
938 return context
->long_message
;
942 /* Stores message into context' long_message buffer.
943 * Application can pick the message up using isds_long_message().
944 * NULL @message truncates the buffer but does not deallocate it.
945 * @message is coded in locale encoding */
946 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
947 const char *message
) {
951 if (!context
) return IE_INVALID_CONTEXT
;
953 /* FIXME: Check for integer overflow */
954 length
= 1 + ((message
) ? strlen(message
) : 0);
955 buffer
= realloc(context
->long_message
, length
);
956 if (!buffer
) return IE_NOMEM
;
959 strcpy(buffer
, message
);
963 context
->long_message
= buffer
;
968 /* Appends message into context' long_message buffer.
969 * Application can pick the message up using isds_long_message().
970 * NULL message has void effect. */
971 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
972 const char *message
) {
974 size_t old_length
, length
;
976 if (!context
) return IE_INVALID_CONTEXT
;
977 if (!message
) return IE_SUCCESS
;
978 if (!context
->long_message
)
979 return isds_log_message(context
, message
);
981 old_length
= strlen(context
->long_message
);
982 /* FIXME: Check for integer overflow */
983 length
= 1 + old_length
+ strlen(message
);
984 buffer
= realloc(context
->long_message
, length
);
985 if (!buffer
) return IE_NOMEM
;
987 strcpy(buffer
+ old_length
, message
);
989 context
->long_message
= buffer
;
994 /* Stores formatted message into context' long_message buffer.
995 * Application can pick the message up using isds_long_message(). */
996 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
997 const char *format
, ...) {
1001 if (!context
) return IE_INVALID_CONTEXT
;
1002 va_start(ap
, format
);
1003 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
1006 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
1011 * @facilities is bit mask of isds_log_facility values,
1012 * @level is verbosity level. */
1013 void isds_set_logging(const unsigned int facilities
,
1014 const isds_log_level level
) {
1015 log_facilities
= facilities
;
1020 /* Register callback function libisds calls when new global log message is
1021 * produced by library. Library logs to stderr by default.
1022 * @callback is function provided by application libisds will call. See type
1023 * definition for @callback argument explanation. Pass NULL to revert logging to
1024 * default behaviour.
1025 * @data is application specific data @callback gets as last argument */
1026 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1027 log_callback
= callback
;
1028 log_callback_data
= data
;
1032 /* Log @message in class @facility with log @level into global log. @message
1033 * is printf(3) formatting string, variadic arguments may be necessary.
1034 * For debugging purposes. */
1035 _hidden isds_error
isds_log(const isds_log_facility facility
,
1036 const isds_log_level level
, const char *message
, ...) {
1038 char *buffer
= NULL
;
1041 if (level
> log_level
) return IE_SUCCESS
;
1042 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1043 if (!message
) return IE_INVAL
;
1046 /* Pass message to application supplied callback function */
1047 va_start(ap
, message
);
1048 length
= isds_vasprintf(&buffer
, message
, ap
);
1055 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1059 /* Default: Log it to stderr */
1060 va_start(ap
, message
);
1061 vfprintf(stderr
, message
, ap
);
1063 /* Line buffered printf is default.
1071 /* Set timeout in milliseconds for each network job like connecting to server
1072 * or sending message. Use 0 to disable timeout limits. */
1073 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1074 const unsigned int timeout
) {
1075 if (!context
) return IE_INVALID_CONTEXT
;
1076 zfree(context
->long_message
);
1079 context
->timeout
= timeout
;
1081 if (context
->curl
) {
1084 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1086 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1087 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1090 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1091 context
->timeout
/ 1000);
1092 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1093 if (curl_err
) return IE_ERROR
;
1097 #else /* not HAVE_LIBCURL */
1103 /* Register callback function libisds calls periodically during HTTP data
1105 * @context is session context
1106 * @callback is function provided by application libisds will call. See type
1107 * definition for @callback argument explanation.
1108 * @data is application specific data @callback gets as last argument */
1109 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1110 isds_progress_callback callback
, void *data
) {
1111 if (!context
) return IE_INVALID_CONTEXT
;
1112 zfree(context
->long_message
);
1115 context
->progress_callback
= callback
;
1116 context
->progress_callback_data
= data
;
1119 #else /* not HAVE_LIBCURL */
1125 /* Change context settings.
1126 * @context is context which setting will be applied to
1127 * @option is name of option. It determines the type of last argument. See
1128 * isds_option definition for more info.
1129 * @... is value of new setting. Type is determined by @option
1131 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1133 isds_error err
= IE_SUCCESS
;
1136 char *pointer
, *string
;
1139 if (!context
) return IE_INVALID_CONTEXT
;
1140 zfree(context
->long_message
);
1142 va_start(ap
, option
);
1144 #define REPLACE_VA_BOOLEAN(destination) { \
1145 if (!(destination)) { \
1146 (destination) = malloc(sizeof(*(destination))); \
1147 if (!(destination)) { \
1148 err = IE_NOMEM; goto leave; \
1151 *(destination) = (_Bool) !!va_arg(ap, int); \
1154 #define REPLACE_VA_STRING(destination) { \
1155 string = va_arg(ap, char *); \
1157 pointer = realloc((destination), 1 + strlen(string)); \
1158 if (!pointer) { err = IE_NOMEM; goto leave; } \
1159 strcpy(pointer, string); \
1160 (destination) = pointer; \
1162 free(destination); \
1163 (destination) = NULL; \
1168 case IOPT_TLS_VERIFY_SERVER
:
1170 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1172 err
= IE_NOTSUP
; goto leave
;
1175 case IOPT_TLS_CA_FILE
:
1177 REPLACE_VA_STRING(context
->tls_ca_file
);
1179 err
= IE_NOTSUP
; goto leave
;
1182 case IOPT_TLS_CA_DIRECTORY
:
1184 REPLACE_VA_STRING(context
->tls_ca_dir
);
1186 err
= IE_NOTSUP
; goto leave
;
1189 case IOPT_TLS_CRL_FILE
:
1191 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1192 REPLACE_VA_STRING(context
->tls_crl_file
);
1194 isds_log_message(context
,
1195 _("Curl library does not support CRL definition"));
1197 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1199 err
= IE_NOTSUP
; goto leave
;
1200 #endif /* not HAVE_LIBCURL */
1202 case IOPT_NORMALIZE_MIME_TYPE
:
1203 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1207 err
= IE_ENUM
; goto leave
;
1210 #undef REPLACE_VA_STRING
1211 #undef REPLACE_VA_BOOLEAN
1220 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1221 * Destination for NULL argument will not be touched.
1222 * Destination pointers must be freed before calling this function.
1223 * If @username is @context->saved_username, the saved_username will not be
1224 * replaced. The saved_username is clobbered only if context has set otp
1226 * Return IE_SUCCESS on success. */
1227 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1228 const char *username
, const char *password
,
1229 const struct isds_pki_credentials
*pki_credentials
) {
1230 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1232 /* FIXME: mlock password
1233 * (I have a library) */
1236 context
->username
= strdup(username
);
1237 if (context
->otp
&& context
->saved_username
!= username
)
1238 context
->saved_username
= strdup(username
);
1241 if (NULL
== context
->otp_credentials
)
1242 context
->password
= strdup(password
);
1244 context
->password
= _isds_astrcat(password
,
1245 context
->otp_credentials
->otp_code
);
1247 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1249 if ((NULL
!= username
&& NULL
== context
->username
) ||
1250 (NULL
!= password
&& NULL
== context
->password
) ||
1251 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1252 (context
->otp
&& NULL
!= context
->username
&&
1253 NULL
== context
->saved_username
)) {
1262 /* Connect and log into ISDS server.
1263 * All required arguments will be copied, you do not have to keep them after
1265 * ISDS supports six different authentication methods. Exact method is
1266 * selected on @username, @password, @pki_credentials, and @otp arguments:
1267 * - If @pki_credentials == NULL, @username and @password must be supplied
1269 * - If @otp == NULL, simple authentication by username and password will
1271 * - If @otp != NULL, authentication by username and password and OTP
1273 * - If @pki_credentials != NULL, then
1274 * - If @username == NULL, only certificate will be used
1275 * - If @username != NULL, then
1276 * - If @password == NULL, then certificate will be used and
1277 * @username shifts meaning to box ID. This is used for hosted
1279 * - Otherwise all three arguments will be used.
1280 * Please note, that different cases require different certificate type
1281 * (system qualified one or commercial non qualified one). This library
1282 * does not check such political issues. Please see ISDS Specification
1284 * @url is base address of ISDS web service. Pass extern isds_locator
1285 * variable to use production ISDS instance without client certificate
1286 * authentication (or extern isds_cert_locator with client certificate
1287 * authentication or extern isds_otp_locators with OTP authentication).
1288 * Passing NULL has the same effect, autoselection between isds_locator,
1289 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1290 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1291 * isds_otp_testing_locator) variable to select testing instance.
1292 * @username is user name of ISDS user or box ID
1293 * @password is user's secret password
1294 * @pki_credentials defines public key cryptographic material to use in client
1296 * @otp selects one-time password authentication method to use, defines OTP
1297 * code (if known) and returns fine grade resolution of OTP procedure.
1299 * IE_SUCCESS if authentication succeeds
1300 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1301 * requested, fine grade reason will be set into @otp->resolution. Error
1302 * message from server can be obtained by isds_long_message() call.
1303 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1304 * server has sent OTP code through side channel. Application is expected to
1305 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1306 * this call to complete second phase of TOTP authentication;
1307 * or other appropriate error. */
1308 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1309 const char *username
, const char *password
,
1310 const struct isds_pki_credentials
*pki_credentials
,
1311 struct isds_otp
*otp
) {
1313 isds_error err
= IE_NOT_LOGGED_IN
;
1314 isds_error soap_err
;
1315 xmlNsPtr isds_ns
= NULL
;
1316 xmlNodePtr request
= NULL
;
1317 #endif /* HAVE_LIBCURL */
1319 if (!context
) return IE_INVALID_CONTEXT
;
1320 zfree(context
->long_message
);
1323 /* Close connection if already logged in */
1324 if (context
->curl
) {
1325 _isds_close_connection(context
);
1328 /* Store configuration */
1329 context
->type
= CTX_TYPE_ISDS
;
1330 zfree(context
->url
);
1332 /* Mangle base URI according to requested authentication method */
1333 if (NULL
== pki_credentials
) {
1334 isds_log(ILF_SEC
, ILL_INFO
,
1335 _("Selected authentication method: no certificate, "
1336 "username and password\n"));
1337 if (!username
|| !password
) {
1338 isds_log_message(context
,
1339 _("Both username and password must be supplied"));
1342 context
->otp_credentials
= otp
;
1343 context
->otp
= (NULL
!= context
->otp_credentials
);
1345 if (!context
->otp
) {
1346 /* Default locator is official system (without certificate or
1348 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1350 const char *authenticator_uri
= NULL
;
1351 if (!url
) url
= isds_otp_locator
;
1352 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1353 switch (context
->otp_credentials
->method
) {
1355 isds_log(ILF_SEC
, ILL_INFO
,
1356 _("Selected authentication method: "
1357 "HMAC-based one-time password\n"));
1359 "%sas/processLogin?type=hotp&uri=%sapps/";
1362 isds_log(ILF_SEC
, ILL_INFO
,
1363 _("Selected authentication method: "
1364 "Time-based one-time password\n"));
1365 if (context
->otp_credentials
->otp_code
== NULL
) {
1366 isds_log(ILF_SEC
, ILL_INFO
,
1367 _("OTP code has not been provided by "
1368 "application, requesting server for "
1371 "%sas/processLogin?type=totp&sendSms=true&"
1374 isds_log(ILF_SEC
, ILL_INFO
,
1375 _("OTP code has been provided by "
1376 "application, not requesting server "
1379 "%sas/processLogin?type=totp&"
1384 isds_log_message(context
,
1385 _("Unknown one-time password authentication "
1386 "method requested by application"));
1389 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
, url
))
1393 /* Default locator is official system (with client certificate) */
1395 context
->otp_credentials
= NULL
;
1396 if (!url
) url
= isds_cert_locator
;
1399 isds_log(ILF_SEC
, ILL_INFO
,
1400 _("Selected authentication method: system certificate, "
1401 "no username and no password\n"));
1403 context
->url
= _isds_astrcat(url
, "cert/");
1406 isds_log(ILF_SEC
, ILL_INFO
,
1407 _("Selected authentication method: system certificate, "
1408 "box ID and no password\n"));
1409 context
->url
= _isds_astrcat(url
, "hspis/");
1411 isds_log(ILF_SEC
, ILL_INFO
,
1412 _("Selected authentication method: commercial "
1413 "certificate, username and password\n"));
1414 context
->url
= _isds_astrcat(url
, "certds/");
1418 if (!(context
->url
))
1421 /* Prepare CURL handle */
1422 context
->curl
= curl_easy_init();
1423 if (!(context
->curl
))
1426 /* Build log-in request */
1427 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1429 isds_log_message(context
, _("Could not build ISDS log-in request"));
1432 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1434 isds_log_message(context
, _("Could not create ISDS name space"));
1435 xmlFreeNode(request
);
1438 xmlSetNs(request
, isds_ns
);
1440 /* Store credentials */
1441 _isds_discard_credentials(context
, 1);
1442 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1443 _isds_discard_credentials(context
, 1);
1444 xmlFreeNode(request
);
1448 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1451 /* XXX: ISDS documentation does not specify response body for
1452 * DummyOperation request. However real server sends back
1453 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1454 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1455 * SOAP body content, e.g. the dmStatus element. */
1457 /* Send log-in request */
1458 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1461 /* Revert context URL from OTP authentication service URL to OTP web
1462 * service base URL for subsequent calls. Potenial isds_login() retry
1463 * will re-set context URL again. */
1464 zfree(context
->url
);
1465 context
->url
= _isds_astrcat(url
, "apps/");
1466 if (context
->url
== NULL
) {
1467 soap_err
= IE_NOMEM
;
1469 /* Detach pointer to OTP credentials from context */
1470 context
->otp_credentials
= NULL
;
1473 /* Remove credentials */
1474 _isds_discard_credentials(context
, 0);
1476 /* Destroy log-in request */
1477 xmlFreeNode(request
);
1480 _isds_close_connection(context
);
1484 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1485 * authentication succeeded if soap_err == IE_SUCCESS */
1489 isds_log(ILF_ISDS
, ILL_DEBUG
,
1490 _("User %s has been logged into server %s successfully\n"),
1493 #else /* not HAVE_LIBCURL */
1499 /* Log out from ISDS server discards credentials and connection configuration. */
1500 isds_error
isds_logout(struct isds_ctx
*context
) {
1501 if (!context
) return IE_INVALID_CONTEXT
;
1502 zfree(context
->long_message
);
1505 if (context
->curl
) {
1507 isds_error err
= _isds_invalidate_otp_cookie(context
);
1508 if (err
) return err
;
1511 /* Close connection */
1512 _isds_close_connection(context
);
1514 /* Discard credentials for sure. They should not survive isds_login(),
1515 * even successful .*/
1516 _isds_discard_credentials(context
, 1);
1518 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1520 _isds_discard_credentials(context
, 1);
1522 zfree(context
->url
);
1524 #else /* not HAVE_LIBCURL */
1530 /* Verify connection to ISDS is alive and server is responding.
1531 * Send dummy request to ISDS and expect dummy response. */
1532 isds_error
isds_ping(struct isds_ctx
*context
) {
1534 isds_error soap_err
;
1535 xmlNsPtr isds_ns
= NULL
;
1536 xmlNodePtr request
= NULL
;
1537 #endif /* HAVE_LIBCURL */
1539 if (!context
) return IE_INVALID_CONTEXT
;
1540 zfree(context
->long_message
);
1543 /* Check if connection is established */
1544 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1547 /* Build dummy request */
1548 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1550 isds_log_message(context
, _("Could build ISDS dummy request"));
1553 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1555 isds_log_message(context
, _("Could not create ISDS name space"));
1556 xmlFreeNode(request
);
1559 xmlSetNs(request
, isds_ns
);
1561 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1563 /* XXX: ISDS documentation does not specify response body for
1564 * DummyOperation request. However real server sends back
1565 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1566 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1567 * SOAP body content, e.g. the dmStatus element. */
1569 /* Send dummy request */
1570 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1572 /* Destroy log-in request */
1573 xmlFreeNode(request
);
1576 isds_log(ILF_ISDS
, ILL_DEBUG
,
1577 _("ISDS server could not be contacted\n"));
1581 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1582 * authentication succeeded if soap_err == IE_SUCCESS */
1585 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1588 #else /* not HAVE_LIBCURL */
1594 /* Send bogus request to ISDS.
1595 * Just for test purposes */
1596 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1599 xmlNsPtr isds_ns
= NULL
;
1600 xmlNodePtr request
= NULL
;
1601 xmlDocPtr response
= NULL
;
1602 xmlChar
*code
= NULL
, *message
= NULL
;
1605 if (!context
) return IE_INVALID_CONTEXT
;
1606 zfree(context
->long_message
);
1609 /* Check if connection is established */
1610 if (!context
->curl
) {
1611 /* Testing printf message */
1612 isds_printf_message(context
, "%s", _("I said connection closed"));
1613 return IE_CONNECTION_CLOSED
;
1617 /* Build dummy request */
1618 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1620 isds_log_message(context
, _("Could build ISDS bogus request"));
1623 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1625 isds_log_message(context
, _("Could not create ISDS name space"));
1626 xmlFreeNode(request
);
1629 xmlSetNs(request
, isds_ns
);
1631 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1633 /* Sent bogus request */
1634 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1636 /* Destroy request */
1637 xmlFreeNode(request
);
1640 isds_log(ILF_ISDS
, ILL_DEBUG
,
1641 _("Processing ISDS response on bogus request failed\n"));
1642 xmlFreeDoc(response
);
1646 /* Check for response status */
1647 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1648 &code
, &message
, NULL
);
1650 isds_log(ILF_ISDS
, ILL_DEBUG
,
1651 _("ISDS response on bogus request is missing status\n"));
1654 xmlFreeDoc(response
);
1657 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1658 char *code_locale
= _isds_utf82locale((char*)code
);
1659 char *message_locale
= _isds_utf82locale((char*)message
);
1660 isds_log(ILF_ISDS
, ILL_DEBUG
,
1661 _("Server refused bogus request (code=%s, message=%s)\n"),
1662 code_locale
, message_locale
);
1663 /* XXX: Literal error messages from ISDS are Czech messages
1664 * (English sometimes) in UTF-8. It's hard to catch them for
1665 * translation. Successfully gettextized would return in locale
1666 * encoding, unsuccessfully translated would pass in UTF-8. */
1667 isds_log_message(context
, message_locale
);
1669 free(message_locale
);
1672 xmlFreeDoc(response
);
1679 xmlFreeDoc(response
);
1681 isds_log(ILF_ISDS
, ILL_DEBUG
,
1682 _("Bogus message accepted by server. This should not happen.\n"));
1685 #else /* not HAVE_LIBCURL */
1692 /* Serialize XML subtree to buffer preserving XML indentation.
1693 * @context is session context
1694 * @subtree is XML element to be serialized (with children)
1695 * @buffer is automatically reallocated buffer where serialize to
1696 * @length is size of serialized stream in bytes
1697 * @return standard error code, free @buffer in case of error */
1698 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1699 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1700 isds_error err
= IE_SUCCESS
;
1701 xmlBufferPtr xml_buffer
= NULL
;
1702 xmlSaveCtxtPtr save_ctx
= NULL
;
1703 xmlDocPtr subtree_doc
= NULL
;
1704 xmlNodePtr subtree_copy
;
1708 if (!context
) return IE_INVALID_CONTEXT
;
1709 if (!buffer
) return IE_INVAL
;
1711 if (!subtree
|| !length
) return IE_INVAL
;
1713 /* Make temporary XML document with @subtree root element */
1714 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1715 * It can result in not well-formed on invalid XML tree (e.g. name space
1716 * prefix definition can miss. */
1719 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1721 isds_log_message(context
, _("Could not build temporary document"));
1726 /* XXX: Copy subtree and attach the copy to document.
1727 * One node can not bee attached into more document at the same time.
1728 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1730 * XXX: Check xmlSaveTree() too. */
1731 subtree_copy
= xmlCopyNodeList(subtree
);
1732 if (!subtree_copy
) {
1733 isds_log_message(context
, _("Could not copy subtree"));
1737 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1739 /* Only this way we get namespace definition as @xmlns:isds,
1740 * otherwise we get namespace prefix without definition */
1741 /* FIXME: Don't overwrite original default namespace */
1742 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1744 isds_log_message(context
, _("Could not create ISDS name space"));
1748 xmlSetNs(subtree_copy
, isds_ns
);
1751 /* Serialize the document into buffer */
1752 xml_buffer
= xmlBufferCreate();
1754 isds_log_message(context
, _("Could not create xmlBuffer"));
1758 /* Last argument 0 means to not format the XML tree */
1759 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1761 isds_log_message(context
, _("Could not create XML serializer"));
1765 /* XXX: According LibXML documentation, this function does not return
1766 * meaningful value yet */
1767 xmlSaveDoc(save_ctx
, subtree_doc
);
1768 if (-1 == xmlSaveFlush(save_ctx
)) {
1769 isds_log_message(context
,
1770 _("Could not serialize XML subtree"));
1774 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1775 * even after xmlSaveFlush(). Thus close it here */
1776 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1779 /* Store and detach buffer from xml_buffer */
1780 *buffer
= xml_buffer
->content
;
1781 *length
= xml_buffer
->use
;
1782 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1785 new_buffer
= realloc(*buffer
, *length
);
1786 if (new_buffer
) *buffer
= new_buffer
;
1794 xmlSaveClose(save_ctx
);
1795 xmlBufferFree(xml_buffer
);
1796 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1799 #endif /* HAVE_LIBCURL */
1803 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1804 * @context is session context
1805 * @document is original document where @nodeset points to
1806 * @nodeset is XPath node set to dump (recursively)
1807 * @buffer is automatically reallocated buffer where serialize to
1808 * @length is size of serialized stream in bytes
1809 * @return standard error code, free @buffer in case of error */
1810 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1811 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1812 void **buffer
, size_t *length
) {
1813 isds_error err
= IE_SUCCESS
;
1814 xmlBufferPtr xml_buffer
= NULL
;
1817 if (!context
) return IE_INVALID_CONTEXT
;
1818 if (!buffer
) return IE_INVAL
;
1820 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1823 /* Empty node set results into NULL buffer */
1824 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1828 /* Resulting the document into buffer */
1829 xml_buffer
= xmlBufferCreate();
1831 isds_log_message(context
, _("Could not create xmlBuffer"));
1836 /* Iterate over all nodes */
1837 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1839 * XXX: xmlNodeDump() appends to xml_buffer. */
1841 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1842 isds_log_message(context
, _("Could not dump XML node"));
1848 /* Store and detach buffer from xml_buffer */
1849 *buffer
= xml_buffer
->content
;
1850 *length
= xml_buffer
->use
;
1851 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1854 new_buffer
= realloc(*buffer
, *length
);
1855 if (new_buffer
) *buffer
= new_buffer
;
1864 xmlBufferFree(xml_buffer
);
1870 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1871 * @context is session context
1872 * @document is original document where @nodeset points to
1873 * @nodeset is XPath node set to dump (recursively)
1874 * @buffer is automatically reallocated buffer where serialize to
1875 * @length is size of serialized stream in bytes
1876 * @return standard error code, free @buffer in case of error */
1877 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1878 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1879 void **buffer
, size_t *length
) {
1880 isds_error err
= IE_SUCCESS
;
1881 xmlBufferPtr xml_buffer
= NULL
;
1882 xmlSaveCtxtPtr save_ctx
= NULL
;
1885 if (!context
) return IE_INVALID_CONTEXT
;
1886 if (!buffer
) return IE_INVAL
;
1888 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1891 /* Empty node set results into NULL buffer */
1892 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1896 /* Resulting the document into buffer */
1897 xml_buffer
= xmlBufferCreate();
1899 isds_log_message(context
, _("Could not create xmlBuffer"));
1903 if (xmlSubstituteEntitiesDefault(1)) {
1904 isds_log_message(context
, _("Could not disable attribute escaping"));
1908 /* Last argument means:
1909 * 0 to not format the XML tree
1910 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1911 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1912 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1914 isds_log_message(context
, _("Could not create XML serializer"));
1918 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1919 isds_log_message(context, _("Could not disable attribute escaping"));
1925 /* Iterate over all nodes */
1926 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1928 * XXX: xmlNodeDump() appends to xml_buffer. */
1930 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1932 /* XXX: According LibXML documentation, this function does not return
1933 * meaningful value yet */
1934 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1935 if (-1 == xmlSaveFlush(save_ctx
)) {
1936 isds_log_message(context
,
1937 _("Could not serialize XML subtree"));
1943 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1944 * even after xmlSaveFlush(). Thus close it here */
1945 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1947 /* Store and detach buffer from xml_buffer */
1948 *buffer
= xml_buffer
->content
;
1949 *length
= xml_buffer
->use
;
1950 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1953 new_buffer
= realloc(*buffer
, *length
);
1954 if (new_buffer
) *buffer
= new_buffer
;
1962 xmlSaveClose(save_ctx
);
1963 xmlBufferFree(xml_buffer
);
1970 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1971 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1972 if (!string
|| !type
) return IE_INVAL
;
1974 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1976 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1978 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1979 *type
= DBTYPE_PFO_ADVOK
;
1980 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1981 *type
= DBTYPE_PFO_DANPOR
;
1982 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1983 *type
= DBTYPE_PFO_INSSPR
;
1984 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1986 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1987 *type
= DBTYPE_PO_ZAK
;
1988 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1989 *type
= DBTYPE_PO_REQ
;
1990 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1992 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1993 *type
= DBTYPE_OVM_NOTAR
;
1994 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1995 *type
= DBTYPE_OVM_EXEKUT
;
1996 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1997 *type
= DBTYPE_OVM_REQ
;
2004 /* Convert ISDS dbType enum @type to UTF-8 string.
2005 * @Return pointer to static string, or NULL if unknown enum value */
2006 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2008 /* DBTYPE_SYSTEM is invalid value from point of view of public
2009 * SOAP interface. */
2010 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2011 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2012 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2013 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2014 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2015 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2016 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2017 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2018 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2019 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2020 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2021 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2022 default: return NULL
; break;
2027 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2028 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2029 if (!string
|| !type
) return IE_INVAL
;
2031 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2032 *type
= USERTYPE_PRIMARY
;
2033 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2034 *type
= USERTYPE_ENTRUSTED
;
2035 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2036 *type
= USERTYPE_ADMINISTRATOR
;
2037 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2038 *type
= USERTYPE_OFFICIAL
;
2039 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2040 *type
= USERTYPE_OFFICIAL_CERT
;
2041 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2042 *type
= USERTYPE_LIQUIDATOR
;
2049 /* Convert ISDS userType enum @type to UTF-8 string.
2050 * @Return pointer to static string, or NULL if unknown enum value */
2051 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2053 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2054 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2055 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2056 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2057 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2058 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2059 default: return NULL
; break;
2064 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2065 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2066 isds_sender_type
*type
) {
2067 if (!string
|| !type
) return IE_INVAL
;
2069 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2070 *type
= SENDERTYPE_PRIMARY
;
2071 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2072 *type
= SENDERTYPE_ENTRUSTED
;
2073 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2074 *type
= SENDERTYPE_ADMINISTRATOR
;
2075 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2076 *type
= SENDERTYPE_OFFICIAL
;
2077 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2078 *type
= SENDERTYPE_VIRTUAL
;
2079 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2080 *type
= SENDERTYPE_OFFICIAL_CERT
;
2081 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2082 *type
= SENDERTYPE_LIQUIDATOR
;
2089 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2090 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2091 isds_payment_type
*type
) {
2092 if (!string
|| !type
) return IE_INVAL
;
2094 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2095 *type
= PAYMENT_SENDER
;
2096 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2097 *type
= PAYMENT_RESPONSE
;
2098 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2099 *type
= PAYMENT_SPONSOR
;
2100 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2101 *type
= PAYMENT_SPONSOR_LIMITED
;
2102 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2103 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2104 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2105 *type
= PAYMENT_STAMP
;
2112 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2113 * ciEventType is integer but we convert it from string representation
2115 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2116 isds_credit_event_type
*type
) {
2117 if (!string
|| !type
) return IE_INVAL
;
2119 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2120 *type
= ISDS_CREDIT_CHARGED
;
2121 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2122 *type
= ISDS_CREDIT_DISCHARGED
;
2123 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2124 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2125 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2126 *type
= ISDS_CREDIT_STORAGE_SET
;
2127 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2128 *type
= ISDS_CREDIT_EXPIRED
;
2135 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2136 * @Return pointer to static string, or NULL if unknown enum value */
2137 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2139 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2140 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2141 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2142 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2143 default: return NULL
; break;
2148 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2149 * ISDSSearch2/searchType value.
2150 * @Return pointer to static string, or NULL if unknown enum value */
2151 static const xmlChar
*isds_fulltext_target2string(
2152 const isds_fulltext_target type
) {
2154 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2155 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2156 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2157 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2158 default: return NULL
; break;
2161 #endif /* HAVE_LIBCURL */
2164 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2165 * @Return IE_ENUM if @string is not valid enum member */
2166 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2167 isds_FileMetaType
*type
) {
2168 if (!string
|| !type
) return IE_INVAL
;
2170 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2171 *type
= FILEMETATYPE_MAIN
;
2172 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2173 *type
= FILEMETATYPE_ENCLOSURE
;
2174 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2175 *type
= FILEMETATYPE_SIGNATURE
;
2176 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2177 *type
= FILEMETATYPE_META
;
2184 /* Convert UTF-8 @string to ISDS hash @algorithm.
2185 * @Return IE_ENUM if @string is not valid enum member */
2186 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2187 isds_hash_algorithm
*algorithm
) {
2188 if (!string
|| !algorithm
) return IE_INVAL
;
2190 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2191 *algorithm
= HASH_ALGORITHM_MD5
;
2192 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2193 *algorithm
= HASH_ALGORITHM_SHA_1
;
2194 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2195 *algorithm
= HASH_ALGORITHM_SHA_224
;
2196 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2197 *algorithm
= HASH_ALGORITHM_SHA_256
;
2198 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2199 *algorithm
= HASH_ALGORITHM_SHA_384
;
2200 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2201 *algorithm
= HASH_ALGORITHM_SHA_512
;
2209 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2210 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2211 if (!time
|| !string
) return IE_INVAL
;
2213 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2214 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2221 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2222 * respects the @time microseconds too. */
2223 static isds_error
timeval2timestring(const struct timeval
*time
,
2227 if (!time
|| !string
) return IE_INVAL
;
2229 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2230 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2232 /* TODO: small negative year should be formatted as "-0012". This is not
2233 * true for glibc "%04d". We should implement it.
2234 * time->tv_usec type is su_seconds_t which is required to be signed
2235 * integer to accomodate values from range [-1, 1000000].
2236 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2237 if (-1 == isds_asprintf((char **) string
,
2238 "%04d-%02d-%02dT%02d:%02d:%02d.%06jd",
2239 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2240 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2241 (intmax_t)time
->tv_usec
))
2246 #endif /* HAVE_LIBCURL */
2249 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2250 * It respects microseconds too. Microseconds are rounded half up.
2251 * In case of error, @time will be freed. */
2252 static isds_error
timestring2timeval(const xmlChar
*string
,
2253 struct timeval
**time
) {
2255 char *offset
, *delim
, *endptr
;
2256 const int subsecond_resolution
= 6;
2257 char subseconds
[subsecond_resolution
+ 1];
2259 int offset_hours
, offset_minutes
;
2261 long int long_number
;
2266 if (!time
) return IE_INVAL
;
2272 memset(&broken
, 0, sizeof(broken
));
2275 *time
= calloc(1, sizeof(**time
));
2276 if (!*time
) return IE_NOMEM
;
2278 memset(*time
, 0, sizeof(**time
));
2282 /* xsd:date is ISO 8601 string, thus ASCII */
2283 /*TODO: negative year */
2287 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2288 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2289 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2295 broken
.tm_year
-= 1900;
2297 offset
= (char*)string
+ i
;
2299 /* Parse date and time without subseconds and offset */
2300 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2307 /* Get subseconds */
2308 if (*offset
== '.' ) {
2311 /* Copy first 6 digits, pad it with zeros.
2312 * Current server implementation uses only millisecond resolution. */
2313 /* TODO: isdigit() is locale sensitive */
2315 i
< subsecond_resolution
&& isdigit(*offset
);
2317 subseconds
[i
] = *offset
;
2319 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2320 /* Check 7th digit for rounding */
2321 if (*offset
>= '5') round_up
= 1;
2324 for (; i
< subsecond_resolution
; i
++) {
2325 subseconds
[i
] = '0';
2327 subseconds
[subsecond_resolution
] = '\0';
2329 /* Convert it into integer */
2330 long_number
= strtol(subseconds
, &endptr
, 10);
2331 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2332 long_number
== LONG_MAX
) {
2336 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2337 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2338 * microseconds" and "the type shall be a signed integer capable of
2339 * storing values at least in the range [-1, 1000000]. */
2340 if (long_number
< -1 || long_number
>= 1000000) {
2344 (*time
)->tv_usec
= long_number
;
2346 /* Round the subseconds */
2348 if (999999 == (*time
)->tv_usec
) {
2349 (*time
)->tv_usec
= 0;
2356 /* move to the zone offset delimiter or signal NULL*/
2357 delim
= strchr(offset
, '-');
2359 delim
= strchr(offset
, '+');
2361 delim
= strchr(offset
, 'Z');
2365 /* Get zone offset */
2366 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2367 * "" equals to "Z" and it means UTC zone. */
2368 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2369 * colon separator */
2370 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2371 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2375 if (*offset
== '+') {
2376 broken
.tm_hour
-= offset_hours
;
2377 broken
.tm_min
-= offset_minutes
;
2379 broken
.tm_hour
+= offset_hours
;
2380 broken
.tm_min
+= offset_minutes
;
2384 /* Convert to time_t */
2385 (*time
)->tv_sec
= _isds_timegm(&broken
);
2386 if ((*time
)->tv_sec
== (time_t) -1) {
2395 /* Convert unsigned int into isds_message_status.
2396 * @context is session context
2397 * @number is pointer to number value. NULL will be treated as invalid value.
2398 * @status is automatically reallocated status
2399 * @return IE_SUCCESS, or error code and free status */
2400 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2401 const unsigned long int *number
, isds_message_status
**status
) {
2402 if (!context
) return IE_INVALID_CONTEXT
;
2403 if (!status
) return IE_INVAL
;
2405 free(*status
); *status
= NULL
;
2406 if (!number
) return IE_INVAL
;
2408 if (*number
< 1 || *number
> 10) {
2409 isds_printf_message(context
, _("Invalid message status value: %lu"),
2414 *status
= malloc(sizeof(**status
));
2415 if (!*status
) return IE_NOMEM
;
2417 **status
= 1 << *number
;
2422 /* Convert event description string into isds_event members type and
2424 * @string is raw event description starting with event prefix
2425 * @event is structure where to store type and stripped description to
2426 * @return standard error code, unknown prefix is not classified as an error.
2428 static isds_error
eventstring2event(const xmlChar
*string
,
2429 struct isds_event
* event
) {
2430 const xmlChar
*known_prefixes
[] = {
2441 const isds_event_type types
[] = {
2442 EVENT_ENTERED_SYSTEM
,
2443 EVENT_ACCEPTED_BY_RECIPIENT
,
2444 EVENT_ACCEPTED_BY_FICTION
,
2445 EVENT_UNDELIVERABLE
,
2446 EVENT_COMMERCIAL_ACCEPTED
,
2448 EVENT_PRIMARY_LOGIN
,
2449 EVENT_ENTRUSTED_LOGIN
,
2455 if (!string
|| !event
) return IE_INVAL
;
2458 event
->type
= malloc(sizeof(*event
->type
));
2459 if (!(event
->type
)) return IE_NOMEM
;
2461 zfree(event
->description
);
2463 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2465 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2467 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2468 /* Prefix is known */
2469 *event
->type
= types
[index
];
2471 /* Strip prefix from description and spaces */
2472 /* TODO: Recognize all white spaces from UCS blank class and
2473 * operate on UTF-8 chars. */
2474 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2475 event
->description
= strdup((char *) (string
+ length
));
2476 if (!(event
->description
)) return IE_NOMEM
;
2482 /* Unknown event prefix.
2483 * XSD allows any string */
2484 char *string_locale
= _isds_utf82locale((char *) string
);
2485 isds_log(ILF_ISDS
, ILL_WARNING
,
2486 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2487 free(string_locale
);
2489 *event
->type
= EVENT_UKNOWN
;
2490 event
->description
= strdup((char *) string
);
2491 if (!(event
->description
)) return IE_NOMEM
;
2497 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2498 * and leave label */
2499 #define EXTRACT_STRING(element, string) { \
2500 xmlXPathFreeObject(result); \
2501 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2502 if (NULL == (result)) { \
2506 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2507 if (result->nodesetval->nodeNr > 1) { \
2508 isds_printf_message(context, _("Multiple %s element"), element); \
2512 (string) = (char *) \
2513 xmlXPathCastNodeSetToString(result->nodesetval); \
2514 if (NULL == (string)) { \
2521 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2523 char *string = NULL; \
2524 EXTRACT_STRING(element, string); \
2527 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2528 if (!(booleanPtr)) { \
2534 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2535 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2536 *(booleanPtr) = 1; \
2537 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2538 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2539 *(booleanPtr) = 0; \
2541 char *string_locale = _isds_utf82locale((char*)string); \
2542 isds_printf_message(context, \
2543 _("%s value is not valid boolean: %s"), \
2544 element, string_locale); \
2545 free(string_locale); \
2555 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2557 char *string = NULL; \
2558 EXTRACT_STRING(element, string); \
2560 if (NULL == string) { \
2561 isds_printf_message(context, _("%s element is empty"), element); \
2565 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2566 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2568 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2569 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2572 char *string_locale = _isds_utf82locale((char*)string); \
2573 isds_printf_message(context, \
2574 _("%s value is not valid boolean: %s"), \
2575 element, string_locale); \
2576 free(string_locale); \
2585 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2587 char *string = NULL; \
2588 EXTRACT_STRING(element, string); \
2593 number = strtol((char*)string, &endptr, 10); \
2595 if (*endptr != '\0') { \
2596 char *string_locale = _isds_utf82locale((char *)string); \
2597 isds_printf_message(context, \
2598 _("%s is not valid integer: %s"), \
2599 element, string_locale); \
2600 free(string_locale); \
2606 if (number == LONG_MIN || number == LONG_MAX) { \
2607 char *string_locale = _isds_utf82locale((char *)string); \
2608 isds_printf_message(context, \
2609 _("%s value out of range of long int: %s"), \
2610 element, string_locale); \
2611 free(string_locale); \
2617 free(string); string = NULL; \
2619 if (!(preallocated)) { \
2620 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2621 if (!(longintPtr)) { \
2626 *(longintPtr) = number; \
2630 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2632 char *string = NULL; \
2633 EXTRACT_STRING(element, string); \
2638 number = strtol((char*)string, &endptr, 10); \
2640 if (*endptr != '\0') { \
2641 char *string_locale = _isds_utf82locale((char *)string); \
2642 isds_printf_message(context, \
2643 _("%s is not valid integer: %s"), \
2644 element, string_locale); \
2645 free(string_locale); \
2651 if (number == LONG_MIN || number == LONG_MAX) { \
2652 char *string_locale = _isds_utf82locale((char *)string); \
2653 isds_printf_message(context, \
2654 _("%s value out of range of long int: %s"), \
2655 element, string_locale); \
2656 free(string_locale); \
2662 free(string); string = NULL; \
2664 isds_printf_message(context, \
2665 _("%s value is negative: %ld"), element, number); \
2670 if (!(preallocated)) { \
2671 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2672 if (!(ulongintPtr)) { \
2677 *(ulongintPtr) = number; \
2681 #define EXTRACT_DATE(element, tmPtr) { \
2682 char *string = NULL; \
2683 EXTRACT_STRING(element, string); \
2684 if (NULL != string) { \
2685 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2686 if (NULL == (tmPtr)) { \
2691 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2693 if (err == IE_NOTSUP) { \
2695 char *string_locale = _isds_utf82locale(string); \
2696 char *element_locale = _isds_utf82locale(element); \
2697 isds_printf_message(context, _("Invalid %s value: %s"), \
2698 element_locale, string_locale); \
2699 free(string_locale); \
2700 free(element_locale); \
2709 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2710 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2712 if ((required) && (!string)) { \
2713 char *attribute_locale = _isds_utf82locale(attribute); \
2714 char *element_locale = \
2715 _isds_utf82locale((char *)xpath_ctx->node->name); \
2716 isds_printf_message(context, \
2717 _("Could not extract required %s attribute value from " \
2718 "%s element"), attribute_locale, element_locale); \
2719 free(element_locale); \
2720 free(attribute_locale); \
2727 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2729 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2730 (xmlChar *) (string)); \
2732 isds_printf_message(context, \
2733 _("Could not add %s child to %s element"), \
2734 element, (parent)->name); \
2740 #define INSERT_STRING(parent, element, string) \
2741 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2743 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2745 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2746 else { INSERT_STRING(parent, element, "false"); } \
2749 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2752 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2754 INSERT_STRING(parent, element, NULL); \
2758 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2759 if ((longintPtr)) { \
2760 /* FIXME: locale sensitive */ \
2761 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2765 INSERT_STRING(parent, element, buffer) \
2766 free(buffer); (buffer) = NULL; \
2767 } else { INSERT_STRING(parent, element, NULL) } \
2770 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2771 if ((ulongintPtr)) { \
2772 /* FIXME: locale sensitive */ \
2773 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2777 INSERT_STRING(parent, element, buffer) \
2778 free(buffer); (buffer) = NULL; \
2779 } else { INSERT_STRING(parent, element, NULL) } \
2782 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2784 /* FIXME: locale sensitive */ \
2785 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2789 INSERT_STRING(parent, element, buffer) \
2790 free(buffer); (buffer) = NULL; \
2793 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2795 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2797 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2798 (xmlChar *) (string)); \
2799 if (!attribute_node) { \
2800 isds_printf_message(context, _("Could not add %s " \
2801 "attribute to %s element"), \
2802 (attribute), (parent)->name); \
2808 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2810 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2811 if (length > (maximum)) { \
2812 isds_printf_message(context, \
2813 ngettext("%s has more than %d characters", \
2814 "%s has more than %d characters", (maximum)), \
2815 (name), (maximum)); \
2819 if (length < (minimum)) { \
2820 isds_printf_message(context, \
2821 ngettext("%s has less than %d characters", \
2822 "%s has less than %d characters", (minimum)), \
2823 (name), (minimum)); \
2830 #define INSERT_ELEMENT(child, parent, element) \
2832 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2834 isds_printf_message(context, \
2835 _("Could not add %s child to %s element"), \
2836 (element), (parent)->name); \
2843 /* Find child element by name in given XPath context and switch context onto
2844 * it. The child must be uniq and must exist. Otherwise fails.
2845 * @context is ISDS context
2846 * @child is child element name
2847 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2848 * into it child. In error case, the @xpath_ctx keeps original value. */
2849 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2850 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2851 isds_error err
= IE_SUCCESS
;
2852 xmlXPathObjectPtr result
= NULL
;
2854 if (!context
) return IE_INVALID_CONTEXT
;
2855 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2858 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2865 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2866 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2867 char *child_locale
= _isds_utf82locale((char*) child
);
2868 isds_printf_message(context
,
2869 _("%s element does not contain %s child"),
2870 parent_locale
, child_locale
);
2872 free(parent_locale
);
2878 if (result
->nodesetval
->nodeNr
> 1) {
2879 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2880 char *child_locale
= _isds_utf82locale((char*) child
);
2881 isds_printf_message(context
,
2882 _("%s element contains multiple %s children"),
2883 parent_locale
, child_locale
);
2885 free(parent_locale
);
2890 /* Switch context */
2891 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2894 xmlXPathFreeObject(result
);
2901 /* Find and convert XSD:gPersonName group in current node into structure
2902 * @context is ISDS context
2903 * @personName is automatically reallocated person name structure. If no member
2904 * value is found, will be freed.
2905 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2907 * In case of error @personName will be freed. */
2908 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2909 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2910 isds_error err
= IE_SUCCESS
;
2911 xmlXPathObjectPtr result
= NULL
;
2913 if (!context
) return IE_INVALID_CONTEXT
;
2914 if (!personName
) return IE_INVAL
;
2915 isds_PersonName_free(personName
);
2916 if (!xpath_ctx
) return IE_INVAL
;
2919 *personName
= calloc(1, sizeof(**personName
));
2925 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2926 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2927 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2928 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2930 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2931 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2932 isds_PersonName_free(personName
);
2935 if (err
) isds_PersonName_free(personName
);
2936 xmlXPathFreeObject(result
);
2941 /* Find and convert XSD:gAddress group in current node into structure
2942 * @context is ISDS context
2943 * @address is automatically reallocated address structure. If no member
2944 * value is found, will be freed.
2945 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2947 * In case of error @address will be freed. */
2948 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2949 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2950 isds_error err
= IE_SUCCESS
;
2951 xmlXPathObjectPtr result
= NULL
;
2953 if (!context
) return IE_INVALID_CONTEXT
;
2954 if (!address
) return IE_INVAL
;
2955 isds_Address_free(address
);
2956 if (!xpath_ctx
) return IE_INVAL
;
2959 *address
= calloc(1, sizeof(**address
));
2965 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2966 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2967 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2968 EXTRACT_STRING("isds:adNumberInMunicipality",
2969 (*address
)->adNumberInMunicipality
);
2970 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2971 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2973 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2974 !(*address
)->adNumberInStreet
&&
2975 !(*address
)->adNumberInMunicipality
&&
2976 !(*address
)->adZipCode
&& !(*address
)->adState
)
2977 isds_Address_free(address
);
2980 if (err
) isds_Address_free(address
);
2981 xmlXPathFreeObject(result
);
2986 /* Find and convert isds:biDate element in current node into structure
2987 * @context is ISDS context
2988 * @biDate is automatically reallocated birth date structure. If no member
2989 * value is found, will be freed.
2990 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2992 * In case of error @biDate will be freed. */
2993 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2994 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2995 isds_error err
= IE_SUCCESS
;
2996 xmlXPathObjectPtr result
= NULL
;
2997 char *string
= NULL
;
2999 if (!context
) return IE_INVALID_CONTEXT
;
3000 if (!biDate
) return IE_INVAL
;
3002 if (!xpath_ctx
) return IE_INVAL
;
3004 EXTRACT_STRING("isds:biDate", string
);
3006 *biDate
= calloc(1, sizeof(**biDate
));
3011 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3013 if (err
== IE_NOTSUP
) {
3015 char *string_locale
= _isds_utf82locale(string
);
3016 isds_printf_message(context
,
3017 _("Invalid isds:biDate value: %s"), string_locale
);
3018 free(string_locale
);
3025 if (err
) zfree(*biDate
);
3027 xmlXPathFreeObject(result
);
3032 /* Convert isds:dBOwnerInfo XML tree into structure
3033 * @context is ISDS context
3034 * @db_owner_info is automatically reallocated box owner info structure
3035 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3036 * In case of error @db_owner_info will be freed. */
3037 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3038 struct isds_DbOwnerInfo
**db_owner_info
,
3039 xmlXPathContextPtr xpath_ctx
) {
3040 isds_error err
= IE_SUCCESS
;
3041 xmlXPathObjectPtr result
= NULL
;
3042 char *string
= NULL
;
3044 if (!context
) return IE_INVALID_CONTEXT
;
3045 if (!db_owner_info
) return IE_INVAL
;
3046 isds_DbOwnerInfo_free(db_owner_info
);
3047 if (!xpath_ctx
) return IE_INVAL
;
3050 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3051 if (!*db_owner_info
) {
3056 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3058 EXTRACT_STRING("isds:dbType", string
);
3060 (*db_owner_info
)->dbType
=
3061 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3062 if (!(*db_owner_info
)->dbType
) {
3066 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3068 zfree((*db_owner_info
)->dbType
);
3069 if (err
== IE_ENUM
) {
3071 char *string_locale
= _isds_utf82locale(string
);
3072 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3074 free(string_locale
);
3081 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3083 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3085 if (err
) goto leave
;
3087 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3089 (*db_owner_info
)->birthInfo
=
3090 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3091 if (!(*db_owner_info
)->birthInfo
) {
3095 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3097 if (err
) goto leave
;
3098 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3099 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3100 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3101 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3102 !(*db_owner_info
)->birthInfo
->biCity
&&
3103 !(*db_owner_info
)->birthInfo
->biCounty
&&
3104 !(*db_owner_info
)->birthInfo
->biState
)
3105 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3107 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3108 if (err
) goto leave
;
3110 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3111 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3112 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3113 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3114 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3116 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3118 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3119 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3120 (*db_owner_info
)->dbOpenAddressing
);
3123 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3125 xmlXPathFreeObject(result
);
3130 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3131 * @context is session context
3132 * @owner is libisds structure with box description
3133 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3134 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3135 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3137 isds_error err
= IE_SUCCESS
;
3139 xmlChar
*string
= NULL
;
3141 if (!context
) return IE_INVALID_CONTEXT
;
3142 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3145 /* Build XSD:tDbOwnerInfo */
3146 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3147 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3150 if (owner
->dbType
) {
3151 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3153 isds_printf_message(context
, _("Invalid dbType value: %d"),
3158 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3160 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3161 if (owner
->personName
) {
3162 INSERT_STRING(db_owner_info
, "pnFirstName",
3163 owner
->personName
->pnFirstName
);
3164 INSERT_STRING(db_owner_info
, "pnMiddleName",
3165 owner
->personName
->pnMiddleName
);
3166 INSERT_STRING(db_owner_info
, "pnLastName",
3167 owner
->personName
->pnLastName
);
3168 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3169 owner
->personName
->pnLastNameAtBirth
);
3171 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3172 if (owner
->birthInfo
) {
3173 if (owner
->birthInfo
->biDate
) {
3174 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3175 INSERT_STRING(db_owner_info
, "biDate", string
);
3176 free(string
); string
= NULL
;
3178 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3179 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3180 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3182 if (owner
->address
) {
3183 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3184 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3185 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3186 owner
->address
->adNumberInStreet
);
3187 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3188 owner
->address
->adNumberInMunicipality
);
3189 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3190 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3192 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3193 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3194 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3196 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3197 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3199 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3200 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3202 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3204 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3205 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3206 owner
->dbOpenAddressing
);
3214 /* Convert XSD:tDbUserInfo XML tree into structure
3215 * @context is ISDS context
3216 * @db_user_info is automatically reallocated user info structure
3217 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3218 * In case of error @db_user_info will be freed. */
3219 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3220 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3221 isds_error err
= IE_SUCCESS
;
3222 xmlXPathObjectPtr result
= NULL
;
3223 char *string
= NULL
;
3225 if (!context
) return IE_INVALID_CONTEXT
;
3226 if (!db_user_info
) return IE_INVAL
;
3227 isds_DbUserInfo_free(db_user_info
);
3228 if (!xpath_ctx
) return IE_INVAL
;
3231 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3232 if (!*db_user_info
) {
3237 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3239 EXTRACT_STRING("isds:userType", string
);
3241 (*db_user_info
)->userType
=
3242 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3243 if (!(*db_user_info
)->userType
) {
3247 err
= string2isds_UserType((xmlChar
*)string
,
3248 (*db_user_info
)->userType
);
3250 zfree((*db_user_info
)->userType
);
3251 if (err
== IE_ENUM
) {
3253 char *string_locale
= _isds_utf82locale(string
);
3254 isds_printf_message(context
,
3255 _("Unknown isds:userType value: %s"), string_locale
);
3256 free(string_locale
);
3263 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3265 (*db_user_info
)->personName
=
3266 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3267 if (!(*db_user_info
)->personName
) {
3272 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3274 if (err
) goto leave
;
3276 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3277 if (err
) goto leave
;
3279 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3280 if (err
) goto leave
;
3282 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3283 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3285 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3286 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3287 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3289 /* ???: Default value is "CZ" according specification. Should we provide
3291 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3294 if (err
) isds_DbUserInfo_free(db_user_info
);
3296 xmlXPathFreeObject(result
);
3301 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3302 * @context is session context
3303 * @user is libisds structure with user description
3304 * @db_user_info is XML element of XSD:tDbUserInfo */
3305 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3306 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3308 isds_error err
= IE_SUCCESS
;
3310 xmlChar
*string
= NULL
;
3312 if (!context
) return IE_INVALID_CONTEXT
;
3313 if (!user
|| !db_user_info
) return IE_INVAL
;
3315 /* Build XSD:tDbUserInfo */
3316 if (user
->personName
) {
3317 INSERT_STRING(db_user_info
, "pnFirstName",
3318 user
->personName
->pnFirstName
);
3319 INSERT_STRING(db_user_info
, "pnMiddleName",
3320 user
->personName
->pnMiddleName
);
3321 INSERT_STRING(db_user_info
, "pnLastName",
3322 user
->personName
->pnLastName
);
3323 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3324 user
->personName
->pnLastNameAtBirth
);
3326 if (user
->address
) {
3327 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3328 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3329 INSERT_STRING(db_user_info
, "adNumberInStreet",
3330 user
->address
->adNumberInStreet
);
3331 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3332 user
->address
->adNumberInMunicipality
);
3333 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3334 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3337 if (!tm2datestring(user
->biDate
, &string
))
3338 INSERT_STRING(db_user_info
, "biDate", string
);
3341 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3342 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3345 if (user
->userType
) {
3346 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3348 isds_printf_message(context
, _("Invalid userType value: %d"),
3353 INSERT_STRING(db_user_info
, "userType", type_string
);
3356 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3357 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3358 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3359 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3360 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3361 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3362 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3363 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3364 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3372 /* Convert XSD:tPDZRec XML tree into structure
3373 * @context is ISDS context
3374 * @permission is automatically reallocated commercial permission structure
3375 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3376 * In case of error @permission will be freed. */
3377 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3378 struct isds_commercial_permission
**permission
,
3379 xmlXPathContextPtr xpath_ctx
) {
3380 isds_error err
= IE_SUCCESS
;
3381 xmlXPathObjectPtr result
= NULL
;
3382 char *string
= NULL
;
3384 if (!context
) return IE_INVALID_CONTEXT
;
3385 if (!permission
) return IE_INVAL
;
3386 isds_commercial_permission_free(permission
);
3387 if (!xpath_ctx
) return IE_INVAL
;
3390 *permission
= calloc(1, sizeof(**permission
));
3396 EXTRACT_STRING("isds:PDZType", string
);
3398 err
= string2isds_payment_type((xmlChar
*)string
,
3399 &(*permission
)->type
);
3401 if (err
== IE_ENUM
) {
3403 char *string_locale
= _isds_utf82locale(string
);
3404 isds_printf_message(context
,
3405 _("Unknown isds:PDZType value: %s"), string_locale
);
3406 free(string_locale
);
3413 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3414 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3416 EXTRACT_STRING("isds:PDZExpire", string
);
3418 err
= timestring2timeval((xmlChar
*) string
,
3419 &((*permission
)->expiration
));
3421 char *string_locale
= _isds_utf82locale(string
);
3422 if (err
== IE_DATE
) err
= IE_ISDS
;
3423 isds_printf_message(context
,
3424 _("Could not convert PDZExpire as ISO time: %s"),
3426 free(string_locale
);
3432 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3433 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3436 if (err
) isds_commercial_permission_free(permission
);
3438 xmlXPathFreeObject(result
);
3443 /* Convert XSD:tCiRecord XML tree into structure
3444 * @context is ISDS context
3445 * @event is automatically reallocated commercial credit event structure
3446 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3447 * In case of error @event will be freed. */
3448 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3449 struct isds_credit_event
**event
,
3450 xmlXPathContextPtr xpath_ctx
) {
3451 isds_error err
= IE_SUCCESS
;
3452 xmlXPathObjectPtr result
= NULL
;
3453 char *string
= NULL
;
3454 long int *number_ptr
;
3456 if (!context
) return IE_INVALID_CONTEXT
;
3457 if (!event
) return IE_INVAL
;
3458 isds_credit_event_free(event
);
3459 if (!xpath_ctx
) return IE_INVAL
;
3462 *event
= calloc(1, sizeof(**event
));
3468 EXTRACT_STRING("isds:ciEventTime", string
);
3470 err
= timestring2timeval((xmlChar
*) string
,
3473 char *string_locale
= _isds_utf82locale(string
);
3474 if (err
== IE_DATE
) err
= IE_ISDS
;
3475 isds_printf_message(context
,
3476 _("Could not convert ciEventTime as ISO time: %s"),
3478 free(string_locale
);
3484 EXTRACT_STRING("isds:ciEventType", string
);
3486 err
= string2isds_credit_event_type((xmlChar
*)string
,
3489 if (err
== IE_ENUM
) {
3491 char *string_locale
= _isds_utf82locale(string
);
3492 isds_printf_message(context
,
3493 _("Unknown isds:ciEventType value: %s"), string_locale
);
3494 free(string_locale
);
3501 number_ptr
= &((*event
)->credit_change
);
3502 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3503 number_ptr
= &(*event
)->new_credit
;
3504 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3506 switch((*event
)->type
) {
3507 case ISDS_CREDIT_CHARGED
:
3508 EXTRACT_STRING("isds:ciTransID",
3509 (*event
)->details
.charged
.transaction
);
3511 case ISDS_CREDIT_DISCHARGED
:
3512 EXTRACT_STRING("isds:ciTransID",
3513 (*event
)->details
.discharged
.transaction
);
3515 case ISDS_CREDIT_MESSAGE_SENT
:
3516 EXTRACT_STRING("isds:ciRecipientID",
3517 (*event
)->details
.message_sent
.recipient
);
3518 EXTRACT_STRING("isds:ciPDZID",
3519 (*event
)->details
.message_sent
.message_id
);
3521 case ISDS_CREDIT_STORAGE_SET
:
3522 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3523 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3524 EXTRACT_DATE("isds:ciNewFrom",
3525 (*event
)->details
.storage_set
.new_valid_from
);
3526 EXTRACT_DATE("isds:ciNewTo",
3527 (*event
)->details
.storage_set
.new_valid_to
);
3528 EXTRACT_LONGINT("isds:ciOldCapacity",
3529 (*event
)->details
.storage_set
.old_capacity
, 0);
3530 EXTRACT_DATE("isds:ciOldFrom",
3531 (*event
)->details
.storage_set
.old_valid_from
);
3532 EXTRACT_DATE("isds:ciOldTo",
3533 (*event
)->details
.storage_set
.old_valid_to
);
3534 EXTRACT_STRING("isds:ciDoneBy",
3535 (*event
)->details
.storage_set
.initiator
);
3537 case ISDS_CREDIT_EXPIRED
:
3542 if (err
) isds_credit_event_free(event
);
3544 xmlXPathFreeObject(result
);
3549 #endif /* HAVE_LIBCURL */
3552 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3553 * isds_envelope structure. The envelope is automatically allocated but not
3554 * reallocated. The date are just appended into envelope structure.
3555 * @context is ISDS context
3556 * @envelope is automatically allocated message envelope structure
3557 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3558 * In case of error @envelope will be freed. */
3559 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3560 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3561 isds_error err
= IE_SUCCESS
;
3562 xmlXPathObjectPtr result
= NULL
;
3564 if (!context
) return IE_INVALID_CONTEXT
;
3565 if (!envelope
) return IE_INVAL
;
3566 if (!xpath_ctx
) return IE_INVAL
;
3570 /* Allocate envelope */
3571 *envelope
= calloc(1, sizeof(**envelope
));
3577 /* Else free former data */
3578 zfree((*envelope
)->dmSenderOrgUnit
);
3579 zfree((*envelope
)->dmSenderOrgUnitNum
);
3580 zfree((*envelope
)->dbIDRecipient
);
3581 zfree((*envelope
)->dmRecipientOrgUnit
);
3582 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3583 zfree((*envelope
)->dmToHands
);
3584 zfree((*envelope
)->dmAnnotation
);
3585 zfree((*envelope
)->dmRecipientRefNumber
);
3586 zfree((*envelope
)->dmSenderRefNumber
);
3587 zfree((*envelope
)->dmRecipientIdent
);
3588 zfree((*envelope
)->dmSenderIdent
);
3589 zfree((*envelope
)->dmLegalTitleLaw
);
3590 zfree((*envelope
)->dmLegalTitleYear
);
3591 zfree((*envelope
)->dmLegalTitleSect
);
3592 zfree((*envelope
)->dmLegalTitlePar
);
3593 zfree((*envelope
)->dmLegalTitlePoint
);
3594 zfree((*envelope
)->dmPersonalDelivery
);
3595 zfree((*envelope
)->dmAllowSubstDelivery
);
3598 /* Extract envelope elements added by sender or ISDS
3599 * (XSD: gMessageEnvelopeSub type) */
3600 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3601 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3602 (*envelope
)->dmSenderOrgUnitNum
, 0);
3603 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3604 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3605 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3606 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3607 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3608 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3609 EXTRACT_STRING("isds:dmRecipientRefNumber",
3610 (*envelope
)->dmRecipientRefNumber
);
3611 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3612 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3613 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3615 /* Extract envelope elements regarding law reference */
3616 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3617 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3618 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3619 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3620 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3622 /* Extract envelope other elements */
3623 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3624 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3625 (*envelope
)->dmAllowSubstDelivery
);
3628 if (err
) isds_envelope_free(envelope
);
3629 xmlXPathFreeObject(result
);
3635 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3636 * isds_envelope structure. The envelope is automatically allocated but not
3637 * reallocated. The date are just appended into envelope structure.
3638 * @context is ISDS context
3639 * @envelope is automatically allocated message envelope structure
3640 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3641 * In case of error @envelope will be freed. */
3642 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3643 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3644 isds_error err
= IE_SUCCESS
;
3645 xmlXPathObjectPtr result
= NULL
;
3647 if (!context
) return IE_INVALID_CONTEXT
;
3648 if (!envelope
) return IE_INVAL
;
3649 if (!xpath_ctx
) return IE_INVAL
;
3653 /* Allocate envelope */
3654 *envelope
= calloc(1, sizeof(**envelope
));
3660 /* Else free former data */
3661 zfree((*envelope
)->dmID
);
3662 zfree((*envelope
)->dbIDSender
);
3663 zfree((*envelope
)->dmSender
);
3664 zfree((*envelope
)->dmSenderAddress
);
3665 zfree((*envelope
)->dmSenderType
);
3666 zfree((*envelope
)->dmRecipient
);
3667 zfree((*envelope
)->dmRecipientAddress
);
3668 zfree((*envelope
)->dmAmbiguousRecipient
);
3671 /* Extract envelope elements added by ISDS
3672 * (XSD: gMessageEnvelope type) */
3673 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3674 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3675 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3676 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3677 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3678 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3679 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3680 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3681 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3682 (*envelope
)->dmAmbiguousRecipient
);
3684 /* Extract envelope elements added by sender and ISDS
3685 * (XSD: gMessageEnvelope type) */
3686 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3687 if (err
) goto leave
;
3690 if (err
) isds_envelope_free(envelope
);
3691 xmlXPathFreeObject(result
);
3696 /* Convert other envelope elements from XML tree into isds_envelope structure:
3697 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3698 * The envelope is automatically allocated but not reallocated.
3699 * The data are just appended into envelope structure.
3700 * @context is ISDS context
3701 * @envelope is automatically allocated message envelope structure
3702 * @xpath_ctx is XPath context with current node as parent desired elements
3703 * In case of error @envelope will be freed. */
3704 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3705 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3706 isds_error err
= IE_SUCCESS
;
3707 xmlXPathObjectPtr result
= NULL
;
3708 char *string
= NULL
;
3709 unsigned long int *unumber
= NULL
;
3711 if (!context
) return IE_INVALID_CONTEXT
;
3712 if (!envelope
) return IE_INVAL
;
3713 if (!xpath_ctx
) return IE_INVAL
;
3718 *envelope
= calloc(1, sizeof(**envelope
));
3725 zfree((*envelope
)->dmMessageStatus
);
3726 zfree((*envelope
)->dmAttachmentSize
);
3727 zfree((*envelope
)->dmDeliveryTime
);
3728 zfree((*envelope
)->dmAcceptanceTime
);
3732 /* dmMessageStatus element is mandatory */
3733 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3735 isds_log_message(context
,
3736 _("Missing mandatory sisds:dmMessageStatus integer"));
3740 err
= uint2isds_message_status(context
, unumber
,
3741 &((*envelope
)->dmMessageStatus
));
3743 if (err
== IE_ENUM
) err
= IE_ISDS
;
3746 free(unumber
); unumber
= NULL
;
3748 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3751 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3753 err
= timestring2timeval((xmlChar
*) string
,
3754 &((*envelope
)->dmDeliveryTime
));
3756 char *string_locale
= _isds_utf82locale(string
);
3757 if (err
== IE_DATE
) err
= IE_ISDS
;
3758 isds_printf_message(context
,
3759 _("Could not convert dmDeliveryTime as ISO time: %s"),
3761 free(string_locale
);
3767 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3769 err
= timestring2timeval((xmlChar
*) string
,
3770 &((*envelope
)->dmAcceptanceTime
));
3772 char *string_locale
= _isds_utf82locale(string
);
3773 if (err
== IE_DATE
) err
= IE_ISDS
;
3774 isds_printf_message(context
,
3775 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3777 free(string_locale
);
3784 if (err
) isds_envelope_free(envelope
);
3787 xmlXPathFreeObject(result
);
3792 /* Convert message type attribute of current element into isds_envelope
3794 * TODO: This function can be incorporated into append_status_size_times() as
3795 * they are called always together.
3796 * The envelope is automatically allocated but not reallocated.
3797 * The data are just appended into envelope structure.
3798 * @context is ISDS context
3799 * @envelope is automatically allocated message envelope structure
3800 * @xpath_ctx is XPath context with current node as parent of attribute
3801 * carrying message type
3802 * In case of error @envelope will be freed. */
3803 static isds_error
append_message_type(struct isds_ctx
*context
,
3804 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3805 isds_error err
= IE_SUCCESS
;
3807 if (!context
) return IE_INVALID_CONTEXT
;
3808 if (!envelope
) return IE_INVAL
;
3809 if (!xpath_ctx
) return IE_INVAL
;
3814 *envelope
= calloc(1, sizeof(**envelope
));
3821 zfree((*envelope
)->dmType
);
3825 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3827 if (!(*envelope
)->dmType
) {
3828 /* Use default value */
3829 (*envelope
)->dmType
= strdup("V");
3830 if (!(*envelope
)->dmType
) {
3834 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3835 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3836 isds_printf_message(context
,
3837 _("Message type in dmType attribute is not 1 character long: "
3846 if (err
) isds_envelope_free(envelope
);
3852 /* Convert dmType isds_envelope member into XML attribute and append it to
3854 * @context is ISDS context
3855 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3856 * @dm_envelope is XML element the resulting attribute will be appended to.
3857 * @return error code, in case of error context' message is filled. */
3858 static isds_error
insert_message_type(struct isds_ctx
*context
,
3859 const char *type
, xmlNodePtr dm_envelope
) {
3860 isds_error err
= IE_SUCCESS
;
3861 xmlAttrPtr attribute_node
;
3863 if (!context
) return IE_INVALID_CONTEXT
;
3864 if (!dm_envelope
) return IE_INVAL
;
3866 /* Insert optional message type */
3868 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3869 char *type_locale
= _isds_utf82locale(type
);
3870 isds_printf_message(context
,
3871 _("Message type in envelope is not 1 character long: %s"),
3877 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3883 #endif /* HAVE_LIBCURL */
3886 /* Extract message document into reallocated document structure
3887 * @context is ISDS context
3888 * @document is automatically reallocated message documents structure
3889 * @xpath_ctx is XPath context with current node as isds:dmFile
3890 * In case of error @document will be freed. */
3891 static isds_error
extract_document(struct isds_ctx
*context
,
3892 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3893 isds_error err
= IE_SUCCESS
;
3894 xmlXPathObjectPtr result
= NULL
;
3895 xmlNodePtr file_node
;
3896 char *string
= NULL
;
3898 if (!context
) return IE_INVALID_CONTEXT
;
3899 if (!document
) return IE_INVAL
;
3900 isds_document_free(document
);
3901 if (!xpath_ctx
) return IE_INVAL
;
3902 file_node
= xpath_ctx
->node
;
3904 *document
= calloc(1, sizeof(**document
));
3910 /* Extract document meta data */
3911 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3912 if (context
->normalize_mime_type
) {
3913 const char *normalized_type
=
3914 isds_normalize_mime_type((*document
)->dmMimeType
);
3915 if (NULL
!= normalized_type
&&
3916 normalized_type
!= (*document
)->dmMimeType
) {
3917 char *new_type
= strdup(normalized_type
);
3918 if (NULL
== new_type
) {
3919 isds_printf_message(context
,
3920 _("Not enough memory to normalize document MIME type"));
3924 free((*document
)->dmMimeType
);
3925 (*document
)->dmMimeType
= new_type
;
3929 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3930 err
= string2isds_FileMetaType((xmlChar
*)string
,
3931 &((*document
)->dmFileMetaType
));
3933 char *meta_type_locale
= _isds_utf82locale(string
);
3934 isds_printf_message(context
,
3935 _("Document has invalid dmFileMetaType attribute value: %s"),
3937 free(meta_type_locale
);
3943 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3944 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3945 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3946 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3949 /* Extract document data.
3950 * Base64 encoded blob or XML subtree must be presented. */
3952 /* Check for dmEncodedContent */
3953 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3960 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3961 /* Here we have Base64 blob */
3962 (*document
)->is_xml
= 0;
3964 if (result
->nodesetval
->nodeNr
> 1) {
3965 isds_printf_message(context
,
3966 _("Document has more dmEncodedContent elements"));
3971 xmlXPathFreeObject(result
); result
= NULL
;
3972 EXTRACT_STRING("isds:dmEncodedContent", string
);
3974 /* Decode non-empty document */
3975 if (string
&& string
[0] != '\0') {
3976 (*document
)->data_length
=
3977 _isds_b64decode(string
, &((*document
)->data
));
3978 if ((*document
)->data_length
== (size_t) -1) {
3979 isds_printf_message(context
,
3980 _("Error while Base64-decoding document content"));
3986 /* No Base64 blob, try XML document */
3987 xmlXPathFreeObject(result
); result
= NULL
;
3988 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3995 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3996 /* Here we have XML document */
3997 (*document
)->is_xml
= 1;
3999 if (result
->nodesetval
->nodeNr
> 1) {
4000 isds_printf_message(context
,
4001 _("Document has more dmXMLContent elements"));
4006 /* XXX: We cannot serialize the content simply because:
4007 * - XML document may point out of its scope (e.g. to message
4009 * - isds:dmXMLContent can contain more elements, no element,
4011 * - it's not the XML way
4012 * Thus we provide the only right solution: XML DOM. Let's
4013 * application to cope with this hot potato :) */
4014 (*document
)->xml_node_list
=
4015 result
->nodesetval
->nodeTab
[0]->children
;
4017 /* No base64 blob, nor XML document */
4018 isds_printf_message(context
,
4019 _("Document has no dmEncodedContent, nor dmXMLContent "
4028 if (err
) isds_document_free(document
);
4030 xmlXPathFreeObject(result
);
4031 xpath_ctx
->node
= file_node
;
4037 /* Extract message documents into reallocated list of documents
4038 * @context is ISDS context
4039 * @documents is automatically reallocated message documents list structure
4040 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4041 * In case of error @documents will be freed. */
4042 static isds_error
extract_documents(struct isds_ctx
*context
,
4043 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4044 isds_error err
= IE_SUCCESS
;
4045 xmlXPathObjectPtr result
= NULL
;
4046 xmlNodePtr files_node
;
4047 struct isds_list
*document
, *prev_document
= NULL
;
4049 if (!context
) return IE_INVALID_CONTEXT
;
4050 if (!documents
) return IE_INVAL
;
4051 isds_list_free(documents
);
4052 if (!xpath_ctx
) return IE_INVAL
;
4053 files_node
= xpath_ctx
->node
;
4055 /* Find documents */
4056 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4063 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4064 isds_printf_message(context
,
4065 _("Message does not contain any document"));
4071 /* Iterate over documents */
4072 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4074 /* Allocate and append list item */
4075 document
= calloc(1, sizeof(*document
));
4080 document
->destructor
= (void (*)(void **))isds_document_free
;
4081 if (i
== 0) *documents
= document
;
4082 else prev_document
->next
= document
;
4083 prev_document
= document
;
4085 /* Extract document */
4086 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4087 err
= extract_document(context
,
4088 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4089 if (err
) goto leave
;
4094 if (err
) isds_list_free(documents
);
4095 xmlXPathFreeObject(result
);
4096 xpath_ctx
->node
= files_node
;
4102 /* Convert isds:dmRecord XML tree into structure
4103 * @context is ISDS context
4104 * @envelope is automatically reallocated message envelope structure
4105 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4106 * In case of error @envelope will be freed. */
4107 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4108 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4109 isds_error err
= IE_SUCCESS
;
4110 xmlXPathObjectPtr result
= NULL
;
4112 if (!context
) return IE_INVALID_CONTEXT
;
4113 if (!envelope
) return IE_INVAL
;
4114 isds_envelope_free(envelope
);
4115 if (!xpath_ctx
) return IE_INVAL
;
4118 *envelope
= calloc(1, sizeof(**envelope
));
4125 /* Extract tRecord data */
4126 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4128 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4129 * dmAcceptanceTime. */
4130 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4131 if (err
) goto leave
;
4133 /* Extract envelope elements added by sender and ISDS
4134 * (XSD: gMessageEnvelope type) */
4135 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4136 if (err
) goto leave
;
4138 /* Get message type */
4139 err
= append_message_type(context
, envelope
, xpath_ctx
);
4140 if (err
) goto leave
;
4144 if (err
) isds_envelope_free(envelope
);
4145 xmlXPathFreeObject(result
);
4150 /* Convert XSD:tStateChangesRecord type XML tree into structure
4151 * @context is ISDS context
4152 * @changed_status is automatically reallocated message state change structure
4153 * @xpath_ctx is XPath context with current node as element of
4154 * XSD:tStateChangesRecord type
4155 * In case of error @changed_status will be freed. */
4156 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4157 struct isds_message_status_change
**changed_status
,
4158 xmlXPathContextPtr xpath_ctx
) {
4159 isds_error err
= IE_SUCCESS
;
4160 xmlXPathObjectPtr result
= NULL
;
4161 unsigned long int *unumber
= NULL
;
4162 char *string
= NULL
;
4164 if (!context
) return IE_INVALID_CONTEXT
;
4165 if (!changed_status
) return IE_INVAL
;
4166 isds_message_status_change_free(changed_status
);
4167 if (!xpath_ctx
) return IE_INVAL
;
4170 *changed_status
= calloc(1, sizeof(**changed_status
));
4171 if (!*changed_status
) {
4177 /* Extract tGetStateChangesInput data */
4178 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4180 /* dmEventTime is mandatory */
4181 EXTRACT_STRING("isds:dmEventTime", string
);
4183 err
= timestring2timeval((xmlChar
*) string
,
4184 &((*changed_status
)->time
));
4186 char *string_locale
= _isds_utf82locale(string
);
4187 if (err
== IE_DATE
) err
= IE_ISDS
;
4188 isds_printf_message(context
,
4189 _("Could not convert dmEventTime as ISO time: %s"),
4191 free(string_locale
);
4197 /* dmMessageStatus element is mandatory */
4198 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4200 isds_log_message(context
,
4201 _("Missing mandatory isds:dmMessageStatus integer"));
4205 err
= uint2isds_message_status(context
, unumber
,
4206 &((*changed_status
)->dmMessageStatus
));
4208 if (err
== IE_ENUM
) err
= IE_ISDS
;
4217 if (err
) isds_message_status_change_free(changed_status
);
4218 xmlXPathFreeObject(result
);
4221 #endif /* HAVE_LIBCURL */
4224 /* Find and convert isds:dmHash XML tree into structure
4225 * @context is ISDS context
4226 * @envelope is automatically reallocated message hash structure
4227 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4228 * In case of error @hash will be freed. */
4229 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4230 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4231 isds_error err
= IE_SUCCESS
;
4232 xmlNodePtr old_ctx_node
;
4233 xmlXPathObjectPtr result
= NULL
;
4234 char *string
= NULL
;
4236 if (!context
) return IE_INVALID_CONTEXT
;
4237 if (!hash
) return IE_INVAL
;
4238 isds_hash_free(hash
);
4239 if (!xpath_ctx
) return IE_INVAL
;
4241 old_ctx_node
= xpath_ctx
->node
;
4243 *hash
= calloc(1, sizeof(**hash
));
4250 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4251 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4260 /* Get hash algorithm */
4261 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4262 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4264 if (err
== IE_ENUM
) {
4265 char *string_locale
= _isds_utf82locale(string
);
4266 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4268 free(string_locale
);
4274 /* Get hash value */
4275 EXTRACT_STRING(".", string
);
4277 isds_printf_message(context
,
4278 _("sisds:dmHash element is missing hash value"));
4282 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4283 if ((*hash
)->length
== (size_t) -1) {
4284 isds_printf_message(context
,
4285 _("Error while Base64-decoding hash value"));
4291 if (err
) isds_hash_free(hash
);
4293 xmlXPathFreeObject(result
);
4294 xpath_ctx
->node
= old_ctx_node
;
4299 /* Find and append isds:dmQTimestamp XML tree into envelope.
4300 * Because one service is allowed to miss time-stamp content, and we think
4301 * other could too (flaw in specification), this function is deliberated and
4302 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4303 * @context is ISDS context
4304 * @envelope is automatically allocated envelope structure
4305 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4307 * In case of error @envelope will be freed. */
4308 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4309 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4310 isds_error err
= IE_SUCCESS
;
4311 xmlXPathObjectPtr result
= NULL
;
4312 char *string
= NULL
;
4314 if (!context
) return IE_INVALID_CONTEXT
;
4315 if (!envelope
) return IE_INVAL
;
4317 isds_envelope_free(envelope
);
4322 *envelope
= calloc(1, sizeof(**envelope
));
4328 zfree((*envelope
)->timestamp
);
4329 (*envelope
)->timestamp_length
= 0;
4332 /* Get dmQTimestamp */
4333 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4335 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4338 (*envelope
)->timestamp_length
=
4339 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4340 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4341 isds_printf_message(context
,
4342 _("Error while Base64-decoding time stamp value"));
4348 if (err
) isds_envelope_free(envelope
);
4350 xmlXPathFreeObject(result
);
4355 /* Convert XSD tReturnedMessage XML tree into message structure.
4356 * It does not store serialized XML tree into message->raw.
4357 * It does store (pointer to) parsed XML tree into message->xml if needed.
4358 * @context is ISDS context
4359 * @include_documents Use true if documents must be extracted
4360 * (tReturnedMessage XSD type), use false if documents shall be omitted
4361 * (tReturnedMessageEnvelope).
4362 * @message is automatically reallocated message structure
4363 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4365 * In case of error @message will be freed. */
4366 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4367 const _Bool include_documents
, struct isds_message
**message
,
4368 xmlXPathContextPtr xpath_ctx
) {
4369 isds_error err
= IE_SUCCESS
;
4370 xmlNodePtr message_node
;
4372 if (!context
) return IE_INVALID_CONTEXT
;
4373 if (!message
) return IE_INVAL
;
4374 isds_message_free(message
);
4375 if (!xpath_ctx
) return IE_INVAL
;
4378 *message
= calloc(1, sizeof(**message
));
4384 /* Save message XPATH context node */
4385 message_node
= xpath_ctx
->node
;
4389 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4390 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4391 if (err
) { err
= IE_ERROR
; goto leave
; }
4392 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4393 if (err
) goto leave
;
4395 if (include_documents
) {
4396 struct isds_list
*item
;
4398 /* Extract dmFiles */
4399 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4401 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4402 err
= IE_ISDS
; goto leave
;
4404 if (err
) { err
= IE_ERROR
; goto leave
; }
4405 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4406 if (err
) goto leave
;
4408 /* Store xmlDoc of this message if needed */
4409 /* Only if we got a XML document in all the documents. */
4410 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4411 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4412 (*message
)->xml
= xpath_ctx
->doc
;
4419 /* Restore context to message */
4420 xpath_ctx
->node
= message_node
;
4422 /* Extract dmHash */
4423 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4425 if (err
) goto leave
;
4427 /* Extract dmQTimestamp, */
4428 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4430 if (err
) goto leave
;
4432 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4433 * dmAcceptanceTime. */
4434 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4435 if (err
) goto leave
;
4437 /* Get message type */
4438 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4439 if (err
) goto leave
;
4442 if (err
) isds_message_free(message
);
4447 /* Extract message event into reallocated isds_event structure
4448 * @context is ISDS context
4449 * @event is automatically reallocated message event structure
4450 * @xpath_ctx is XPath context with current node as isds:dmEvent
4451 * In case of error @event will be freed. */
4452 static isds_error
extract_event(struct isds_ctx
*context
,
4453 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4454 isds_error err
= IE_SUCCESS
;
4455 xmlXPathObjectPtr result
= NULL
;
4456 xmlNodePtr event_node
;
4457 char *string
= NULL
;
4459 if (!context
) return IE_INVALID_CONTEXT
;
4460 if (!event
) return IE_INVAL
;
4461 isds_event_free(event
);
4462 if (!xpath_ctx
) return IE_INVAL
;
4463 event_node
= xpath_ctx
->node
;
4465 *event
= calloc(1, sizeof(**event
));
4471 /* Extract event data.
4472 * All elements are optional according XSD. That's funny. */
4473 EXTRACT_STRING("sisds:dmEventTime", string
);
4475 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4477 char *string_locale
= _isds_utf82locale(string
);
4478 if (err
== IE_DATE
) err
= IE_ISDS
;
4479 isds_printf_message(context
,
4480 _("Could not convert dmEventTime as ISO time: %s"),
4482 free(string_locale
);
4488 /* dmEventDescr element has prefix and the rest */
4489 EXTRACT_STRING("sisds:dmEventDescr", string
);
4491 err
= eventstring2event((xmlChar
*) string
, *event
);
4492 if (err
) goto leave
;
4497 if (err
) isds_event_free(event
);
4499 xmlXPathFreeObject(result
);
4500 xpath_ctx
->node
= event_node
;
4505 /* Convert element of XSD tEventsArray type from XML tree into
4506 * isds_list of isds_event's structure. The list is automatically reallocated.
4507 * @context is ISDS context
4508 * @events is automatically reallocated list of event structures
4509 * @xpath_ctx is XPath context with current node as tEventsArray
4510 * In case of error @events will be freed. */
4511 static isds_error
extract_events(struct isds_ctx
*context
,
4512 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4513 isds_error err
= IE_SUCCESS
;
4514 xmlXPathObjectPtr result
= NULL
;
4515 xmlNodePtr events_node
;
4516 struct isds_list
*event
, *prev_event
= NULL
;
4518 if (!context
) return IE_INVALID_CONTEXT
;
4519 if (!events
) return IE_INVAL
;
4520 if (!xpath_ctx
) return IE_INVAL
;
4521 events_node
= xpath_ctx
->node
;
4524 isds_list_free(events
);
4527 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4534 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4535 isds_printf_message(context
,
4536 _("Delivery info does not contain any event"));
4542 /* Iterate over events */
4543 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4545 /* Allocate and append list item */
4546 event
= calloc(1, sizeof(*event
));
4551 event
->destructor
= (void (*)(void **))isds_event_free
;
4552 if (i
== 0) *events
= event
;
4553 else prev_event
->next
= event
;
4557 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4558 err
= extract_event(context
,
4559 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4560 if (err
) goto leave
;
4565 if (err
) isds_list_free(events
);
4566 xmlXPathFreeObject(result
);
4567 xpath_ctx
->node
= events_node
;
4573 /* Insert Base64 encoded data as element with text child.
4574 * @context is session context
4575 * @parent is XML node to append @element with @data as child
4576 * @ns is XML namespace of @element, use NULL to inherit from @parent
4577 * @element is UTF-8 encoded name of new element
4578 * @data is bit stream to encode into @element
4579 * @length is size of @data in bytes
4580 * @return standard error code and fill long error message if needed */
4581 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4582 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4583 const void *data
, size_t length
) {
4584 isds_error err
= IE_SUCCESS
;
4587 if (!context
) return IE_INVALID_CONTEXT
;
4588 if (!data
&& length
> 0) return IE_INVAL
;
4589 if (!parent
|| !element
) return IE_INVAL
;
4591 xmlChar
*base64data
= NULL
;
4592 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4594 isds_printf_message(context
,
4595 ngettext("Not enough memory to encode %zd byte into Base64",
4596 "Not enough memory to encode %zd bytes into Base64",
4602 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4610 /* Convert isds_document structure into XML tree and append to dmFiles node.
4611 * @context is session context
4612 * @document is ISDS document
4613 * @dm_files is XML element the resulting tree will be appended to as a child.
4614 * @return error code, in case of error context' message is filled. */
4615 static isds_error
insert_document(struct isds_ctx
*context
,
4616 struct isds_document
*document
, xmlNodePtr dm_files
) {
4617 isds_error err
= IE_SUCCESS
;
4618 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4619 xmlAttrPtr attribute_node
;
4621 if (!context
) return IE_INVALID_CONTEXT
;
4622 if (!document
|| !dm_files
) return IE_INVAL
;
4624 /* Allocate new dmFile */
4625 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4627 isds_printf_message(context
, _("Could not allocate main dmFile"));
4631 /* Append the new dmFile.
4632 * XXX: Main document must go first */
4633 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4634 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4636 file
= xmlAddChild(dm_files
, new_file
);
4639 xmlFreeNode(new_file
); new_file
= NULL
;
4640 isds_printf_message(context
, _("Could not add dmFile child to "
4641 "%s element"), dm_files
->name
);
4646 /* @dmMimeType is required */
4647 if (!document
->dmMimeType
) {
4648 isds_log_message(context
,
4649 _("Document is missing mandatory MIME type definition"));
4653 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4655 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4657 isds_printf_message(context
,
4658 _("Document has unknown dmFileMetaType: %ld"),
4659 document
->dmFileMetaType
);
4663 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4665 if (document
->dmFileGuid
) {
4666 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4668 if (document
->dmUpFileGuid
) {
4669 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4672 /* @dmFileDescr is required */
4673 if (!document
->dmFileDescr
) {
4674 isds_log_message(context
,
4675 _("Document is missing mandatory description (title)"));
4679 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4681 if (document
->dmFormat
) {
4682 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4686 /* Insert content (body) of the document. */
4687 if (document
->is_xml
) {
4688 /* XML document requested */
4690 /* Allocate new dmXMLContent */
4691 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4693 isds_printf_message(context
,
4694 _("Could not allocate dmXMLContent element"));
4699 node
= xmlAddChild(file
, xmlcontent
);
4701 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4702 isds_printf_message(context
,
4703 _("Could not add dmXMLContent child to %s element"),
4709 /* Copy non-empty node list */
4710 if (document
->xml_node_list
) {
4711 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4712 document
->xml_node_list
);
4714 isds_printf_message(context
,
4715 _("Not enough memory to copy XML document"));
4720 if (!xmlAddChildList(node
, content
)) {
4721 xmlFreeNodeList(content
);
4722 isds_printf_message(context
,
4723 _("Error while adding XML document into dmXMLContent"));
4727 /* XXX: We cannot free the content here because it's part of node's
4728 * document since now. It will be freed with it automatically. */
4731 /* Binary document requested */
4732 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4733 document
->data
, document
->data_length
);
4734 if (err
) goto leave
;
4742 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4743 * The copy must be preallocated, the date are just appended into structure.
4744 * @context is ISDS context
4745 * @copy is message copy structure
4746 * @xpath_ctx is XPath context with current node as tMStatus */
4747 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4748 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4749 isds_error err
= IE_SUCCESS
;
4750 xmlXPathObjectPtr result
= NULL
;
4751 char *code
= NULL
, *message
= NULL
;
4753 if (!context
) return IE_INVALID_CONTEXT
;
4754 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4756 /* Free old values */
4757 zfree(copy
->dmStatus
);
4760 /* Get error specific to this copy */
4761 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4763 isds_log_message(context
,
4764 _("Missing isds:dmStatusCode under "
4765 "XSD:tMStatus type element"));
4770 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4771 /* This copy failed */
4772 copy
->error
= IE_ISDS
;
4773 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4775 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4776 if (!copy
->dmStatus
) {
4777 copy
->dmStatus
= code
;
4781 copy
->dmStatus
= code
;
4785 /* This copy succeeded. In this case only, message ID is valid */
4786 copy
->error
= IE_SUCCESS
;
4788 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4790 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4791 "but did not returned assigned message ID\n"));
4799 xmlXPathFreeObject(result
);
4804 /* Insert struct isds_approval data (box approval) into XML tree
4805 * @context is session context
4806 * @approval is libisds structure with approval description. NULL is
4808 * @parent is XML element to append @approval to */
4809 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4810 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4812 isds_error err
= IE_SUCCESS
;
4815 if (!context
) return IE_INVALID_CONTEXT
;
4816 if (!parent
) return IE_INVAL
;
4818 if (!approval
) return IE_SUCCESS
;
4820 /* Build XSD:gExtApproval */
4821 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4822 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4829 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4831 * @context is session context
4832 * @service_name is name of SERVICE_DB_ACCESS
4833 * @response is reallocated server SOAP body response as XML document
4834 * @raw_response is reallocated bit stream with response body. Use
4835 * NULL if you don't care
4836 * @raw_response_length is size of @raw_response in bytes
4837 * @code is reallocated ISDS status code
4838 * @status_message is reallocated ISDS status message
4839 * @return error coded from lower layer, context message will be set up
4841 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4842 const xmlChar
*service_name
,
4843 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4844 xmlChar
**code
, xmlChar
**status_message
) {
4846 isds_error err
= IE_SUCCESS
;
4847 char *service_name_locale
= NULL
;
4848 xmlNodePtr request
= NULL
, node
;
4849 xmlNsPtr isds_ns
= NULL
;
4851 if (!context
) return IE_INVALID_CONTEXT
;
4852 if (!service_name
) return IE_INVAL
;
4853 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4854 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4856 /* Free output argument */
4857 xmlFreeDoc(*response
); *response
= NULL
;
4858 if (raw_response
) zfree(*raw_response
);
4860 zfree(*status_message
);
4863 /* Check if connection is established
4864 * TODO: This check should be done downstairs. */
4865 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4867 service_name_locale
= _isds_utf82locale((char*)service_name
);
4868 if (!service_name_locale
) {
4874 request
= xmlNewNode(NULL
, service_name
);
4876 isds_printf_message(context
,
4877 _("Could not build %s request"), service_name_locale
);
4881 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4883 isds_log_message(context
, _("Could not create ISDS name space"));
4887 xmlSetNs(request
, isds_ns
);
4890 /* Add XSD:tDummyInput child */
4891 INSERT_STRING(request
, "dbDummy", NULL
);
4894 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4895 service_name_locale
);
4898 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4899 raw_response
, raw_response_length
);
4900 xmlFreeNode(request
); request
= NULL
;
4903 isds_log(ILF_ISDS
, ILL_DEBUG
,
4904 _("Processing ISDS response on %s request failed\n"),
4905 service_name_locale
);
4909 /* Check for response status */
4910 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4911 code
, status_message
, NULL
);
4913 isds_log(ILF_ISDS
, ILL_DEBUG
,
4914 _("ISDS response on %s request is missing status\n"),
4915 service_name_locale
);
4919 /* Request processed, but nothing found */
4920 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4921 char *code_locale
= _isds_utf82locale((char*) *code
);
4922 char *status_message_locale
=
4923 _isds_utf82locale((char*) *status_message
);
4924 isds_log(ILF_ISDS
, ILL_DEBUG
,
4925 _("Server refused %s request (code=%s, message=%s)\n"),
4926 service_name_locale
, code_locale
, status_message_locale
);
4927 isds_log_message(context
, status_message_locale
);
4929 free(status_message_locale
);
4935 free(service_name_locale
);
4936 xmlFreeNode(request
);
4942 /* Get data about logged in user and his box. */
4943 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4944 struct isds_DbOwnerInfo
**db_owner_info
) {
4945 isds_error err
= IE_SUCCESS
;
4947 xmlDocPtr response
= NULL
;
4948 xmlChar
*code
= NULL
, *message
= NULL
;
4949 xmlXPathContextPtr xpath_ctx
= NULL
;
4950 xmlXPathObjectPtr result
= NULL
;
4951 char *string
= NULL
;
4954 if (!context
) return IE_INVALID_CONTEXT
;
4955 zfree(context
->long_message
);
4956 if (!db_owner_info
) return IE_INVAL
;
4957 isds_DbOwnerInfo_free(db_owner_info
);
4960 /* Check if connection is established */
4961 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4964 /* Do request and check for success */
4965 err
= build_send_check_dbdummy_request(context
,
4966 BAD_CAST
"GetOwnerInfoFromLogin",
4967 &response
, NULL
, NULL
, &code
, &message
);
4968 if (err
) goto leave
;
4972 /* Prepare structure */
4973 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4974 if (!*db_owner_info
) {
4978 xpath_ctx
= xmlXPathNewContext(response
);
4983 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4988 /* Set context node */
4989 result
= xmlXPathEvalExpression(BAD_CAST
4990 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4995 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4996 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5000 if (result
->nodesetval
->nodeNr
> 1) {
5001 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5005 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5006 xmlXPathFreeObject(result
); result
= NULL
;
5009 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5014 isds_DbOwnerInfo_free(db_owner_info
);
5018 xmlXPathFreeObject(result
);
5019 xmlXPathFreeContext(xpath_ctx
);
5023 xmlFreeDoc(response
);
5026 isds_log(ILF_ISDS
, ILL_DEBUG
,
5027 _("GetOwnerInfoFromLogin request processed by server "
5028 "successfully.\n"));
5029 #else /* not HAVE_LIBCURL */
5037 /* Get data about logged in user. */
5038 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5039 struct isds_DbUserInfo
**db_user_info
) {
5040 isds_error err
= IE_SUCCESS
;
5042 xmlDocPtr response
= NULL
;
5043 xmlChar
*code
= NULL
, *message
= NULL
;
5044 xmlXPathContextPtr xpath_ctx
= NULL
;
5045 xmlXPathObjectPtr result
= NULL
;
5048 if (!context
) return IE_INVALID_CONTEXT
;
5049 zfree(context
->long_message
);
5050 if (!db_user_info
) return IE_INVAL
;
5051 isds_DbUserInfo_free(db_user_info
);
5054 /* Check if connection is established */
5055 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5058 /* Do request and check for success */
5059 err
= build_send_check_dbdummy_request(context
,
5060 BAD_CAST
"GetUserInfoFromLogin",
5061 &response
, NULL
, NULL
, &code
, &message
);
5062 if (err
) goto leave
;
5066 /* Prepare structure */
5067 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5068 if (!*db_user_info
) {
5072 xpath_ctx
= xmlXPathNewContext(response
);
5077 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5082 /* Set context node */
5083 result
= xmlXPathEvalExpression(BAD_CAST
5084 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5089 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5090 isds_log_message(context
, _("Missing dbUserInfo element"));
5094 if (result
->nodesetval
->nodeNr
> 1) {
5095 isds_log_message(context
, _("Multiple dbUserInfo element"));
5099 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5100 xmlXPathFreeObject(result
); result
= NULL
;
5103 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5107 isds_DbUserInfo_free(db_user_info
);
5110 xmlXPathFreeObject(result
);
5111 xmlXPathFreeContext(xpath_ctx
);
5115 xmlFreeDoc(response
);
5118 isds_log(ILF_ISDS
, ILL_DEBUG
,
5119 _("GetUserInfoFromLogin request processed by server "
5120 "successfully.\n"));
5121 #else /* not HAVE_LIBCURL */
5129 /* Get expiration time of current password
5130 * @context is session context
5131 * @expiration is automatically reallocated time when password expires. If
5132 * password expiration is disabled, NULL will be returned. In case of error
5133 * it will be nulled too. */
5134 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5135 struct timeval
**expiration
) {
5136 isds_error err
= IE_SUCCESS
;
5138 xmlDocPtr response
= NULL
;
5139 xmlChar
*code
= NULL
, *message
= NULL
;
5140 xmlXPathContextPtr xpath_ctx
= NULL
;
5141 xmlXPathObjectPtr result
= NULL
;
5142 char *string
= NULL
;
5145 if (!context
) return IE_INVALID_CONTEXT
;
5146 zfree(context
->long_message
);
5147 if (!expiration
) return IE_INVAL
;
5151 /* Check if connection is established */
5152 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5155 /* Do request and check for success */
5156 err
= build_send_check_dbdummy_request(context
,
5157 BAD_CAST
"GetPasswordInfo",
5158 &response
, NULL
, NULL
, &code
, &message
);
5159 if (err
) goto leave
;
5163 xpath_ctx
= xmlXPathNewContext(response
);
5168 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5173 /* Set context node */
5174 result
= xmlXPathEvalExpression(BAD_CAST
5175 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5180 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5181 isds_log_message(context
,
5182 _("Missing GetPasswordInfoResponse element"));
5186 if (result
->nodesetval
->nodeNr
> 1) {
5187 isds_log_message(context
,
5188 _("Multiple GetPasswordInfoResponse element"));
5192 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5193 xmlXPathFreeObject(result
); result
= NULL
;
5195 /* Extract expiration date */
5196 EXTRACT_STRING("isds:pswExpDate", string
);
5198 /* And convert it if any returned. Otherwise expiration is disabled. */
5199 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5201 char *string_locale
= _isds_utf82locale(string
);
5202 if (err
== IE_DATE
) err
= IE_ISDS
;
5203 isds_printf_message(context
,
5204 _("Could not convert pswExpDate as ISO time: %s"),
5206 free(string_locale
);
5219 xmlXPathFreeObject(result
);
5220 xmlXPathFreeContext(xpath_ctx
);
5224 xmlFreeDoc(response
);
5227 isds_log(ILF_ISDS
, ILL_DEBUG
,
5228 _("GetPasswordInfo request processed by server "
5229 "successfully.\n"));
5230 #else /* not HAVE_LIBCURL */
5239 /* Request delivering new TOTP code from ISDS through side channel before
5240 * changing password.
5241 * @context is session context
5242 * @password is current password.
5243 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5244 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5245 * function for more details.
5246 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5247 * NULL, if you don't care.
5248 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5250 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5251 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5252 isds_error err
= IE_SUCCESS
;
5253 char *saved_url
= NULL
; /* No copy */
5254 #if HAVE_CURL_REAUTHORIZATION_BUG
5255 CURL
*saved_curl
= NULL
; /* No copy */
5257 xmlNsPtr isds_ns
= NULL
;
5258 xmlNodePtr request
= NULL
;
5259 xmlDocPtr response
= NULL
;
5260 xmlChar
*code
= NULL
, *message
= NULL
;
5261 const xmlChar
*codes
[] = {
5266 const char *meanings
[] = {
5267 N_("Unexpected error"),
5268 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5269 N_("One-time code could not been sent. Try later again.")
5271 const isds_otp_resolution resolutions
[] = {
5272 OTP_RESOLUTION_UNKNOWN
,
5273 OTP_RESOLUTION_TO_FAST
,
5274 OTP_RESOLUTION_TOTP_NOT_SENT
5277 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5278 zfree(context
->long_message
);
5279 if (NULL
== password
) {
5280 isds_log_message(context
,
5281 _("Second argument (password) of isds_change_password() "
5286 /* Check if connection is established
5287 * TODO: This check should be done downstairs. */
5288 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5290 if (!context
->otp
) {
5291 isds_log_message(context
, _("This function requires OTP-authenticated "
5293 return IE_INVALID_CONTEXT
;
5296 isds_log_message(context
, _("If one-time password authentication "
5297 "method is in use, requesting new OTP code requires "
5298 "one-time credentials argument either"));
5301 if (otp
->method
!= OTP_TIME
) {
5302 isds_log_message(context
, _("Requesting new time-based OTP code from "
5303 "server requires one-time password authentication "
5307 if (otp
->otp_code
!= NULL
) {
5308 isds_log_message(context
, _("Requesting new time-based OTP code from "
5309 "server requires undefined OTP code member in "
5310 "one-time credentials argument"));
5316 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5318 isds_log_message(context
, _("Could not build SendSMSCode request"));
5321 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5323 isds_log_message(context
, _("Could not create ISDS name space"));
5324 xmlFreeNode(request
);
5327 xmlSetNs(request
, isds_ns
);
5329 /* Change URL temporarily for sending this request only */
5331 char *new_url
= NULL
;
5332 if ((err
= _isds_build_url_from_context(context
,
5333 "%.*sasws/changePassword", &new_url
))) {
5336 saved_url
= context
->url
;
5337 context
->url
= new_url
;
5340 /* Store credentials for sending this request only */
5341 context
->otp_credentials
= otp
;
5342 _isds_discard_credentials(context
, 0);
5343 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5345 _isds_discard_credentials(context
, 0);
5348 #if HAVE_CURL_REAUTHORIZATION_BUG
5349 saved_curl
= context
->curl
;
5350 context
->curl
= curl_easy_init();
5351 if (NULL
== context
->curl
) {
5355 if (context
->timeout
) {
5356 err
= isds_set_timeout(context
, context
->timeout
);
5357 if (err
) goto leave
;
5361 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5364 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5366 /* Remove temporal credentials */
5367 _isds_discard_credentials(context
, 0);
5368 /* Detach pointer to OTP credentials from context */
5369 context
->otp_credentials
= NULL
;
5370 /* Keep context->otp true to keep signaling this is OTP session */
5372 /* Destroy request */
5373 xmlFreeNode(request
); request
= NULL
;
5376 isds_log(ILF_ISDS
, ILL_DEBUG
,
5377 _("Processing ISDS response on SendSMSCode request failed\n"));
5381 /* Check for response status */
5382 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5383 &code
, &message
, (xmlChar
**)refnumber
);
5385 isds_log(ILF_ISDS
, ILL_DEBUG
,
5386 _("ISDS response on SendSMSCode request is missing "
5391 /* Check for error */
5392 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5393 char *code_locale
= _isds_utf82locale((char*)code
);
5394 char *message_locale
= _isds_utf82locale((char*)message
);
5396 isds_log(ILF_ISDS
, ILL_DEBUG
,
5397 _("Server refused to send new code on SendSMSCode "
5398 "request (code=%s, message=%s)\n"),
5399 code_locale
, message_locale
);
5401 /* Check for known error codes */
5402 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5403 if (!xmlStrcmp(code
, codes
[i
])) break;
5405 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5406 isds_log_message(context
, _(meanings
[i
]));
5407 /* Mimic otp->resolution according to the code, specification does
5408 * prescribe OTP header to be available. */
5409 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5410 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5411 otp
->resolution
= resolutions
[i
];
5413 isds_log_message(context
, message_locale
);
5416 free(message_locale
);
5422 /* Otherwise new code sent successfully */
5423 /* Mimic otp->resolution according to the code, specification does
5424 * prescribe OTP header to be available. */
5425 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5426 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5429 if (NULL
!= saved_url
) {
5430 /* Revert URL to original one */
5431 zfree(context
->url
);
5432 context
->url
= saved_url
;
5434 #if HAVE_CURL_REAUTHORIZATION_BUG
5435 if (NULL
!= saved_curl
) {
5436 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5437 context
->curl
= saved_curl
;
5443 xmlFreeDoc(response
);
5444 xmlFreeNode(request
);
5447 isds_log(ILF_ISDS
, ILL_DEBUG
,
5448 _("New OTP code has been sent successfully on SendSMSCode "
5454 /* Convert response status code to isds_error code and set long message
5455 * @context is context to save long message to
5456 * @map is mapping from codes to errors and messages. Pass NULL for generic
5458 * @code is status code to translate
5459 * @message is non-localized status message to put into long message in case
5460 * of uknown error. It can be NULL if server did not provide any.
5461 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5462 * invalid invocation. */
5463 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5464 const struct code_map_isds_error
*map
,
5465 const xmlChar
*code
, const xmlChar
*message
) {
5467 isds_log_message(context
,
5468 _("NULL status code passed to statuscode2isds_error()"));
5473 /* Check for known error codes */
5474 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5475 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5476 isds_log_message(context
, _(map
->meanings
[i
]));
5477 return map
->errors
[i
];
5483 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5484 char *message_locale
= _isds_utf82locale((char*)message
);
5485 if (NULL
== message_locale
)
5486 isds_log_message(context
, _("ISDS server returned unknown error"));
5488 isds_log_message(context
, message_locale
);
5489 free(message_locale
);
5498 /* Change user password in ISDS.
5499 * User must supply old password, new password will takes effect after some
5500 * time, current session can continue. Password must fulfill some constraints.
5501 * @context is session context
5502 * @old_password is current password.
5503 * @new_password is requested new password
5504 * @otp auxiliary data required if one-time password authentication is in use,
5505 * defines OTP code (if known) and returns fine grade resolution of OTP
5506 * procedure. Pass NULL, if one-time password authentication is not needed.
5507 * Please note the @otp argument must match OTP method used at log-in time. See
5508 * isds_login() function for more details.
5509 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5510 * NULL, if you don't care.
5511 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5512 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5513 * awaiting OTP code that has been delivered by side channel to the user. */
5514 isds_error
isds_change_password(struct isds_ctx
*context
,
5515 const char *old_password
, const char *new_password
,
5516 struct isds_otp
*otp
, char **refnumber
) {
5517 isds_error err
= IE_SUCCESS
;
5519 char *saved_url
= NULL
; /* No copy */
5520 #if HAVE_CURL_REAUTHORIZATION_BUG
5521 CURL
*saved_curl
= NULL
; /* No copy */
5523 xmlNsPtr isds_ns
= NULL
;
5524 xmlNodePtr request
= NULL
, node
;
5525 xmlDocPtr response
= NULL
;
5526 xmlChar
*code
= NULL
, *message
= NULL
;
5527 const xmlChar
*codes
[] = {
5540 const char *meanings
[] = {
5541 N_("Password length must be between 8 and 32 characters"),
5542 N_("Password cannot be reused"), /* Server does not distinguish 1067
5543 and 1091 on ChangePasswordOTP */
5544 N_("Password contains forbidden character"),
5545 N_("Password must contain at least one upper-case letter, "
5546 "one lower-case, and one digit"),
5547 N_("Password cannot contain sequence of three identical characters"),
5548 N_("Password cannot contain user identifier"),
5549 N_("Password is too simmple"),
5550 N_("Old password is not valid"),
5551 N_("Password cannot be reused"),
5552 N_("Unexpected error"),
5553 N_("LDAP update error")
5557 if (!context
) return IE_INVALID_CONTEXT
;
5558 zfree(context
->long_message
);
5559 if (NULL
!= refnumber
)
5561 if (NULL
== old_password
) {
5562 isds_log_message(context
,
5563 _("Second argument (old password) of isds_change_password() "
5567 if (NULL
== otp
&& NULL
== new_password
) {
5568 isds_log_message(context
,
5569 _("Third argument (new password) of isds_change_password() "
5575 /* Check if connection is established
5576 * TODO: This check should be done downstairs. */
5577 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5579 if (context
->otp
&& NULL
== otp
) {
5580 isds_log_message(context
, _("If one-time password authentication "
5581 "method is in use, changing password requires one-time "
5582 "credentials either"));
5586 /* Build ChangeISDSPassword request */
5587 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5588 BAD_CAST
"ChangePasswordOTP");
5590 isds_log_message(context
, (NULL
== otp
) ?
5591 _("Could not build ChangeISDSPassword request") :
5592 _("Could not build ChangePasswordOTP request"));
5595 isds_ns
= xmlNewNs(request
,
5596 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5599 isds_log_message(context
, _("Could not create ISDS name space"));
5600 xmlFreeNode(request
);
5603 xmlSetNs(request
, isds_ns
);
5605 INSERT_STRING(request
, "dbOldPassword", old_password
);
5606 INSERT_STRING(request
, "dbNewPassword", new_password
);
5609 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5610 switch (otp
->method
) {
5612 isds_log(ILF_SEC
, ILL_INFO
,
5613 _("Selected authentication method: "
5614 "HMAC-based one-time password\n"));
5615 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5618 isds_log(ILF_SEC
, ILL_INFO
,
5619 _("Selected authentication method: "
5620 "Time-based one-time password\n"));
5621 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5622 if (otp
->otp_code
== NULL
) {
5623 isds_log(ILF_SEC
, ILL_INFO
,
5624 _("OTP code has not been provided by "
5625 "application, requesting server for "
5627 err
= _isds_request_totp_code(context
, old_password
, otp
,
5629 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5633 isds_log(ILF_SEC
, ILL_INFO
,
5634 _("OTP code has been provided by "
5635 "application, not requesting server "
5640 isds_log_message(context
,
5641 _("Unknown one-time password authentication "
5642 "method requested by application"));
5647 /* Change URL temporarily for sending this request only */
5649 char *new_url
= NULL
;
5650 if ((err
= _isds_build_url_from_context(context
,
5651 "%.*sasws/changePassword", &new_url
))) {
5654 saved_url
= context
->url
;
5655 context
->url
= new_url
;
5658 /* Store credentials for sending this request only */
5659 context
->otp_credentials
= otp
;
5660 _isds_discard_credentials(context
, 0);
5661 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5662 old_password
, NULL
))) {
5663 _isds_discard_credentials(context
, 0);
5666 #if HAVE_CURL_REAUTHORIZATION_BUG
5667 saved_curl
= context
->curl
;
5668 context
->curl
= curl_easy_init();
5669 if (NULL
== context
->curl
) {
5673 if (context
->timeout
) {
5674 err
= isds_set_timeout(context
, context
->timeout
);
5675 if (err
) goto leave
;
5680 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5681 _("Sending ChangeISDSPassword request to ISDS\n") :
5682 _("Sending ChangePasswordOTP request to ISDS\n"));
5685 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5686 request
, &response
, NULL
, NULL
);
5689 /* Remove temporal credentials */
5690 _isds_discard_credentials(context
, 0);
5691 /* Detach pointer to OTP credentials from context */
5692 context
->otp_credentials
= NULL
;
5693 /* Keep context->otp true to keep signaling this is OTP session */
5696 /* Destroy request */
5697 xmlFreeNode(request
); request
= NULL
;
5700 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5701 _("Processing ISDS response on ChangeISDSPassword "
5702 "request failed\n") :
5703 _("Processing ISDS response on ChangePasswordOTP "
5704 "request failed\n"));
5708 /* Check for response status */
5709 err
= isds_response_status(context
,
5710 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5711 &code
, &message
, (xmlChar
**)refnumber
);
5713 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5714 _("ISDS response on ChangeISDSPassword request is missing "
5716 _("ISDS response on ChangePasswordOTP request is missing "
5721 /* Check for known error codes */
5722 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5723 if (!xmlStrcmp(code
, codes
[i
])) {
5724 char *code_locale
= _isds_utf82locale((char*)code
);
5725 char *message_locale
= _isds_utf82locale((char*)message
);
5726 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5727 _("Server refused to change password on ChangeISDSPassword "
5728 "request (code=%s, message=%s)\n") :
5729 _("Server refused to change password on ChangePasswordOTP "
5730 "request (code=%s, message=%s)\n"),
5731 code_locale
, message_locale
);
5733 free(message_locale
);
5734 isds_log_message(context
, _(meanings
[i
]));
5741 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5742 char *code_locale
= _isds_utf82locale((char*)code
);
5743 char *message_locale
= _isds_utf82locale((char*)message
);
5744 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5745 _("Server refused to change password on ChangeISDSPassword "
5746 "request (code=%s, message=%s)\n") :
5747 _("Server refused to change password on ChangePasswordOTP "
5748 "request (code=%s, message=%s)\n"),
5749 code_locale
, message_locale
);
5750 isds_log_message(context
, message_locale
);
5752 free(message_locale
);
5757 /* Otherwise password changed successfully */
5760 if (NULL
!= saved_url
) {
5761 /* Revert URL to original one */
5762 zfree(context
->url
);
5763 context
->url
= saved_url
;
5765 #if HAVE_CURL_REAUTHORIZATION_BUG
5766 if (NULL
!= saved_curl
) {
5767 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5768 context
->curl
= saved_curl
;
5774 xmlFreeDoc(response
);
5775 xmlFreeNode(request
);
5778 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5779 _("Password changed successfully on ChangeISDSPassword "
5781 _("Password changed successfully on ChangePasswordOTP "
5783 #else /* not HAVE_LIBCURL */
5792 /* Generic middle part with request sending and response check.
5793 * It sends prepared request and checks for error code.
5794 * @context is ISDS session context.
5795 * @service is ISDS service handler
5796 * @service_name is name in scope of given @service
5797 * @request is XML tree with request. Will be freed to save memory.
5798 * @response is XML document outputting ISDS response.
5799 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5800 * @map is mapping from status code to library error. Pass NULL if no special
5801 * handling is requested.
5802 * NULL, if you don't care. */
5803 static isds_error
send_destroy_request_check_response(
5804 struct isds_ctx
*context
,
5805 const isds_service service
, const xmlChar
*service_name
,
5806 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5807 const struct code_map_isds_error
*map
) {
5808 isds_error err
= IE_SUCCESS
;
5809 char *service_name_locale
= NULL
;
5810 xmlChar
*code
= NULL
, *message
= NULL
;
5813 if (!context
) return IE_INVALID_CONTEXT
;
5814 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5818 /* Check if connection is established
5819 * TODO: This check should be done downstairs. */
5820 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5822 service_name_locale
= _isds_utf82locale((char*) service_name
);
5823 if (!service_name_locale
) {
5828 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5829 service_name_locale
);
5832 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5833 xmlFreeNode(*request
); *request
= NULL
;
5836 isds_log(ILF_ISDS
, ILL_DEBUG
,
5837 _("Processing ISDS response on %s request failed\n"),
5838 service_name_locale
);
5842 /* Check for response status */
5843 err
= isds_response_status(context
, service
, *response
,
5844 &code
, &message
, refnumber
);
5846 isds_log(ILF_ISDS
, ILL_DEBUG
,
5847 _("ISDS response on %s request is missing status\n"),
5848 service_name_locale
);
5852 err
= statuscode2isds_error(context
, map
, code
, message
);
5854 /* Request processed, but server failed */
5855 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5856 char *code_locale
= _isds_utf82locale((char*) code
);
5857 char *message_locale
= _isds_utf82locale((char*) message
);
5858 isds_log(ILF_ISDS
, ILL_DEBUG
,
5859 _("Server refused %s request (code=%s, message=%s)\n"),
5860 service_name_locale
, code_locale
, message_locale
);
5862 free(message_locale
);
5870 if (err
&& *response
) {
5871 xmlFreeDoc(*response
);
5875 xmlFreeNode(*request
);
5878 free(service_name_locale
);
5884 /* Generic bottom half with request sending.
5885 * It sends prepared request, checks for error code, destroys response and
5886 * request and log success or failure.
5887 * @context is ISDS session context.
5888 * @service is ISDS service handler
5889 * @service_name is name in scope of given @service
5890 * @request is XML tree with request. Will be freed to save memory.
5891 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5892 * NULL, if you don't care. */
5893 static isds_error
send_request_check_drop_response(
5894 struct isds_ctx
*context
,
5895 const isds_service service
, const xmlChar
*service_name
,
5896 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5897 isds_error err
= IE_SUCCESS
;
5898 xmlDocPtr response
= NULL
;
5901 if (!context
) return IE_INVALID_CONTEXT
;
5902 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5905 /* Send request and check response*/
5906 err
= send_destroy_request_check_response(context
,
5907 service
, service_name
, request
, &response
, refnumber
, NULL
);
5909 xmlFreeDoc(response
);
5912 xmlFreeNode(*request
);
5917 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5918 isds_log(ILF_ISDS
, ILL_DEBUG
,
5919 _("%s request processed by server successfully.\n"),
5920 service_name_locale
);
5921 free(service_name_locale
);
5928 /* Insert isds_credentials_delivery structure into XML request if not NULL
5929 * @context is session context
5930 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5931 * credentials delivery. The email field is passed.
5932 * @parent is XML element where to insert */
5933 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5934 const struct isds_credentials_delivery
*credentials_delivery
,
5935 xmlNodePtr parent
) {
5936 isds_error err
= IE_SUCCESS
;
5939 if (!context
) return IE_INVALID_CONTEXT
;
5940 if (!parent
) return IE_INVAL
;
5942 if (credentials_delivery
) {
5943 /* Following elements are valid only for services:
5944 * NewAccessData, AddDataBoxUser, CreateDataBox */
5945 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5946 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5954 /* Extract credentials delivery from ISDS response.
5955 * @context is session context
5956 * @credentials_delivery is pointer to valid structure to fill in returned
5957 * user's password (and new log-in name). If NULL, do not extract the data.
5958 * @response is pointer to XML document with ISDS response
5959 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5960 * @return IE_SUCCESS even if new user name has not been found because it's not
5961 * clear whether it's returned always. */
5962 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5963 struct isds_credentials_delivery
*credentials_delivery
,
5964 xmlDocPtr response
, const char *request_name
) {
5965 isds_error err
= IE_SUCCESS
;
5966 xmlXPathContextPtr xpath_ctx
= NULL
;
5967 xmlXPathObjectPtr result
= NULL
;
5968 char *xpath_query
= NULL
;
5970 if (!context
) return IE_INVALID_CONTEXT
;
5971 if (credentials_delivery
) {
5972 zfree(credentials_delivery
->token
);
5973 zfree(credentials_delivery
->new_user_name
);
5975 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5978 /* Extract optional token */
5979 if (credentials_delivery
) {
5980 xpath_ctx
= xmlXPathNewContext(response
);
5985 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5990 /* Verify root element */
5991 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5996 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6001 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6002 char *request_name_locale
= _isds_utf82locale(request_name
);
6003 isds_log(ILF_ISDS
, ILL_WARNING
,
6004 _("Wrong element in ISDS response for %s request "
6005 "while extracting credentials delivery details\n"),
6006 request_name_locale
);
6007 free(request_name_locale
);
6011 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6014 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6016 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6018 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6019 if (!credentials_delivery
->token
) {
6020 char *request_name_locale
= _isds_utf82locale(request_name
);
6021 isds_log(ILF_ISDS
, ILL_ERR
,
6022 _("ISDS did not return token on %s request "
6023 "even if requested\n"), request_name_locale
);
6024 free(request_name_locale
);
6031 xmlXPathFreeObject(result
);
6032 xmlXPathFreeContext(xpath_ctx
);
6038 /* Build XSD:tCreateDBInput request type for box creating.
6039 * @context is session context
6040 * @request outputs built XML tree
6041 * @service_name is request name of SERVICE_DB_MANIPULATION service
6042 * @box is box description to create including single primary user (in case of
6044 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6045 * box, or contact address of PFO box owner)
6046 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6047 * @upper_box_id is optional ID of supper box if currently created box is
6049 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6051 * @credentials_delivery is valid pointer if ISDS should return token that box
6052 * owner can use to obtain his new credentials in on-line way. Then valid email
6053 * member value should be supplied.
6054 * @approval is optional external approval of box manipulation */
6055 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6056 xmlNodePtr
*request
, const xmlChar
*service_name
,
6057 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6058 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6059 const xmlChar
*ceo_label
,
6060 const struct isds_credentials_delivery
*credentials_delivery
,
6061 const struct isds_approval
*approval
) {
6062 isds_error err
= IE_SUCCESS
;
6063 xmlNsPtr isds_ns
= NULL
;
6064 xmlNodePtr node
, dbPrimaryUsers
;
6065 xmlChar
*string
= NULL
;
6066 const struct isds_list
*item
;
6069 if (!context
) return IE_INVALID_CONTEXT
;
6070 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6074 /* Build CreateDataBox-similar request */
6075 *request
= xmlNewNode(NULL
, service_name
);
6077 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6078 isds_printf_message(context
, _("Could build %s request"),
6079 service_name_locale
);
6080 free(service_name_locale
);
6083 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6084 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6086 isds_log_message(context
, _("Could not create ISDS1 name space"));
6087 xmlFreeNode(*request
);
6091 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6093 isds_log_message(context
, _("Could not create ISDS name space"));
6094 xmlFreeNode(*request
);
6098 xmlSetNs(*request
, isds_ns
);
6100 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6101 err
= insert_DbOwnerInfo(context
, box
, node
);
6102 if (err
) goto leave
;
6105 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6106 * verbose documentation allows none dbUserInfo */
6107 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6108 for (item
= users
; item
; item
= item
->next
) {
6110 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6111 err
= insert_DbUserInfo(context
,
6112 (struct isds_DbUserInfo
*) item
->data
, node
);
6113 if (err
) goto leave
;
6117 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6118 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6119 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6121 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6122 if (err
) goto leave
;
6124 err
= insert_GExtApproval(context
, approval
, *request
);
6125 if (err
) goto leave
;
6129 xmlFreeNode(*request
);
6135 #endif /* HAVE_LIBCURL */
6139 * @context is session context
6140 * @box is box description to create including single primary user (in case of
6141 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6142 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6143 * box, or contact address of PFO box owner)
6144 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6145 * @upper_box_id is optional ID of supper box if currently created box is
6147 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6148 * @credentials_delivery is NULL if new password should be delivered off-line
6149 * to box owner. It is valid pointer if owner should obtain new password on-line
6150 * on dedicated web server. Then input @credentials_delivery.email value is
6151 * his e-mail address he must provide to dedicated web server together
6152 * with output reallocated @credentials_delivery.token member. Output
6153 * member @credentials_delivery.new_user_name is unused up on this call.
6154 * @approval is optional external approval of box manipulation
6155 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6156 * NULL, if you don't care.*/
6157 isds_error
isds_add_box(struct isds_ctx
*context
,
6158 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6159 const char *former_names
, const char *upper_box_id
,
6160 const char *ceo_label
,
6161 struct isds_credentials_delivery
*credentials_delivery
,
6162 const struct isds_approval
*approval
, char **refnumber
) {
6163 isds_error err
= IE_SUCCESS
;
6165 xmlNodePtr request
= NULL
;
6166 xmlDocPtr response
= NULL
;
6167 xmlXPathContextPtr xpath_ctx
= NULL
;
6168 xmlXPathObjectPtr result
= NULL
;
6172 if (!context
) return IE_INVALID_CONTEXT
;
6173 zfree(context
->long_message
);
6174 if (credentials_delivery
) {
6175 zfree(credentials_delivery
->token
);
6176 zfree(credentials_delivery
->new_user_name
);
6178 if (!box
) return IE_INVAL
;
6181 /* Scratch box ID */
6184 /* Build CreateDataBox request */
6185 err
= build_CreateDBInput_request(context
,
6186 &request
, BAD_CAST
"CreateDataBox",
6187 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6188 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6189 if (err
) goto leave
;
6191 /* Send it to server and process response */
6192 err
= send_destroy_request_check_response(context
,
6193 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6194 &response
, (xmlChar
**) refnumber
, NULL
);
6196 /* Extract box ID */
6197 xpath_ctx
= xmlXPathNewContext(response
);
6202 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6206 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6208 /* Extract optional token */
6209 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6213 xmlXPathFreeObject(result
);
6214 xmlXPathFreeContext(xpath_ctx
);
6215 xmlFreeDoc(response
);
6216 xmlFreeNode(request
);
6219 isds_log(ILF_ISDS
, ILL_DEBUG
,
6220 _("CreateDataBox request processed by server successfully.\n"));
6222 #else /* not HAVE_LIBCURL */
6230 /* Notify ISDS about new PFO entity.
6231 * This function has no real effect.
6232 * @context is session context
6233 * @box is PFO description including single primary user.
6234 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6235 * @former_names is optional undocumented string. Pass NULL if you don't care.
6236 * @upper_box_id is optional ID of supper box if currently created box is
6238 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6239 * @approval is optional external approval of box manipulation
6240 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6241 * NULL, if you don't care.*/
6242 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6243 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6244 const char *former_names
, const char *upper_box_id
,
6245 const char *ceo_label
, const struct isds_approval
*approval
,
6247 isds_error err
= IE_SUCCESS
;
6249 xmlNodePtr request
= NULL
;
6252 if (!context
) return IE_INVALID_CONTEXT
;
6253 zfree(context
->long_message
);
6254 if (!box
) return IE_INVAL
;
6257 /* Build CreateDataBoxPFOInfo request */
6258 err
= build_CreateDBInput_request(context
,
6259 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6260 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6261 (xmlChar
*) ceo_label
, NULL
, approval
);
6262 if (err
) goto leave
;
6264 /* Send it to server and process response */
6265 err
= send_request_check_drop_response(context
,
6266 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6267 (xmlChar
**) refnumber
);
6268 /* XXX: XML Schema names output dbID element but textual documentation
6269 * states no box identifier is returned. */
6271 xmlFreeNode(request
);
6272 #else /* not HAVE_LIBCURL */
6279 /* Common implementation for removing given box.
6280 * @context is session context
6281 * @service_name is UTF-8 encoded name fo ISDS service
6282 * @box is box description to delete
6283 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6284 * carry sane value. If NULL, do not inject this information into request.
6285 * @approval is optional external approval of box manipulation
6286 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6287 * NULL, if you don't care.*/
6288 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6289 const xmlChar
*service_name
,
6290 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6291 const struct isds_approval
*approval
, char **refnumber
) {
6292 isds_error err
= IE_SUCCESS
;
6294 xmlNsPtr isds_ns
= NULL
;
6295 xmlNodePtr request
= NULL
;
6297 xmlChar
*string
= NULL
;
6301 if (!context
) return IE_INVALID_CONTEXT
;
6302 zfree(context
->long_message
);
6303 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6307 /* Build DeleteDataBox(Promptly) request */
6308 request
= xmlNewNode(NULL
, service_name
);
6310 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6311 isds_printf_message(context
,
6312 _("Could build %s request"), service_name_locale
);
6313 free(service_name_locale
);
6316 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6318 isds_log_message(context
, _("Could not create ISDS name space"));
6319 xmlFreeNode(request
);
6322 xmlSetNs(request
, isds_ns
);
6324 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6325 err
= insert_DbOwnerInfo(context
, box
, node
);
6326 if (err
) goto leave
;
6329 err
= tm2datestring(since
, &string
);
6331 isds_log_message(context
,
6332 _("Could not convert `since' argument to ISO date string"));
6335 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6339 err
= insert_GExtApproval(context
, approval
, request
);
6340 if (err
) goto leave
;
6343 /* Send it to server and process response */
6344 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6345 service_name
, &request
, (xmlChar
**) refnumber
);
6348 xmlFreeNode(request
);
6350 #else /* not HAVE_LIBCURL */
6357 /* Remove given box permanently.
6358 * @context is session context
6359 * @box is box description to delete
6360 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6362 * @approval is optional external approval of box manipulation
6363 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6364 * NULL, if you don't care.*/
6365 isds_error
isds_delete_box(struct isds_ctx
*context
,
6366 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6367 const struct isds_approval
*approval
, char **refnumber
) {
6368 if (!context
) return IE_INVALID_CONTEXT
;
6369 zfree(context
->long_message
);
6370 if (!box
|| !since
) return IE_INVAL
;
6372 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6373 box
, since
, approval
, refnumber
);
6377 /* Undocumented function.
6378 * @context is session context
6379 * @box is box description to delete
6380 * @approval is optional external approval of box manipulation
6381 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6382 * NULL, if you don't care.*/
6383 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6384 const struct isds_DbOwnerInfo
*box
,
6385 const struct isds_approval
*approval
, char **refnumber
) {
6386 if (!context
) return IE_INVALID_CONTEXT
;
6387 zfree(context
->long_message
);
6388 if (!box
) return IE_INVAL
;
6390 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6391 box
, NULL
, approval
, refnumber
);
6395 /* Update data about given box.
6396 * @context is session context
6397 * @old_box current box description
6398 * @new_box are updated data about @old_box
6399 * @approval is optional external approval of box manipulation
6400 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6401 * NULL, if you don't care.*/
6402 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6403 const struct isds_DbOwnerInfo
*old_box
,
6404 const struct isds_DbOwnerInfo
*new_box
,
6405 const struct isds_approval
*approval
, char **refnumber
) {
6406 isds_error err
= IE_SUCCESS
;
6408 xmlNsPtr isds_ns
= NULL
;
6409 xmlNodePtr request
= NULL
;
6414 if (!context
) return IE_INVALID_CONTEXT
;
6415 zfree(context
->long_message
);
6416 if (!old_box
|| !new_box
) return IE_INVAL
;
6420 /* Build UpdateDataBoxDescr request */
6421 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6423 isds_log_message(context
,
6424 _("Could build UpdateDataBoxDescr request"));
6427 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6429 isds_log_message(context
, _("Could not create ISDS name space"));
6430 xmlFreeNode(request
);
6433 xmlSetNs(request
, isds_ns
);
6435 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6436 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6437 if (err
) goto leave
;
6439 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6440 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6441 if (err
) goto leave
;
6443 err
= insert_GExtApproval(context
, approval
, request
);
6444 if (err
) goto leave
;
6447 /* Send it to server and process response */
6448 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6449 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6452 xmlFreeNode(request
);
6453 #else /* not HAVE_LIBCURL */
6462 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6464 * @context is session context
6465 * @service is SOAP service
6466 * @service_name is name of request in @service
6467 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6468 * @box_id is box ID of interest
6469 * @approval is optional external approval of box manipulation
6470 * @response is server SOAP body response as XML document
6471 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6472 * NULL, if you don't care.
6473 * @return error coded from lower layer, context message will be set up
6475 static isds_error
build_send_dbid_request_check_response(
6476 struct isds_ctx
*context
, const isds_service service
,
6477 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6478 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6479 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6481 isds_error err
= IE_SUCCESS
;
6482 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6483 xmlNodePtr request
= NULL
, node
;
6484 xmlNsPtr isds_ns
= NULL
;
6486 if (!context
) return IE_INVALID_CONTEXT
;
6487 if (!service_name
|| !box_id
) return IE_INVAL
;
6488 if (!response
) return IE_INVAL
;
6490 /* Free output argument */
6491 xmlFreeDoc(*response
); *response
= NULL
;
6493 /* Prepare strings */
6494 service_name_locale
= _isds_utf82locale((char*)service_name
);
6495 if (!service_name_locale
) {
6499 box_id_locale
= _isds_utf82locale((char*)box_id
);
6500 if (!box_id_locale
) {
6506 request
= xmlNewNode(NULL
, service_name
);
6508 isds_printf_message(context
,
6509 _("Could not build %s request for %s box"), service_name_locale
,
6514 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6516 isds_log_message(context
, _("Could not create ISDS name space"));
6520 xmlSetNs(request
, isds_ns
);
6522 /* Add XSD:tIdDbInput children */
6523 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6524 INSERT_STRING(request
, box_id_element
, box_id
);
6525 err
= insert_GExtApproval(context
, approval
, request
);
6526 if (err
) goto leave
;
6528 /* Send request and check response*/
6529 err
= send_destroy_request_check_response(context
,
6530 service
, service_name
, &request
, response
, refnumber
, NULL
);
6533 free(service_name_locale
);
6534 free(box_id_locale
);
6535 xmlFreeNode(request
);
6538 #endif /* HAVE_LIBCURL */
6541 /* Get data about all users assigned to given box.
6542 * @context is session context
6544 * @users is automatically reallocated list of struct isds_DbUserInfo */
6545 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6546 struct isds_list
**users
) {
6547 isds_error err
= IE_SUCCESS
;
6549 xmlDocPtr response
= NULL
;
6550 xmlXPathContextPtr xpath_ctx
= NULL
;
6551 xmlXPathObjectPtr result
= NULL
;
6553 struct isds_list
*item
, *prev_item
= NULL
;
6556 if (!context
) return IE_INVALID_CONTEXT
;
6557 zfree(context
->long_message
);
6558 if (!users
|| !box_id
) return IE_INVAL
;
6559 isds_list_free(users
);
6563 /* Do request and check for success */
6564 err
= build_send_dbid_request_check_response(context
,
6565 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6566 BAD_CAST box_id
, NULL
, &response
, NULL
);
6567 if (err
) goto leave
;
6571 /* Prepare structure */
6572 xpath_ctx
= xmlXPathNewContext(response
);
6577 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6582 /* Set context node */
6583 result
= xmlXPathEvalExpression(BAD_CAST
6584 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6590 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6591 /* Iterate over all users */
6592 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6594 /* Prepare structure */
6595 item
= calloc(1, sizeof(*item
));
6600 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6601 if (i
== 0) *users
= item
;
6602 else prev_item
->next
= item
;
6606 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6607 err
= extract_DbUserInfo(context
,
6608 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6609 if (err
) goto leave
;
6615 isds_list_free(users
);
6618 xmlXPathFreeObject(result
);
6619 xmlXPathFreeContext(xpath_ctx
);
6620 xmlFreeDoc(response
);
6623 isds_log(ILF_ISDS
, ILL_DEBUG
,
6624 _("GetDataBoxUsers request processed by server "
6625 "successfully.\n"));
6626 #else /* not HAVE_LIBCURL */
6634 /* Update data about user assigned to given box.
6635 * @context is session context
6636 * @box is box identification
6637 * @old_user identifies user to update
6638 * @new_user are updated data about @old_user
6639 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6640 * NULL, if you don't care.*/
6641 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6642 const struct isds_DbOwnerInfo
*box
,
6643 const struct isds_DbUserInfo
*old_user
,
6644 const struct isds_DbUserInfo
*new_user
,
6646 isds_error err
= IE_SUCCESS
;
6648 xmlNsPtr isds_ns
= NULL
;
6649 xmlNodePtr request
= NULL
;
6654 if (!context
) return IE_INVALID_CONTEXT
;
6655 zfree(context
->long_message
);
6656 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6660 /* Build UpdateDataBoxUser request */
6661 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6663 isds_log_message(context
,
6664 _("Could build UpdateDataBoxUser request"));
6667 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6669 isds_log_message(context
, _("Could not create ISDS name space"));
6670 xmlFreeNode(request
);
6673 xmlSetNs(request
, isds_ns
);
6675 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6676 err
= insert_DbOwnerInfo(context
, box
, node
);
6677 if (err
) goto leave
;
6679 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6680 err
= insert_DbUserInfo(context
, old_user
, node
);
6681 if (err
) goto leave
;
6683 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6684 err
= insert_DbUserInfo(context
, new_user
, node
);
6685 if (err
) goto leave
;
6687 /* Send it to server and process response */
6688 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6689 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6692 xmlFreeNode(request
);
6693 #else /* not HAVE_LIBCURL */
6701 /* Undocumented function.
6702 * @context is session context
6703 * @box_id is UTF-8 encoded box identifier
6704 * @token is UTF-8 encoded temporary password
6705 * @user_id outputs UTF-8 encoded reallocated user identifier
6706 * @password outpus UTF-8 encoded reallocated user password
6707 * Output arguments will be nulled in case of error */
6708 isds_error
isds_activate(struct isds_ctx
*context
,
6709 const char *box_id
, const char *token
,
6710 char **user_id
, char **password
) {
6711 isds_error err
= IE_SUCCESS
;
6713 xmlNsPtr isds_ns
= NULL
;
6714 xmlNodePtr request
= NULL
, node
;
6715 xmlDocPtr response
= NULL
;
6716 xmlXPathContextPtr xpath_ctx
= NULL
;
6717 xmlXPathObjectPtr result
= NULL
;
6721 if (!context
) return IE_INVALID_CONTEXT
;
6722 zfree(context
->long_message
);
6724 if (user_id
) zfree(*user_id
);
6725 if (password
) zfree(*password
);
6727 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6731 /* Build Activate request */
6732 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6734 isds_log_message(context
, _("Could build Activate request"));
6737 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6739 isds_log_message(context
, _("Could not create ISDS name space"));
6740 xmlFreeNode(request
);
6743 xmlSetNs(request
, isds_ns
);
6745 INSERT_STRING(request
, "dbAccessDataId", token
);
6746 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6747 INSERT_STRING(request
, "dbID", box_id
);
6750 /* Send request and check response*/
6751 err
= send_destroy_request_check_response(context
,
6752 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6753 &response
, NULL
, NULL
);
6754 if (err
) goto leave
;
6758 xpath_ctx
= xmlXPathNewContext(response
);
6763 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6767 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6773 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6774 isds_log_message(context
, _("Missing ActivateResponse element"));
6778 if (result
->nodesetval
->nodeNr
> 1) {
6779 isds_log_message(context
, _("Multiple ActivateResponse element"));
6783 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6784 xmlXPathFreeObject(result
); result
= NULL
;
6786 EXTRACT_STRING("isds:userId", *user_id
);
6788 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6789 "but did not return `userId' element.\n"));
6791 EXTRACT_STRING("isds:password", *password
);
6793 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6794 "but did not return `password' element.\n"));
6797 xmlXPathFreeObject(result
);
6798 xmlXPathFreeContext(xpath_ctx
);
6799 xmlFreeDoc(response
);
6800 xmlFreeNode(request
);
6803 isds_log(ILF_ISDS
, ILL_DEBUG
,
6804 _("Activate request processed by server successfully.\n"));
6805 #else /* not HAVE_LIBCURL */
6813 /* Reset credentials of user assigned to given box.
6814 * @context is session context
6815 * @box is box identification
6816 * @user identifies user to reset password
6817 * @fee_paid is true if fee has been paid, false otherwise
6818 * @approval is optional external approval of box manipulation
6819 * @credentials_delivery is NULL if new password should be delivered off-line
6820 * to the user. It is valid pointer if user should obtain new password on-line
6821 * on dedicated web server. Then input @credentials_delivery.email value is
6822 * user's e-mail address user must provide to dedicated web server together
6823 * with @credentials_delivery.token. The output reallocated token user needs
6824 * to use to authorize on the web server to view his new password. Output
6825 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6826 * ISDS changed up on this call. (No reason why server could change the name
6828 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6829 * NULL, if you don't care.*/
6830 isds_error
isds_reset_password(struct isds_ctx
*context
,
6831 const struct isds_DbOwnerInfo
*box
,
6832 const struct isds_DbUserInfo
*user
,
6833 const _Bool fee_paid
, const struct isds_approval
*approval
,
6834 struct isds_credentials_delivery
*credentials_delivery
,
6836 isds_error err
= IE_SUCCESS
;
6838 xmlNsPtr isds_ns
= NULL
;
6839 xmlNodePtr request
= NULL
, node
;
6840 xmlDocPtr response
= NULL
;
6844 if (!context
) return IE_INVALID_CONTEXT
;
6845 zfree(context
->long_message
);
6847 if (credentials_delivery
) {
6848 zfree(credentials_delivery
->token
);
6849 zfree(credentials_delivery
->new_user_name
);
6851 if (!box
|| !user
) return IE_INVAL
;
6855 /* Build NewAccessData request */
6856 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6858 isds_log_message(context
,
6859 _("Could build NewAccessData request"));
6862 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6864 isds_log_message(context
, _("Could not create ISDS name space"));
6865 xmlFreeNode(request
);
6868 xmlSetNs(request
, isds_ns
);
6870 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6871 err
= insert_DbOwnerInfo(context
, box
, node
);
6872 if (err
) goto leave
;
6874 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6875 err
= insert_DbUserInfo(context
, user
, node
);
6876 if (err
) goto leave
;
6878 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6880 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6881 if (err
) goto leave
;
6883 err
= insert_GExtApproval(context
, approval
, request
);
6884 if (err
) goto leave
;
6886 /* Send request and check response*/
6887 err
= send_destroy_request_check_response(context
,
6888 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6889 &response
, (xmlChar
**) refnumber
, NULL
);
6890 if (err
) goto leave
;
6893 /* Extract optional token */
6894 err
= extract_credentials_delivery(context
, credentials_delivery
,
6895 response
, "NewAccessData");
6898 xmlFreeDoc(response
);
6899 xmlFreeNode(request
);
6902 isds_log(ILF_ISDS
, ILL_DEBUG
,
6903 _("NewAccessData request processed by server "
6904 "successfully.\n"));
6905 #else /* not HAVE_LIBCURL */
6913 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6914 * code, destroy response and log success.
6915 * @context is ISDS session context.
6916 * @service_name is name of SERVICE_DB_MANIPULATION service
6917 * @box is box identification
6918 * @user identifies user to remove
6919 * @credentials_delivery is NULL if new user's password should be delivered
6920 * off-line to the user. It is valid pointer if user should obtain new
6921 * password on-line on dedicated web server. Then input
6922 * @credentials_delivery.email value is user's e-mail address user must
6923 * provide to dedicated web server together with @credentials_delivery.token.
6924 * The output reallocated token user needs to use to authorize on the web
6925 * server to view his new password. Output reallocated
6926 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6927 * assingned or changed up on this call.
6928 * @approval is optional external approval of box manipulation
6929 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6930 * NULL, if you don't care. */
6931 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6932 struct isds_ctx
*context
, const xmlChar
*service_name
,
6933 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6934 struct isds_credentials_delivery
*credentials_delivery
,
6935 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6936 isds_error err
= IE_SUCCESS
;
6938 xmlNsPtr isds_ns
= NULL
;
6939 xmlNodePtr request
= NULL
, node
;
6940 xmlDocPtr response
= NULL
;
6944 if (!context
) return IE_INVALID_CONTEXT
;
6945 zfree(context
->long_message
);
6946 if (credentials_delivery
) {
6947 zfree(credentials_delivery
->token
);
6948 zfree(credentials_delivery
->new_user_name
);
6950 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6955 /* Build NewAccessData or similar request */
6956 request
= xmlNewNode(NULL
, service_name
);
6958 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6959 isds_printf_message(context
, _("Could not build %s request"),
6960 service_name_locale
);
6961 free(service_name_locale
);
6964 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6966 isds_log_message(context
, _("Could not create ISDS name space"));
6967 xmlFreeNode(request
);
6970 xmlSetNs(request
, isds_ns
);
6972 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6973 err
= insert_DbOwnerInfo(context
, box
, node
);
6974 if (err
) goto leave
;
6976 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6977 err
= insert_DbUserInfo(context
, user
, node
);
6978 if (err
) goto leave
;
6980 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6981 if (err
) goto leave
;
6983 err
= insert_GExtApproval(context
, approval
, request
);
6984 if (err
) goto leave
;
6987 /* Send request and check response*/
6988 err
= send_destroy_request_check_response(context
,
6989 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6992 xmlFreeNode(request
);
6995 /* Pick up credentials_delivery if requested */
6996 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6997 (char *)service_name
);
7000 xmlFreeDoc(response
);
7001 if (request
) xmlFreeNode(request
);
7004 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7005 isds_log(ILF_ISDS
, ILL_DEBUG
,
7006 _("%s request processed by server successfully.\n"),
7007 service_name_locale
);
7008 free(service_name_locale
);
7010 #else /* not HAVE_LIBCURL */
7018 /* Assign new user to given box.
7019 * @context is session context
7020 * @box is box identification
7021 * @user defines new user to add
7022 * @credentials_delivery is NULL if new user's password should be delivered
7023 * off-line to the user. It is valid pointer if user should obtain new
7024 * password on-line on dedicated web server. Then input
7025 * @credentials_delivery.email value is user's e-mail address user must
7026 * provide to dedicated web server together with @credentials_delivery.token.
7027 * The output reallocated token user needs to use to authorize on the web
7028 * server to view his new password. Output reallocated
7029 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7030 * assingned up on this call.
7031 * @approval is optional external approval of box manipulation
7032 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7033 * NULL, if you don't care.*/
7034 isds_error
isds_add_user(struct isds_ctx
*context
,
7035 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7036 struct isds_credentials_delivery
*credentials_delivery
,
7037 const struct isds_approval
*approval
, char **refnumber
) {
7038 return build_send_manipulationboxuser_request_check_drop_response(context
,
7039 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
7040 approval
, (xmlChar
**) refnumber
);
7044 /* Remove user assigned to given box.
7045 * @context is session context
7046 * @box is box identification
7047 * @user identifies user to remove
7048 * @approval is optional external approval of box manipulation
7049 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7050 * NULL, if you don't care.*/
7051 isds_error
isds_delete_user(struct isds_ctx
*context
,
7052 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7053 const struct isds_approval
*approval
, char **refnumber
) {
7054 return build_send_manipulationboxuser_request_check_drop_response(context
,
7055 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
7056 (xmlChar
**) refnumber
);
7060 /* Get list of boxes in ZIP archive.
7061 * @context is session context
7062 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7063 * System recognizes following values currently: ALL (all boxes), UPG
7064 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
7065 * receiving commercial messages). This argument is a string because
7066 * specification states new values can appear in the future. Not all list
7067 * types are available to all users.
7068 * @buffer is automatically reallocated memory to store the list of boxes. The
7069 * list is zipped CSV file.
7070 * @buffer_length is size of @buffer data in bytes.
7071 * In case of error @buffer will be freed and @buffer_length will be
7073 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7074 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7075 isds_error err
= IE_SUCCESS
;
7077 xmlNsPtr isds_ns
= NULL
;
7078 xmlNodePtr request
= NULL
, node
;
7079 xmlDocPtr response
= NULL
;
7080 xmlXPathContextPtr xpath_ctx
= NULL
;
7081 xmlXPathObjectPtr result
= NULL
;
7082 char *string
= NULL
;
7086 if (!context
) return IE_INVALID_CONTEXT
;
7087 zfree(context
->long_message
);
7088 if (buffer
) zfree(*buffer
);
7089 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7093 /* Check if connection is established
7094 * TODO: This check should be done downstairs. */
7095 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7098 /* Build AuthenticateMessage request */
7099 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7101 isds_log_message(context
,
7102 _("Could not build GetDataBoxList request"));
7105 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7107 isds_log_message(context
, _("Could not create ISDS name space"));
7108 xmlFreeNode(request
);
7111 xmlSetNs(request
, isds_ns
);
7112 INSERT_STRING(request
, "dblType", list_identifier
);
7114 /* Send request to server and process response */
7115 err
= send_destroy_request_check_response(context
,
7116 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7117 &response
, NULL
, NULL
);
7118 if (err
) goto leave
;
7121 /* Extract Base-64 encoded ZIP file */
7122 xpath_ctx
= xmlXPathNewContext(response
);
7127 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7131 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7133 /* Decode non-empty archive */
7134 if (string
&& string
[0] != '\0') {
7135 *buffer_length
= _isds_b64decode(string
, buffer
);
7136 if (*buffer_length
== (size_t) -1) {
7137 isds_printf_message(context
,
7138 _("Error while Base64-decoding box list archive"));
7147 xmlXPathFreeObject(result
);
7148 xmlXPathFreeContext(xpath_ctx
);
7149 xmlFreeDoc(response
);
7150 xmlFreeNode(request
);
7153 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7154 "processed by server successfully.\n"));
7156 #else /* not HAVE_LIBCURL */
7164 /* Find boxes suiting given criteria.
7165 * @criteria is filter. You should fill in at least some members.
7166 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7167 * possibly empty. Input NULL or valid old structure.
7169 * IE_SUCCESS if search succeeded, @boxes contains useful data
7170 * IE_NOEXIST if no such box exists, @boxes will be NULL
7171 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7172 * contains still valid data
7173 * other code if something bad happens. @boxes will be NULL. */
7174 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7175 const struct isds_DbOwnerInfo
*criteria
,
7176 struct isds_list
**boxes
) {
7177 isds_error err
= IE_SUCCESS
;
7179 _Bool truncated
= 0;
7180 xmlNsPtr isds_ns
= NULL
;
7181 xmlNodePtr request
= NULL
;
7182 xmlDocPtr response
= NULL
;
7183 xmlChar
*code
= NULL
, *message
= NULL
;
7184 xmlNodePtr db_owner_info
;
7185 xmlXPathContextPtr xpath_ctx
= NULL
;
7186 xmlXPathObjectPtr result
= NULL
;
7187 xmlChar
*string
= NULL
;
7191 if (!context
) return IE_INVALID_CONTEXT
;
7192 zfree(context
->long_message
);
7193 if (!boxes
) return IE_INVAL
;
7194 isds_list_free(boxes
);
7201 /* Check if connection is established
7202 * TODO: This check should be done downstairs. */
7203 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7206 /* Build FindDataBox request */
7207 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7209 isds_log_message(context
,
7210 _("Could build FindDataBox request"));
7213 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7215 isds_log_message(context
, _("Could not create ISDS name space"));
7216 xmlFreeNode(request
);
7219 xmlSetNs(request
, isds_ns
);
7220 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7221 if (!db_owner_info
) {
7222 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7223 "FindDataBox element"));
7224 xmlFreeNode(request
);
7228 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7229 if (err
) goto leave
;
7232 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7235 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7237 /* Destroy request */
7238 xmlFreeNode(request
); request
= NULL
;
7241 isds_log(ILF_ISDS
, ILL_DEBUG
,
7242 _("Processing ISDS response on FindDataBox "
7243 "request failed\n"));
7247 /* Check for response status */
7248 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7249 &code
, &message
, NULL
);
7251 isds_log(ILF_ISDS
, ILL_DEBUG
,
7252 _("ISDS response on FindDataBox request is missing status\n"));
7256 /* Request processed, but nothing found */
7257 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7258 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7259 char *code_locale
= _isds_utf82locale((char*)code
);
7260 char *message_locale
= _isds_utf82locale((char*)message
);
7261 isds_log(ILF_ISDS
, ILL_DEBUG
,
7262 _("Server did not found any box on FindDataBox request "
7263 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7264 isds_log_message(context
, message_locale
);
7266 free(message_locale
);
7271 /* Warning, not a error */
7272 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7273 char *code_locale
= _isds_utf82locale((char*)code
);
7274 char *message_locale
= _isds_utf82locale((char*)message
);
7275 isds_log(ILF_ISDS
, ILL_DEBUG
,
7276 _("Server truncated response on FindDataBox request "
7277 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7278 isds_log_message(context
, message_locale
);
7280 free(message_locale
);
7285 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7286 char *code_locale
= _isds_utf82locale((char*)code
);
7287 char *message_locale
= _isds_utf82locale((char*)message
);
7288 isds_log(ILF_ISDS
, ILL_DEBUG
,
7289 _("Server refused FindDataBox request "
7290 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7291 isds_log_message(context
, message_locale
);
7293 free(message_locale
);
7298 xpath_ctx
= xmlXPathNewContext(response
);
7303 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7308 /* Extract boxes if they present */
7309 result
= xmlXPathEvalExpression(BAD_CAST
7310 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7316 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7317 struct isds_list
*item
, *prev_item
= NULL
;
7318 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7319 item
= calloc(1, sizeof(*item
));
7325 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7326 if (i
== 0) *boxes
= item
;
7327 else prev_item
->next
= item
;
7330 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7331 err
= extract_DbOwnerInfo(context
,
7332 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7333 if (err
) goto leave
;
7339 isds_list_free(boxes
);
7341 if (truncated
) err
= IE_2BIG
;
7345 xmlFreeNode(request
);
7346 xmlXPathFreeObject(result
);
7347 xmlXPathFreeContext(xpath_ctx
);
7351 xmlFreeDoc(response
);
7354 isds_log(ILF_ISDS
, ILL_DEBUG
,
7355 _("FindDataBox request processed by server successfully.\n"));
7356 #else /* not HAVE_LIBCURL */
7365 /* Convert a string with match markers into a plain string with list of
7366 * pointers to the matches
7367 * @string is an UTF-8 encoded non-constant string with match markers
7368 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7369 * The markers will be removed from the string.
7370 * @starts is a reallocated list of static pointers into the @string pointing
7371 * to places where match start markers occured.
7372 * @ends is a reallocated list of static pointers into the @string pointing
7373 * to places where match end markers occured.
7374 * @return IE_SUCCESS in case of no failure. */
7375 static isds_error
interpret_matches(xmlChar
*string
,
7376 struct isds_list
**starts
, struct isds_list
**ends
) {
7377 isds_error err
= IE_SUCCESS
;
7378 xmlChar
*pointer
, *destination
, *source
;
7379 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7381 isds_list_free(starts
);
7382 isds_list_free(ends
);
7383 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7384 if (NULL
== string
) return IE_SUCCESS
;
7386 for (pointer
= string
; *pointer
!= '\0';) {
7387 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7388 /* Remove the start marker */
7389 for (source
= pointer
+ 14, destination
= pointer
;
7390 *source
!= '\0'; source
++, destination
++) {
7391 *destination
= *source
;
7393 *destination
= '\0';
7394 /* Append the pointer into the list */
7395 item
= calloc(1, sizeof(*item
));
7400 item
->destructor
= (void (*)(void **))NULL
;
7401 item
->data
= pointer
;
7402 if (NULL
== prev_start
) *starts
= item
;
7403 else prev_start
->next
= item
;
7405 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7406 /* Remove the end marker */
7407 for (source
= pointer
+ 12, destination
= pointer
;
7408 *source
!= '\0'; source
++, destination
++) {
7409 *destination
= *source
;
7411 *destination
= '\0';
7412 /* Append the pointer into the list */
7413 item
= calloc(1, sizeof(*item
));
7418 item
->destructor
= (void (*)(void **))NULL
;
7419 item
->data
= pointer
;
7420 if (NULL
== prev_end
) *ends
= item
;
7421 else prev_end
->next
= item
;
7430 isds_list_free(starts
);
7431 isds_list_free(ends
);
7437 /* Convert isds:dbResult XML tree into structure
7438 * @context is ISDS context.
7439 * @fulltext_result is automatically reallocated found box structure.
7440 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7441 * @collect_matches is true to interpret match markers.
7442 * In case of error @result will be freed. */
7443 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7444 struct isds_fulltext_result
**fulltext_result
,
7445 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7446 isds_error err
= IE_SUCCESS
;
7447 xmlXPathObjectPtr result
= NULL
;
7448 char *string
= NULL
;
7450 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7451 if (NULL
== fulltext_result
) return IE_INVAL
;
7452 isds_fulltext_result_free(fulltext_result
);
7453 if (!xpath_ctx
) return IE_INVAL
;
7456 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7457 if (NULL
== *fulltext_result
) {
7463 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7465 EXTRACT_STRING("isds:dbType", string
);
7466 if (NULL
== string
) {
7468 isds_log_message(context
, _("Empty isds:dbType element"));
7471 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7473 if (err
== IE_ENUM
) {
7475 char *string_locale
= _isds_utf82locale(string
);
7476 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7478 free(string_locale
);
7484 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7485 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7487 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7488 if (err
) goto leave
;
7490 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7491 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7492 (*fulltext_result
)->dbEffectiveOVM
);
7494 EXTRACT_STRING("isds:dbSendOptions", string
);
7495 if (NULL
== string
) {
7497 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7500 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7501 (*fulltext_result
)->active
= 1;
7502 (*fulltext_result
)->public_sending
= 1;
7503 (*fulltext_result
)->commercial_sending
= 0;
7504 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7505 (*fulltext_result
)->active
= 1;
7506 (*fulltext_result
)->public_sending
= 1;
7507 (*fulltext_result
)->commercial_sending
= 1;
7508 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7509 (*fulltext_result
)->active
= 1;
7510 (*fulltext_result
)->public_sending
= 0;
7511 (*fulltext_result
)->commercial_sending
= 1;
7512 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7513 (*fulltext_result
)->active
= 1;
7514 (*fulltext_result
)->public_sending
= 0;
7515 (*fulltext_result
)->commercial_sending
= 0;
7516 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7517 (*fulltext_result
)->active
= 0;
7518 (*fulltext_result
)->public_sending
= 0;
7519 (*fulltext_result
)->commercial_sending
= 0;
7522 char *string_locale
= _isds_utf82locale(string
);
7523 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7525 free(string_locale
);
7530 /* Interpret match marks */
7531 if (collect_matches
) {
7532 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7533 &((*fulltext_result
)->name_match_start
),
7534 &((*fulltext_result
)->name_match_end
));
7535 if (err
) goto leave
;
7536 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7537 &((*fulltext_result
)->address_match_start
),
7538 &((*fulltext_result
)->address_match_end
));
7539 if (err
) goto leave
;
7543 if (err
) isds_fulltext_result_free(fulltext_result
);
7545 xmlXPathFreeObject(result
);
7548 #endif /* HAVE_LIBCURL */
7551 /* Find boxes matching a given full-text criteria.
7552 * @context is a session context
7553 * @query is a non-empty string which consists of words to search
7554 * @target selects box attributes to search for @query words. Pass NULL if you
7556 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7557 * to search in all box types. Pass NULL to let server to use default value
7558 * which is DBTYPE_SYSTEM.
7559 * @page_size defines count of boxes to constitute a response page. It counts
7560 * from zero. Pass NULL to let server to use a default value (50 now).
7561 * @page_number defines ordinar number of the response page to return. It
7562 * counts from zero. Pass NULL to let server to use a default value (0 now).
7563 * @track_matches points to true for marking @query words found in the box
7564 * attributes. It points to false for not marking. Pass NULL to let the server
7565 * to use default value (false now).
7566 * @total_matching_boxes outputs reallocated number of all boxes matching the
7567 * query. Will be pointer to NULL if server did not provide the value.
7568 * Pass NULL if you don't care.
7569 * @current_page_beginning outputs reallocated ordinar number of the first box
7570 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7571 * server did not provide the value. Pass NULL if you don't care.
7572 * @current_page_size outputs reallocated count of boxes in the this @boxes
7573 * page. It will be pointer to NULL if the server did not provide the value.
7574 * Pass NULL if you don't care.
7575 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7576 * is the last one, false if more boxes match, NULL if the server did not
7577 * provude the value. Pass NULL if you don't care.
7578 * @boxes outputs reallocated list of isds_fulltext_result structures,
7581 * IE_SUCCESS if search succeeded
7582 * IE_2BIG if @page_size is too large
7583 * other code if something bad happens; output arguments will be NULL. */
7584 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7586 const isds_fulltext_target
*target
,
7587 const isds_DbType
*box_type
,
7588 const unsigned long int *page_size
,
7589 const unsigned long int *page_number
,
7590 const _Bool
*track_matches
,
7591 unsigned long int **total_matching_boxes
,
7592 unsigned long int **current_page_beginning
,
7593 unsigned long int **current_page_size
,
7595 struct isds_list
**boxes
) {
7596 isds_error err
= IE_SUCCESS
;
7598 xmlNsPtr isds_ns
= NULL
;
7599 xmlNodePtr request
= NULL
;
7600 xmlDocPtr response
= NULL
;
7602 xmlXPathContextPtr xpath_ctx
= NULL
;
7603 xmlXPathObjectPtr result
= NULL
;
7604 const xmlChar
*static_string
= NULL
;
7605 xmlChar
*string
= NULL
;
7607 const xmlChar
*codes
[] = {
7617 const char *meanings
[] = {
7618 N_("You are not allowed to perform the search"),
7619 N_("The query string is empty"),
7620 N_("Searched box ID is malformed"),
7621 N_("Searched organization ID is malformed"),
7622 N_("Invalid input"),
7623 N_("Requested page size is too large"),
7624 N_("Search engine internal error")
7626 const isds_error errors
[] = {
7635 struct code_map_isds_error map
= {
7637 .meanings
= meanings
,
7643 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7644 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7645 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7646 if (NULL
!= last_page
) zfree(*last_page
);
7647 isds_list_free(boxes
);
7649 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7650 zfree(context
->long_message
);
7652 if (NULL
== boxes
) return IE_INVAL
;
7654 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7655 isds_log_message(context
, _("Query string must be non-empty"));
7660 /* Check if connection is established
7661 * TODO: This check should be done downstairs. */
7662 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7664 /* Build FindDataBox request */
7665 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7666 if (NULL
== request
) {
7667 isds_log_message(context
,
7668 _("Could not build ISDSSearch2 request"));
7671 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7672 if(NULL
== isds_ns
) {
7673 isds_log_message(context
, _("Could not create ISDS name space"));
7674 xmlFreeNode(request
);
7677 xmlSetNs(request
, isds_ns
);
7679 INSERT_STRING(request
, "searchText", query
);
7681 if (NULL
!= target
) {
7682 static_string
= isds_fulltext_target2string(*(target
));
7683 if (NULL
== static_string
) {
7684 isds_printf_message(context
, _("Invalid target value: %d"),
7690 INSERT_STRING(request
, "searchType", static_string
);
7691 static_string
= NULL
;
7693 if (NULL
!= box_type
) {
7694 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7695 if (DBTYPE_SYSTEM
== *box_type
) {
7696 static_string
= BAD_CAST
"ALL";
7698 static_string
= isds_DbType2string(*(box_type
));
7699 if (NULL
== static_string
) {
7700 isds_printf_message(context
, _("Invalid box type value: %d"),
7707 INSERT_STRING(request
, "searchScope", static_string
);
7708 static_string
= NULL
;
7710 INSERT_ULONGINT(request
, "page", page_number
, string
);
7711 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7712 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7714 /* Send request and check response */
7715 err
= send_destroy_request_check_response(context
,
7716 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7717 &request
, &response
, NULL
, &map
);
7718 if (err
) goto leave
;
7720 /* Parse response */
7721 xpath_ctx
= xmlXPathNewContext(response
);
7722 if (NULL
== xpath_ctx
) {
7726 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7730 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7736 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7737 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7741 if (result
->nodesetval
->nodeNr
> 1) {
7742 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7746 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7747 xmlXPathFreeObject(result
); result
= NULL
;
7750 /* Extract counters */
7751 if (NULL
!= total_matching_boxes
) {
7752 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7754 if (NULL
!= current_page_size
) {
7755 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7757 if (NULL
!= current_page_beginning
) {
7758 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7760 if (NULL
!= last_page
) {
7761 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7763 xmlXPathFreeObject(result
); result
= NULL
;
7765 /* Extract boxes if they present */
7766 result
= xmlXPathEvalExpression(BAD_CAST
7767 "isds:dbResults/isds:dbResult", xpath_ctx
);
7768 if (NULL
== result
) {
7772 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7773 struct isds_list
*item
, *prev_item
= NULL
;
7774 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7775 item
= calloc(1, sizeof(*item
));
7781 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7782 if (i
== 0) *boxes
= item
;
7783 else prev_item
->next
= item
;
7786 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7787 err
= extract_dbResult(context
,
7788 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7789 (NULL
== track_matches
) ? 0 : *track_matches
);
7790 if (err
) goto leave
;
7796 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7797 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7798 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7799 if (NULL
!= last_page
) zfree(*last_page
);
7800 isds_list_free(boxes
);
7804 xmlFreeNode(request
);
7805 xmlXPathFreeObject(result
);
7806 xmlXPathFreeContext(xpath_ctx
);
7807 xmlFreeDoc(response
);
7810 isds_log(ILF_ISDS
, ILL_DEBUG
,
7811 _("ISDSSearch2 request processed by server successfully.\n"));
7812 #else /* not HAVE_LIBCURL */
7820 /* Get status of a box.
7821 * @context is ISDS session context.
7822 * @box_id is UTF-8 encoded box identifier as zero terminated string
7823 * @box_status is return value of box status.
7825 * IE_SUCCESS if box has been found and its status retrieved
7826 * IE_NOEXIST if box is not known to ISDS server
7827 * or other appropriate error.
7828 * You can use isds_DbState to enumerate box status. However out of enum
7829 * range value can be returned too. This is feature because ISDS
7830 * specification leaves the set of values open.
7831 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7832 * the box has been deleted, but ISDS still lists its former existence. */
7833 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7834 long int *box_status
) {
7835 isds_error err
= IE_SUCCESS
;
7837 xmlNsPtr isds_ns
= NULL
;
7838 xmlNodePtr request
= NULL
, db_id
;
7839 xmlDocPtr response
= NULL
;
7840 xmlXPathContextPtr xpath_ctx
= NULL
;
7841 xmlXPathObjectPtr result
= NULL
;
7842 xmlChar
*string
= NULL
;
7844 const xmlChar
*codes
[] = {
7850 const char *meanings
[] = {
7851 "The box does not exist",
7852 "Box ID is malformed",
7855 const isds_error errors
[] = {
7860 struct code_map_isds_error map
= {
7862 .meanings
= meanings
,
7867 if (!context
) return IE_INVALID_CONTEXT
;
7868 zfree(context
->long_message
);
7869 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7872 /* Check if connection is established
7873 * TODO: This check should be done downstairs. */
7874 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7877 /* Build CheckDataBox request */
7878 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7880 isds_log_message(context
,
7881 _("Could build CheckDataBox request"));
7884 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7886 isds_log_message(context
, _("Could not create ISDS name space"));
7887 xmlFreeNode(request
);
7890 xmlSetNs(request
, isds_ns
);
7891 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7893 isds_log_message(context
, _("Could not add dbID child to "
7894 "CheckDataBox element"));
7895 xmlFreeNode(request
);
7900 /* Send request and check response*/
7901 err
= send_destroy_request_check_response(context
,
7902 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7903 &request
, &response
, NULL
, &map
);
7904 if (err
) goto leave
;
7908 xpath_ctx
= xmlXPathNewContext(response
);
7913 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7917 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7923 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7924 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7928 if (result
->nodesetval
->nodeNr
> 1) {
7929 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7933 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7934 xmlXPathFreeObject(result
); result
= NULL
;
7936 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7941 xmlXPathFreeObject(result
);
7942 xmlXPathFreeContext(xpath_ctx
);
7944 xmlFreeDoc(response
);
7947 isds_log(ILF_ISDS
, ILL_DEBUG
,
7948 _("CheckDataBox request processed by server successfully.\n"));
7949 #else /* not HAVE_LIBCURL */
7957 /* Get list of permissions to send commercial messages.
7958 * @context is ISDS session context.
7959 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7960 * @permissions is a reallocated list of permissions (struct
7961 * isds_commercial_permission*) to send commercial messages from @box_id. The
7962 * order of permissions is significant as the server applies the permissions
7963 * and associated pre-paid credits in the order. Empty list means no
7966 * IE_SUCCESS if the list has been obtained correctly,
7967 * or other appropriate error. */
7968 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7969 const char *box_id
, struct isds_list
**permissions
) {
7970 isds_error err
= IE_SUCCESS
;
7972 xmlDocPtr response
= NULL
;
7973 xmlXPathContextPtr xpath_ctx
= NULL
;
7974 xmlXPathObjectPtr result
= NULL
;
7977 if (!context
) return IE_INVALID_CONTEXT
;
7978 zfree(context
->long_message
);
7979 if (NULL
== permissions
) return IE_INVAL
;
7980 isds_list_free(permissions
);
7981 if (NULL
== box_id
) return IE_INVAL
;
7984 /* Check if connection is established */
7985 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7987 /* Do request and check for success */
7988 err
= build_send_dbid_request_check_response(context
,
7989 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7990 BAD_CAST box_id
, NULL
, &response
, NULL
);
7992 isds_log(ILF_ISDS
, ILL_DEBUG
,
7993 _("PDZInfo request processed by server successfully.\n"));
7997 /* Prepare structure */
7998 xpath_ctx
= xmlXPathNewContext(response
);
8003 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8008 /* Set context node */
8009 result
= xmlXPathEvalExpression(BAD_CAST
8010 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8016 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8017 struct isds_list
*prev_item
= NULL
;
8019 /* Iterate over all permission records */
8020 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8021 struct isds_list
*item
;
8023 /* Prepare structure */
8024 item
= calloc(1, sizeof(*item
));
8029 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8030 if (i
== 0) *permissions
= item
;
8031 else prev_item
->next
= item
;
8035 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8036 err
= extract_DbPDZRecord(context
,
8037 (struct isds_commercial_permission
**) (&item
->data
),
8039 if (err
) goto leave
;
8045 isds_list_free(permissions
);
8048 xmlXPathFreeObject(result
);
8049 xmlXPathFreeContext(xpath_ctx
);
8050 xmlFreeDoc(response
);
8052 #else /* not HAVE_LIBCURL */
8060 /* Get details about credit for sending pre-paid commercial messages.
8061 * @context is ISDS session context.
8062 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8063 * @from_date is first day of credit history to return in @history. Only
8064 * tm_year, tm_mon and tm_mday carry sane value.
8065 * @to_date is last day of credit history to return in @history. Only
8066 * tm_year, tm_mon and tm_mday carry sane value.
8067 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8068 * if you don't care. This and all other credit values are integers in
8069 * hundredths of Czech Crowns.
8070 * @email outputs notification e-mail address where notifications about credit
8071 * are sent. This is automatically reallocated string. Pass NULL if you don't
8072 * care. It can return NULL if no address is defined.
8073 * @history outputs auto-reallocated list of pointers to struct
8074 * isds_credit_event. Events in closed interval @from_time to @to_time are
8075 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8076 * are sorted by time.
8078 * IE_SUCCESS if the credit details have been obtained correctly,
8079 * or other appropriate error. Please note that server allows to retrieve
8080 * only limited history of events. */
8081 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8083 const struct tm
*from_date
, const struct tm
*to_date
,
8084 long int *credit
, char **email
, struct isds_list
**history
) {
8085 isds_error err
= IE_SUCCESS
;
8087 char *box_id_locale
= NULL
;
8088 xmlNodePtr request
= NULL
, node
;
8089 xmlNsPtr isds_ns
= NULL
;
8090 xmlChar
*string
= NULL
;
8092 xmlDocPtr response
= NULL
;
8093 xmlXPathContextPtr xpath_ctx
= NULL
;
8094 xmlXPathObjectPtr result
= NULL
;
8096 const xmlChar
*codes
[] = {
8104 const char *meanings
[] = {
8105 "Insufficient priviledges for the box",
8106 "The box does not exist",
8107 "Date is too long (history is not available after 15 months)",
8108 "Interval is too long (limit is 3 months)",
8111 const isds_error errors
[] = {
8118 struct code_map_isds_error map
= {
8120 .meanings
= meanings
,
8125 if (!context
) return IE_INVALID_CONTEXT
;
8126 zfree(context
->long_message
);
8128 /* Free output argument */
8129 if (NULL
!= credit
) *credit
= 0;
8130 if (NULL
!= email
) zfree(*email
);
8131 isds_list_free(history
);
8133 if (NULL
== box_id
) return IE_INVAL
;
8136 /* Check if connection is established */
8137 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8139 box_id_locale
= _isds_utf82locale((char*)box_id
);
8140 if (NULL
== box_id_locale
) {
8146 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8147 if (NULL
== request
) {
8148 isds_printf_message(context
,
8149 _("Could not build DataBoxCreditInfo request for %s box"),
8154 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8156 isds_log_message(context
, _("Could not create ISDS name space"));
8160 xmlSetNs(request
, isds_ns
);
8162 /* Add mandatory XSD:tIdDbInput child */
8163 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8164 /* Add mandatory dates elements with optional values */
8166 err
= tm2datestring(from_date
, &string
);
8168 isds_log_message(context
,
8169 _("Could not convert `from_date' argument to ISO date "
8173 INSERT_STRING(request
, "ciFromDate", string
);
8176 INSERT_STRING(request
, "ciFromDate", NULL
);
8179 err
= tm2datestring(to_date
, &string
);
8181 isds_log_message(context
,
8182 _("Could not convert `to_date' argument to ISO date "
8186 INSERT_STRING(request
, "ciTodate", string
);
8189 INSERT_STRING(request
, "ciTodate", NULL
);
8192 /* Send request and check response*/
8193 err
= send_destroy_request_check_response(context
,
8194 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8195 &request
, &response
, NULL
, &map
);
8196 if (err
) goto leave
;
8200 /* Set context to the root */
8201 xpath_ctx
= xmlXPathNewContext(response
);
8206 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8210 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8216 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8217 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8221 if (result
->nodesetval
->nodeNr
> 1) {
8222 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8226 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8227 xmlXPathFreeObject(result
); result
= NULL
;
8229 /* Extract common data */
8230 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8231 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8233 /* Extract records */
8234 if (NULL
== history
) goto leave
;
8235 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8241 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8242 struct isds_list
*prev_item
= NULL
;
8244 /* Iterate over all records */
8245 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8246 struct isds_list
*item
;
8248 /* Prepare structure */
8249 item
= calloc(1, sizeof(*item
));
8254 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8255 if (i
== 0) *history
= item
;
8256 else prev_item
->next
= item
;
8260 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8261 err
= extract_CiRecord(context
,
8262 (struct isds_credit_event
**) (&item
->data
),
8264 if (err
) goto leave
;
8270 isds_log(ILF_ISDS
, ILL_DEBUG
,
8271 _("DataBoxCreditInfo request processed by server successfully.\n"));
8274 isds_list_free(history
);
8275 if (NULL
!= email
) zfree(*email
)
8278 free(box_id_locale
);
8279 xmlXPathFreeObject(result
);
8280 xmlXPathFreeContext(xpath_ctx
);
8281 xmlFreeDoc(response
);
8283 #else /* not HAVE_LIBCURL */
8291 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8292 * code, destroy response and log success.
8293 * @context is ISDS session context.
8294 * @service_name is name of SERVICE_DB_MANIPULATION service
8295 * @box_id is UTF-8 encoded box identifier as zero terminated string
8296 * @approval is optional external approval of box manipulation
8297 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8298 * NULL, if you don't care. */
8299 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8300 struct isds_ctx
*context
, const xmlChar
*service_name
,
8301 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8302 xmlChar
**refnumber
) {
8303 isds_error err
= IE_SUCCESS
;
8305 xmlDocPtr response
= NULL
;
8308 if (!context
) return IE_INVALID_CONTEXT
;
8309 zfree(context
->long_message
);
8310 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8313 /* Check if connection is established */
8314 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8316 /* Do request and check for success */
8317 err
= build_send_dbid_request_check_response(context
,
8318 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8319 &response
, refnumber
);
8320 xmlFreeDoc(response
);
8323 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8324 isds_log(ILF_ISDS
, ILL_DEBUG
,
8325 _("%s request processed by server successfully.\n"),
8326 service_name_locale
);
8327 free(service_name_locale
);
8329 #else /* not HAVE_LIBCURL */
8337 /* Switch box into state where box can receive commercial messages (off by
8339 * @context is ISDS session context.
8340 * @box_id is UTF-8 encoded box identifier as zero terminated string
8341 * @allow is true for enable, false for disable commercial messages income
8342 * @approval is optional external approval of box manipulation
8343 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8344 * NULL, if you don't care. */
8345 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8346 const char *box_id
, const _Bool allow
,
8347 const struct isds_approval
*approval
, char **refnumber
) {
8348 return build_send_manipulationdbid_request_check_drop_response(context
,
8349 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8350 BAD_CAST
"ClearOpenAddressing",
8351 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8355 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8356 * message acceptance). This is just a box permission. Sender must apply
8357 * such role by sending each message.
8358 * @context is ISDS session context.
8359 * @box_id is UTF-8 encoded box identifier as zero terminated string
8360 * @allow is true for enable, false for disable OVM role permission
8361 * @approval is optional external approval of box manipulation
8362 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8363 * NULL, if you don't care. */
8364 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8365 const char *box_id
, const _Bool allow
,
8366 const struct isds_approval
*approval
, char **refnumber
) {
8367 return build_send_manipulationdbid_request_check_drop_response(context
,
8368 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8369 BAD_CAST
"ClearEffectiveOVM",
8370 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8374 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8375 * code, destroy response and log success.
8376 * @context is ISDS session context.
8377 * @service_name is name of SERVICE_DB_MANIPULATION service
8378 * @owner is structure describing box
8379 * @approval is optional external approval of box manipulation
8380 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8381 * NULL, if you don't care. */
8382 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8383 struct isds_ctx
*context
, const xmlChar
*service_name
,
8384 const struct isds_DbOwnerInfo
*owner
,
8385 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8386 isds_error err
= IE_SUCCESS
;
8388 char *service_name_locale
= NULL
;
8389 xmlNodePtr request
= NULL
, db_owner_info
;
8390 xmlNsPtr isds_ns
= NULL
;
8394 if (!context
) return IE_INVALID_CONTEXT
;
8395 zfree(context
->long_message
);
8396 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8399 service_name_locale
= _isds_utf82locale((char*)service_name
);
8400 if (!service_name_locale
) {
8406 request
= xmlNewNode(NULL
, service_name
);
8408 isds_printf_message(context
,
8409 _("Could not build %s request"), service_name_locale
);
8413 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8415 isds_log_message(context
, _("Could not create ISDS name space"));
8419 xmlSetNs(request
, isds_ns
);
8422 /* Add XSD:tOwnerInfoInput child*/
8423 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8424 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8425 if (err
) goto leave
;
8427 /* Add XSD:gExtApproval*/
8428 err
= insert_GExtApproval(context
, approval
, request
);
8429 if (err
) goto leave
;
8431 /* Send it to server and process response */
8432 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8433 service_name
, &request
, refnumber
);
8436 xmlFreeNode(request
);
8437 free(service_name_locale
);
8438 #else /* not HAVE_LIBCURL */
8446 /* Switch box accessibility state on request of box owner.
8447 * Despite the name, owner must do the request off-line. This function is
8448 * designed for such off-line meeting points (e.g. Czech POINT).
8449 * @context is ISDS session context.
8450 * @box identifies box to switch accessibility state.
8451 * @allow is true for making accessible, false to disallow access.
8452 * @approval is optional external approval of box manipulation
8453 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8454 * NULL, if you don't care. */
8455 isds_error
isds_switch_box_accessibility_on_owner_request(
8456 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8457 const _Bool allow
, const struct isds_approval
*approval
,
8459 return build_send_manipulationdbowner_request_check_drop_response(context
,
8460 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8461 BAD_CAST
"DisableOwnDataBox",
8462 box
, approval
, (xmlChar
**) refnumber
);
8466 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8468 * @context is ISDS session context.
8469 * @box identifies box to switch accessibility state.
8470 * @since is date since accessibility has been denied. This can be past too.
8471 * Only tm_year, tm_mon and tm_mday carry sane value.
8472 * @approval is optional external approval of box manipulation
8473 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8474 * NULL, if you don't care. */
8475 isds_error
isds_disable_box_accessibility_externaly(
8476 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8477 const struct tm
*since
, const struct isds_approval
*approval
,
8479 isds_error err
= IE_SUCCESS
;
8481 char *service_name_locale
= NULL
;
8482 xmlNodePtr request
= NULL
, node
;
8483 xmlNsPtr isds_ns
= NULL
;
8484 xmlChar
*string
= NULL
;
8488 if (!context
) return IE_INVALID_CONTEXT
;
8489 zfree(context
->long_message
);
8490 if (!box
|| !since
) return IE_INVAL
;
8494 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8496 isds_printf_message(context
,
8497 _("Could not build %s request"), "DisableDataBoxExternally");
8501 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8503 isds_log_message(context
, _("Could not create ISDS name space"));
8507 xmlSetNs(request
, isds_ns
);
8510 /* Add @box identification */
8511 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8512 err
= insert_DbOwnerInfo(context
, box
, node
);
8513 if (err
) goto leave
;
8515 /* Add @since date */
8516 err
= tm2datestring(since
, &string
);
8518 isds_log_message(context
,
8519 _("Could not convert `since' argument to ISO date string"));
8522 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8526 err
= insert_GExtApproval(context
, approval
, request
);
8527 if (err
) goto leave
;
8529 /* Send it to server and process response */
8530 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8531 BAD_CAST
"DisableDataBoxExternally", &request
,
8532 (xmlChar
**) refnumber
);
8536 xmlFreeNode(request
);
8537 free(service_name_locale
);
8538 #else /* not HAVE_LIBCURL */
8547 /* Insert struct isds_message data (envelope (recipient data optional) and
8548 * documents into XML tree
8549 * @context is session context
8550 * @outgoing_message is libisds structure with message data
8551 * @create_message is XML CreateMessage or CreateMultipleMessage element
8552 * @process_recipient true for recipient data serialization, false for no
8554 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8555 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8556 const _Bool process_recipient
) {
8558 isds_error err
= IE_SUCCESS
;
8559 xmlNodePtr envelope
, dm_files
, node
;
8560 xmlChar
*string
= NULL
;
8562 if (!context
) return IE_INVALID_CONTEXT
;
8563 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8566 /* Build envelope */
8567 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8569 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8570 "%s element"), create_message
->name
);
8574 if (!outgoing_message
->envelope
) {
8575 isds_log_message(context
, _("Outgoing message is missing envelope"));
8580 /* Insert optional message type */
8581 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8583 if (err
) goto leave
;
8585 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8586 outgoing_message
->envelope
->dmSenderOrgUnit
);
8587 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8588 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8590 if (process_recipient
) {
8591 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8592 isds_log_message(context
,
8593 _("Outgoing message is missing recipient box identifier"));
8597 INSERT_STRING(envelope
, "dbIDRecipient",
8598 outgoing_message
->envelope
->dbIDRecipient
);
8600 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8601 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8602 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8603 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8604 INSERT_STRING(envelope
, "dmToHands",
8605 outgoing_message
->envelope
->dmToHands
);
8608 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8610 INSERT_STRING(envelope
, "dmAnnotation",
8611 outgoing_message
->envelope
->dmAnnotation
);
8613 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8614 0, 50, "dmRecipientRefNumber");
8615 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8616 outgoing_message
->envelope
->dmRecipientRefNumber
);
8618 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8619 0, 50, "dmSenderRefNumber");
8620 INSERT_STRING(envelope
, "dmSenderRefNumber",
8621 outgoing_message
->envelope
->dmSenderRefNumber
);
8623 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8624 0, 50, "dmRecipientIdent");
8625 INSERT_STRING(envelope
, "dmRecipientIdent",
8626 outgoing_message
->envelope
->dmRecipientIdent
);
8628 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8629 0, 50, "dmSenderIdent");
8630 INSERT_STRING(envelope
, "dmSenderIdent",
8631 outgoing_message
->envelope
->dmSenderIdent
);
8633 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8634 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8635 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8636 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8637 INSERT_STRING(envelope
, "dmLegalTitleSect",
8638 outgoing_message
->envelope
->dmLegalTitleSect
);
8639 INSERT_STRING(envelope
, "dmLegalTitlePar",
8640 outgoing_message
->envelope
->dmLegalTitlePar
);
8641 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8642 outgoing_message
->envelope
->dmLegalTitlePoint
);
8644 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8645 outgoing_message
->envelope
->dmPersonalDelivery
);
8646 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8647 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8649 /* ???: Should we require value for dbEffectiveOVM sender?
8650 * ISDS has default as true */
8651 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8652 INSERT_BOOLEAN(envelope
, "dmPublishOwnID",
8653 outgoing_message
->envelope
->dmPublishOwnID
);
8656 /* Append dmFiles */
8657 if (!outgoing_message
->documents
) {
8658 isds_log_message(context
,
8659 _("Outgoing message is missing list of documents"));
8663 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8665 isds_printf_message(context
, _("Could not add dmFiles child to "
8666 "%s element"), create_message
->name
);
8671 /* Check for document hierarchy */
8672 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8673 if (err
) goto leave
;
8675 /* Process each document */
8676 for (struct isds_list
*item
=
8677 (struct isds_list
*) outgoing_message
->documents
;
8678 item
; item
= item
->next
) {
8680 isds_log_message(context
,
8681 _("List of documents contains empty item"));
8685 /* FIXME: Check for dmFileMetaType and for document references.
8686 * Only first document can be of MAIN type */
8687 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8690 if (err
) goto leave
;
8697 #endif /* HAVE_LIBCURL */
8700 /* Send a message via ISDS to a recipient
8701 * @context is session context
8702 * @outgoing_message is message to send; Some members are mandatory (like
8703 * dbIDRecipient), some are optional and some are irrelevant (especially data
8704 * about sender). Included pointer to isds_list documents must contain at
8705 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8706 * members will be filled with valid data from ISDS. Exact list of write
8707 * members is subject to change. Currently dmID is changed.
8708 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8709 isds_error
isds_send_message(struct isds_ctx
*context
,
8710 struct isds_message
*outgoing_message
) {
8712 isds_error err
= IE_SUCCESS
;
8714 xmlNsPtr isds_ns
= NULL
;
8715 xmlNodePtr request
= NULL
;
8716 xmlDocPtr response
= NULL
;
8717 xmlChar
*code
= NULL
, *message
= NULL
;
8718 xmlXPathContextPtr xpath_ctx
= NULL
;
8719 xmlXPathObjectPtr result
= NULL
;
8720 /*_Bool message_is_complete = 0;*/
8723 if (!context
) return IE_INVALID_CONTEXT
;
8724 zfree(context
->long_message
);
8725 if (!outgoing_message
) return IE_INVAL
;
8728 /* Check if connection is established
8729 * TODO: This check should be done downstairs. */
8730 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8733 /* Build CreateMessage request */
8734 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8736 isds_log_message(context
,
8737 _("Could not build CreateMessage request"));
8740 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8742 isds_log_message(context
, _("Could not create ISDS name space"));
8743 xmlFreeNode(request
);
8746 xmlSetNs(request
, isds_ns
);
8748 /* Append envelope and files */
8749 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8750 if (err
) goto leave
;
8753 /* Signal we can serialize message since now */
8754 /*message_is_complete = 1;*/
8757 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8760 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8762 /* Don't' destroy request, we want to provide it to application later */
8765 isds_log(ILF_ISDS
, ILL_DEBUG
,
8766 _("Processing ISDS response on CreateMessage "
8767 "request failed\n"));
8771 /* Check for response status */
8772 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8773 &code
, &message
, NULL
);
8775 isds_log(ILF_ISDS
, ILL_DEBUG
,
8776 _("ISDS response on CreateMessage request "
8777 "is missing status\n"));
8781 /* Request processed, but refused by server or server failed */
8782 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8783 char *box_id_locale
=
8784 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8785 char *code_locale
= _isds_utf82locale((char*)code
);
8786 char *message_locale
= _isds_utf82locale((char*)message
);
8787 isds_log(ILF_ISDS
, ILL_DEBUG
,
8788 _("Server did not accept message for %s on CreateMessage "
8789 "request (code=%s, message=%s)\n"),
8790 box_id_locale
, code_locale
, message_locale
);
8791 isds_log_message(context
, message_locale
);
8792 free(box_id_locale
);
8794 free(message_locale
);
8801 xpath_ctx
= xmlXPathNewContext(response
);
8806 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8810 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8816 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8817 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8821 if (result
->nodesetval
->nodeNr
> 1) {
8822 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8826 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8827 xmlXPathFreeObject(result
); result
= NULL
;
8829 if (outgoing_message
->envelope
->dmID
) {
8830 free(outgoing_message
->envelope
->dmID
);
8831 outgoing_message
->envelope
->dmID
= NULL
;
8833 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8834 if (!outgoing_message
->envelope
->dmID
) {
8835 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8836 "but did not return assigned message ID\n"));
8840 /* TODO: Serialize message into structure member raw */
8841 /* XXX: Each web service transport message in different format.
8842 * Therefore it's not possible to save them directly.
8843 * To save them, one must figure out common format.
8844 * We can leave it on application, or we can implement the ESS format. */
8845 /*if (message_is_complete) {
8846 if (outgoing_message->envelope->dmID) {
8848 /* Add assigned message ID as first child*/
8849 /*xmlNodePtr dmid_text = xmlNewText(
8850 (xmlChar *) outgoing_message->envelope->dmID);
8851 if (!dmid_text) goto serialization_failed;
8853 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8855 if (!dmid_element) {
8856 xmlFreeNode(dmid_text);
8857 goto serialization_failed;
8860 xmlNodePtr dmid_element_with_text =
8861 xmlAddChild(dmid_element, dmid_text);
8862 if (!dmid_element_with_text) {
8863 xmlFreeNode(dmid_element);
8864 xmlFreeNode(dmid_text);
8865 goto serialization_failed;
8868 node = xmlAddPrevSibling(envelope->childern,
8869 dmid_element_with_text);
8871 xmlFreeNodeList(dmid_element_with_text);
8872 goto serialization_failed;
8876 /* Serialize message with ID into raw */
8877 /*buffer = serialize_element(envelope)*/
8880 serialization_failed:
8885 xmlXPathFreeObject(result
);
8886 xmlXPathFreeContext(xpath_ctx
);
8890 xmlFreeDoc(response
);
8891 xmlFreeNode(request
);
8894 isds_log(ILF_ISDS
, ILL_DEBUG
,
8895 _("CreateMessage request processed by server "
8896 "successfully.\n"));
8897 #else /* not HAVE_LIBCURL */
8905 /* Send a message via ISDS to a multiple recipients
8906 * @context is session context
8907 * @outgoing_message is message to send; Some members are mandatory,
8908 * some are optional and some are irrelevant (especially data
8909 * about sender). Data about recipient will be substituted by ISDS from
8910 * @copies. Included pointer to isds_list documents must
8911 * contain at least one document of FILEMETATYPE_MAIN.
8912 * @copies is list of isds_message_copy structures addressing all desired
8913 * recipients. This is read-write structure, some members will be filled with
8914 * valid data from ISDS (message IDs, error codes, error descriptions).
8916 * ISDS_SUCCESS if all messages have been sent
8917 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8918 * succeeded messages can be identified by copies->data->error),
8919 * or other error code if something other goes wrong. */
8920 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8921 const struct isds_message
*outgoing_message
,
8922 struct isds_list
*copies
) {
8924 isds_error err
= IE_SUCCESS
;
8926 isds_error append_err
;
8927 xmlNsPtr isds_ns
= NULL
;
8928 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8929 struct isds_list
*item
;
8930 struct isds_message_copy
*copy
;
8931 xmlDocPtr response
= NULL
;
8932 xmlChar
*code
= NULL
, *message
= NULL
;
8933 xmlXPathContextPtr xpath_ctx
= NULL
;
8934 xmlXPathObjectPtr result
= NULL
;
8935 xmlChar
*string
= NULL
;
8939 if (!context
) return IE_INVALID_CONTEXT
;
8940 zfree(context
->long_message
);
8941 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8944 /* Check if connection is established
8945 * TODO: This check should be done downstairs. */
8946 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8949 /* Build CreateMultipleMessage request */
8950 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8952 isds_log_message(context
,
8953 _("Could not build CreateMultipleMessage request"));
8956 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8958 isds_log_message(context
, _("Could not create ISDS name space"));
8959 xmlFreeNode(request
);
8962 xmlSetNs(request
, isds_ns
);
8965 /* Build recipients */
8966 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8968 isds_log_message(context
, _("Could not add dmRecipients child to "
8969 "CreateMultipleMessage element"));
8970 xmlFreeNode(request
);
8974 /* Insert each recipient */
8975 for (item
= copies
; item
; item
= item
->next
) {
8976 copy
= (struct isds_message_copy
*) item
->data
;
8978 isds_log_message(context
,
8979 _("`copies' list item contains empty data"));
8984 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8986 isds_log_message(context
, _("Could not add dmRecipient child to "
8987 "dmRecipients element"));
8992 if (!copy
->dbIDRecipient
) {
8993 isds_log_message(context
,
8994 _("Message copy is missing recipient box identifier"));
8998 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
8999 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9000 copy
->dmRecipientOrgUnit
);
9001 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9002 copy
->dmRecipientOrgUnitNum
, string
);
9003 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9006 /* Append envelope and files */
9007 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9008 if (err
) goto leave
;
9011 isds_log(ILF_ISDS
, ILL_DEBUG
,
9012 _("Sending CreateMultipleMessage request to ISDS\n"));
9015 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9017 isds_log(ILF_ISDS
, ILL_DEBUG
,
9018 _("Processing ISDS response on CreateMultipleMessage "
9019 "request failed\n"));
9023 /* Check for response status */
9024 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9025 &code
, &message
, NULL
);
9027 isds_log(ILF_ISDS
, ILL_DEBUG
,
9028 _("ISDS response on CreateMultipleMessage request "
9029 "is missing status\n"));
9033 /* Request processed, but some copies failed */
9034 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9035 char *box_id_locale
=
9036 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9037 char *code_locale
= _isds_utf82locale((char*)code
);
9038 char *message_locale
= _isds_utf82locale((char*)message
);
9039 isds_log(ILF_ISDS
, ILL_DEBUG
,
9040 _("Server did accept message for multiple recipients "
9041 "on CreateMultipleMessage request but delivery to "
9042 "some of them failed (code=%s, message=%s)\n"),
9043 box_id_locale
, code_locale
, message_locale
);
9044 isds_log_message(context
, message_locale
);
9045 free(box_id_locale
);
9047 free(message_locale
);
9048 err
= IE_PARTIAL_SUCCESS
;
9051 /* Request refused by server as whole */
9052 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9053 char *box_id_locale
=
9054 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9055 char *code_locale
= _isds_utf82locale((char*)code
);
9056 char *message_locale
= _isds_utf82locale((char*)message
);
9057 isds_log(ILF_ISDS
, ILL_DEBUG
,
9058 _("Server did not accept message for multiple recipients "
9059 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9060 box_id_locale
, code_locale
, message_locale
);
9061 isds_log_message(context
, message_locale
);
9062 free(box_id_locale
);
9064 free(message_locale
);
9071 xpath_ctx
= xmlXPathNewContext(response
);
9076 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9080 result
= xmlXPathEvalExpression(
9081 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9082 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9088 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9089 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9094 /* Extract message ID and delivery status for each copy */
9095 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9096 item
= item
->next
, i
++) {
9097 copy
= (struct isds_message_copy
*) item
->data
;
9098 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9100 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9106 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9107 isds_printf_message(context
, _("ISDS returned unexpected number of "
9108 "message copy delivery states: %d"),
9109 result
->nodesetval
->nodeNr
);
9118 xmlXPathFreeObject(result
);
9119 xmlXPathFreeContext(xpath_ctx
);
9123 xmlFreeDoc(response
);
9124 xmlFreeNode(request
);
9127 isds_log(ILF_ISDS
, ILL_DEBUG
,
9128 _("CreateMultipleMessageResponse request processed by server "
9129 "successfully.\n"));
9130 #else /* not HAVE_LIBCURL */
9138 /* Get list of messages. This is common core for getting sent or received
9140 * Any criterion argument can be NULL, if you don't care about it.
9141 * @context is session context. Must not be NULL.
9142 * @outgoing_direction is true if you want list of outgoing messages,
9143 * it's false if you want incoming messages.
9144 * @from_time is minimal time and date of message sending inclusive.
9145 * @to_time is maximal time and date of message sending inclusive
9146 * @organization_unit_number is number of sender/recipient respectively.
9147 * @status_filter is bit field of isds_message_status values. Use special
9148 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9149 * all values, you can use bit-wise arithmetic if you want.)
9150 * @offset is index of first message we are interested in. First message is 1.
9151 * Set to 0 (or 1) if you don't care.
9152 * @number is maximal length of list you want to get as input value, outputs
9153 * number of messages matching these criteria. Can be NULL if you don't care
9154 * (applies to output value either).
9155 * @messages is automatically reallocated list of isds_message's. Be ware that
9156 * it returns only brief overview (envelope and some other fields) about each
9157 * message, not the complete message. FIXME: Specify exact fields.
9158 * The list is sorted by delivery time in ascending order.
9159 * Use NULL if you don't care about don't need the data (useful if you want to
9160 * know only the @number). If you provide &NULL, list will be allocated on
9161 * heap, if you provide pointer to non-NULL, list will be freed automatically
9162 * at first. Also in case of error the list will be NULLed.
9163 * @return IE_SUCCESS or appropriate error code. */
9164 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9165 _Bool outgoing_direction
,
9166 const struct timeval
*from_time
, const struct timeval
*to_time
,
9167 const long int *organization_unit_number
,
9168 const unsigned int status_filter
,
9169 const unsigned long int offset
, unsigned long int *number
,
9170 struct isds_list
**messages
) {
9172 isds_error err
= IE_SUCCESS
;
9174 xmlNsPtr isds_ns
= NULL
;
9175 xmlNodePtr request
= NULL
, node
;
9176 xmlDocPtr response
= NULL
;
9177 xmlChar
*code
= NULL
, *message
= NULL
;
9178 xmlXPathContextPtr xpath_ctx
= NULL
;
9179 xmlXPathObjectPtr result
= NULL
;
9180 xmlChar
*string
= NULL
;
9184 if (!context
) return IE_INVALID_CONTEXT
;
9185 zfree(context
->long_message
);
9187 /* Free former message list if any */
9188 if (messages
) isds_list_free(messages
);
9191 /* Check if connection is established
9192 * TODO: This check should be done downstairs. */
9193 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9195 /* Build GetListOf*Messages request */
9196 request
= xmlNewNode(NULL
,
9197 (outgoing_direction
) ?
9198 BAD_CAST
"GetListOfSentMessages" :
9199 BAD_CAST
"GetListOfReceivedMessages"
9202 isds_log_message(context
,
9203 (outgoing_direction
) ?
9204 _("Could not build GetListOfSentMessages request") :
9205 _("Could not build GetListOfReceivedMessages request")
9209 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9211 isds_log_message(context
, _("Could not create ISDS name space"));
9212 xmlFreeNode(request
);
9215 xmlSetNs(request
, isds_ns
);
9219 err
= timeval2timestring(from_time
, &string
);
9220 if (err
) goto leave
;
9222 INSERT_STRING(request
, "dmFromTime", string
);
9223 free(string
); string
= NULL
;
9226 err
= timeval2timestring(to_time
, &string
);
9227 if (err
) goto leave
;
9229 INSERT_STRING(request
, "dmToTime", string
);
9230 free(string
); string
= NULL
;
9232 if (outgoing_direction
) {
9233 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9234 organization_unit_number
, string
);
9236 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9237 organization_unit_number
, string
);
9240 if (status_filter
> MESSAGESTATE_ANY
) {
9241 isds_printf_message(context
,
9242 _("Invalid message state filter value: %ld"), status_filter
);
9246 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9249 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9251 INSERT_STRING(request
, "dmOffset", "1");
9254 /* number 0 means no limit */
9255 if (number
&& *number
== 0) {
9256 INSERT_STRING(request
, "dmLimit", NULL
);
9258 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9262 isds_log(ILF_ISDS
, ILL_DEBUG
,
9263 (outgoing_direction
) ?
9264 _("Sending GetListOfSentMessages request to ISDS\n") :
9265 _("Sending GetListOfReceivedMessages request to ISDS\n")
9269 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9270 xmlFreeNode(request
); request
= NULL
;
9273 isds_log(ILF_ISDS
, ILL_DEBUG
,
9274 (outgoing_direction
) ?
9275 _("Processing ISDS response on GetListOfSentMessages "
9276 "request failed\n") :
9277 _("Processing ISDS response on GetListOfReceivedMessages "
9283 /* Check for response status */
9284 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9285 &code
, &message
, NULL
);
9287 isds_log(ILF_ISDS
, ILL_DEBUG
,
9288 (outgoing_direction
) ?
9289 _("ISDS response on GetListOfSentMessages request "
9290 "is missing status\n") :
9291 _("ISDS response on GetListOfReceivedMessages request "
9292 "is missing status\n")
9297 /* Request processed, but nothing found */
9298 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9299 char *code_locale
= _isds_utf82locale((char*)code
);
9300 char *message_locale
= _isds_utf82locale((char*)message
);
9301 isds_log(ILF_ISDS
, ILL_DEBUG
,
9302 (outgoing_direction
) ?
9303 _("Server refused GetListOfSentMessages request "
9304 "(code=%s, message=%s)\n") :
9305 _("Server refused GetListOfReceivedMessages request "
9306 "(code=%s, message=%s)\n"),
9307 code_locale
, message_locale
);
9308 isds_log_message(context
, message_locale
);
9310 free(message_locale
);
9317 xpath_ctx
= xmlXPathNewContext(response
);
9322 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9326 result
= xmlXPathEvalExpression(
9327 (outgoing_direction
) ?
9328 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9329 "isds:dmRecords/isds:dmRecord" :
9330 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9331 "isds:dmRecords/isds:dmRecord",
9338 /* Fill output arguments in */
9339 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9340 struct isds_envelope
*envelope
;
9341 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9343 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9344 /* Create new message */
9345 item
= calloc(1, sizeof(*item
));
9350 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9351 item
->data
= calloc(1, sizeof(struct isds_message
));
9353 isds_list_free(&item
);
9358 /* Extract envelope data */
9359 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9361 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9363 isds_list_free(&item
);
9367 /* Attach extracted envelope */
9368 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9370 /* Append new message into the list */
9372 *messages
= last_item
= item
;
9374 last_item
->next
= item
;
9379 if (number
) *number
= count
;
9383 isds_list_free(messages
);
9387 xmlXPathFreeObject(result
);
9388 xmlXPathFreeContext(xpath_ctx
);
9392 xmlFreeDoc(response
);
9393 xmlFreeNode(request
);
9396 isds_log(ILF_ISDS
, ILL_DEBUG
,
9397 (outgoing_direction
) ?
9398 _("GetListOfSentMessages request processed by server "
9399 "successfully.\n") :
9400 _("GetListOfReceivedMessages request processed by server "
9403 #else /* not HAVE_LIBCURL */
9410 /* Get list of outgoing (already sent) messages.
9411 * Any criterion argument can be NULL, if you don't care about it.
9412 * @context is session context. Must not be NULL.
9413 * @from_time is minimal time and date of message sending inclusive.
9414 * @to_time is maximal time and date of message sending inclusive
9415 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9416 * @status_filter is bit field of isds_message_status values. Use special
9417 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9418 * all values, you can use bit-wise arithmetic if you want.)
9419 * @offset is index of first message we are interested in. First message is 1.
9420 * Set to 0 (or 1) if you don't care.
9421 * @number is maximal length of list you want to get as input value, outputs
9422 * number of messages matching these criteria. Can be NULL if you don't care
9423 * (applies to output value either).
9424 * @messages is automatically reallocated list of isds_message's. Be ware that
9425 * it returns only brief overview (envelope and some other fields) about each
9426 * message, not the complete message. FIXME: Specify exact fields.
9427 * The list is sorted by delivery time in ascending order.
9428 * Use NULL if you don't care about the meta data (useful if you want to know
9429 * only the @number). If you provide &NULL, list will be allocated on heap,
9430 * if you provide pointer to non-NULL, list will be freed automatically at
9431 * first. Also in case of error the list will be NULLed.
9432 * @return IE_SUCCESS or appropriate error code. */
9433 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9434 const struct timeval
*from_time
, const struct timeval
*to_time
,
9435 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9436 const unsigned long int offset
, unsigned long int *number
,
9437 struct isds_list
**messages
) {
9439 return isds_get_list_of_messages(
9441 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9447 /* Get list of incoming (addressed to you) messages.
9448 * Any criterion argument can be NULL, if you don't care about it.
9449 * @context is session context. Must not be NULL.
9450 * @from_time is minimal time and date of message sending inclusive.
9451 * @to_time is maximal time and date of message sending inclusive
9452 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9453 * @status_filter is bit field of isds_message_status values. Use special
9454 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9455 * all values, you can use bit-wise arithmetic if you want.)
9456 * @offset is index of first message we are interested in. First message is 1.
9457 * Set to 0 (or 1) if you don't care.
9458 * @number is maximal length of list you want to get as input value, outputs
9459 * number of messages matching these criteria. Can be NULL if you don't care
9460 * (applies to output value either).
9461 * @messages is automatically reallocated list of isds_message's. Be ware that
9462 * it returns only brief overview (envelope and some other fields) about each
9463 * message, not the complete message. FIXME: Specify exact fields.
9464 * Use NULL if you don't care about the meta data (useful if you want to know
9465 * only the @number). If you provide &NULL, list will be allocated on heap,
9466 * if you provide pointer to non-NULL, list will be freed automatically at
9467 * first. Also in case of error the list will be NULLed.
9468 * @return IE_SUCCESS or appropriate error code. */
9469 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9470 const struct timeval
*from_time
, const struct timeval
*to_time
,
9471 const long int *dmRecipientOrgUnitNum
,
9472 const unsigned int status_filter
,
9473 const unsigned long int offset
, unsigned long int *number
,
9474 struct isds_list
**messages
) {
9476 return isds_get_list_of_messages(
9478 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9484 /* Get list of sent message state changes.
9485 * Any criterion argument can be NULL, if you don't care about it.
9486 * @context is session context. Must not be NULL.
9487 * @from_time is minimal time and date of status changes inclusive
9488 * @to_time is maximal time and date of status changes inclusive
9489 * @changed_states is automatically reallocated list of
9490 * isds_message_status_change's. If you provide &NULL, list will be allocated
9491 * on heap, if you provide pointer to non-NULL, list will be freed
9492 * automatically at first. Also in case of error the list will be NULLed.
9493 * XXX: The list item ordering is not specified.
9494 * XXX: Server provides only `recent' changes.
9495 * @return IE_SUCCESS or appropriate error code. */
9496 isds_error
isds_get_list_of_sent_message_state_changes(
9497 struct isds_ctx
*context
,
9498 const struct timeval
*from_time
, const struct timeval
*to_time
,
9499 struct isds_list
**changed_states
) {
9501 isds_error err
= IE_SUCCESS
;
9503 xmlNsPtr isds_ns
= NULL
;
9504 xmlNodePtr request
= NULL
, node
;
9505 xmlDocPtr response
= NULL
;
9506 xmlXPathContextPtr xpath_ctx
= NULL
;
9507 xmlXPathObjectPtr result
= NULL
;
9508 xmlChar
*string
= NULL
;
9512 if (!context
) return IE_INVALID_CONTEXT
;
9513 zfree(context
->long_message
);
9515 /* Free former message list if any */
9516 isds_list_free(changed_states
);
9519 /* Check if connection is established
9520 * TODO: This check should be done downstairs. */
9521 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9523 /* Build GetMessageStateChanges request */
9524 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9526 isds_log_message(context
,
9527 _("Could not build GetMessageStateChanges request"));
9530 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9532 isds_log_message(context
, _("Could not create ISDS name space"));
9533 xmlFreeNode(request
);
9536 xmlSetNs(request
, isds_ns
);
9540 err
= timeval2timestring(from_time
, &string
);
9541 if (err
) goto leave
;
9543 INSERT_STRING(request
, "dmFromTime", string
);
9547 err
= timeval2timestring(to_time
, &string
);
9548 if (err
) goto leave
;
9550 INSERT_STRING(request
, "dmToTime", string
);
9555 err
= send_destroy_request_check_response(context
,
9556 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9557 &response
, NULL
, NULL
);
9558 if (err
) goto leave
;
9562 xpath_ctx
= xmlXPathNewContext(response
);
9567 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9571 result
= xmlXPathEvalExpression(
9572 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9573 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9579 /* Fill output arguments in */
9580 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9581 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9583 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9584 /* Create new status change */
9585 item
= calloc(1, sizeof(*item
));
9591 (void(*)(void**)) &isds_message_status_change_free
;
9593 /* Extract message status change */
9594 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9595 err
= extract_StateChangesRecord(context
,
9596 (struct isds_message_status_change
**) &item
->data
,
9599 isds_list_free(&item
);
9603 /* Append new message status change into the list */
9604 if (!*changed_states
) {
9605 *changed_states
= last_item
= item
;
9607 last_item
->next
= item
;
9615 isds_list_free(changed_states
);
9619 xmlXPathFreeObject(result
);
9620 xmlXPathFreeContext(xpath_ctx
);
9621 xmlFreeDoc(response
);
9622 xmlFreeNode(request
);
9625 isds_log(ILF_ISDS
, ILL_DEBUG
,
9626 _("GetMessageStateChanges request processed by server "
9627 "successfully.\n"));
9628 #else /* not HAVE_LIBCURL */
9636 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9638 * @context is session context
9639 * @service is ISDS WS service handler
9640 * @service_name is name of SERVICE_DM_OPERATIONS
9641 * @message_id is message ID to send as service argument to ISDS
9642 * @response is reallocated server SOAP body response as XML document
9643 * @raw_response is reallocated bit stream with response body. Use
9644 * NULL if you don't care
9645 * @raw_response_length is size of @raw_response in bytes
9646 * @code is reallocated ISDS status code
9647 * @status_message is reallocated ISDS status message
9648 * @return error coded from lower layer, context message will be set up
9650 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9651 const isds_service service
, const xmlChar
*service_name
,
9652 const char *message_id
,
9653 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9654 xmlChar
**code
, xmlChar
**status_message
) {
9656 isds_error err
= IE_SUCCESS
;
9657 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9658 xmlNodePtr request
= NULL
, node
;
9659 xmlNsPtr isds_ns
= NULL
;
9661 if (!context
) return IE_INVALID_CONTEXT
;
9662 if (!service_name
|| !message_id
) return IE_INVAL
;
9663 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9664 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9666 /* Free output argument */
9667 xmlFreeDoc(*response
); *response
= NULL
;
9668 if (raw_response
) zfree(*raw_response
);
9670 zfree(*status_message
);
9673 /* Check if connection is established
9674 * TODO: This check should be done downstairs. */
9675 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9677 service_name_locale
= _isds_utf82locale((char*)service_name
);
9678 message_id_locale
= _isds_utf82locale(message_id
);
9679 if (!service_name_locale
|| !message_id_locale
) {
9685 request
= xmlNewNode(NULL
, service_name
);
9687 isds_printf_message(context
,
9688 _("Could not build %s request for %s message ID"),
9689 service_name_locale
, message_id_locale
);
9693 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9695 isds_log_message(context
, _("Could not create ISDS name space"));
9699 xmlSetNs(request
, isds_ns
);
9702 /* Add requested ID */
9703 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9704 if (err
) goto leave
;
9705 INSERT_STRING(request
, "dmID", message_id
);
9708 isds_log(ILF_ISDS
, ILL_DEBUG
,
9709 _("Sending %s request for %s message ID to ISDS\n"),
9710 service_name_locale
, message_id_locale
);
9713 err
= _isds(context
, service
, request
, response
,
9714 raw_response
, raw_response_length
);
9715 xmlFreeNode(request
); request
= NULL
;
9718 isds_log(ILF_ISDS
, ILL_DEBUG
,
9719 _("Processing ISDS response on %s request failed\n"),
9720 service_name_locale
);
9724 /* Check for response status */
9725 err
= isds_response_status(context
, service
, *response
,
9726 code
, status_message
, NULL
);
9728 isds_log(ILF_ISDS
, ILL_DEBUG
,
9729 _("ISDS response on %s request is missing status\n"),
9730 service_name_locale
);
9734 /* Request processed, but nothing found */
9735 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9736 char *code_locale
= _isds_utf82locale((char*) *code
);
9737 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9738 isds_log(ILF_ISDS
, ILL_DEBUG
,
9739 _("Server refused %s request for %s message ID "
9740 "(code=%s, message=%s)\n"),
9741 service_name_locale
, message_id_locale
,
9742 code_locale
, status_message_locale
);
9743 isds_log_message(context
, status_message_locale
);
9745 free(status_message_locale
);
9751 free(message_id_locale
);
9752 free(service_name_locale
);
9753 xmlFreeNode(request
);
9758 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9759 * signed data and free ISDS response.
9760 * @context is session context
9761 * @message_id is UTF-8 encoded message ID for logging purpose
9762 * @response is parsed XML document. It will be freed and NULLed in the middle
9763 * of function run to save memory. This is not guaranteed in case of error.
9764 * @request_name is name of ISDS request used to construct response root
9765 * element name and for logging purpose.
9766 * @raw is reallocated output buffer with DER encoded CMS data
9767 * @raw_length is size of @raw buffer in bytes
9768 * @returns standard error codes, in case of error, @raw will be freed and
9769 * NULLed, @response sometimes. */
9770 static isds_error
find_extract_signed_data_free_response(
9771 struct isds_ctx
*context
, const xmlChar
*message_id
,
9772 xmlDocPtr
*response
, const xmlChar
*request_name
,
9773 void **raw
, size_t *raw_length
) {
9775 isds_error err
= IE_SUCCESS
;
9776 char *xpath_expression
= NULL
;
9777 xmlXPathContextPtr xpath_ctx
= NULL
;
9778 xmlXPathObjectPtr result
= NULL
;
9779 char *encoded_structure
= NULL
;
9781 if (!context
) return IE_INVALID_CONTEXT
;
9782 if (!raw
) return IE_INVAL
;
9784 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9787 /* Build XPath expression */
9788 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9789 "Response/isds:dmSignature");
9790 if (!xpath_expression
) return IE_NOMEM
;
9793 xpath_ctx
= xmlXPathNewContext(*response
);
9798 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9802 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9807 /* Empty response */
9808 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9809 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9810 isds_printf_message(context
,
9811 _("Server did not return any signed data for message ID `%s' "
9813 message_id_locale
, request_name
);
9814 free(message_id_locale
);
9818 /* More responses */
9819 if (result
->nodesetval
->nodeNr
> 1) {
9820 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9821 isds_printf_message(context
,
9822 _("Server did return more signed data for message ID `%s' "
9824 message_id_locale
, request_name
);
9825 free(message_id_locale
);
9830 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9832 /* Extract PKCS#7 structure */
9833 EXTRACT_STRING(".", encoded_structure
);
9834 if (!encoded_structure
) {
9835 isds_log_message(context
, _("dmSignature element is empty"));
9838 /* Here we have delivery info as standalone CMS in encoded_structure.
9839 * We don't need any other data, free them: */
9840 xmlXPathFreeObject(result
); result
= NULL
;
9841 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9842 xmlFreeDoc(*response
); *response
= NULL
;
9845 /* Decode PKCS#7 to DER format */
9846 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9847 if (*raw_length
== (size_t) -1) {
9848 isds_log_message(context
,
9849 _("Error while Base64-decoding PKCS#7 structure"));
9860 free(encoded_structure
);
9861 xmlXPathFreeObject(result
);
9862 xmlXPathFreeContext(xpath_ctx
);
9863 free(xpath_expression
);
9867 #endif /* HAVE_LIBCURL */
9870 /* Download incoming message envelope identified by ID.
9871 * @context is session context
9872 * @message_id is message identifier (you can get them from
9873 * isds_get_list_of_received_messages())
9874 * @message is automatically reallocated message retrieved from ISDS.
9875 * It will miss documents per se. Use isds_get_received_message(), if you are
9876 * interested in documents (content) too.
9877 * Returned hash and timestamp require documents to be verifiable. */
9878 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9879 const char *message_id
, struct isds_message
**message
) {
9881 isds_error err
= IE_SUCCESS
;
9883 xmlDocPtr response
= NULL
;
9884 xmlChar
*code
= NULL
, *status_message
= NULL
;
9885 xmlXPathContextPtr xpath_ctx
= NULL
;
9886 xmlXPathObjectPtr result
= NULL
;
9889 if (!context
) return IE_INVALID_CONTEXT
;
9890 zfree(context
->long_message
);
9892 /* Free former message if any */
9893 if (!message
) return IE_INVAL
;
9894 isds_message_free(message
);
9897 /* Do request and check for success */
9898 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9899 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9900 &response
, NULL
, NULL
, &code
, &status_message
);
9901 if (err
) goto leave
;
9904 xpath_ctx
= xmlXPathNewContext(response
);
9909 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9913 result
= xmlXPathEvalExpression(
9914 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9915 "isds:dmReturnedMessageEnvelope",
9921 /* Empty response */
9922 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9923 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9924 isds_printf_message(context
,
9925 _("Server did not return any envelope for ID `%s' "
9926 "on MessageEnvelopeDownload request"), message_id_locale
);
9927 free(message_id_locale
);
9932 if (result
->nodesetval
->nodeNr
> 1) {
9933 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9934 isds_printf_message(context
,
9935 _("Server did return more envelopes for ID `%s' "
9936 "on MessageEnvelopeDownload request"), message_id_locale
);
9937 free(message_id_locale
);
9942 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9944 /* Extract the envelope (= message without documents, hence 0) */
9945 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9946 if (err
) goto leave
;
9949 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9950 &(*message
)->raw_length
);
9954 isds_message_free(message
);
9957 xmlXPathFreeObject(result
);
9958 xmlXPathFreeContext(xpath_ctx
);
9961 free(status_message
);
9962 if (!*message
|| !(*message
)->xml
) {
9963 xmlFreeDoc(response
);
9967 isds_log(ILF_ISDS
, ILL_DEBUG
,
9968 _("MessageEnvelopeDownload request processed by server "
9971 #else /* not HAVE_LIBCURL */
9978 /* Load delivery info of any format from buffer.
9979 * @context is session context
9980 * @raw_type advertises format of @buffer content. Only delivery info types
9982 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9983 * retrieve such data from message->raw after calling
9984 * isds_get_signed_delivery_info().
9985 * @length is length of buffer in bytes.
9986 * @message is automatically reallocated message parsed from @buffer.
9987 * @strategy selects how buffer will be attached into raw isds_message member.
9989 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9990 const isds_raw_type raw_type
,
9991 const void *buffer
, const size_t length
,
9992 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9994 isds_error err
= IE_SUCCESS
;
9995 message_ns_type message_ns
;
9996 xmlDocPtr message_doc
= NULL
;
9997 xmlXPathContextPtr xpath_ctx
= NULL
;
9998 xmlXPathObjectPtr result
= NULL
;
9999 void *xml_stream
= NULL
;
10000 size_t xml_stream_length
= 0;
10002 if (!context
) return IE_INVALID_CONTEXT
;
10003 zfree(context
->long_message
);
10004 if (!message
) return IE_INVAL
;
10005 isds_message_free(message
);
10006 if (!buffer
) return IE_INVAL
;
10009 /* Select buffer format and extract XML from CMS*/
10010 switch (raw_type
) {
10011 case RAWTYPE_DELIVERYINFO
:
10012 message_ns
= MESSAGE_NS_UNSIGNED
;
10013 xml_stream
= (void *) buffer
;
10014 xml_stream_length
= length
;
10017 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10018 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10019 xml_stream
= (void *) buffer
;
10020 xml_stream_length
= length
;
10023 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10024 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10025 err
= _isds_extract_cms_data(context
, buffer
, length
,
10026 &xml_stream
, &xml_stream_length
);
10027 if (err
) goto leave
;
10031 isds_log_message(context
, _("Bad raw delivery representation type"));
10036 if (_isds_sizet2int(xml_stream_length
) >= 0) {
10037 isds_log(ILF_ISDS
, ILL_DEBUG
,
10038 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10039 _isds_sizet2int(xml_stream_length
), xml_stream
);
10042 /* Convert delivery info XML stream into XPath context */
10043 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10044 if (!message_doc
) {
10048 xpath_ctx
= xmlXPathNewContext(message_doc
);
10053 /* XXX: Name spaces mangled for signed delivery info:
10054 * http://isds.czechpoint.cz/v20/delivery:
10056 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10058 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10059 * <p:dmID>170272</p:dmID>
10062 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10064 * </q:dmEvents>...</q:dmEvents>
10066 * </q:GetDeliveryInfoResponse>
10068 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10072 result
= xmlXPathEvalExpression(
10073 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10079 /* Empty delivery info */
10080 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10081 isds_printf_message(context
,
10082 _("XML document is not sisds:dmDelivery document"));
10086 /* More delivery info's */
10087 if (result
->nodesetval
->nodeNr
> 1) {
10088 isds_printf_message(context
,
10089 _("XML document has more sisds:dmDelivery elements"));
10093 /* One delivery info */
10094 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10096 /* Extract the envelope (= message without documents, hence 0).
10097 * XXX: extract_TReturnedMessage() can obtain attachments size,
10098 * but delivery info carries none. It's coded as option elements,
10099 * so it should work. */
10100 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10101 if (err
) goto leave
;
10103 /* Extract events */
10104 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10105 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10106 if (err
) { err
= IE_ERROR
; goto leave
; }
10107 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10108 if (err
) goto leave
;
10110 /* Append raw CMS structure into message */
10111 (*message
)->raw_type
= raw_type
;
10112 switch (strategy
) {
10113 case BUFFER_DONT_STORE
:
10116 (*message
)->raw
= malloc(length
);
10117 if (!(*message
)->raw
) {
10121 memcpy((*message
)->raw
, buffer
, length
);
10122 (*message
)->raw_length
= length
;
10125 (*message
)->raw
= (void *) buffer
;
10126 (*message
)->raw_length
= length
;
10135 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10136 isds_message_free(message
);
10139 xmlXPathFreeObject(result
);
10140 xmlXPathFreeContext(xpath_ctx
);
10141 if (!*message
|| !(*message
)->xml
) {
10142 xmlFreeDoc(message_doc
);
10144 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10147 isds_log(ILF_ISDS
, ILL_DEBUG
,
10148 _("Delivery info loaded successfully.\n"));
10153 /* Download signed delivery info-sheet of given message identified by ID.
10154 * @context is session context
10155 * @message_id is message identifier (you can get them from
10156 * isds_get_list_of_{sent,received}_messages())
10157 * @message is automatically reallocated message retrieved from ISDS.
10158 * It will miss documents per se. Use isds_get_signed_received_message(),
10159 * if you are interested in documents (content). OTOH, only this function
10160 * can get list events message has gone through. */
10161 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10162 const char *message_id
, struct isds_message
**message
) {
10164 isds_error err
= IE_SUCCESS
;
10166 xmlDocPtr response
= NULL
;
10167 xmlChar
*code
= NULL
, *status_message
= NULL
;
10169 size_t raw_length
= 0;
10172 if (!context
) return IE_INVALID_CONTEXT
;
10173 zfree(context
->long_message
);
10175 /* Free former message if any */
10176 if (!message
) return IE_INVAL
;
10177 isds_message_free(message
);
10180 /* Do request and check for success */
10181 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10182 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10183 &response
, NULL
, NULL
, &code
, &status_message
);
10184 if (err
) goto leave
;
10186 /* Find signed delivery info, extract it into raw and maybe free
10188 err
= find_extract_signed_data_free_response(context
,
10189 (xmlChar
*)message_id
, &response
,
10190 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10191 if (err
) goto leave
;
10193 /* Parse delivery info */
10194 err
= isds_load_delivery_info(context
,
10195 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10196 message
, BUFFER_MOVE
);
10197 if (err
) goto leave
;
10203 isds_message_free(message
);
10208 free(status_message
);
10209 xmlFreeDoc(response
);
10212 isds_log(ILF_ISDS
, ILL_DEBUG
,
10213 _("GetSignedDeliveryInfo request processed by server "
10216 #else /* not HAVE_LIBCURL */
10223 /* Download delivery info-sheet of given message identified by ID.
10224 * @context is session context
10225 * @message_id is message identifier (you can get them from
10226 * isds_get_list_of_{sent,received}_messages())
10227 * @message is automatically reallocated message retrieved from ISDS.
10228 * It will miss documents per se. Use isds_get_received_message(), if you are
10229 * interested in documents (content). OTOH, only this function can get list
10230 * of events message has gone through. */
10231 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10232 const char *message_id
, struct isds_message
**message
) {
10234 isds_error err
= IE_SUCCESS
;
10236 xmlDocPtr response
= NULL
;
10237 xmlChar
*code
= NULL
, *status_message
= NULL
;
10238 xmlNodePtr delivery_node
= NULL
;
10240 size_t raw_length
= 0;
10243 if (!context
) return IE_INVALID_CONTEXT
;
10244 zfree(context
->long_message
);
10246 /* Free former message if any */
10247 if (!message
) return IE_INVAL
;
10248 isds_message_free(message
);
10251 /* Do request and check for success */
10252 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10253 BAD_CAST
"GetDeliveryInfo", message_id
,
10254 &response
, NULL
, NULL
, &code
, &status_message
);
10255 if (err
) goto leave
;
10258 /* Serialize delivery info */
10259 delivery_node
= xmlDocGetRootElement(response
);
10260 if (!delivery_node
) {
10261 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10262 isds_printf_message(context
,
10263 _("Server did not return any delivery info for ID `%s' "
10264 "on GetDeliveryInfo request"), message_id_locale
);
10265 free(message_id_locale
);
10269 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10270 if (err
) goto leave
;
10272 /* Parse delivery info */
10273 /* TODO: Here we parse the response second time. We could single delivery
10274 * parser from isds_load_delivery_info() to make things faster. */
10275 err
= isds_load_delivery_info(context
,
10276 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10277 message
, BUFFER_MOVE
);
10278 if (err
) goto leave
;
10285 isds_message_free(message
);
10290 free(status_message
);
10291 xmlFreeDoc(response
);
10294 isds_log(ILF_ISDS
, ILL_DEBUG
,
10295 _("GetDeliveryInfo request processed by server "
10298 #else /* not HAVE_LIBCURL */
10305 /* Download incoming message identified by ID.
10306 * @context is session context
10307 * @message_id is message identifier (you can get them from
10308 * isds_get_list_of_received_messages())
10309 * @message is automatically reallocated message retrieved from ISDS */
10310 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10311 const char *message_id
, struct isds_message
**message
) {
10313 isds_error err
= IE_SUCCESS
;
10315 xmlDocPtr response
= NULL
;
10316 void *xml_stream
= NULL
;
10317 size_t xml_stream_length
;
10318 xmlChar
*code
= NULL
, *status_message
= NULL
;
10319 xmlXPathContextPtr xpath_ctx
= NULL
;
10320 xmlXPathObjectPtr result
= NULL
;
10321 char *phys_path
= NULL
;
10322 size_t phys_start
, phys_end
;
10325 if (!context
) return IE_INVALID_CONTEXT
;
10326 zfree(context
->long_message
);
10328 /* Free former message if any */
10329 if (NULL
== message
) return IE_INVAL
;
10330 if (message
) isds_message_free(message
);
10333 /* Do request and check for success */
10334 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10335 BAD_CAST
"MessageDownload", message_id
,
10336 &response
, &xml_stream
, &xml_stream_length
,
10337 &code
, &status_message
);
10338 if (err
) goto leave
;
10341 xpath_ctx
= xmlXPathNewContext(response
);
10346 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10350 result
= xmlXPathEvalExpression(
10351 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10357 /* Empty response */
10358 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10359 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10360 isds_printf_message(context
,
10361 _("Server did not return any message for ID `%s' "
10362 "on MessageDownload request"), message_id_locale
);
10363 free(message_id_locale
);
10367 /* More messages */
10368 if (result
->nodesetval
->nodeNr
> 1) {
10369 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10370 isds_printf_message(context
,
10371 _("Server did return more messages for ID `%s' "
10372 "on MessageDownload request"), message_id_locale
);
10373 free(message_id_locale
);
10378 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10380 /* Extract the message */
10381 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10382 if (err
) goto leave
;
10384 /* Locate raw XML blob */
10385 phys_path
= strdup(
10386 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10387 PHYSXML_ELEMENT_SEPARATOR
10388 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10389 PHYSXML_ELEMENT_SEPARATOR
10390 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10396 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10397 phys_path
, &phys_start
, &phys_end
);
10400 isds_log_message(context
,
10401 _("Substring with isds:MessageDownloadResponse element "
10402 "could not be located in raw SOAP message"));
10405 /* Save XML blob */
10406 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10407 &(*message)->raw_length);*/
10408 /* TODO: Store name space declarations from ancestors */
10409 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10410 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10411 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10412 (*message
)->raw
= malloc((*message
)->raw_length
);
10413 if (!(*message
)->raw
) {
10417 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10422 isds_message_free(message
);
10427 xmlXPathFreeObject(result
);
10428 xmlXPathFreeContext(xpath_ctx
);
10431 free(status_message
);
10433 if (!*message
|| !(*message
)->xml
) {
10434 xmlFreeDoc(response
);
10438 isds_log(ILF_ISDS
, ILL_DEBUG
,
10439 _("MessageDownload request processed by server "
10442 #else /* not HAVE_LIBCURL */
10449 /* Load message of any type from buffer.
10450 * @context is session context
10451 * @raw_type defines content type of @buffer. Only message types are allowed.
10452 * @buffer is message raw representation. Format (CMS, plain signed,
10453 * message direction) is defined in @raw_type. You can retrieve such data
10454 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10455 * @length is length of buffer in bytes.
10456 * @message is automatically reallocated message parsed from @buffer.
10457 * @strategy selects how buffer will be attached into raw isds_message member.
10459 isds_error
isds_load_message(struct isds_ctx
*context
,
10460 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10461 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10463 isds_error err
= IE_SUCCESS
;
10464 void *xml_stream
= NULL
;
10465 size_t xml_stream_length
= 0;
10466 message_ns_type message_ns
;
10467 xmlDocPtr message_doc
= NULL
;
10468 xmlXPathContextPtr xpath_ctx
= NULL
;
10469 xmlXPathObjectPtr result
= NULL
;
10471 if (!context
) return IE_INVALID_CONTEXT
;
10472 zfree(context
->long_message
);
10473 if (!message
) return IE_INVAL
;
10474 isds_message_free(message
);
10475 if (!buffer
) return IE_INVAL
;
10478 /* Select buffer format and extract XML from CMS*/
10479 switch (raw_type
) {
10480 case RAWTYPE_INCOMING_MESSAGE
:
10481 message_ns
= MESSAGE_NS_UNSIGNED
;
10482 xml_stream
= (void *) buffer
;
10483 xml_stream_length
= length
;
10486 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10487 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10488 xml_stream
= (void *) buffer
;
10489 xml_stream_length
= length
;
10492 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10493 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10494 err
= _isds_extract_cms_data(context
, buffer
, length
,
10495 &xml_stream
, &xml_stream_length
);
10496 if (err
) goto leave
;
10499 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10500 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10501 xml_stream
= (void *) buffer
;
10502 xml_stream_length
= length
;
10505 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10506 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10507 err
= _isds_extract_cms_data(context
, buffer
, length
,
10508 &xml_stream
, &xml_stream_length
);
10509 if (err
) goto leave
;
10513 isds_log_message(context
, _("Bad raw message representation type"));
10518 if (_isds_sizet2int(xml_stream_length
) >= 0) {
10519 isds_log(ILF_ISDS
, ILL_DEBUG
,
10520 _("Loading message:\n%.*s\nEnd of message\n"),
10521 _isds_sizet2int(xml_stream_length
), xml_stream
);
10524 /* Convert messages XML stream into XPath context */
10525 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10526 if (!message_doc
) {
10530 xpath_ctx
= xmlXPathNewContext(message_doc
);
10535 /* XXX: Standard name space for unsigned incoming direction:
10536 * http://isds.czechpoint.cz/v20/
10538 * XXX: Name spaces mangled for signed outgoing direction:
10539 * http://isds.czechpoint.cz/v20/SentMessage:
10541 * <q:MessageDownloadResponse
10542 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10543 * <q:dmReturnedMessage>
10544 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10545 * <p:dmID>151916</p:dmID>
10548 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10550 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10551 * </q:dmReturnedMessage>
10552 * </q:MessageDownloadResponse>
10554 * XXX: Name spaces mangled for signed incoming direction:
10555 * http://isds.czechpoint.cz/v20/message:
10557 * <q:MessageDownloadResponse
10558 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10559 * <q:dmReturnedMessage>
10560 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10561 * <p:dmID>151916</p:dmID>
10564 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10566 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10567 * </q:dmReturnedMessage>
10568 * </q:MessageDownloadResponse>
10570 * Stupidity of ISDS developers is unlimited */
10571 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10575 result
= xmlXPathEvalExpression(
10576 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10582 /* Empty message */
10583 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10584 isds_printf_message(context
,
10585 _("XML document does not contain "
10586 "sisds:dmReturnedMessage element"));
10590 /* More messages */
10591 if (result
->nodesetval
->nodeNr
> 1) {
10592 isds_printf_message(context
,
10593 _("XML document has more sisds:dmReturnedMessage elements"));
10598 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10600 /* Extract the message */
10601 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10602 if (err
) goto leave
;
10604 /* Append raw buffer into message */
10605 (*message
)->raw_type
= raw_type
;
10606 switch (strategy
) {
10607 case BUFFER_DONT_STORE
:
10610 (*message
)->raw
= malloc(length
);
10611 if (!(*message
)->raw
) {
10615 memcpy((*message
)->raw
, buffer
, length
);
10616 (*message
)->raw_length
= length
;
10619 (*message
)->raw
= (void *) buffer
;
10620 (*message
)->raw_length
= length
;
10630 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10631 isds_message_free(message
);
10634 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10635 xmlXPathFreeObject(result
);
10636 xmlXPathFreeContext(xpath_ctx
);
10637 if (!*message
|| !(*message
)->xml
) {
10638 xmlFreeDoc(message_doc
);
10642 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10647 /* Determine type of raw message or delivery info according some heuristics.
10648 * It does not validate the raw blob.
10649 * @context is session context
10650 * @raw_type returns content type of @buffer. Valid only if exit code of this
10651 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10652 * reallocated memory.
10653 * @buffer is message raw representation.
10654 * @length is length of buffer in bytes. */
10655 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10656 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10658 void *xml_stream
= NULL
;
10659 size_t xml_stream_length
= 0;
10660 xmlDocPtr document
= NULL
;
10661 xmlNodePtr root
= NULL
;
10663 if (!context
) return IE_INVALID_CONTEXT
;
10664 zfree(context
->long_message
);
10665 if (length
== 0 || !buffer
) return IE_INVAL
;
10666 if (!raw_type
) return IE_INVAL
;
10669 err
= _isds_extract_cms_data(context
, buffer
, length
,
10670 &xml_stream
, &xml_stream_length
);
10672 xml_stream
= (void *) buffer
;
10673 xml_stream_length
= (size_t) length
;
10678 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10680 isds_printf_message(context
,
10681 _("Could not parse data as XML document"));
10686 /* Get root element */
10687 root
= xmlDocGetRootElement(document
);
10689 isds_printf_message(context
,
10690 _("XML document is missing root element"));
10695 if (!root
->ns
|| !root
->ns
->href
) {
10696 isds_printf_message(context
,
10697 _("Root element does not belong to any name space"));
10702 /* Test name space */
10703 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10704 if (xml_stream
== buffer
)
10705 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10707 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10708 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10709 if (xml_stream
== buffer
)
10710 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10712 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10713 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10714 if (xml_stream
== buffer
)
10715 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10717 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10718 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10719 if (xml_stream
!= buffer
) {
10720 isds_printf_message(context
,
10721 _("Document in ISDS name space is encapsulated into CMS" ));
10723 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10724 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10725 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10726 *raw_type
= RAWTYPE_DELIVERYINFO
;
10728 isds_printf_message(context
,
10729 _("Unknown root element in ISDS name space"));
10733 isds_printf_message(context
,
10734 _("Unknown name space"));
10739 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10740 xmlFreeDoc(document
);
10745 /* Download signed incoming/outgoing message identified by ID.
10746 * @context is session context
10747 * @output is true for outgoing message, false for incoming message
10748 * @message_id is message identifier (you can get them from
10749 * isds_get_list_of_{sent,received}_messages())
10750 * @message is automatically reallocated message retrieved from ISDS. The raw
10751 * member will be filled with PKCS#7 structure in DER format. */
10752 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10753 const _Bool outgoing
, const char *message_id
,
10754 struct isds_message
**message
) {
10756 isds_error err
= IE_SUCCESS
;
10758 xmlDocPtr response
= NULL
;
10759 xmlChar
*code
= NULL
, *status_message
= NULL
;
10760 xmlXPathContextPtr xpath_ctx
= NULL
;
10761 xmlXPathObjectPtr result
= NULL
;
10762 char *encoded_structure
= NULL
;
10764 size_t raw_length
= 0;
10767 if (!context
) return IE_INVALID_CONTEXT
;
10768 zfree(context
->long_message
);
10769 if (!message
) return IE_INVAL
;
10770 isds_message_free(message
);
10773 /* Do request and check for success */
10774 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10775 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10776 BAD_CAST
"SignedMessageDownload",
10777 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10778 if (err
) goto leave
;
10780 /* Find signed message, extract it into raw and maybe free
10782 err
= find_extract_signed_data_free_response(context
,
10783 (xmlChar
*)message_id
, &response
,
10784 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10785 BAD_CAST
"SignedMessageDownload",
10786 &raw
, &raw_length
);
10787 if (err
) goto leave
;
10789 /* Parse message */
10790 err
= isds_load_message(context
,
10791 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10792 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10793 raw
, raw_length
, message
, BUFFER_MOVE
);
10794 if (err
) goto leave
;
10800 isds_message_free(message
);
10803 free(encoded_structure
);
10804 xmlXPathFreeObject(result
);
10805 xmlXPathFreeContext(xpath_ctx
);
10809 free(status_message
);
10810 xmlFreeDoc(response
);
10813 isds_log(ILF_ISDS
, ILL_DEBUG
,
10815 _("SignedSentMessageDownload request processed by server "
10816 "successfully.\n") :
10817 _("SignedMessageDownload request processed by server "
10820 #else /* not HAVE_LIBCURL */
10827 /* Download signed incoming message identified by ID.
10828 * @context is session context
10829 * @message_id is message identifier (you can get them from
10830 * isds_get_list_of_received_messages())
10831 * @message is automatically reallocated message retrieved from ISDS. The raw
10832 * member will be filled with PKCS#7 structure in DER format. */
10833 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10834 const char *message_id
, struct isds_message
**message
) {
10835 return isds_get_signed_message(context
, 0, message_id
, message
);
10839 /* Download signed outgoing message identified by ID.
10840 * @context is session context
10841 * @message_id is message identifier (you can get them from
10842 * isds_get_list_of_sent_messages())
10843 * @message is automatically reallocated message retrieved from ISDS. The raw
10844 * member will be filled with PKCS#7 structure in DER format. */
10845 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10846 const char *message_id
, struct isds_message
**message
) {
10847 return isds_get_signed_message(context
, 1, message_id
, message
);
10851 /* Get type and name of user who sent a message identified by ID.
10852 * @context is session context
10853 * @message_id is message identifier
10854 * @sender_type is pointer to automatically allocated type of sender detected
10855 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10856 * library or to the server, NULL will be returned. Pass NULL if you don't
10858 * @raw_sender_type is automatically reallocated UTF-8 string describing
10859 * sender type or NULL if not known to server. Pass NULL if you don't care.
10860 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10861 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10862 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10863 const char *message_id
, isds_sender_type
**sender_type
,
10864 char **raw_sender_type
, char **sender_name
) {
10865 isds_error err
= IE_SUCCESS
;
10867 xmlDocPtr response
= NULL
;
10868 xmlChar
*code
= NULL
, *status_message
= NULL
;
10869 xmlXPathContextPtr xpath_ctx
= NULL
;
10870 xmlXPathObjectPtr result
= NULL
;
10871 char *type_string
= NULL
;
10874 if (!context
) return IE_INVALID_CONTEXT
;
10875 zfree(context
->long_message
);
10876 if (sender_type
) zfree(*sender_type
);
10877 if (raw_sender_type
) zfree(*raw_sender_type
);
10878 if (sender_name
) zfree(*sender_name
);
10879 if (!message_id
) return IE_INVAL
;
10882 /* Do request and check for success */
10883 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10884 BAD_CAST
"GetMessageAuthor",
10885 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10886 if (err
) goto leave
;
10889 xpath_ctx
= xmlXPathNewContext(response
);
10894 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10898 result
= xmlXPathEvalExpression(
10899 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10904 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10905 isds_log_message(context
,
10906 _("Missing GetMessageAuthorResponse element"));
10910 if (result
->nodesetval
->nodeNr
> 1) {
10911 isds_log_message(context
,
10912 _("Multiple GetMessageAuthorResponse element"));
10916 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10917 xmlXPathFreeObject(result
); result
= NULL
;
10919 /* Fill output arguments in */
10920 EXTRACT_STRING("isds:userType", type_string
);
10921 if (NULL
!= type_string
) {
10922 if (NULL
!= sender_type
) {
10923 *sender_type
= calloc(1, sizeof(**sender_type
));
10924 if (NULL
== *sender_type
) {
10929 err
= string2isds_sender_type((xmlChar
*)type_string
,
10932 zfree(*sender_type
);
10933 if (err
== IE_ENUM
) {
10935 char *type_string_locale
= _isds_utf82locale(type_string
);
10936 isds_log(ILF_ISDS
, ILL_WARNING
,
10937 _("Unknown isds:userType value: %s"),
10938 type_string_locale
);
10939 free(type_string_locale
);
10944 if (NULL
!= sender_name
)
10945 EXTRACT_STRING("isds:authorName", *sender_name
);
10949 if (NULL
!= sender_type
) zfree(*sender_type
);
10950 zfree(type_string
);
10951 if (NULL
!= sender_name
) zfree(*sender_name
);
10953 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10955 xmlXPathFreeObject(result
);
10956 xmlXPathFreeContext(xpath_ctx
);
10959 free(status_message
);
10960 xmlFreeDoc(response
);
10963 isds_log(ILF_ISDS
, ILL_DEBUG
,
10964 _("GetMessageAuthor request processed by server "
10965 "successfully.\n"));
10966 #else /* not HAVE_LIBCURL */
10973 /* Retrieve hash of message identified by ID stored in ISDS.
10974 * @context is session context
10975 * @message_id is message identifier
10976 * @hash is automatically reallocated message hash downloaded from ISDS.
10977 * Message must exist in system and must not be deleted. */
10978 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10979 const char *message_id
, struct isds_hash
**hash
) {
10981 isds_error err
= IE_SUCCESS
;
10983 xmlDocPtr response
= NULL
;
10984 xmlChar
*code
= NULL
, *status_message
= NULL
;
10985 xmlXPathContextPtr xpath_ctx
= NULL
;
10986 xmlXPathObjectPtr result
= NULL
;
10989 if (!context
) return IE_INVALID_CONTEXT
;
10990 zfree(context
->long_message
);
10992 isds_hash_free(hash
);
10995 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10996 BAD_CAST
"VerifyMessage", message_id
,
10997 &response
, NULL
, NULL
, &code
, &status_message
);
10998 if (err
) goto leave
;
11002 xpath_ctx
= xmlXPathNewContext(response
);
11007 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11011 result
= xmlXPathEvalExpression(
11012 BAD_CAST
"/isds:VerifyMessageResponse",
11018 /* Empty response */
11019 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11020 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11021 isds_printf_message(context
,
11022 _("Server did not return any response for ID `%s' "
11023 "on VerifyMessage request"), message_id_locale
);
11024 free(message_id_locale
);
11028 /* More responses */
11029 if (result
->nodesetval
->nodeNr
> 1) {
11030 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11031 isds_printf_message(context
,
11032 _("Server did return more responses for ID `%s' "
11033 "on VerifyMessage request"), message_id_locale
);
11034 free(message_id_locale
);
11039 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11041 /* Extract the hash */
11042 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11046 isds_hash_free(hash
);
11049 xmlXPathFreeObject(result
);
11050 xmlXPathFreeContext(xpath_ctx
);
11053 free(status_message
);
11054 xmlFreeDoc(response
);
11057 isds_log(ILF_ISDS
, ILL_DEBUG
,
11058 _("VerifyMessage request processed by server "
11061 #else /* not HAVE_LIBCURL */
11068 /* Erase message specified by @message_id from long term storage. Other
11069 * message cannot be erased on user request.
11070 * @context is session context
11071 * @message_id is message identifier.
11072 * @incoming is true for incoming message, false for outgoing message.
11074 * IE_SUCCESS if message has ben removed
11075 * IE_INVAL if message does not exist in long term storage or message
11076 * belongs to different box
11077 * TODO: IE_NOEPRM if user has no permission to erase a message */
11078 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11079 const char *message_id
, _Bool incoming
) {
11080 isds_error err
= IE_SUCCESS
;
11082 xmlNodePtr request
= NULL
, node
;
11083 xmlNsPtr isds_ns
= NULL
;
11084 xmlDocPtr response
= NULL
;
11085 xmlChar
*code
= NULL
, *status_message
= NULL
;
11088 if (!context
) return IE_INVALID_CONTEXT
;
11089 zfree(context
->long_message
);
11090 if (NULL
== message_id
) return IE_INVAL
;
11093 /* Check if connection is established
11094 * TODO: This check should be done downstairs. */
11095 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11097 /* Build request */
11098 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11100 isds_log_message(context
,
11101 _("Could build EraseMessage request"));
11104 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11106 isds_log_message(context
, _("Could not create ISDS name space"));
11107 xmlFreeNode(request
);
11110 xmlSetNs(request
, isds_ns
);
11112 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11113 if (err
) goto leave
;
11114 INSERT_STRING(request
, "dmID", message_id
);
11116 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11120 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11121 "message ID %s to ISDS\n"), message_id
);
11122 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11123 xmlFreeNode(request
); request
= NULL
;
11126 isds_log(ILF_ISDS
, ILL_DEBUG
,
11127 _("Processing ISDS response on EraseMessage request "
11132 /* Check for response status */
11133 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11134 &code
, &status_message
, NULL
);
11136 isds_log(ILF_ISDS
, ILL_DEBUG
,
11137 _("ISDS response on EraseMessage request is missing "
11142 /* Check server status code */
11143 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11144 isds_log_message(context
, _("Message to erase belongs to other box"));
11146 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11147 isds_log_message(context
, _("Message to erase is not saved in "
11148 "long term storage or the direction does not match"));
11150 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11151 char *code_locale
= _isds_utf82locale((char*) code
);
11152 char *message_locale
= _isds_utf82locale((char*) status_message
);
11153 isds_log(ILF_ISDS
, ILL_DEBUG
,
11154 _("Server refused EraseMessage request "
11155 "(code=%s, message=%s)\n"),
11156 code_locale
, message_locale
);
11157 isds_log_message(context
, message_locale
);
11159 free(message_locale
);
11166 free(status_message
);
11167 xmlFreeDoc(response
);
11168 xmlFreeNode(request
);
11171 isds_log(ILF_ISDS
, ILL_DEBUG
,
11172 _("EraseMessage request processed by server "
11175 #else /* not HAVE_LIBCURL */
11182 /* Mark message as read. This is a transactional commit function to acknowledge
11183 * to ISDS the message has been downloaded and processed by client properly.
11184 * @context is session context
11185 * @message_id is message identifier. */
11186 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11187 const char *message_id
) {
11189 isds_error err
= IE_SUCCESS
;
11191 xmlDocPtr response
= NULL
;
11192 xmlChar
*code
= NULL
, *status_message
= NULL
;
11195 if (!context
) return IE_INVALID_CONTEXT
;
11196 zfree(context
->long_message
);
11199 /* Do request and check for success */
11200 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11201 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11202 &response
, NULL
, NULL
, &code
, &status_message
);
11205 free(status_message
);
11206 xmlFreeDoc(response
);
11209 isds_log(ILF_ISDS
, ILL_DEBUG
,
11210 _("MarkMessageAsDownloaded request processed by server "
11213 #else /* not HAVE_LIBCURL */
11220 /* Mark message as received by recipient. This is applicable only to
11221 * commercial message. Use envelope->dmType message member to distinguish
11222 * commercial message from government message. Government message is
11223 * received automatically (by law), commercial message on recipient request.
11224 * @context is session context
11225 * @message_id is message identifier. */
11226 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11227 const char *message_id
) {
11229 isds_error err
= IE_SUCCESS
;
11231 xmlDocPtr response
= NULL
;
11232 xmlChar
*code
= NULL
, *status_message
= NULL
;
11235 if (!context
) return IE_INVALID_CONTEXT
;
11236 zfree(context
->long_message
);
11239 /* Do request and check for success */
11240 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11241 BAD_CAST
"ConfirmDelivery", message_id
,
11242 &response
, NULL
, NULL
, &code
, &status_message
);
11245 free(status_message
);
11246 xmlFreeDoc(response
);
11249 isds_log(ILF_ISDS
, ILL_DEBUG
,
11250 _("ConfirmDelivery request processed by server "
11253 #else /* not HAVE_LIBCURL */
11260 /* Send document for authorized conversion into Czech POINT system.
11261 * This is public anonymous service, no log-in necessary. Special context is
11262 * used to reuse keep-a-live HTTPS connection.
11263 * @context is Czech POINT session context. DO NOT use context connected to
11264 * ISDS server. Use new context or context used by this function previously.
11265 * @document is document to convert. Only data, data_length, dmFileDescr and
11266 * is_xml members are significant. Be ware that not all document formats can be
11267 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11268 * @id is reallocated identifier assigned by Czech POINT system to
11269 * your document on submit. Use is to tell it to Czech POINT officer.
11270 * @date is reallocated document submit date (submitted documents
11271 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11273 isds_error
czp_convert_document(struct isds_ctx
*context
,
11274 const struct isds_document
*document
,
11275 char **id
, struct tm
**date
) {
11276 isds_error err
= IE_SUCCESS
;
11278 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11279 xmlNodePtr request
= NULL
, node
;
11280 xmlDocPtr response
= NULL
;
11282 xmlXPathContextPtr xpath_ctx
= NULL
;
11283 xmlXPathObjectPtr result
= NULL
;
11284 long int status
= -1;
11285 long int *status_ptr
= &status
;
11286 char *string
= NULL
;
11290 if (!context
) return IE_INVALID_CONTEXT
;
11291 zfree(context
->long_message
);
11292 if (!document
|| !id
|| !date
) return IE_INVAL
;
11294 if (document
->is_xml
) {
11295 isds_log_message(context
,
11296 _("XML documents cannot be submitted to conversion"));
11300 /* Free output arguments */
11305 /* Store configuration */
11306 context
->type
= CTX_TYPE_CZP
;
11307 free(context
->url
);
11308 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11309 if (!(context
->url
))
11312 /* Prepare CURL handle if not yet connected */
11313 if (!context
->curl
) {
11314 context
->curl
= curl_easy_init();
11315 if (!(context
->curl
))
11319 /* Build conversion request */
11320 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11322 isds_log_message(context
,
11323 _("Could not build Czech POINT conversion request"));
11326 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11328 isds_log_message(context
,
11329 _("Could not create Czech POINT deposit name space"));
11330 xmlFreeNode(request
);
11333 xmlSetNs(request
, deposit_ns
);
11335 /* Insert children. They are in empty namespace! */
11336 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11338 isds_log_message(context
, _("Could not create empty name space"));
11342 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11343 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11344 document
->dmFileDescr
);
11346 /* Document encoded in Base64 */
11347 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11348 document
->data
, document
->data_length
);
11349 if (err
) goto leave
;
11351 isds_log(ILF_ISDS
, ILL_DEBUG
,
11352 _("Submitting document for conversion into Czech POINT deposit"));
11354 /* Send conversion request */
11355 err
= _czp_czpdeposit(context
, request
, &response
);
11356 xmlFreeNode(request
); request
= NULL
;
11359 czp_do_close_connection(context
);
11364 /* Extract response */
11365 xpath_ctx
= xmlXPathNewContext(response
);
11370 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11374 result
= xmlXPathEvalExpression(
11375 BAD_CAST
"/deposit:saveDocumentResponse/return",
11381 /* Empty response */
11382 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11383 isds_printf_message(context
,
11384 _("Missing `return' element in Czech POINT deposit response"));
11388 /* More responses */
11389 if (result
->nodesetval
->nodeNr
> 1) {
11390 isds_printf_message(context
,
11391 _("Multiple `return' element in Czech POINT deposit response"));
11396 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11399 EXTRACT_LONGINT("status", status_ptr
, 1);
11401 EXTRACT_STRING("statusMsg", string
);
11402 char *string_locale
= _isds_utf82locale(string
);
11403 isds_printf_message(context
,
11404 _("Czech POINT deposit refused document for conversion "
11405 "(code=%ld, message=%s)"),
11406 status
, string_locale
);
11407 free(string_locale
);
11412 /* Get document ID */
11413 EXTRACT_STRING("documentID", *id
);
11415 /* Get submit date */
11416 EXTRACT_STRING("dateInserted", string
);
11418 *date
= calloc(1, sizeof(**date
));
11423 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11425 if (err
== IE_NOTSUP
) {
11427 char *string_locale
= _isds_utf82locale(string
);
11428 isds_printf_message(context
,
11429 _("Invalid dateInserted value: %s"), string_locale
);
11430 free(string_locale
);
11438 xmlXPathFreeObject(result
);
11439 xmlXPathFreeContext(xpath_ctx
);
11441 xmlFreeDoc(response
);
11442 xmlFreeNode(request
);
11445 char *id_locale
= _isds_utf82locale((char *) *id
);
11446 isds_log(ILF_ISDS
, ILL_DEBUG
,
11447 _("Document %s has been submitted for conversion "
11448 "to server successfully\n"), id_locale
);
11451 #else /* not HAVE_LIBCURL */
11458 /* Close possibly opened connection to Czech POINT document deposit.
11459 * @context is Czech POINT session context. */
11460 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11461 if (!context
) return IE_INVALID_CONTEXT
;
11462 zfree(context
->long_message
);
11464 return czp_do_close_connection(context
);
11471 /* Send request for new box creation in testing ISDS instance.
11472 * It's not possible to request for a production box currently, as it
11473 * communicates via e-mail.
11474 * XXX: This function does not work either. Server complains about invalid
11476 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11478 * @context is special session context for box creation request. DO NOT use
11479 * standard context as it could reveal your password. Use fresh new context or
11480 * context previously used by this function.
11481 * @box is box description to create including single primary user (in case of
11482 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11483 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11484 * box, or contact address of PFO box owner). The email member is mandatory as
11485 * it will be used to deliver credentials.
11486 * @former_names is former name of box owner. Pass NULL if you don't care.
11487 * @approval is optional external approval of box manipulation
11488 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11489 * NULL, if you don't care.*/
11490 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11491 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11492 const char *former_names
, const struct isds_approval
*approval
,
11493 char **refnumber
) {
11494 isds_error err
= IE_SUCCESS
;
11496 xmlNodePtr request
= NULL
;
11497 xmlDocPtr response
= NULL
;
11498 xmlXPathContextPtr xpath_ctx
= NULL
;
11499 xmlXPathObjectPtr result
= NULL
;
11503 if (!context
) return IE_INVALID_CONTEXT
;
11504 zfree(context
->long_message
);
11505 if (!box
) return IE_INVAL
;
11508 if (!box
->email
|| box
->email
[0] == '\0') {
11509 isds_log_message(context
, _("E-mail field is mandatory"));
11513 /* Scratch box ID */
11516 /* Store configuration */
11517 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11518 free(context
->url
);
11519 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11520 if (!(context
->url
))
11523 /* Prepare CURL handle if not yet connected */
11524 if (!context
->curl
) {
11525 context
->curl
= curl_easy_init();
11526 if (!(context
->curl
))
11530 /* Build CreateDataBox request */
11531 err
= build_CreateDBInput_request(context
,
11532 &request
, BAD_CAST
"CreateDataBox",
11533 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11534 if (err
) goto leave
;
11536 /* Send it to server and process response */
11537 err
= send_destroy_request_check_response(context
,
11538 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11539 &response
, (xmlChar
**) refnumber
, NULL
);
11540 if (err
) goto leave
;
11542 /* Extract box ID */
11543 xpath_ctx
= xmlXPathNewContext(response
);
11548 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11552 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11555 xmlXPathFreeObject(result
);
11556 xmlXPathFreeContext(xpath_ctx
);
11557 xmlFreeDoc(response
);
11558 xmlFreeNode(request
);
11561 isds_log(ILF_ISDS
, ILL_DEBUG
,
11562 _("CreateDataBox request processed by server successfully.\n"));
11564 #else /* not HAVE_LIBCURL */
11572 /* Submit CMS signed message to ISDS to verify its originality. This is
11573 * stronger form of isds_verify_message_hash() because ISDS does more checks
11574 * than simple one (potentialy old weak) hash comparison.
11575 * @context is session context
11576 * @message is memory with raw CMS signed message bit stream
11577 * @length is @message size in bytes
11579 * IE_SUCCESS if message originates in ISDS
11580 * IE_NOTEQUAL if message is unknown to ISDS
11581 * other code for other errors */
11582 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11583 const void *message
, size_t length
) {
11584 isds_error err
= IE_SUCCESS
;
11586 xmlNsPtr isds_ns
= NULL
;
11587 xmlNodePtr request
= NULL
;
11588 xmlDocPtr response
= NULL
;
11589 xmlXPathContextPtr xpath_ctx
= NULL
;
11590 xmlXPathObjectPtr result
= NULL
;
11591 _Bool
*authentic
= NULL
;
11594 if (!context
) return IE_INVALID_CONTEXT
;
11595 zfree(context
->long_message
);
11596 if (!message
|| length
== 0) return IE_INVAL
;
11599 /* Check if connection is established
11600 * TODO: This check should be done downstairs. */
11601 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11604 /* Build AuthenticateMessage request */
11605 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11607 isds_log_message(context
,
11608 _("Could not build AuthenticateMessage request"));
11611 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11613 isds_log_message(context
, _("Could not create ISDS name space"));
11614 xmlFreeNode(request
);
11617 xmlSetNs(request
, isds_ns
);
11619 /* Insert Base64 encoded message */
11620 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11622 if (err
) goto leave
;
11624 /* Send request to server and process response */
11625 err
= send_destroy_request_check_response(context
,
11626 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11627 &response
, NULL
, NULL
);
11628 if (err
) goto leave
;
11631 /* ISDS has decided */
11632 xpath_ctx
= xmlXPathNewContext(response
);
11637 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11642 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11645 isds_log_message(context
,
11646 _("Server did not return any response on "
11647 "AuthenticateMessage request"));
11652 isds_log(ILF_ISDS
, ILL_DEBUG
,
11653 _("ISDS authenticated the message successfully\n"));
11655 isds_log_message(context
, _("ISDS does not know the message"));
11662 xmlXPathFreeObject(result
);
11663 xmlXPathFreeContext(xpath_ctx
);
11665 xmlFreeDoc(response
);
11666 xmlFreeNode(request
);
11667 #else /* not HAVE_LIBCURL */
11675 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11676 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11678 * @context is session context
11679 * @input_data is memory with raw CMS signed message or delivery info bit
11680 * stream to re-sign
11681 * @input_length is @input_data size in bytes
11682 * @output_data is pointer to auto-allocated memory where to store re-signed
11683 * input data blob. Caller must free it.
11684 * @output_data is pointer where to store @output_data size in bytes
11685 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11686 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11688 * IE_SUCCESS if CMS blob has been re-signed successfully
11689 * other code for other errors */
11690 isds_error
isds_resign_message(struct isds_ctx
*context
,
11691 const void *input_data
, size_t input_length
,
11692 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11693 isds_error err
= IE_SUCCESS
;
11695 xmlNsPtr isds_ns
= NULL
;
11696 xmlNodePtr request
= NULL
;
11697 xmlDocPtr response
= NULL
;
11698 xmlXPathContextPtr xpath_ctx
= NULL
;
11699 xmlXPathObjectPtr result
= NULL
;
11700 char *string
= NULL
;
11701 const xmlChar
*codes
[] = {
11708 const char *meanings
[] = {
11710 "Message is not original",
11711 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11712 "Time stamp could not been generated in time"
11714 const isds_error errors
[] = {
11720 struct code_map_isds_error map
= {
11722 .meanings
= meanings
,
11727 if (NULL
!= output_data
) *output_data
= NULL
;
11728 if (NULL
!= output_length
) *output_length
= 0;
11729 if (NULL
!= valid_to
) *valid_to
= NULL
;
11731 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11732 zfree(context
->long_message
);
11733 if (NULL
== input_data
|| 0 == input_length
) {
11734 isds_log_message(context
, _("Empty CMS blob on input"));
11737 if (NULL
== output_data
|| NULL
== output_length
) {
11738 isds_log_message(context
,
11739 _("NULL pointer provided for output CMS blob"));
11744 /* Check if connection is established
11745 * TODO: This check should be done downstairs. */
11746 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11749 /* Build Re-signISDSDocument request */
11750 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11752 isds_log_message(context
,
11753 _("Could not build Re-signISDSDocument request"));
11756 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11758 isds_log_message(context
, _("Could not create ISDS name space"));
11759 xmlFreeNode(request
);
11762 xmlSetNs(request
, isds_ns
);
11764 /* Insert Base64 encoded CMS blob */
11765 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11766 input_data
, input_length
);
11767 if (err
) goto leave
;
11769 /* Send request to server and process response */
11770 err
= send_destroy_request_check_response(context
,
11771 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11772 &response
, NULL
, &map
);
11773 if (err
) goto leave
;
11776 /* Extract re-signed data */
11777 xpath_ctx
= xmlXPathNewContext(response
);
11782 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11786 result
= xmlXPathEvalExpression(
11787 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11792 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11793 isds_log_message(context
,
11794 _("Missing Re-signISDSDocumentResponse element"));
11798 if (result
->nodesetval
->nodeNr
> 1) {
11799 isds_log_message(context
,
11800 _("Multiple Re-signISDSDocumentResponse element"));
11804 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11805 xmlXPathFreeObject(result
); result
= NULL
;
11807 EXTRACT_STRING("isds:dmResultDoc", string
);
11808 /* Decode non-empty data */
11809 if (NULL
!= string
&& string
[0] != '\0') {
11810 *output_length
= _isds_b64decode(string
, output_data
);
11811 if (*output_length
== (size_t) -1) {
11812 isds_log_message(context
,
11813 _("Error while Base64-decoding re-signed data"));
11818 isds_log_message(context
, _("Server did not send re-signed data"));
11824 if (NULL
!= valid_to
) {
11825 /* Get time stamp expiration date */
11826 EXTRACT_STRING("isds:dmValidTo", string
);
11827 if (NULL
!= string
) {
11828 *valid_to
= calloc(1, sizeof(**valid_to
));
11833 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11835 if (err
== IE_NOTSUP
) {
11837 char *string_locale
= _isds_utf82locale(string
);
11838 isds_printf_message(context
,
11839 _("Invalid dmValidTo value: %s"), string_locale
);
11840 free(string_locale
);
11850 xmlXPathFreeObject(result
);
11851 xmlXPathFreeContext(xpath_ctx
);
11853 xmlFreeDoc(response
);
11854 xmlFreeNode(request
);
11855 #else /* not HAVE_LIBCURL */
11862 #undef INSERT_ELEMENT
11863 #undef CHECK_FOR_STRING_LENGTH
11864 #undef INSERT_STRING_ATTRIBUTE
11865 #undef INSERT_ULONGINTNOPTR
11866 #undef INSERT_ULONGINT
11867 #undef INSERT_LONGINT
11868 #undef INSERT_BOOLEAN
11869 #undef INSERT_SCALAR_BOOLEAN
11870 #undef INSERT_STRING
11871 #undef INSERT_STRING_WITH_NS
11872 #undef EXTRACT_STRING_ATTRIBUTE
11873 #undef EXTRACT_ULONGINT
11874 #undef EXTRACT_LONGINT
11875 #undef EXTRACT_BOOLEAN
11876 #undef EXTRACT_STRING
11879 /* Compute hash of message from raw representation and store it into envelope.
11880 * Original hash structure will be destroyed in envelope.
11881 * @context is session context
11882 * @message is message carrying raw XML message blob
11883 * @algorithm is desired hash algorithm to use */
11884 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11885 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11886 isds_error err
= IE_SUCCESS
;
11888 void *xml_stream
= NULL
;
11889 size_t xml_stream_length
;
11890 size_t phys_start
, phys_end
;
11891 char *phys_path
= NULL
;
11892 struct isds_hash
*new_hash
= NULL
;
11895 if (!context
) return IE_INVALID_CONTEXT
;
11896 zfree(context
->long_message
);
11897 if (!message
) return IE_INVAL
;
11899 if (!message
->raw
) {
11900 isds_log_message(context
,
11901 _("Message does not carry raw representation"));
11905 switch (message
->raw_type
) {
11906 case RAWTYPE_INCOMING_MESSAGE
:
11908 xml_stream
= message
->raw
;
11909 xml_stream_length
= message
->raw_length
;
11912 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11913 nsuri
= SISDS_INCOMING_NS
;
11914 xml_stream
= message
->raw
;
11915 xml_stream_length
= message
->raw_length
;
11918 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11919 nsuri
= SISDS_INCOMING_NS
;
11920 err
= _isds_extract_cms_data(context
,
11921 message
->raw
, message
->raw_length
,
11922 &xml_stream
, &xml_stream_length
);
11923 if (err
) goto leave
;
11926 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11927 nsuri
= SISDS_OUTGOING_NS
;
11928 xml_stream
= message
->raw
;
11929 xml_stream_length
= message
->raw_length
;
11932 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11933 nsuri
= SISDS_OUTGOING_NS
;
11934 err
= _isds_extract_cms_data(context
,
11935 message
->raw
, message
->raw_length
,
11936 &xml_stream
, &xml_stream_length
);
11937 if (err
) goto leave
;
11941 isds_log_message(context
, _("Bad raw representation type"));
11947 /* XXX: Hash is computed from original string representing isds:dmDm
11948 * subtree. That means no encoding, white space, xmlns attributes changes.
11949 * In other words, input for hash can be invalid XML stream. */
11950 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11951 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11952 PHYSXML_ELEMENT_SEPARATOR
,
11953 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11954 PHYSXML_ELEMENT_SEPARATOR
11955 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11959 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11960 phys_path
, &phys_start
, &phys_end
);
11963 isds_log_message(context
,
11964 _("Substring with isds:dmDM element could not be located "
11965 "in raw message"));
11971 new_hash
= calloc(1, sizeof(*new_hash
));
11976 new_hash
->algorithm
= algorithm
;
11977 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11980 isds_log_message(context
, _("Could not compute message hash"));
11984 /* Save computed hash */
11985 if (!message
->envelope
) {
11986 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11987 if (!message
->envelope
) {
11992 isds_hash_free(&message
->envelope
->hash
);
11993 message
->envelope
->hash
= new_hash
;
11997 isds_hash_free(&new_hash
);
12001 if (xml_stream
!= message
->raw
) free(xml_stream
);
12006 /* Compare two hashes.
12007 * @h1 is first hash
12008 * @h2 is another hash
12010 * IE_SUCCESS if hashes equal
12011 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12012 * IE_ENUM if not comparable, but both structures defined
12013 * IE_INVAL if some of the structures are undefined (NULL)
12014 * IE_ERROR if internal error occurs */
12015 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12016 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12017 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12018 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12019 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12020 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12022 for (size_t i
= 0; i
< h1
->length
; i
++) {
12023 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12024 return IE_NOTEQUAL
;
12030 /* Check message has gone through ISDS by comparing message hash stored in
12031 * ISDS and locally computed hash. You must provide message with valid raw
12032 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12033 * This is convenient wrapper for isds_download_message_hash(),
12034 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12035 * @context is session context
12036 * @message is message with valid raw and envelope member; envelope->hash
12037 * member will be changed during function run. Use envelope on heap only.
12039 * IE_SUCCESS if message originates in ISDS
12040 * IE_NOTEQUAL if message is unknown to ISDS
12041 * other code for other errors */
12042 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12043 struct isds_message
*message
) {
12044 isds_error err
= IE_SUCCESS
;
12045 struct isds_hash
*downloaded_hash
= NULL
;
12047 if (!context
) return IE_INVALID_CONTEXT
;
12048 zfree(context
->long_message
);
12049 if (!message
) return IE_INVAL
;
12051 if (!message
->envelope
) {
12052 isds_log_message(context
,
12053 _("Given message structure is missing envelope"));
12056 if (!message
->raw
) {
12057 isds_log_message(context
,
12058 _("Given message structure is missing raw representation"));
12062 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12064 if (err
) goto leave
;
12066 err
= isds_compute_message_hash(context
, message
,
12067 downloaded_hash
->algorithm
);
12068 if (err
) goto leave
;
12070 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12073 isds_hash_free(&downloaded_hash
);
12078 /* Search for document by document ID in list of documents. IDs are compared
12080 * @documents is list of isds_documents
12081 * @id is document identifier
12082 * @return first matching document or NULL. */
12083 const struct isds_document
*isds_find_document_by_id(
12084 const struct isds_list
*documents
, const char *id
) {
12085 const struct isds_list
*item
;
12086 const struct isds_document
*document
;
12088 for (item
= documents
; item
; item
= item
->next
) {
12089 document
= (struct isds_document
*) item
->data
;
12090 if (!document
) continue;
12092 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12100 /* Normalize @mime_type to be proper MIME type.
12101 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12102 * guess regular MIME type (e.g. "application/pdf").
12103 * @mime_type is UTF-8 encoded MIME type to fix
12104 * @return original @mime_type if no better interpretation exists, or
12105 * constant static UTF-8 encoded string with proper MIME type. */
12106 const char *isds_normalize_mime_type(const char *mime_type
) {
12107 if (!mime_type
) return NULL
;
12109 for (size_t offset
= 0;
12110 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12112 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12113 extension_map_mime
[offset
]))
12114 return (const char *) extension_map_mime
[offset
+ 1];
12121 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12122 struct isds_message **message);
12123 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12124 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12125 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12126 struct isds_address **address);
12128 int isds_message_free(struct isds_message **message);
12129 int isds_address_free(struct isds_address **address);
12133 /* Makes known all relevant namespaces to given XPath context
12134 * @xpath_ctx is XPath context
12135 * @message_ns selects proper message name space. Unsigned and signed
12136 * messages and delivery info's differ in prefix and URI. */
12137 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12138 const message_ns_type message_ns
) {
12139 const xmlChar
*message_namespace
= NULL
;
12141 if (!xpath_ctx
) return IE_ERROR
;
12143 switch(message_ns
) {
12145 message_namespace
= BAD_CAST ISDS1_NS
; break;
12146 case MESSAGE_NS_UNSIGNED
:
12147 message_namespace
= BAD_CAST ISDS_NS
; break;
12148 case MESSAGE_NS_SIGNED_INCOMING
:
12149 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12150 case MESSAGE_NS_SIGNED_OUTGOING
:
12151 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12152 case MESSAGE_NS_SIGNED_DELIVERY
:
12153 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12158 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12160 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12162 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12164 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12166 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12168 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))