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
, ...) {
679 isds_vasprintf(&text
, msg
, ap
);
683 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
688 /* Initialize ISDS library.
689 * Global function, must be called before other functions.
690 * If it fails you can not use ISDS library and must call isds_cleanup() to
691 * free partially initialized global variables. */
692 isds_error
isds_init(void) {
693 /* NULL global variables */
694 log_facilities
= ILF_ALL
;
695 log_level
= ILL_WARNING
;
697 log_callback_data
= NULL
;
700 /* Initialize gettext */
701 bindtextdomain(PACKAGE
, LOCALEDIR
);
705 /* Initialize CURL */
706 if (curl_global_init(CURL_GLOBAL_ALL
)) {
707 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
710 #endif /* HAVE_LIBCURL */
712 /* Initialise cryptographic back-ends. */
713 if (IE_SUCCESS
!= _isds_init_crypto()) {
714 isds_log(ILF_ISDS
, ILL_CRIT
,
715 _("initialisation of cryptographic back-end failed\n"));
719 /* This can _exit() current program. Find not so assertive check. */
721 xmlSetGenericErrorFunc(NULL
, log_xml
);
724 if (_isds_init_expat(&version_expat
)) {
725 isds_log(ILF_ISDS
, ILL_CRIT
,
726 _("expat library initialization failed\n"));
730 /* Allocate global variables */
737 /* Deinitialize ISDS library.
738 * Global function, must be called as last library function. */
739 isds_error
isds_cleanup(void) {
745 curl_global_cleanup();
752 /* Return version string of this library. Version of dependencies can be
753 * embedded. Do no try to parse it. You must free it. */
754 char *isds_version(void) {
757 isds_asprintf(&buffer
,
759 # ifndef USE_OPENSSL_BACKEND
760 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
762 _("%s (%s, %s, %s, libxml2 %s)"),
765 # ifndef USE_OPENSSL_BACKEND
766 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
768 _("%s (%s, %s, libxml2 %s)"),
775 #ifndef USE_OPENSSL_BACKEND
776 version_gpgme
, version_gcrypt
,
780 version_expat
, xmlParserVersion
);
785 /* Return text description of ISDS error */
786 const char *isds_strerror(const isds_error error
) {
789 return(_("Success")); break;
791 return(_("Unspecified error")); break;
793 return(_("Not supported")); break;
795 return(_("Invalid value")); break;
796 case IE_INVALID_CONTEXT
:
797 return(_("Invalid context")); break;
798 case IE_NOT_LOGGED_IN
:
799 return(_("Not logged in")); break;
800 case IE_CONNECTION_CLOSED
:
801 return(_("Connection closed")); break;
803 return(_("Timed out")); break;
805 return(_("Not exist")); break;
807 return(_("Out of memory")); break;
809 return(_("Network problem")); break;
811 return(_("HTTP problem")); break;
813 return(_("SOAP problem")); break;
815 return(_("XML problem")); break;
817 return(_("ISDS server problem")); break;
819 return(_("Invalid enum value")); break;
821 return(_("Invalid date value")); break;
823 return(_("Too big")); break;
825 return(_("Too small")); break;
827 return(_("Value not unique")); break;
829 return(_("Values not equal")); break;
830 case IE_PARTIAL_SUCCESS
:
831 return(_("Some suboperations failed")); break;
833 return(_("Operation aborted")); break;
835 return(_("Security problem")); break;
837 return(_("Unknown error"));
842 /* Create ISDS context.
843 * Each context can be used for different sessions to (possibly) different
844 * ISDS server with different credentials. */
845 struct isds_ctx
*isds_ctx_create(void) {
846 struct isds_ctx
*context
;
847 context
= malloc(sizeof(*context
));
848 if (context
) memset(context
, 0, sizeof(*context
));
853 /* Close possibly opened connection to Czech POINT document deposit without
854 * resetting long_message buffer.
855 * XXX: Do not use czp_close_connection() if you do not want to destroy log
857 * @context is Czech POINT session context. */
858 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
859 if (!context
) return IE_INVALID_CONTEXT
;
860 _isds_close_connection(context
);
865 /* Discard credentials.
866 * @context is ISDS context
867 * @discard_saved_username is true for removing saved username, false for
869 * Only that. It does not cause log out, connection close or similar. */
870 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
871 _Bool discard_saved_username
) {
872 if(!context
) return IE_INVALID_CONTEXT
;
874 if (context
->username
) {
875 memset(context
->username
, 0, strlen(context
->username
));
876 zfree(context
->username
);
878 if (context
->password
) {
879 memset(context
->password
, 0, strlen(context
->password
));
880 zfree(context
->password
);
882 isds_pki_credentials_free(&context
->pki_credentials
);
883 if (discard_saved_username
&& context
->saved_username
) {
884 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
885 zfree(context
->saved_username
);
890 #endif /* HAVE_LIBCURL */
893 /* Destroy ISDS context and free memory.
894 * @context will be NULLed on success. */
895 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
896 if (!context
|| !*context
) {
897 return IE_INVALID_CONTEXT
;
901 /* Discard credentials and close connection */
902 switch ((*context
)->type
) {
903 case CTX_TYPE_NONE
: break;
904 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
906 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
907 czp_do_close_connection(*context
); break;
911 _isds_discard_credentials(*context
, 1);
913 /* Free other structures */
914 free((*context
)->url
);
915 free((*context
)->tls_verify_server
);
916 free((*context
)->tls_ca_file
);
917 free((*context
)->tls_ca_dir
);
918 free((*context
)->tls_crl_file
);
919 #endif /* HAVE_LIBCURL */
920 free((*context
)->long_message
);
928 /* Return long message text produced by library function, e.g. detailed error
929 * message. Returned pointer is only valid until new library function is
930 * called for the same context. Could be NULL, especially if NULL context is
931 * supplied. Return string is locale encoded. */
932 char *isds_long_message(const struct isds_ctx
*context
) {
933 if (!context
) return NULL
;
934 return context
->long_message
;
938 /* Stores message into context' long_message buffer.
939 * Application can pick the message up using isds_long_message().
940 * NULL @message truncates the buffer but does not deallocate it.
941 * @message is coded in locale encoding */
942 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
943 const char *message
) {
947 if (!context
) return IE_INVALID_CONTEXT
;
949 /* FIXME: Check for integer overflow */
950 length
= 1 + ((message
) ? strlen(message
) : 0);
951 buffer
= realloc(context
->long_message
, length
);
952 if (!buffer
) return IE_NOMEM
;
955 strcpy(buffer
, message
);
959 context
->long_message
= buffer
;
964 /* Appends message into context' long_message buffer.
965 * Application can pick the message up using isds_long_message().
966 * NULL message has void effect. */
967 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
968 const char *message
) {
970 size_t old_length
, length
;
972 if (!context
) return IE_INVALID_CONTEXT
;
973 if (!message
) return IE_SUCCESS
;
974 if (!context
->long_message
)
975 return isds_log_message(context
, message
);
977 old_length
= strlen(context
->long_message
);
978 /* FIXME: Check for integer overflow */
979 length
= 1 + old_length
+ strlen(message
);
980 buffer
= realloc(context
->long_message
, length
);
981 if (!buffer
) return IE_NOMEM
;
983 strcpy(buffer
+ old_length
, message
);
985 context
->long_message
= buffer
;
990 /* Stores formatted message into context' long_message buffer.
991 * Application can pick the message up using isds_long_message(). */
992 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
993 const char *format
, ...) {
997 if (!context
) return IE_INVALID_CONTEXT
;
998 va_start(ap
, format
);
999 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
1002 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
1007 * @facilities is bit mask of isds_log_facility values,
1008 * @level is verbosity level. */
1009 void isds_set_logging(const unsigned int facilities
,
1010 const isds_log_level level
) {
1011 log_facilities
= facilities
;
1016 /* Register callback function libisds calls when new global log message is
1017 * produced by library. Library logs to stderr by default.
1018 * @callback is function provided by application libisds will call. See type
1019 * definition for @callback argument explanation. Pass NULL to revert logging to
1020 * default behaviour.
1021 * @data is application specific data @callback gets as last argument */
1022 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1023 log_callback
= callback
;
1024 log_callback_data
= data
;
1028 /* Log @message in class @facility with log @level into global log. @message
1029 * is printf(3) formatting string, variadic arguments may be necessary.
1030 * For debugging purposes. */
1031 _hidden isds_error
isds_log(const isds_log_facility facility
,
1032 const isds_log_level level
, const char *message
, ...) {
1034 char *buffer
= NULL
;
1037 if (level
> log_level
) return IE_SUCCESS
;
1038 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1039 if (!message
) return IE_INVAL
;
1042 /* Pass message to application supplied callback function */
1043 va_start(ap
, message
);
1044 length
= isds_vasprintf(&buffer
, message
, ap
);
1051 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1055 /* Default: Log it to stderr */
1056 va_start(ap
, message
);
1057 vfprintf(stderr
, message
, ap
);
1059 /* Line buffered printf is default.
1067 /* Set timeout in milliseconds for each network job like connecting to server
1068 * or sending message. Use 0 to disable timeout limits. */
1069 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1070 const unsigned int timeout
) {
1071 if (!context
) return IE_INVALID_CONTEXT
;
1072 zfree(context
->long_message
);
1075 context
->timeout
= timeout
;
1077 if (context
->curl
) {
1080 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1082 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1083 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1086 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1087 context
->timeout
/ 1000);
1088 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1089 if (curl_err
) return IE_ERROR
;
1093 #else /* not HAVE_LIBCURL */
1099 /* Register callback function libisds calls periodically during HTTP data
1101 * @context is session context
1102 * @callback is function provided by application libisds will call. See type
1103 * definition for @callback argument explanation.
1104 * @data is application specific data @callback gets as last argument */
1105 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1106 isds_progress_callback callback
, void *data
) {
1107 if (!context
) return IE_INVALID_CONTEXT
;
1108 zfree(context
->long_message
);
1111 context
->progress_callback
= callback
;
1112 context
->progress_callback_data
= data
;
1115 #else /* not HAVE_LIBCURL */
1121 /* Change context settings.
1122 * @context is context which setting will be applied to
1123 * @option is name of option. It determines the type of last argument. See
1124 * isds_option definition for more info.
1125 * @... is value of new setting. Type is determined by @option
1127 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1129 isds_error err
= IE_SUCCESS
;
1132 char *pointer
, *string
;
1135 if (!context
) return IE_INVALID_CONTEXT
;
1136 zfree(context
->long_message
);
1138 va_start(ap
, option
);
1140 #define REPLACE_VA_BOOLEAN(destination) { \
1141 if (!(destination)) { \
1142 (destination) = malloc(sizeof(*(destination))); \
1143 if (!(destination)) { \
1144 err = IE_NOMEM; goto leave; \
1147 *(destination) = (_Bool) !!va_arg(ap, int); \
1150 #define REPLACE_VA_STRING(destination) { \
1151 string = va_arg(ap, char *); \
1153 pointer = realloc((destination), 1 + strlen(string)); \
1154 if (!pointer) { err = IE_NOMEM; goto leave; } \
1155 strcpy(pointer, string); \
1156 (destination) = pointer; \
1158 free(destination); \
1159 (destination) = NULL; \
1164 case IOPT_TLS_VERIFY_SERVER
:
1166 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1168 err
= IE_NOTSUP
; goto leave
;
1171 case IOPT_TLS_CA_FILE
:
1173 REPLACE_VA_STRING(context
->tls_ca_file
);
1175 err
= IE_NOTSUP
; goto leave
;
1178 case IOPT_TLS_CA_DIRECTORY
:
1180 REPLACE_VA_STRING(context
->tls_ca_dir
);
1182 err
= IE_NOTSUP
; goto leave
;
1185 case IOPT_TLS_CRL_FILE
:
1187 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1188 REPLACE_VA_STRING(context
->tls_crl_file
);
1190 isds_log_message(context
,
1191 _("Curl library does not support CRL definition"));
1193 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1195 err
= IE_NOTSUP
; goto leave
;
1196 #endif /* not HAVE_LIBCURL */
1198 case IOPT_NORMALIZE_MIME_TYPE
:
1199 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1203 err
= IE_ENUM
; goto leave
;
1206 #undef REPLACE_VA_STRING
1207 #undef REPLACE_VA_BOOLEAN
1216 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1217 * Destination for NULL argument will not be touched.
1218 * Destination pointers must be freed before calling this function.
1219 * If @username is @context->saved_username, the saved_username will not be
1220 * replaced. The saved_username is clobbered only if context has set otp
1222 * Return IE_SUCCESS on success. */
1223 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1224 const char *username
, const char *password
,
1225 const struct isds_pki_credentials
*pki_credentials
) {
1226 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1228 /* FIXME: mlock password
1229 * (I have a library) */
1232 context
->username
= strdup(username
);
1233 if (context
->otp
&& context
->saved_username
!= username
)
1234 context
->saved_username
= strdup(username
);
1237 if (NULL
== context
->otp_credentials
)
1238 context
->password
= strdup(password
);
1240 context
->password
= _isds_astrcat(password
,
1241 context
->otp_credentials
->otp_code
);
1243 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1245 if ((NULL
!= username
&& NULL
== context
->username
) ||
1246 (NULL
!= password
&& NULL
== context
->password
) ||
1247 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1248 (context
->otp
&& NULL
!= context
->username
&&
1249 NULL
== context
->saved_username
)) {
1258 /* Connect and log into ISDS server.
1259 * All required arguments will be copied, you do not have to keep them after
1261 * ISDS supports six different authentication methods. Exact method is
1262 * selected on @username, @password, @pki_credentials, and @otp arguments:
1263 * - If @pki_credentials == NULL, @username and @password must be supplied
1265 * - If @otp == NULL, simple authentication by username and password will
1267 * - If @otp != NULL, authentication by username and password and OTP
1269 * - If @pki_credentials != NULL, then
1270 * - If @username == NULL, only certificate will be used
1271 * - If @username != NULL, then
1272 * - If @password == NULL, then certificate will be used and
1273 * @username shifts meaning to box ID. This is used for hosted
1275 * - Otherwise all three arguments will be used.
1276 * Please note, that different cases require different certificate type
1277 * (system qualified one or commercial non qualified one). This library
1278 * does not check such political issues. Please see ISDS Specification
1280 * @url is base address of ISDS web service. Pass extern isds_locator
1281 * variable to use production ISDS instance without client certificate
1282 * authentication (or extern isds_cert_locator with client certificate
1283 * authentication or extern isds_otp_locators with OTP authentication).
1284 * Passing NULL has the same effect, autoselection between isds_locator,
1285 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1286 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1287 * isds_otp_testing_locator) variable to select testing instance.
1288 * @username is user name of ISDS user or box ID
1289 * @password is user's secret password
1290 * @pki_credentials defines public key cryptographic material to use in client
1292 * @otp selects one-time password authentication method to use, defines OTP
1293 * code (if known) and returns fine grade resolution of OTP procedure.
1295 * IE_SUCCESS if authentication succeeds
1296 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1297 * requested, fine grade reason will be set into @otp->resolution. Error
1298 * message from server can be obtained by isds_long_message() call.
1299 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1300 * server has sent OTP code through side channel. Application is expected to
1301 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1302 * this call to complete second phase of TOTP authentication;
1303 * or other appropriate error. */
1304 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1305 const char *username
, const char *password
,
1306 const struct isds_pki_credentials
*pki_credentials
,
1307 struct isds_otp
*otp
) {
1309 isds_error err
= IE_NOT_LOGGED_IN
;
1310 isds_error soap_err
;
1311 xmlNsPtr isds_ns
= NULL
;
1312 xmlNodePtr request
= NULL
;
1313 #endif /* HAVE_LIBCURL */
1315 if (!context
) return IE_INVALID_CONTEXT
;
1316 zfree(context
->long_message
);
1319 /* Close connection if already logged in */
1320 if (context
->curl
) {
1321 _isds_close_connection(context
);
1324 /* Store configuration */
1325 context
->type
= CTX_TYPE_ISDS
;
1326 zfree(context
->url
);
1328 /* Mangle base URI according to requested authentication method */
1329 if (NULL
== pki_credentials
) {
1330 isds_log(ILF_SEC
, ILL_INFO
,
1331 _("Selected authentication method: no certificate, "
1332 "username and password\n"));
1333 if (!username
|| !password
) {
1334 isds_log_message(context
,
1335 _("Both username and password must be supplied"));
1338 context
->otp_credentials
= otp
;
1339 context
->otp
= (NULL
!= context
->otp_credentials
);
1341 if (!context
->otp
) {
1342 /* Default locator is official system (without certificate or
1344 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1346 const char *authenticator_uri
= NULL
;
1347 if (!url
) url
= isds_otp_locator
;
1348 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1349 switch (context
->otp_credentials
->method
) {
1351 isds_log(ILF_SEC
, ILL_INFO
,
1352 _("Selected authentication method: "
1353 "HMAC-based one-time password\n"));
1355 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1358 isds_log(ILF_SEC
, ILL_INFO
,
1359 _("Selected authentication method: "
1360 "Time-based one-time password\n"));
1361 if (context
->otp_credentials
->otp_code
== NULL
) {
1362 isds_log(ILF_SEC
, ILL_INFO
,
1363 _("OTP code has not been provided by "
1364 "application, requesting server for "
1367 "%1$sas/processLogin?type=totp&sendSms=true&"
1370 isds_log(ILF_SEC
, ILL_INFO
,
1371 _("OTP code has been provided by "
1372 "application, not requesting server "
1375 "%1$sas/processLogin?type=totp&"
1380 isds_log_message(context
,
1381 _("Unknown one-time password authentication "
1382 "method requested by application"));
1385 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1389 /* Default locator is official system (with client certificate) */
1391 context
->otp_credentials
= NULL
;
1392 if (!url
) url
= isds_cert_locator
;
1395 isds_log(ILF_SEC
, ILL_INFO
,
1396 _("Selected authentication method: system certificate, "
1397 "no username and no password\n"));
1399 context
->url
= _isds_astrcat(url
, "cert/");
1402 isds_log(ILF_SEC
, ILL_INFO
,
1403 _("Selected authentication method: system certificate, "
1404 "box ID and no password\n"));
1405 context
->url
= _isds_astrcat(url
, "hspis/");
1407 isds_log(ILF_SEC
, ILL_INFO
,
1408 _("Selected authentication method: commercial "
1409 "certificate, username and password\n"));
1410 context
->url
= _isds_astrcat(url
, "certds/");
1414 if (!(context
->url
))
1417 /* Prepare CURL handle */
1418 context
->curl
= curl_easy_init();
1419 if (!(context
->curl
))
1422 /* Build log-in request */
1423 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1425 isds_log_message(context
, _("Could not build ISDS log-in request"));
1428 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1430 isds_log_message(context
, _("Could not create ISDS name space"));
1431 xmlFreeNode(request
);
1434 xmlSetNs(request
, isds_ns
);
1436 /* Store credentials */
1437 _isds_discard_credentials(context
, 1);
1438 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1439 _isds_discard_credentials(context
, 1);
1440 xmlFreeNode(request
);
1444 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1447 /* XXX: ISDS documentation does not specify response body for
1448 * DummyOperation request. However real server sends back
1449 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1450 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1451 * SOAP body content, e.g. the dmStatus element. */
1453 /* Send log-in request */
1454 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1457 /* Revert context URL from OTP authentication service URL to OTP web
1458 * service base URL for subsequent calls. Potenial isds_login() retry
1459 * will re-set context URL again. */
1460 zfree(context
->url
);
1461 context
->url
= _isds_astrcat(url
, "apps/");
1462 if (context
->url
== NULL
) {
1463 soap_err
= IE_NOMEM
;
1465 /* Detach pointer to OTP credentials from context */
1466 context
->otp_credentials
= NULL
;
1469 /* Remove credentials */
1470 _isds_discard_credentials(context
, 0);
1472 /* Destroy log-in request */
1473 xmlFreeNode(request
);
1476 _isds_close_connection(context
);
1480 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1481 * authentication succeeded if soap_err == IE_SUCCESS */
1485 isds_log(ILF_ISDS
, ILL_DEBUG
,
1486 _("User %s has been logged into server %s successfully\n"),
1489 #else /* not HAVE_LIBCURL */
1495 /* Log out from ISDS server discards credentials and connection configuration. */
1496 isds_error
isds_logout(struct isds_ctx
*context
) {
1497 if (!context
) return IE_INVALID_CONTEXT
;
1498 zfree(context
->long_message
);
1501 if (context
->curl
) {
1503 isds_error err
= _isds_invalidate_otp_cookie(context
);
1504 if (err
) return err
;
1507 /* Close connection */
1508 _isds_close_connection(context
);
1510 /* Discard credentials for sure. They should not survive isds_login(),
1511 * even successful .*/
1512 _isds_discard_credentials(context
, 1);
1514 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1516 _isds_discard_credentials(context
, 1);
1518 zfree(context
->url
);
1520 #else /* not HAVE_LIBCURL */
1526 /* Verify connection to ISDS is alive and server is responding.
1527 * Send dummy request to ISDS and expect dummy response. */
1528 isds_error
isds_ping(struct isds_ctx
*context
) {
1530 isds_error soap_err
;
1531 xmlNsPtr isds_ns
= NULL
;
1532 xmlNodePtr request
= NULL
;
1533 #endif /* HAVE_LIBCURL */
1535 if (!context
) return IE_INVALID_CONTEXT
;
1536 zfree(context
->long_message
);
1539 /* Check if connection is established */
1540 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1543 /* Build dummy request */
1544 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1546 isds_log_message(context
, _("Could build ISDS dummy request"));
1549 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1551 isds_log_message(context
, _("Could not create ISDS name space"));
1552 xmlFreeNode(request
);
1555 xmlSetNs(request
, isds_ns
);
1557 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1559 /* XXX: ISDS documentation does not specify response body for
1560 * DummyOperation request. However real server sends back
1561 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1562 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1563 * SOAP body content, e.g. the dmStatus element. */
1565 /* Send dummy request */
1566 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1568 /* Destroy log-in request */
1569 xmlFreeNode(request
);
1572 isds_log(ILF_ISDS
, ILL_DEBUG
,
1573 _("ISDS server could not be contacted\n"));
1577 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1578 * authentication succeeded if soap_err == IE_SUCCESS */
1581 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1584 #else /* not HAVE_LIBCURL */
1590 /* Send bogus request to ISDS.
1591 * Just for test purposes */
1592 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1595 xmlNsPtr isds_ns
= NULL
;
1596 xmlNodePtr request
= NULL
;
1597 xmlDocPtr response
= NULL
;
1598 xmlChar
*code
= NULL
, *message
= NULL
;
1601 if (!context
) return IE_INVALID_CONTEXT
;
1602 zfree(context
->long_message
);
1605 /* Check if connection is established */
1606 if (!context
->curl
) {
1607 /* Testing printf message */
1608 isds_printf_message(context
, "%s", _("I said connection closed"));
1609 return IE_CONNECTION_CLOSED
;
1613 /* Build dummy request */
1614 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1616 isds_log_message(context
, _("Could build ISDS bogus request"));
1619 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1621 isds_log_message(context
, _("Could not create ISDS name space"));
1622 xmlFreeNode(request
);
1625 xmlSetNs(request
, isds_ns
);
1627 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1629 /* Sent bogus request */
1630 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1632 /* Destroy request */
1633 xmlFreeNode(request
);
1636 isds_log(ILF_ISDS
, ILL_DEBUG
,
1637 _("Processing ISDS response on bogus request failed\n"));
1638 xmlFreeDoc(response
);
1642 /* Check for response status */
1643 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1644 &code
, &message
, NULL
);
1646 isds_log(ILF_ISDS
, ILL_DEBUG
,
1647 _("ISDS response on bogus request is missing status\n"));
1650 xmlFreeDoc(response
);
1653 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1654 char *code_locale
= _isds_utf82locale((char*)code
);
1655 char *message_locale
= _isds_utf82locale((char*)message
);
1656 isds_log(ILF_ISDS
, ILL_DEBUG
,
1657 _("Server refused bogus request (code=%s, message=%s)\n"),
1658 code_locale
, message_locale
);
1659 /* XXX: Literal error messages from ISDS are Czech messages
1660 * (English sometimes) in UTF-8. It's hard to catch them for
1661 * translation. Successfully gettextized would return in locale
1662 * encoding, unsuccessfully translated would pass in UTF-8. */
1663 isds_log_message(context
, message_locale
);
1665 free(message_locale
);
1668 xmlFreeDoc(response
);
1675 xmlFreeDoc(response
);
1677 isds_log(ILF_ISDS
, ILL_DEBUG
,
1678 _("Bogus message accepted by server. This should not happen.\n"));
1681 #else /* not HAVE_LIBCURL */
1688 /* Serialize XML subtree to buffer preserving XML indentation.
1689 * @context is session context
1690 * @subtree is XML element to be serialized (with children)
1691 * @buffer is automatically reallocated buffer where serialize to
1692 * @length is size of serialized stream in bytes
1693 * @return standard error code, free @buffer in case of error */
1694 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1695 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1696 isds_error err
= IE_SUCCESS
;
1697 xmlBufferPtr xml_buffer
= NULL
;
1698 xmlSaveCtxtPtr save_ctx
= NULL
;
1699 xmlDocPtr subtree_doc
= NULL
;
1700 xmlNodePtr subtree_copy
;
1704 if (!context
) return IE_INVALID_CONTEXT
;
1705 if (!buffer
) return IE_INVAL
;
1707 if (!subtree
|| !length
) return IE_INVAL
;
1709 /* Make temporary XML document with @subtree root element */
1710 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1711 * It can result in not well-formed on invalid XML tree (e.g. name space
1712 * prefix definition can miss. */
1715 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1717 isds_log_message(context
, _("Could not build temporary document"));
1722 /* XXX: Copy subtree and attach the copy to document.
1723 * One node can not bee attached into more document at the same time.
1724 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1726 * XXX: Check xmlSaveTree() too. */
1727 subtree_copy
= xmlCopyNodeList(subtree
);
1728 if (!subtree_copy
) {
1729 isds_log_message(context
, _("Could not copy subtree"));
1733 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1735 /* Only this way we get namespace definition as @xmlns:isds,
1736 * otherwise we get namespace prefix without definition */
1737 /* FIXME: Don't overwrite original default namespace */
1738 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1740 isds_log_message(context
, _("Could not create ISDS name space"));
1744 xmlSetNs(subtree_copy
, isds_ns
);
1747 /* Serialize the document into buffer */
1748 xml_buffer
= xmlBufferCreate();
1750 isds_log_message(context
, _("Could not create xmlBuffer"));
1754 /* Last argument 0 means to not format the XML tree */
1755 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1757 isds_log_message(context
, _("Could not create XML serializer"));
1761 /* XXX: According LibXML documentation, this function does not return
1762 * meaningful value yet */
1763 xmlSaveDoc(save_ctx
, subtree_doc
);
1764 if (-1 == xmlSaveFlush(save_ctx
)) {
1765 isds_log_message(context
,
1766 _("Could not serialize XML subtree"));
1770 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1771 * even after xmlSaveFlush(). Thus close it here */
1772 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1775 /* Store and detach buffer from xml_buffer */
1776 *buffer
= xml_buffer
->content
;
1777 *length
= xml_buffer
->use
;
1778 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1781 new_buffer
= realloc(*buffer
, *length
);
1782 if (new_buffer
) *buffer
= new_buffer
;
1790 xmlSaveClose(save_ctx
);
1791 xmlBufferFree(xml_buffer
);
1792 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1795 #endif /* HAVE_LIBCURL */
1799 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1800 * @context is session context
1801 * @document is original document where @nodeset points to
1802 * @nodeset is XPath node set to dump (recursively)
1803 * @buffer is automatically reallocated buffer where serialize to
1804 * @length is size of serialized stream in bytes
1805 * @return standard error code, free @buffer in case of error */
1806 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1807 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1808 void **buffer
, size_t *length
) {
1809 isds_error err
= IE_SUCCESS
;
1810 xmlBufferPtr xml_buffer
= NULL
;
1813 if (!context
) return IE_INVALID_CONTEXT
;
1814 if (!buffer
) return IE_INVAL
;
1816 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1819 /* Empty node set results into NULL buffer */
1820 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1824 /* Resulting the document into buffer */
1825 xml_buffer
= xmlBufferCreate();
1827 isds_log_message(context
, _("Could not create xmlBuffer"));
1832 /* Iterate over all nodes */
1833 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1835 * XXX: xmlNodeDump() appends to xml_buffer. */
1837 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1838 isds_log_message(context
, _("Could not dump XML node"));
1844 /* Store and detach buffer from xml_buffer */
1845 *buffer
= xml_buffer
->content
;
1846 *length
= xml_buffer
->use
;
1847 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1850 new_buffer
= realloc(*buffer
, *length
);
1851 if (new_buffer
) *buffer
= new_buffer
;
1860 xmlBufferFree(xml_buffer
);
1866 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1867 * @context is session context
1868 * @document is original document where @nodeset points to
1869 * @nodeset is XPath node set to dump (recursively)
1870 * @buffer is automatically reallocated buffer where serialize to
1871 * @length is size of serialized stream in bytes
1872 * @return standard error code, free @buffer in case of error */
1873 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1874 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1875 void **buffer
, size_t *length
) {
1876 isds_error err
= IE_SUCCESS
;
1877 xmlBufferPtr xml_buffer
= NULL
;
1878 xmlSaveCtxtPtr save_ctx
= NULL
;
1881 if (!context
) return IE_INVALID_CONTEXT
;
1882 if (!buffer
) return IE_INVAL
;
1884 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1887 /* Empty node set results into NULL buffer */
1888 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1892 /* Resulting the document into buffer */
1893 xml_buffer
= xmlBufferCreate();
1895 isds_log_message(context
, _("Could not create xmlBuffer"));
1899 if (xmlSubstituteEntitiesDefault(1)) {
1900 isds_log_message(context
, _("Could not disable attribute escaping"));
1904 /* Last argument means:
1905 * 0 to not format the XML tree
1906 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1907 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1908 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1910 isds_log_message(context
, _("Could not create XML serializer"));
1914 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1915 isds_log_message(context, _("Could not disable attribute escaping"));
1921 /* Iterate over all nodes */
1922 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1924 * XXX: xmlNodeDump() appends to xml_buffer. */
1926 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1928 /* XXX: According LibXML documentation, this function does not return
1929 * meaningful value yet */
1930 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1931 if (-1 == xmlSaveFlush(save_ctx
)) {
1932 isds_log_message(context
,
1933 _("Could not serialize XML subtree"));
1939 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1940 * even after xmlSaveFlush(). Thus close it here */
1941 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1943 /* Store and detach buffer from xml_buffer */
1944 *buffer
= xml_buffer
->content
;
1945 *length
= xml_buffer
->use
;
1946 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1949 new_buffer
= realloc(*buffer
, *length
);
1950 if (new_buffer
) *buffer
= new_buffer
;
1958 xmlSaveClose(save_ctx
);
1959 xmlBufferFree(xml_buffer
);
1966 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1967 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1968 if (!string
|| !type
) return IE_INVAL
;
1970 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1972 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1974 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1975 *type
= DBTYPE_PFO_ADVOK
;
1976 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1977 *type
= DBTYPE_PFO_DANPOR
;
1978 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1979 *type
= DBTYPE_PFO_INSSPR
;
1980 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1982 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1983 *type
= DBTYPE_PO_ZAK
;
1984 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1985 *type
= DBTYPE_PO_REQ
;
1986 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1988 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1989 *type
= DBTYPE_OVM_NOTAR
;
1990 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1991 *type
= DBTYPE_OVM_EXEKUT
;
1992 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1993 *type
= DBTYPE_OVM_REQ
;
2000 /* Convert ISDS dbType enum @type to UTF-8 string.
2001 * @Return pointer to static string, or NULL if unknown enum value */
2002 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2004 /* DBTYPE_SYSTEM is invalid value from point of view of public
2005 * SOAP interface. */
2006 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2007 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2008 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2009 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2010 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2011 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2012 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2013 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2014 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2015 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2016 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2017 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2018 default: return NULL
; break;
2023 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2024 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2025 if (!string
|| !type
) return IE_INVAL
;
2027 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2028 *type
= USERTYPE_PRIMARY
;
2029 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2030 *type
= USERTYPE_ENTRUSTED
;
2031 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2032 *type
= USERTYPE_ADMINISTRATOR
;
2033 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2034 *type
= USERTYPE_OFFICIAL
;
2035 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2036 *type
= USERTYPE_OFFICIAL_CERT
;
2037 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2038 *type
= USERTYPE_LIQUIDATOR
;
2045 /* Convert ISDS userType enum @type to UTF-8 string.
2046 * @Return pointer to static string, or NULL if unknown enum value */
2047 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2049 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2050 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2051 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2052 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2053 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2054 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2055 default: return NULL
; break;
2060 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2061 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2062 isds_sender_type
*type
) {
2063 if (!string
|| !type
) return IE_INVAL
;
2065 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2066 *type
= SENDERTYPE_PRIMARY
;
2067 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2068 *type
= SENDERTYPE_ENTRUSTED
;
2069 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2070 *type
= SENDERTYPE_ADMINISTRATOR
;
2071 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2072 *type
= SENDERTYPE_OFFICIAL
;
2073 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2074 *type
= SENDERTYPE_VIRTUAL
;
2075 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2076 *type
= SENDERTYPE_OFFICIAL_CERT
;
2077 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2078 *type
= SENDERTYPE_LIQUIDATOR
;
2085 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2086 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2087 isds_payment_type
*type
) {
2088 if (!string
|| !type
) return IE_INVAL
;
2090 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2091 *type
= PAYMENT_SENDER
;
2092 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2093 *type
= PAYMENT_RESPONSE
;
2094 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2095 *type
= PAYMENT_SPONSOR
;
2096 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2097 *type
= PAYMENT_SPONSOR_LIMITED
;
2098 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2099 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2100 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2101 *type
= PAYMENT_STAMP
;
2108 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2109 * ciEventType is integer but we convert it from string representation
2111 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2112 isds_credit_event_type
*type
) {
2113 if (!string
|| !type
) return IE_INVAL
;
2115 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2116 *type
= ISDS_CREDIT_CHARGED
;
2117 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2118 *type
= ISDS_CREDIT_DISCHARGED
;
2119 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2120 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2121 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2122 *type
= ISDS_CREDIT_STORAGE_SET
;
2123 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2124 *type
= ISDS_CREDIT_EXPIRED
;
2131 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2132 * @Return pointer to static string, or NULL if unknown enum value */
2133 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2135 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2136 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2137 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2138 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2139 default: return NULL
; break;
2144 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2145 * ISDSSearch2/searchType value.
2146 * @Return pointer to static string, or NULL if unknown enum value */
2147 static const xmlChar
*isds_fulltext_target2string(
2148 const isds_fulltext_target type
) {
2150 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2151 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2152 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2153 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2154 default: return NULL
; break;
2157 #endif /* HAVE_LIBCURL */
2160 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2161 * @Return IE_ENUM if @string is not valid enum member */
2162 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2163 isds_FileMetaType
*type
) {
2164 if (!string
|| !type
) return IE_INVAL
;
2166 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2167 *type
= FILEMETATYPE_MAIN
;
2168 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2169 *type
= FILEMETATYPE_ENCLOSURE
;
2170 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2171 *type
= FILEMETATYPE_SIGNATURE
;
2172 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2173 *type
= FILEMETATYPE_META
;
2180 /* Convert UTF-8 @string to ISDS hash @algorithm.
2181 * @Return IE_ENUM if @string is not valid enum member */
2182 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2183 isds_hash_algorithm
*algorithm
) {
2184 if (!string
|| !algorithm
) return IE_INVAL
;
2186 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2187 *algorithm
= HASH_ALGORITHM_MD5
;
2188 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2189 *algorithm
= HASH_ALGORITHM_SHA_1
;
2190 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2191 *algorithm
= HASH_ALGORITHM_SHA_224
;
2192 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2193 *algorithm
= HASH_ALGORITHM_SHA_256
;
2194 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2195 *algorithm
= HASH_ALGORITHM_SHA_384
;
2196 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2197 *algorithm
= HASH_ALGORITHM_SHA_512
;
2205 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2206 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2207 if (!time
|| !string
) return IE_INVAL
;
2209 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2210 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2217 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2218 * respects the @time microseconds too. */
2219 static isds_error
timeval2timestring(const struct timeval
*time
,
2223 if (!time
|| !string
) return IE_INVAL
;
2225 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2226 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2228 /* TODO: small negative year should be formatted as "-0012". This is not
2229 * true for glibc "%04d". We should implement it.
2230 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2231 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2232 if (-1 == isds_asprintf((char **) string
,
2233 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2234 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2235 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2241 #endif /* HAVE_LIBCURL */
2244 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2245 * It respects microseconds too.
2246 * In case of error, @time will be freed. */
2247 static isds_error
timestring2timeval(const xmlChar
*string
,
2248 struct timeval
**time
) {
2250 char *offset
, *delim
, *endptr
;
2252 int offset_hours
, offset_minutes
;
2258 if (!time
) return IE_INVAL
;
2264 memset(&broken
, 0, sizeof(broken
));
2267 *time
= calloc(1, sizeof(**time
));
2268 if (!*time
) return IE_NOMEM
;
2270 memset(*time
, 0, sizeof(**time
));
2274 /* xsd:date is ISO 8601 string, thus ASCII */
2275 /*TODO: negative year */
2279 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2280 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2281 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2287 broken
.tm_year
-= 1900;
2289 offset
= (char*)string
+ i
;
2291 /* Parse date and time without subseconds and offset */
2292 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2299 /* Get subseconds */
2300 if (*offset
== '.' ) {
2303 /* Copy first 6 digits, pad it with zeros.
2304 * XXX: It truncates longer number, no round.
2305 * Current server implementation uses only millisecond resolution. */
2306 /* TODO: isdigit() is locale sensitive */
2308 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
2310 subseconds
[i
] = *offset
;
2312 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
2313 subseconds
[i
] = '0';
2315 subseconds
[6] = '\0';
2317 /* Convert it into integer */
2318 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
2319 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
2320 (*time
)->tv_usec
== LONG_MAX
) {
2325 /* move to the zone offset delimiter or signal NULL*/
2326 delim
= strchr(offset
, '-');
2328 delim
= strchr(offset
, '+');
2330 delim
= strchr(offset
, 'Z');
2334 /* Get zone offset */
2335 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2336 * "" equals to "Z" and it means UTC zone. */
2337 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2338 * colon separator */
2339 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2340 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2344 if (*offset
== '+') {
2345 broken
.tm_hour
-= offset_hours
;
2346 broken
.tm_min
-= offset_minutes
;
2348 broken
.tm_hour
+= offset_hours
;
2349 broken
.tm_min
+= offset_minutes
;
2353 /* Convert to time_t */
2354 (*time
)->tv_sec
= _isds_timegm(&broken
);
2355 if ((*time
)->tv_sec
== (time_t) -1) {
2364 /* Convert unsigned int into isds_message_status.
2365 * @context is session context
2366 * @number is pointer to number value. NULL will be treated as invalid value.
2367 * @status is automatically reallocated status
2368 * @return IE_SUCCESS, or error code and free status */
2369 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2370 const unsigned long int *number
, isds_message_status
**status
) {
2371 if (!context
) return IE_INVALID_CONTEXT
;
2372 if (!status
) return IE_INVAL
;
2374 free(*status
); *status
= NULL
;
2375 if (!number
) return IE_INVAL
;
2377 if (*number
< 1 || *number
> 10) {
2378 isds_printf_message(context
, _("Invalid message status value: %lu"),
2383 *status
= malloc(sizeof(**status
));
2384 if (!*status
) return IE_NOMEM
;
2386 **status
= 1 << *number
;
2391 /* Convert event description string into isds_event members type and
2393 * @string is raw event description starting with event prefix
2394 * @event is structure where to store type and stripped description to
2395 * @return standard error code, unknown prefix is not classified as an error.
2397 static isds_error
eventstring2event(const xmlChar
*string
,
2398 struct isds_event
* event
) {
2399 const xmlChar
*known_prefixes
[] = {
2410 const isds_event_type types
[] = {
2411 EVENT_ENTERED_SYSTEM
,
2412 EVENT_ACCEPTED_BY_RECIPIENT
,
2413 EVENT_ACCEPTED_BY_FICTION
,
2414 EVENT_UNDELIVERABLE
,
2415 EVENT_COMMERCIAL_ACCEPTED
,
2417 EVENT_PRIMARY_LOGIN
,
2418 EVENT_ENTRUSTED_LOGIN
,
2424 if (!string
|| !event
) return IE_INVAL
;
2427 event
->type
= malloc(sizeof(*event
->type
));
2428 if (!(event
->type
)) return IE_NOMEM
;
2430 zfree(event
->description
);
2432 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2434 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2436 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2437 /* Prefix is known */
2438 *event
->type
= types
[index
];
2440 /* Strip prefix from description and spaces */
2441 /* TODO: Recognize all white spaces from UCS blank class and
2442 * operate on UTF-8 chars. */
2443 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2444 event
->description
= strdup((char *) (string
+ length
));
2445 if (!(event
->description
)) return IE_NOMEM
;
2451 /* Unknown event prefix.
2452 * XSD allows any string */
2453 char *string_locale
= _isds_utf82locale((char *) string
);
2454 isds_log(ILF_ISDS
, ILL_WARNING
,
2455 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2456 free(string_locale
);
2458 *event
->type
= EVENT_UKNOWN
;
2459 event
->description
= strdup((char *) string
);
2460 if (!(event
->description
)) return IE_NOMEM
;
2466 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2467 * and leave label */
2468 #define EXTRACT_STRING(element, string) { \
2469 xmlXPathFreeObject(result); \
2470 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2471 if (NULL == (result)) { \
2475 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2476 if (result->nodesetval->nodeNr > 1) { \
2477 isds_printf_message(context, _("Multiple %s element"), element); \
2481 (string) = (char *) \
2482 xmlXPathCastNodeSetToString(result->nodesetval); \
2483 if (NULL == (string)) { \
2490 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2492 char *string = NULL; \
2493 EXTRACT_STRING(element, string); \
2496 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2497 if (!(booleanPtr)) { \
2503 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2504 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2505 *(booleanPtr) = 1; \
2506 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2507 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2508 *(booleanPtr) = 0; \
2510 char *string_locale = _isds_utf82locale((char*)string); \
2511 isds_printf_message(context, \
2512 _("%s value is not valid boolean: %s"), \
2513 element, string_locale); \
2514 free(string_locale); \
2524 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2526 char *string = NULL; \
2527 EXTRACT_STRING(element, string); \
2529 if (NULL == string) { \
2530 isds_printf_message(context, _("%s element is empty"), element); \
2534 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2535 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2537 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2538 !xmlStrcmp((xmlChar *)string, BAD_CAST "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); \
2554 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2556 char *string = NULL; \
2557 EXTRACT_STRING(element, string); \
2562 number = strtol((char*)string, &endptr, 10); \
2564 if (*endptr != '\0') { \
2565 char *string_locale = _isds_utf82locale((char *)string); \
2566 isds_printf_message(context, \
2567 _("%s is not valid integer: %s"), \
2568 element, string_locale); \
2569 free(string_locale); \
2575 if (number == LONG_MIN || number == LONG_MAX) { \
2576 char *string_locale = _isds_utf82locale((char *)string); \
2577 isds_printf_message(context, \
2578 _("%s value out of range of long int: %s"), \
2579 element, string_locale); \
2580 free(string_locale); \
2586 free(string); string = NULL; \
2588 if (!(preallocated)) { \
2589 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2590 if (!(longintPtr)) { \
2595 *(longintPtr) = number; \
2599 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2601 char *string = NULL; \
2602 EXTRACT_STRING(element, string); \
2607 number = strtol((char*)string, &endptr, 10); \
2609 if (*endptr != '\0') { \
2610 char *string_locale = _isds_utf82locale((char *)string); \
2611 isds_printf_message(context, \
2612 _("%s is not valid integer: %s"), \
2613 element, string_locale); \
2614 free(string_locale); \
2620 if (number == LONG_MIN || number == LONG_MAX) { \
2621 char *string_locale = _isds_utf82locale((char *)string); \
2622 isds_printf_message(context, \
2623 _("%s value out of range of long int: %s"), \
2624 element, string_locale); \
2625 free(string_locale); \
2631 free(string); string = NULL; \
2633 isds_printf_message(context, \
2634 _("%s value is negative: %ld"), element, number); \
2639 if (!(preallocated)) { \
2640 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2641 if (!(ulongintPtr)) { \
2646 *(ulongintPtr) = number; \
2650 #define EXTRACT_DATE(element, tmPtr) { \
2651 char *string = NULL; \
2652 EXTRACT_STRING(element, string); \
2653 if (NULL != string) { \
2654 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2655 if (NULL == (tmPtr)) { \
2660 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2662 if (err == IE_NOTSUP) { \
2664 char *string_locale = _isds_utf82locale(string); \
2665 char *element_locale = _isds_utf82locale(element); \
2666 isds_printf_message(context, _("Invalid %s value: %s"), \
2667 element_locale, string_locale); \
2668 free(string_locale); \
2669 free(element_locale); \
2678 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2679 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2681 if ((required) && (!string)) { \
2682 char *attribute_locale = _isds_utf82locale(attribute); \
2683 char *element_locale = \
2684 _isds_utf82locale((char *)xpath_ctx->node->name); \
2685 isds_printf_message(context, \
2686 _("Could not extract required %s attribute value from " \
2687 "%s element"), attribute_locale, element_locale); \
2688 free(element_locale); \
2689 free(attribute_locale); \
2696 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2698 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2699 (xmlChar *) (string)); \
2701 isds_printf_message(context, \
2702 _("Could not add %s child to %s element"), \
2703 element, (parent)->name); \
2709 #define INSERT_STRING(parent, element, string) \
2710 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2712 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2714 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2715 else { INSERT_STRING(parent, element, "false"); } \
2718 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2721 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2723 INSERT_STRING(parent, element, NULL); \
2727 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2728 if ((longintPtr)) { \
2729 /* FIXME: locale sensitive */ \
2730 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2734 INSERT_STRING(parent, element, buffer) \
2735 free(buffer); (buffer) = NULL; \
2736 } else { INSERT_STRING(parent, element, NULL) } \
2739 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2740 if ((ulongintPtr)) { \
2741 /* FIXME: locale sensitive */ \
2742 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2746 INSERT_STRING(parent, element, buffer) \
2747 free(buffer); (buffer) = NULL; \
2748 } else { INSERT_STRING(parent, element, NULL) } \
2751 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2753 /* FIXME: locale sensitive */ \
2754 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2758 INSERT_STRING(parent, element, buffer) \
2759 free(buffer); (buffer) = NULL; \
2762 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2764 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2766 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2767 (xmlChar *) (string)); \
2768 if (!attribute_node) { \
2769 isds_printf_message(context, _("Could not add %s " \
2770 "attribute to %s element"), \
2771 (attribute), (parent)->name); \
2777 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2779 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2780 if (length > (maximum)) { \
2781 isds_printf_message(context, \
2782 ngettext("%s has more than %d characters", \
2783 "%s has more than %d characters", (maximum)), \
2784 (name), (maximum)); \
2788 if (length < (minimum)) { \
2789 isds_printf_message(context, \
2790 ngettext("%s has less than %d characters", \
2791 "%s has less than %d characters", (minimum)), \
2792 (name), (minimum)); \
2799 #define INSERT_ELEMENT(child, parent, element) \
2801 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2803 isds_printf_message(context, \
2804 _("Could not add %s child to %s element"), \
2805 (element), (parent)->name); \
2812 /* Find child element by name in given XPath context and switch context onto
2813 * it. The child must be uniq and must exist. Otherwise fails.
2814 * @context is ISDS context
2815 * @child is child element name
2816 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2817 * into it child. In error case, the @xpath_ctx keeps original value. */
2818 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2819 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2820 isds_error err
= IE_SUCCESS
;
2821 xmlXPathObjectPtr result
= NULL
;
2823 if (!context
) return IE_INVALID_CONTEXT
;
2824 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2827 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2834 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2835 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2836 char *child_locale
= _isds_utf82locale((char*) child
);
2837 isds_printf_message(context
,
2838 _("%s element does not contain %s child"),
2839 parent_locale
, child_locale
);
2841 free(parent_locale
);
2847 if (result
->nodesetval
->nodeNr
> 1) {
2848 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2849 char *child_locale
= _isds_utf82locale((char*) child
);
2850 isds_printf_message(context
,
2851 _("%s element contains multiple %s children"),
2852 parent_locale
, child_locale
);
2854 free(parent_locale
);
2859 /* Switch context */
2860 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2863 xmlXPathFreeObject(result
);
2870 /* Find and convert XSD:gPersonName group in current node into structure
2871 * @context is ISDS context
2872 * @personName is automatically reallocated person name structure. If no member
2873 * value is found, will be freed.
2874 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2876 * In case of error @personName will be freed. */
2877 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2878 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2879 isds_error err
= IE_SUCCESS
;
2880 xmlXPathObjectPtr result
= NULL
;
2882 if (!context
) return IE_INVALID_CONTEXT
;
2883 if (!personName
) return IE_INVAL
;
2884 isds_PersonName_free(personName
);
2885 if (!xpath_ctx
) return IE_INVAL
;
2888 *personName
= calloc(1, sizeof(**personName
));
2894 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2895 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2896 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2897 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2899 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2900 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2901 isds_PersonName_free(personName
);
2904 if (err
) isds_PersonName_free(personName
);
2905 xmlXPathFreeObject(result
);
2910 /* Find and convert XSD:gAddress group in current node into structure
2911 * @context is ISDS context
2912 * @address is automatically reallocated address structure. If no member
2913 * value is found, will be freed.
2914 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2916 * In case of error @address will be freed. */
2917 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2918 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2919 isds_error err
= IE_SUCCESS
;
2920 xmlXPathObjectPtr result
= NULL
;
2922 if (!context
) return IE_INVALID_CONTEXT
;
2923 if (!address
) return IE_INVAL
;
2924 isds_Address_free(address
);
2925 if (!xpath_ctx
) return IE_INVAL
;
2928 *address
= calloc(1, sizeof(**address
));
2934 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2935 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2936 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2937 EXTRACT_STRING("isds:adNumberInMunicipality",
2938 (*address
)->adNumberInMunicipality
);
2939 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2940 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2942 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2943 !(*address
)->adNumberInStreet
&&
2944 !(*address
)->adNumberInMunicipality
&&
2945 !(*address
)->adZipCode
&& !(*address
)->adState
)
2946 isds_Address_free(address
);
2949 if (err
) isds_Address_free(address
);
2950 xmlXPathFreeObject(result
);
2955 /* Find and convert isds:biDate element in current node into structure
2956 * @context is ISDS context
2957 * @biDate is automatically reallocated birth date structure. If no member
2958 * value is found, will be freed.
2959 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2961 * In case of error @biDate will be freed. */
2962 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2963 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2964 isds_error err
= IE_SUCCESS
;
2965 xmlXPathObjectPtr result
= NULL
;
2966 char *string
= NULL
;
2968 if (!context
) return IE_INVALID_CONTEXT
;
2969 if (!biDate
) return IE_INVAL
;
2971 if (!xpath_ctx
) return IE_INVAL
;
2973 EXTRACT_STRING("isds:biDate", string
);
2975 *biDate
= calloc(1, sizeof(**biDate
));
2980 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
2982 if (err
== IE_NOTSUP
) {
2984 char *string_locale
= _isds_utf82locale(string
);
2985 isds_printf_message(context
,
2986 _("Invalid isds:biDate value: %s"), string_locale
);
2987 free(string_locale
);
2994 if (err
) zfree(*biDate
);
2996 xmlXPathFreeObject(result
);
3001 /* Convert isds:dBOwnerInfo XML tree into structure
3002 * @context is ISDS context
3003 * @db_owner_info is automatically reallocated box owner info structure
3004 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3005 * In case of error @db_owner_info will be freed. */
3006 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3007 struct isds_DbOwnerInfo
**db_owner_info
,
3008 xmlXPathContextPtr xpath_ctx
) {
3009 isds_error err
= IE_SUCCESS
;
3010 xmlXPathObjectPtr result
= NULL
;
3011 char *string
= NULL
;
3013 if (!context
) return IE_INVALID_CONTEXT
;
3014 if (!db_owner_info
) return IE_INVAL
;
3015 isds_DbOwnerInfo_free(db_owner_info
);
3016 if (!xpath_ctx
) return IE_INVAL
;
3019 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3020 if (!*db_owner_info
) {
3025 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3027 EXTRACT_STRING("isds:dbType", string
);
3029 (*db_owner_info
)->dbType
=
3030 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3031 if (!(*db_owner_info
)->dbType
) {
3035 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3037 zfree((*db_owner_info
)->dbType
);
3038 if (err
== IE_ENUM
) {
3040 char *string_locale
= _isds_utf82locale(string
);
3041 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3043 free(string_locale
);
3050 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3052 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3054 if (err
) goto leave
;
3056 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3058 (*db_owner_info
)->birthInfo
=
3059 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3060 if (!(*db_owner_info
)->birthInfo
) {
3064 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3066 if (err
) goto leave
;
3067 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3068 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3069 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3070 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3071 !(*db_owner_info
)->birthInfo
->biCity
&&
3072 !(*db_owner_info
)->birthInfo
->biCounty
&&
3073 !(*db_owner_info
)->birthInfo
->biState
)
3074 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3076 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3077 if (err
) goto leave
;
3079 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3080 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3081 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3082 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3083 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3085 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3087 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3088 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3089 (*db_owner_info
)->dbOpenAddressing
);
3092 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3094 xmlXPathFreeObject(result
);
3099 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3100 * @context is session context
3101 * @owner is libisds structure with box description
3102 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3103 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3104 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3106 isds_error err
= IE_SUCCESS
;
3108 xmlChar
*string
= NULL
;
3110 if (!context
) return IE_INVALID_CONTEXT
;
3111 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3114 /* Build XSD:tDbOwnerInfo */
3115 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3116 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3119 if (owner
->dbType
) {
3120 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3122 isds_printf_message(context
, _("Invalid dbType value: %d"),
3127 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3129 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3130 if (owner
->personName
) {
3131 INSERT_STRING(db_owner_info
, "pnFirstName",
3132 owner
->personName
->pnFirstName
);
3133 INSERT_STRING(db_owner_info
, "pnMiddleName",
3134 owner
->personName
->pnMiddleName
);
3135 INSERT_STRING(db_owner_info
, "pnLastName",
3136 owner
->personName
->pnLastName
);
3137 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3138 owner
->personName
->pnLastNameAtBirth
);
3140 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3141 if (owner
->birthInfo
) {
3142 if (owner
->birthInfo
->biDate
) {
3143 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3144 INSERT_STRING(db_owner_info
, "biDate", string
);
3145 free(string
); string
= NULL
;
3147 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3148 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3149 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3151 if (owner
->address
) {
3152 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3153 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3154 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3155 owner
->address
->adNumberInStreet
);
3156 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3157 owner
->address
->adNumberInMunicipality
);
3158 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3159 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3161 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3162 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3163 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3165 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3166 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3168 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3169 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3171 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3173 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3174 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3175 owner
->dbOpenAddressing
);
3183 /* Convert XSD:tDbUserInfo XML tree into structure
3184 * @context is ISDS context
3185 * @db_user_info is automatically reallocated user info structure
3186 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3187 * In case of error @db_user_info will be freed. */
3188 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3189 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3190 isds_error err
= IE_SUCCESS
;
3191 xmlXPathObjectPtr result
= NULL
;
3192 char *string
= NULL
;
3194 if (!context
) return IE_INVALID_CONTEXT
;
3195 if (!db_user_info
) return IE_INVAL
;
3196 isds_DbUserInfo_free(db_user_info
);
3197 if (!xpath_ctx
) return IE_INVAL
;
3200 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3201 if (!*db_user_info
) {
3206 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3208 EXTRACT_STRING("isds:userType", string
);
3210 (*db_user_info
)->userType
=
3211 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3212 if (!(*db_user_info
)->userType
) {
3216 err
= string2isds_UserType((xmlChar
*)string
,
3217 (*db_user_info
)->userType
);
3219 zfree((*db_user_info
)->userType
);
3220 if (err
== IE_ENUM
) {
3222 char *string_locale
= _isds_utf82locale(string
);
3223 isds_printf_message(context
,
3224 _("Unknown isds:userType value: %s"), string_locale
);
3225 free(string_locale
);
3232 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3234 (*db_user_info
)->personName
=
3235 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3236 if (!(*db_user_info
)->personName
) {
3241 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3243 if (err
) goto leave
;
3245 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3246 if (err
) goto leave
;
3248 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3249 if (err
) goto leave
;
3251 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3252 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3254 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3255 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3256 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3258 /* ???: Default value is "CZ" according specification. Should we provide
3260 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3263 if (err
) isds_DbUserInfo_free(db_user_info
);
3265 xmlXPathFreeObject(result
);
3270 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3271 * @context is session context
3272 * @user is libisds structure with user description
3273 * @db_user_info is XML element of XSD:tDbUserInfo */
3274 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3275 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3277 isds_error err
= IE_SUCCESS
;
3279 xmlChar
*string
= NULL
;
3281 if (!context
) return IE_INVALID_CONTEXT
;
3282 if (!user
|| !db_user_info
) return IE_INVAL
;
3284 /* Build XSD:tDbUserInfo */
3285 if (user
->personName
) {
3286 INSERT_STRING(db_user_info
, "pnFirstName",
3287 user
->personName
->pnFirstName
);
3288 INSERT_STRING(db_user_info
, "pnMiddleName",
3289 user
->personName
->pnMiddleName
);
3290 INSERT_STRING(db_user_info
, "pnLastName",
3291 user
->personName
->pnLastName
);
3292 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3293 user
->personName
->pnLastNameAtBirth
);
3295 if (user
->address
) {
3296 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3297 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3298 INSERT_STRING(db_user_info
, "adNumberInStreet",
3299 user
->address
->adNumberInStreet
);
3300 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3301 user
->address
->adNumberInMunicipality
);
3302 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3303 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3306 if (!tm2datestring(user
->biDate
, &string
))
3307 INSERT_STRING(db_user_info
, "biDate", string
);
3310 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3311 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3314 if (user
->userType
) {
3315 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3317 isds_printf_message(context
, _("Invalid userType value: %d"),
3322 INSERT_STRING(db_user_info
, "userType", type_string
);
3325 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3326 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3327 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3328 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3329 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3330 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3331 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3332 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3333 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3341 /* Convert XSD:tPDZRec XML tree into structure
3342 * @context is ISDS context
3343 * @permission is automatically reallocated commercial permission structure
3344 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3345 * In case of error @permission will be freed. */
3346 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3347 struct isds_commercial_permission
**permission
,
3348 xmlXPathContextPtr xpath_ctx
) {
3349 isds_error err
= IE_SUCCESS
;
3350 xmlXPathObjectPtr result
= NULL
;
3351 char *string
= NULL
;
3353 if (!context
) return IE_INVALID_CONTEXT
;
3354 if (!permission
) return IE_INVAL
;
3355 isds_commercial_permission_free(permission
);
3356 if (!xpath_ctx
) return IE_INVAL
;
3359 *permission
= calloc(1, sizeof(**permission
));
3365 EXTRACT_STRING("isds:PDZType", string
);
3367 err
= string2isds_payment_type((xmlChar
*)string
,
3368 &(*permission
)->type
);
3370 if (err
== IE_ENUM
) {
3372 char *string_locale
= _isds_utf82locale(string
);
3373 isds_printf_message(context
,
3374 _("Unknown isds:PDZType value: %s"), string_locale
);
3375 free(string_locale
);
3382 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3383 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3385 EXTRACT_STRING("isds:PDZExpire", string
);
3387 err
= timestring2timeval((xmlChar
*) string
,
3388 &((*permission
)->expiration
));
3390 char *string_locale
= _isds_utf82locale(string
);
3391 if (err
== IE_DATE
) err
= IE_ISDS
;
3392 isds_printf_message(context
,
3393 _("Could not convert PDZExpire as ISO time: %s"),
3395 free(string_locale
);
3401 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3402 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3405 if (err
) isds_commercial_permission_free(permission
);
3407 xmlXPathFreeObject(result
);
3412 /* Convert XSD:tCiRecord XML tree into structure
3413 * @context is ISDS context
3414 * @event is automatically reallocated commercial credit event structure
3415 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3416 * In case of error @event will be freed. */
3417 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3418 struct isds_credit_event
**event
,
3419 xmlXPathContextPtr xpath_ctx
) {
3420 isds_error err
= IE_SUCCESS
;
3421 xmlXPathObjectPtr result
= NULL
;
3422 char *string
= NULL
;
3423 long int *number_ptr
;
3425 if (!context
) return IE_INVALID_CONTEXT
;
3426 if (!event
) return IE_INVAL
;
3427 isds_credit_event_free(event
);
3428 if (!xpath_ctx
) return IE_INVAL
;
3431 *event
= calloc(1, sizeof(**event
));
3437 EXTRACT_STRING("isds:ciEventTime", string
);
3439 err
= timestring2timeval((xmlChar
*) string
,
3442 char *string_locale
= _isds_utf82locale(string
);
3443 if (err
== IE_DATE
) err
= IE_ISDS
;
3444 isds_printf_message(context
,
3445 _("Could not convert ciEventTime as ISO time: %s"),
3447 free(string_locale
);
3453 EXTRACT_STRING("isds:ciEventType", string
);
3455 err
= string2isds_credit_event_type((xmlChar
*)string
,
3458 if (err
== IE_ENUM
) {
3460 char *string_locale
= _isds_utf82locale(string
);
3461 isds_printf_message(context
,
3462 _("Unknown isds:ciEventType value: %s"), string_locale
);
3463 free(string_locale
);
3470 number_ptr
= &((*event
)->credit_change
);
3471 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3472 number_ptr
= &(*event
)->new_credit
;
3473 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3475 switch((*event
)->type
) {
3476 case ISDS_CREDIT_CHARGED
:
3477 EXTRACT_STRING("isds:ciTransID",
3478 (*event
)->details
.charged
.transaction
);
3480 case ISDS_CREDIT_DISCHARGED
:
3481 EXTRACT_STRING("isds:ciTransID",
3482 (*event
)->details
.discharged
.transaction
);
3484 case ISDS_CREDIT_MESSAGE_SENT
:
3485 EXTRACT_STRING("isds:ciRecipientID",
3486 (*event
)->details
.message_sent
.recipient
);
3487 EXTRACT_STRING("isds:ciPDZID",
3488 (*event
)->details
.message_sent
.message_id
);
3490 case ISDS_CREDIT_STORAGE_SET
:
3491 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3492 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3493 EXTRACT_DATE("isds:ciNewFrom",
3494 (*event
)->details
.storage_set
.new_valid_from
);
3495 EXTRACT_DATE("isds:ciNewTo",
3496 (*event
)->details
.storage_set
.new_valid_to
);
3497 EXTRACT_LONGINT("isds:ciOldCapacity",
3498 (*event
)->details
.storage_set
.old_capacity
, 0);
3499 EXTRACT_DATE("isds:ciOldFrom",
3500 (*event
)->details
.storage_set
.old_valid_from
);
3501 EXTRACT_DATE("isds:ciOldTo",
3502 (*event
)->details
.storage_set
.old_valid_to
);
3503 EXTRACT_STRING("isds:ciDoneBy",
3504 (*event
)->details
.storage_set
.initiator
);
3506 case ISDS_CREDIT_EXPIRED
:
3511 if (err
) isds_credit_event_free(event
);
3513 xmlXPathFreeObject(result
);
3518 #endif /* HAVE_LIBCURL */
3521 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3522 * isds_envelope structure. The envelope is automatically allocated but not
3523 * reallocated. The date are just appended into envelope structure.
3524 * @context is ISDS context
3525 * @envelope is automatically allocated message envelope structure
3526 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3527 * In case of error @envelope will be freed. */
3528 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3529 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3530 isds_error err
= IE_SUCCESS
;
3531 xmlXPathObjectPtr result
= NULL
;
3533 if (!context
) return IE_INVALID_CONTEXT
;
3534 if (!envelope
) return IE_INVAL
;
3535 if (!xpath_ctx
) return IE_INVAL
;
3539 /* Allocate envelope */
3540 *envelope
= calloc(1, sizeof(**envelope
));
3546 /* Else free former data */
3547 zfree((*envelope
)->dmSenderOrgUnit
);
3548 zfree((*envelope
)->dmSenderOrgUnitNum
);
3549 zfree((*envelope
)->dbIDRecipient
);
3550 zfree((*envelope
)->dmRecipientOrgUnit
);
3551 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3552 zfree((*envelope
)->dmToHands
);
3553 zfree((*envelope
)->dmAnnotation
);
3554 zfree((*envelope
)->dmRecipientRefNumber
);
3555 zfree((*envelope
)->dmSenderRefNumber
);
3556 zfree((*envelope
)->dmRecipientIdent
);
3557 zfree((*envelope
)->dmSenderIdent
);
3558 zfree((*envelope
)->dmLegalTitleLaw
);
3559 zfree((*envelope
)->dmLegalTitleYear
);
3560 zfree((*envelope
)->dmLegalTitleSect
);
3561 zfree((*envelope
)->dmLegalTitlePar
);
3562 zfree((*envelope
)->dmLegalTitlePoint
);
3563 zfree((*envelope
)->dmPersonalDelivery
);
3564 zfree((*envelope
)->dmAllowSubstDelivery
);
3567 /* Extract envelope elements added by sender or ISDS
3568 * (XSD: gMessageEnvelopeSub type) */
3569 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3570 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3571 (*envelope
)->dmSenderOrgUnitNum
, 0);
3572 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3573 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3574 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3575 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3576 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3577 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3578 EXTRACT_STRING("isds:dmRecipientRefNumber",
3579 (*envelope
)->dmRecipientRefNumber
);
3580 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3581 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3582 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3584 /* Extract envelope elements regarding law reference */
3585 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3586 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3587 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3588 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3589 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3591 /* Extract envelope other elements */
3592 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3593 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3594 (*envelope
)->dmAllowSubstDelivery
);
3597 if (err
) isds_envelope_free(envelope
);
3598 xmlXPathFreeObject(result
);
3604 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3605 * isds_envelope structure. The envelope is automatically allocated but not
3606 * reallocated. The date are just appended into envelope structure.
3607 * @context is ISDS context
3608 * @envelope is automatically allocated message envelope structure
3609 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3610 * In case of error @envelope will be freed. */
3611 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3612 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3613 isds_error err
= IE_SUCCESS
;
3614 xmlXPathObjectPtr result
= NULL
;
3616 if (!context
) return IE_INVALID_CONTEXT
;
3617 if (!envelope
) return IE_INVAL
;
3618 if (!xpath_ctx
) return IE_INVAL
;
3622 /* Allocate envelope */
3623 *envelope
= calloc(1, sizeof(**envelope
));
3629 /* Else free former data */
3630 zfree((*envelope
)->dmID
);
3631 zfree((*envelope
)->dbIDSender
);
3632 zfree((*envelope
)->dmSender
);
3633 zfree((*envelope
)->dmSenderAddress
);
3634 zfree((*envelope
)->dmSenderType
);
3635 zfree((*envelope
)->dmRecipient
);
3636 zfree((*envelope
)->dmRecipientAddress
);
3637 zfree((*envelope
)->dmAmbiguousRecipient
);
3640 /* Extract envelope elements added by ISDS
3641 * (XSD: gMessageEnvelope type) */
3642 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3643 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3644 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3645 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3646 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3647 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3648 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3649 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3650 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3651 (*envelope
)->dmAmbiguousRecipient
);
3653 /* Extract envelope elements added by sender and ISDS
3654 * (XSD: gMessageEnvelope type) */
3655 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3656 if (err
) goto leave
;
3659 if (err
) isds_envelope_free(envelope
);
3660 xmlXPathFreeObject(result
);
3665 /* Convert other envelope elements from XML tree into isds_envelope structure:
3666 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3667 * The envelope is automatically allocated but not reallocated.
3668 * The data are just appended into envelope structure.
3669 * @context is ISDS context
3670 * @envelope is automatically allocated message envelope structure
3671 * @xpath_ctx is XPath context with current node as parent desired elements
3672 * In case of error @envelope will be freed. */
3673 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3674 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3675 isds_error err
= IE_SUCCESS
;
3676 xmlXPathObjectPtr result
= NULL
;
3677 char *string
= NULL
;
3678 unsigned long int *unumber
= NULL
;
3680 if (!context
) return IE_INVALID_CONTEXT
;
3681 if (!envelope
) return IE_INVAL
;
3682 if (!xpath_ctx
) return IE_INVAL
;
3687 *envelope
= calloc(1, sizeof(**envelope
));
3694 zfree((*envelope
)->dmMessageStatus
);
3695 zfree((*envelope
)->dmAttachmentSize
);
3696 zfree((*envelope
)->dmDeliveryTime
);
3697 zfree((*envelope
)->dmAcceptanceTime
);
3701 /* dmMessageStatus element is mandatory */
3702 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3704 isds_log_message(context
,
3705 _("Missing mandatory sisds:dmMessageStatus integer"));
3709 err
= uint2isds_message_status(context
, unumber
,
3710 &((*envelope
)->dmMessageStatus
));
3712 if (err
== IE_ENUM
) err
= IE_ISDS
;
3715 free(unumber
); unumber
= NULL
;
3717 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3720 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3722 err
= timestring2timeval((xmlChar
*) string
,
3723 &((*envelope
)->dmDeliveryTime
));
3725 char *string_locale
= _isds_utf82locale(string
);
3726 if (err
== IE_DATE
) err
= IE_ISDS
;
3727 isds_printf_message(context
,
3728 _("Could not convert dmDeliveryTime as ISO time: %s"),
3730 free(string_locale
);
3736 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3738 err
= timestring2timeval((xmlChar
*) string
,
3739 &((*envelope
)->dmAcceptanceTime
));
3741 char *string_locale
= _isds_utf82locale(string
);
3742 if (err
== IE_DATE
) err
= IE_ISDS
;
3743 isds_printf_message(context
,
3744 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3746 free(string_locale
);
3753 if (err
) isds_envelope_free(envelope
);
3756 xmlXPathFreeObject(result
);
3761 /* Convert message type attribute of current element into isds_envelope
3763 * TODO: This function can be incorporated into append_status_size_times() as
3764 * they are called always together.
3765 * The envelope is automatically allocated but not reallocated.
3766 * The data are just appended into envelope structure.
3767 * @context is ISDS context
3768 * @envelope is automatically allocated message envelope structure
3769 * @xpath_ctx is XPath context with current node as parent of attribute
3770 * carrying message type
3771 * In case of error @envelope will be freed. */
3772 static isds_error
append_message_type(struct isds_ctx
*context
,
3773 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3774 isds_error err
= IE_SUCCESS
;
3776 if (!context
) return IE_INVALID_CONTEXT
;
3777 if (!envelope
) return IE_INVAL
;
3778 if (!xpath_ctx
) return IE_INVAL
;
3783 *envelope
= calloc(1, sizeof(**envelope
));
3790 zfree((*envelope
)->dmType
);
3794 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3796 if (!(*envelope
)->dmType
) {
3797 /* Use default value */
3798 (*envelope
)->dmType
= strdup("V");
3799 if (!(*envelope
)->dmType
) {
3803 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3804 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3805 isds_printf_message(context
,
3806 _("Message type in dmType attribute is not 1 character long: "
3815 if (err
) isds_envelope_free(envelope
);
3821 /* Convert dmType isds_envelope member into XML attribute and append it to
3823 * @context is ISDS context
3824 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3825 * @dm_envelope is XML element the resulting attribute will be appended to.
3826 * @return error code, in case of error context' message is filled. */
3827 static isds_error
insert_message_type(struct isds_ctx
*context
,
3828 const char *type
, xmlNodePtr dm_envelope
) {
3829 isds_error err
= IE_SUCCESS
;
3830 xmlAttrPtr attribute_node
;
3832 if (!context
) return IE_INVALID_CONTEXT
;
3833 if (!dm_envelope
) return IE_INVAL
;
3835 /* Insert optional message type */
3837 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3838 char *type_locale
= _isds_utf82locale(type
);
3839 isds_printf_message(context
,
3840 _("Message type in envelope is not 1 character long: %s"),
3846 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3852 #endif /* HAVE_LIBCURL */
3855 /* Extract message document into reallocated document structure
3856 * @context is ISDS context
3857 * @document is automatically reallocated message documents structure
3858 * @xpath_ctx is XPath context with current node as isds:dmFile
3859 * In case of error @document will be freed. */
3860 static isds_error
extract_document(struct isds_ctx
*context
,
3861 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3862 isds_error err
= IE_SUCCESS
;
3863 xmlXPathObjectPtr result
= NULL
;
3864 xmlNodePtr file_node
;
3865 char *string
= NULL
;
3867 if (!context
) return IE_INVALID_CONTEXT
;
3868 if (!document
) return IE_INVAL
;
3869 isds_document_free(document
);
3870 if (!xpath_ctx
) return IE_INVAL
;
3871 file_node
= xpath_ctx
->node
;
3873 *document
= calloc(1, sizeof(**document
));
3879 /* Extract document meta data */
3880 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3881 if (context
->normalize_mime_type
) {
3882 const char *normalized_type
=
3883 isds_normalize_mime_type((*document
)->dmMimeType
);
3884 if (NULL
!= normalized_type
&&
3885 normalized_type
!= (*document
)->dmMimeType
) {
3886 char *new_type
= strdup(normalized_type
);
3887 if (NULL
== new_type
) {
3888 isds_printf_message(context
,
3889 _("Not enough memory to normalize document MIME type"));
3893 free((*document
)->dmMimeType
);
3894 (*document
)->dmMimeType
= new_type
;
3898 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3899 err
= string2isds_FileMetaType((xmlChar
*)string
,
3900 &((*document
)->dmFileMetaType
));
3902 char *meta_type_locale
= _isds_utf82locale(string
);
3903 isds_printf_message(context
,
3904 _("Document has invalid dmFileMetaType attribute value: %s"),
3906 free(meta_type_locale
);
3912 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3913 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3914 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3915 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3918 /* Extract document data.
3919 * Base64 encoded blob or XML subtree must be presented. */
3921 /* Check for dmEncodedContent */
3922 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3929 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3930 /* Here we have Base64 blob */
3931 (*document
)->is_xml
= 0;
3933 if (result
->nodesetval
->nodeNr
> 1) {
3934 isds_printf_message(context
,
3935 _("Document has more dmEncodedContent elements"));
3940 xmlXPathFreeObject(result
); result
= NULL
;
3941 EXTRACT_STRING("isds:dmEncodedContent", string
);
3943 /* Decode non-empty document */
3944 if (string
&& string
[0] != '\0') {
3945 (*document
)->data_length
=
3946 _isds_b64decode(string
, &((*document
)->data
));
3947 if ((*document
)->data_length
== (size_t) -1) {
3948 isds_printf_message(context
,
3949 _("Error while Base64-decoding document content"));
3955 /* No Base64 blob, try XML document */
3956 xmlXPathFreeObject(result
); result
= NULL
;
3957 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3964 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3965 /* Here we have XML document */
3966 (*document
)->is_xml
= 1;
3968 if (result
->nodesetval
->nodeNr
> 1) {
3969 isds_printf_message(context
,
3970 _("Document has more dmXMLContent elements"));
3975 /* XXX: We cannot serialize the content simply because:
3976 * - XML document may point out of its scope (e.g. to message
3978 * - isds:dmXMLContent can contain more elements, no element,
3980 * - it's not the XML way
3981 * Thus we provide the only right solution: XML DOM. Let's
3982 * application to cope with this hot potato :) */
3983 (*document
)->xml_node_list
=
3984 result
->nodesetval
->nodeTab
[0]->children
;
3986 /* No base64 blob, nor XML document */
3987 isds_printf_message(context
,
3988 _("Document has no dmEncodedContent, nor dmXMLContent "
3997 if (err
) isds_document_free(document
);
3999 xmlXPathFreeObject(result
);
4000 xpath_ctx
->node
= file_node
;
4006 /* Extract message documents into reallocated list of documents
4007 * @context is ISDS context
4008 * @documents is automatically reallocated message documents list structure
4009 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4010 * In case of error @documents will be freed. */
4011 static isds_error
extract_documents(struct isds_ctx
*context
,
4012 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4013 isds_error err
= IE_SUCCESS
;
4014 xmlXPathObjectPtr result
= NULL
;
4015 xmlNodePtr files_node
;
4016 struct isds_list
*document
, *prev_document
= NULL
;
4018 if (!context
) return IE_INVALID_CONTEXT
;
4019 if (!documents
) return IE_INVAL
;
4020 isds_list_free(documents
);
4021 if (!xpath_ctx
) return IE_INVAL
;
4022 files_node
= xpath_ctx
->node
;
4024 /* Find documents */
4025 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4032 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4033 isds_printf_message(context
,
4034 _("Message does not contain any document"));
4040 /* Iterate over documents */
4041 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4043 /* Allocate and append list item */
4044 document
= calloc(1, sizeof(*document
));
4049 document
->destructor
= (void (*)(void **))isds_document_free
;
4050 if (i
== 0) *documents
= document
;
4051 else prev_document
->next
= document
;
4052 prev_document
= document
;
4054 /* Extract document */
4055 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4056 err
= extract_document(context
,
4057 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4058 if (err
) goto leave
;
4063 if (err
) isds_list_free(documents
);
4064 xmlXPathFreeObject(result
);
4065 xpath_ctx
->node
= files_node
;
4071 /* Convert isds:dmRecord XML tree into structure
4072 * @context is ISDS context
4073 * @envelope is automatically reallocated message envelope structure
4074 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4075 * In case of error @envelope will be freed. */
4076 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4077 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4078 isds_error err
= IE_SUCCESS
;
4079 xmlXPathObjectPtr result
= NULL
;
4081 if (!context
) return IE_INVALID_CONTEXT
;
4082 if (!envelope
) return IE_INVAL
;
4083 isds_envelope_free(envelope
);
4084 if (!xpath_ctx
) return IE_INVAL
;
4087 *envelope
= calloc(1, sizeof(**envelope
));
4094 /* Extract tRecord data */
4095 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4097 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4098 * dmAcceptanceTime. */
4099 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4100 if (err
) goto leave
;
4102 /* Extract envelope elements added by sender and ISDS
4103 * (XSD: gMessageEnvelope type) */
4104 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4105 if (err
) goto leave
;
4107 /* Get message type */
4108 err
= append_message_type(context
, envelope
, xpath_ctx
);
4109 if (err
) goto leave
;
4113 if (err
) isds_envelope_free(envelope
);
4114 xmlXPathFreeObject(result
);
4119 /* Convert XSD:tStateChangesRecord type XML tree into structure
4120 * @context is ISDS context
4121 * @changed_status is automatically reallocated message state change structure
4122 * @xpath_ctx is XPath context with current node as element of
4123 * XSD:tStateChangesRecord type
4124 * In case of error @changed_status will be freed. */
4125 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4126 struct isds_message_status_change
**changed_status
,
4127 xmlXPathContextPtr xpath_ctx
) {
4128 isds_error err
= IE_SUCCESS
;
4129 xmlXPathObjectPtr result
= NULL
;
4130 unsigned long int *unumber
= NULL
;
4131 char *string
= NULL
;
4133 if (!context
) return IE_INVALID_CONTEXT
;
4134 if (!changed_status
) return IE_INVAL
;
4135 isds_message_status_change_free(changed_status
);
4136 if (!xpath_ctx
) return IE_INVAL
;
4139 *changed_status
= calloc(1, sizeof(**changed_status
));
4140 if (!*changed_status
) {
4146 /* Extract tGetStateChangesInput data */
4147 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4149 /* dmEventTime is mandatory */
4150 EXTRACT_STRING("isds:dmEventTime", string
);
4152 err
= timestring2timeval((xmlChar
*) string
,
4153 &((*changed_status
)->time
));
4155 char *string_locale
= _isds_utf82locale(string
);
4156 if (err
== IE_DATE
) err
= IE_ISDS
;
4157 isds_printf_message(context
,
4158 _("Could not convert dmEventTime as ISO time: %s"),
4160 free(string_locale
);
4166 /* dmMessageStatus element is mandatory */
4167 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4169 isds_log_message(context
,
4170 _("Missing mandatory isds:dmMessageStatus integer"));
4174 err
= uint2isds_message_status(context
, unumber
,
4175 &((*changed_status
)->dmMessageStatus
));
4177 if (err
== IE_ENUM
) err
= IE_ISDS
;
4186 if (err
) isds_message_status_change_free(changed_status
);
4187 xmlXPathFreeObject(result
);
4190 #endif /* HAVE_LIBCURL */
4193 /* Find and convert isds:dmHash XML tree into structure
4194 * @context is ISDS context
4195 * @envelope is automatically reallocated message hash structure
4196 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4197 * In case of error @hash will be freed. */
4198 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4199 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4200 isds_error err
= IE_SUCCESS
;
4201 xmlNodePtr old_ctx_node
;
4202 xmlXPathObjectPtr result
= NULL
;
4203 char *string
= NULL
;
4205 if (!context
) return IE_INVALID_CONTEXT
;
4206 if (!hash
) return IE_INVAL
;
4207 isds_hash_free(hash
);
4208 if (!xpath_ctx
) return IE_INVAL
;
4210 old_ctx_node
= xpath_ctx
->node
;
4212 *hash
= calloc(1, sizeof(**hash
));
4219 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4220 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4229 /* Get hash algorithm */
4230 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4231 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4233 if (err
== IE_ENUM
) {
4234 char *string_locale
= _isds_utf82locale(string
);
4235 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4237 free(string_locale
);
4243 /* Get hash value */
4244 EXTRACT_STRING(".", string
);
4246 isds_printf_message(context
,
4247 _("sisds:dmHash element is missing hash value"));
4251 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4252 if ((*hash
)->length
== (size_t) -1) {
4253 isds_printf_message(context
,
4254 _("Error while Base64-decoding hash value"));
4260 if (err
) isds_hash_free(hash
);
4262 xmlXPathFreeObject(result
);
4263 xpath_ctx
->node
= old_ctx_node
;
4268 /* Find and append isds:dmQTimestamp XML tree into envelope.
4269 * Because one service is allowed to miss time-stamp content, and we think
4270 * other could too (flaw in specification), this function is deliberated and
4271 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4272 * @context is ISDS context
4273 * @envelope is automatically allocated envelope structure
4274 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4276 * In case of error @envelope will be freed. */
4277 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4278 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4279 isds_error err
= IE_SUCCESS
;
4280 xmlXPathObjectPtr result
= NULL
;
4281 char *string
= NULL
;
4283 if (!context
) return IE_INVALID_CONTEXT
;
4284 if (!envelope
) return IE_INVAL
;
4286 isds_envelope_free(envelope
);
4291 *envelope
= calloc(1, sizeof(**envelope
));
4297 zfree((*envelope
)->timestamp
);
4298 (*envelope
)->timestamp_length
= 0;
4301 /* Get dmQTimestamp */
4302 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4304 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4307 (*envelope
)->timestamp_length
=
4308 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4309 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4310 isds_printf_message(context
,
4311 _("Error while Base64-decoding time stamp value"));
4317 if (err
) isds_envelope_free(envelope
);
4319 xmlXPathFreeObject(result
);
4324 /* Convert XSD tReturnedMessage XML tree into message structure.
4325 * It does not store serialized XML tree into message->raw.
4326 * It does store (pointer to) parsed XML tree into message->xml if needed.
4327 * @context is ISDS context
4328 * @include_documents Use true if documents must be extracted
4329 * (tReturnedMessage XSD type), use false if documents shall be omitted
4330 * (tReturnedMessageEnvelope).
4331 * @message is automatically reallocated message structure
4332 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4334 * In case of error @message will be freed. */
4335 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4336 const _Bool include_documents
, struct isds_message
**message
,
4337 xmlXPathContextPtr xpath_ctx
) {
4338 isds_error err
= IE_SUCCESS
;
4339 xmlNodePtr message_node
;
4341 if (!context
) return IE_INVALID_CONTEXT
;
4342 if (!message
) return IE_INVAL
;
4343 isds_message_free(message
);
4344 if (!xpath_ctx
) return IE_INVAL
;
4347 *message
= calloc(1, sizeof(**message
));
4353 /* Save message XPATH context node */
4354 message_node
= xpath_ctx
->node
;
4358 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4359 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4360 if (err
) { err
= IE_ERROR
; goto leave
; }
4361 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4362 if (err
) goto leave
;
4364 if (include_documents
) {
4365 struct isds_list
*item
;
4367 /* Extract dmFiles */
4368 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4370 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4371 err
= IE_ISDS
; goto leave
;
4373 if (err
) { err
= IE_ERROR
; goto leave
; }
4374 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4375 if (err
) goto leave
;
4377 /* Store xmlDoc of this message if needed */
4378 /* Only if we got a XML document in all the documents. */
4379 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4380 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4381 (*message
)->xml
= xpath_ctx
->doc
;
4388 /* Restore context to message */
4389 xpath_ctx
->node
= message_node
;
4391 /* Extract dmHash */
4392 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4394 if (err
) goto leave
;
4396 /* Extract dmQTimestamp, */
4397 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4399 if (err
) goto leave
;
4401 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4402 * dmAcceptanceTime. */
4403 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4404 if (err
) goto leave
;
4406 /* Get message type */
4407 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4408 if (err
) goto leave
;
4411 if (err
) isds_message_free(message
);
4416 /* Extract message event into reallocated isds_event structure
4417 * @context is ISDS context
4418 * @event is automatically reallocated message event structure
4419 * @xpath_ctx is XPath context with current node as isds:dmEvent
4420 * In case of error @event will be freed. */
4421 static isds_error
extract_event(struct isds_ctx
*context
,
4422 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4423 isds_error err
= IE_SUCCESS
;
4424 xmlXPathObjectPtr result
= NULL
;
4425 xmlNodePtr event_node
;
4426 char *string
= NULL
;
4428 if (!context
) return IE_INVALID_CONTEXT
;
4429 if (!event
) return IE_INVAL
;
4430 isds_event_free(event
);
4431 if (!xpath_ctx
) return IE_INVAL
;
4432 event_node
= xpath_ctx
->node
;
4434 *event
= calloc(1, sizeof(**event
));
4440 /* Extract event data.
4441 * All elements are optional according XSD. That's funny. */
4442 EXTRACT_STRING("sisds:dmEventTime", string
);
4444 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4446 char *string_locale
= _isds_utf82locale(string
);
4447 if (err
== IE_DATE
) err
= IE_ISDS
;
4448 isds_printf_message(context
,
4449 _("Could not convert dmEventTime as ISO time: %s"),
4451 free(string_locale
);
4457 /* dmEventDescr element has prefix and the rest */
4458 EXTRACT_STRING("sisds:dmEventDescr", string
);
4460 err
= eventstring2event((xmlChar
*) string
, *event
);
4461 if (err
) goto leave
;
4466 if (err
) isds_event_free(event
);
4468 xmlXPathFreeObject(result
);
4469 xpath_ctx
->node
= event_node
;
4474 /* Convert element of XSD tEventsArray type from XML tree into
4475 * isds_list of isds_event's structure. The list is automatically reallocated.
4476 * @context is ISDS context
4477 * @events is automatically reallocated list of event structures
4478 * @xpath_ctx is XPath context with current node as tEventsArray
4479 * In case of error @events will be freed. */
4480 static isds_error
extract_events(struct isds_ctx
*context
,
4481 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4482 isds_error err
= IE_SUCCESS
;
4483 xmlXPathObjectPtr result
= NULL
;
4484 xmlNodePtr events_node
;
4485 struct isds_list
*event
, *prev_event
= NULL
;
4487 if (!context
) return IE_INVALID_CONTEXT
;
4488 if (!events
) return IE_INVAL
;
4489 if (!xpath_ctx
) return IE_INVAL
;
4490 events_node
= xpath_ctx
->node
;
4493 isds_list_free(events
);
4496 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4503 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4504 isds_printf_message(context
,
4505 _("Delivery info does not contain any event"));
4511 /* Iterate over events */
4512 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4514 /* Allocate and append list item */
4515 event
= calloc(1, sizeof(*event
));
4520 event
->destructor
= (void (*)(void **))isds_event_free
;
4521 if (i
== 0) *events
= event
;
4522 else prev_event
->next
= event
;
4526 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4527 err
= extract_event(context
,
4528 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4529 if (err
) goto leave
;
4534 if (err
) isds_list_free(events
);
4535 xmlXPathFreeObject(result
);
4536 xpath_ctx
->node
= events_node
;
4542 /* Insert Base64 encoded data as element with text child.
4543 * @context is session context
4544 * @parent is XML node to append @element with @data as child
4545 * @ns is XML namespace of @element, use NULL to inherit from @parent
4546 * @element is UTF-8 encoded name of new element
4547 * @data is bit stream to encode into @element
4548 * @length is size of @data in bytes
4549 * @return standard error code and fill long error message if needed */
4550 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4551 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4552 const void *data
, size_t length
) {
4553 isds_error err
= IE_SUCCESS
;
4556 if (!context
) return IE_INVALID_CONTEXT
;
4557 if (!data
&& length
> 0) return IE_INVAL
;
4558 if (!parent
|| !element
) return IE_INVAL
;
4560 xmlChar
*base64data
= NULL
;
4561 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4563 isds_printf_message(context
,
4564 ngettext("Not enough memory to encode %zd byte into Base64",
4565 "Not enough memory to encode %zd bytes into Base64",
4571 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4579 /* Convert isds_document structure into XML tree and append to dmFiles node.
4580 * @context is session context
4581 * @document is ISDS document
4582 * @dm_files is XML element the resulting tree will be appended to as a child.
4583 * @return error code, in case of error context' message is filled. */
4584 static isds_error
insert_document(struct isds_ctx
*context
,
4585 struct isds_document
*document
, xmlNodePtr dm_files
) {
4586 isds_error err
= IE_SUCCESS
;
4587 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4588 xmlAttrPtr attribute_node
;
4590 if (!context
) return IE_INVALID_CONTEXT
;
4591 if (!document
|| !dm_files
) return IE_INVAL
;
4593 /* Allocate new dmFile */
4594 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4596 isds_printf_message(context
, _("Could not allocate main dmFile"));
4600 /* Append the new dmFile.
4601 * XXX: Main document must go first */
4602 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4603 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4605 file
= xmlAddChild(dm_files
, new_file
);
4608 xmlFreeNode(new_file
); new_file
= NULL
;
4609 isds_printf_message(context
, _("Could not add dmFile child to "
4610 "%s element"), dm_files
->name
);
4615 /* @dmMimeType is required */
4616 if (!document
->dmMimeType
) {
4617 isds_log_message(context
,
4618 _("Document is missing mandatory MIME type definition"));
4622 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4624 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4626 isds_printf_message(context
,
4627 _("Document has unknown dmFileMetaType: %ld"),
4628 document
->dmFileMetaType
);
4632 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4634 if (document
->dmFileGuid
) {
4635 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4637 if (document
->dmUpFileGuid
) {
4638 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4641 /* @dmFileDescr is required */
4642 if (!document
->dmFileDescr
) {
4643 isds_log_message(context
,
4644 _("Document is missing mandatory description (title)"));
4648 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4650 if (document
->dmFormat
) {
4651 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4655 /* Insert content (body) of the document. */
4656 if (document
->is_xml
) {
4657 /* XML document requested */
4659 /* Allocate new dmXMLContent */
4660 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4662 isds_printf_message(context
,
4663 _("Could not allocate dmXMLContent element"));
4668 node
= xmlAddChild(file
, xmlcontent
);
4670 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4671 isds_printf_message(context
,
4672 _("Could not add dmXMLContent child to %s element"),
4678 /* Copy non-empty node list */
4679 if (document
->xml_node_list
) {
4680 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4681 document
->xml_node_list
);
4683 isds_printf_message(context
,
4684 _("Not enough memory to copy XML document"));
4689 if (!xmlAddChildList(node
, content
)) {
4690 xmlFreeNodeList(content
);
4691 isds_printf_message(context
,
4692 _("Error while adding XML document into dmXMLContent"));
4696 /* XXX: We cannot free the content here because it's part of node's
4697 * document since now. It will be freed with it automatically. */
4700 /* Binary document requested */
4701 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4702 document
->data
, document
->data_length
);
4703 if (err
) goto leave
;
4711 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4712 * The copy must be preallocated, the date are just appended into structure.
4713 * @context is ISDS context
4714 * @copy is message copy structure
4715 * @xpath_ctx is XPath context with current node as tMStatus */
4716 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4717 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4718 isds_error err
= IE_SUCCESS
;
4719 xmlXPathObjectPtr result
= NULL
;
4720 char *code
= NULL
, *message
= NULL
;
4722 if (!context
) return IE_INVALID_CONTEXT
;
4723 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4725 /* Free old values */
4726 zfree(copy
->dmStatus
);
4729 /* Get error specific to this copy */
4730 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4732 isds_log_message(context
,
4733 _("Missing isds:dmStatusCode under "
4734 "XSD:tMStatus type element"));
4739 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4740 /* This copy failed */
4741 copy
->error
= IE_ISDS
;
4742 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4744 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4745 if (!copy
->dmStatus
) {
4746 copy
->dmStatus
= code
;
4750 copy
->dmStatus
= code
;
4754 /* This copy succeeded. In this case only, message ID is valid */
4755 copy
->error
= IE_SUCCESS
;
4757 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4759 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4760 "but did not returned assigned message ID\n"));
4768 xmlXPathFreeObject(result
);
4773 /* Insert struct isds_approval data (box approval) into XML tree
4774 * @context is session context
4775 * @approval is libisds structure with approval description. NULL is
4777 * @parent is XML element to append @approval to */
4778 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4779 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4781 isds_error err
= IE_SUCCESS
;
4784 if (!context
) return IE_INVALID_CONTEXT
;
4785 if (!parent
) return IE_INVAL
;
4787 if (!approval
) return IE_SUCCESS
;
4789 /* Build XSD:gExtApproval */
4790 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4791 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4798 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4800 * @context is session context
4801 * @service_name is name of SERVICE_DB_ACCESS
4802 * @response is reallocated server SOAP body response as XML document
4803 * @raw_response is reallocated bit stream with response body. Use
4804 * NULL if you don't care
4805 * @raw_response_length is size of @raw_response in bytes
4806 * @code is reallocated ISDS status code
4807 * @status_message is reallocated ISDS status message
4808 * @return error coded from lower layer, context message will be set up
4810 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4811 const xmlChar
*service_name
,
4812 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4813 xmlChar
**code
, xmlChar
**status_message
) {
4815 isds_error err
= IE_SUCCESS
;
4816 char *service_name_locale
= NULL
;
4817 xmlNodePtr request
= NULL
, node
;
4818 xmlNsPtr isds_ns
= NULL
;
4820 if (!context
) return IE_INVALID_CONTEXT
;
4821 if (!service_name
) return IE_INVAL
;
4822 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4823 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4825 /* Free output argument */
4826 xmlFreeDoc(*response
); *response
= NULL
;
4827 if (raw_response
) zfree(*raw_response
);
4829 zfree(*status_message
);
4832 /* Check if connection is established
4833 * TODO: This check should be done downstairs. */
4834 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4836 service_name_locale
= _isds_utf82locale((char*)service_name
);
4837 if (!service_name_locale
) {
4843 request
= xmlNewNode(NULL
, service_name
);
4845 isds_printf_message(context
,
4846 _("Could not build %s request"), service_name_locale
);
4850 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4852 isds_log_message(context
, _("Could not create ISDS name space"));
4856 xmlSetNs(request
, isds_ns
);
4859 /* Add XSD:tDummyInput child */
4860 INSERT_STRING(request
, "dbDummy", NULL
);
4863 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4864 service_name_locale
);
4867 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4868 raw_response
, raw_response_length
);
4869 xmlFreeNode(request
); request
= NULL
;
4872 isds_log(ILF_ISDS
, ILL_DEBUG
,
4873 _("Processing ISDS response on %s request failed\n"),
4874 service_name_locale
);
4878 /* Check for response status */
4879 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4880 code
, status_message
, NULL
);
4882 isds_log(ILF_ISDS
, ILL_DEBUG
,
4883 _("ISDS response on %s request is missing status\n"),
4884 service_name_locale
);
4888 /* Request processed, but nothing found */
4889 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4890 char *code_locale
= _isds_utf82locale((char*) *code
);
4891 char *status_message_locale
=
4892 _isds_utf82locale((char*) *status_message
);
4893 isds_log(ILF_ISDS
, ILL_DEBUG
,
4894 _("Server refused %s request (code=%s, message=%s)\n"),
4895 service_name_locale
, code_locale
, status_message_locale
);
4896 isds_log_message(context
, status_message_locale
);
4898 free(status_message_locale
);
4904 free(service_name_locale
);
4905 xmlFreeNode(request
);
4911 /* Get data about logged in user and his box. */
4912 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4913 struct isds_DbOwnerInfo
**db_owner_info
) {
4914 isds_error err
= IE_SUCCESS
;
4916 xmlDocPtr response
= NULL
;
4917 xmlChar
*code
= NULL
, *message
= NULL
;
4918 xmlXPathContextPtr xpath_ctx
= NULL
;
4919 xmlXPathObjectPtr result
= NULL
;
4920 char *string
= NULL
;
4923 if (!context
) return IE_INVALID_CONTEXT
;
4924 zfree(context
->long_message
);
4925 if (!db_owner_info
) return IE_INVAL
;
4926 isds_DbOwnerInfo_free(db_owner_info
);
4929 /* Check if connection is established */
4930 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4933 /* Do request and check for success */
4934 err
= build_send_check_dbdummy_request(context
,
4935 BAD_CAST
"GetOwnerInfoFromLogin",
4936 &response
, NULL
, NULL
, &code
, &message
);
4937 if (err
) goto leave
;
4941 /* Prepare structure */
4942 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4943 if (!*db_owner_info
) {
4947 xpath_ctx
= xmlXPathNewContext(response
);
4952 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4957 /* Set context node */
4958 result
= xmlXPathEvalExpression(BAD_CAST
4959 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4964 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4965 isds_log_message(context
, _("Missing dbOwnerInfo element"));
4969 if (result
->nodesetval
->nodeNr
> 1) {
4970 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
4974 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4975 xmlXPathFreeObject(result
); result
= NULL
;
4978 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
4983 isds_DbOwnerInfo_free(db_owner_info
);
4987 xmlXPathFreeObject(result
);
4988 xmlXPathFreeContext(xpath_ctx
);
4992 xmlFreeDoc(response
);
4995 isds_log(ILF_ISDS
, ILL_DEBUG
,
4996 _("GetOwnerInfoFromLogin request processed by server "
4997 "successfully.\n"));
4998 #else /* not HAVE_LIBCURL */
5006 /* Get data about logged in user. */
5007 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5008 struct isds_DbUserInfo
**db_user_info
) {
5009 isds_error err
= IE_SUCCESS
;
5011 xmlDocPtr response
= NULL
;
5012 xmlChar
*code
= NULL
, *message
= NULL
;
5013 xmlXPathContextPtr xpath_ctx
= NULL
;
5014 xmlXPathObjectPtr result
= NULL
;
5017 if (!context
) return IE_INVALID_CONTEXT
;
5018 zfree(context
->long_message
);
5019 if (!db_user_info
) return IE_INVAL
;
5020 isds_DbUserInfo_free(db_user_info
);
5023 /* Check if connection is established */
5024 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5027 /* Do request and check for success */
5028 err
= build_send_check_dbdummy_request(context
,
5029 BAD_CAST
"GetUserInfoFromLogin",
5030 &response
, NULL
, NULL
, &code
, &message
);
5031 if (err
) goto leave
;
5035 /* Prepare structure */
5036 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5037 if (!*db_user_info
) {
5041 xpath_ctx
= xmlXPathNewContext(response
);
5046 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5051 /* Set context node */
5052 result
= xmlXPathEvalExpression(BAD_CAST
5053 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5058 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5059 isds_log_message(context
, _("Missing dbUserInfo element"));
5063 if (result
->nodesetval
->nodeNr
> 1) {
5064 isds_log_message(context
, _("Multiple dbUserInfo element"));
5068 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5069 xmlXPathFreeObject(result
); result
= NULL
;
5072 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5076 isds_DbUserInfo_free(db_user_info
);
5079 xmlXPathFreeObject(result
);
5080 xmlXPathFreeContext(xpath_ctx
);
5084 xmlFreeDoc(response
);
5087 isds_log(ILF_ISDS
, ILL_DEBUG
,
5088 _("GetUserInfoFromLogin request processed by server "
5089 "successfully.\n"));
5090 #else /* not HAVE_LIBCURL */
5098 /* Get expiration time of current password
5099 * @context is session context
5100 * @expiration is automatically reallocated time when password expires. If
5101 * password expiration is disabled, NULL will be returned. In case of error
5102 * it will be nulled too. */
5103 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5104 struct timeval
**expiration
) {
5105 isds_error err
= IE_SUCCESS
;
5107 xmlDocPtr response
= NULL
;
5108 xmlChar
*code
= NULL
, *message
= NULL
;
5109 xmlXPathContextPtr xpath_ctx
= NULL
;
5110 xmlXPathObjectPtr result
= NULL
;
5111 char *string
= NULL
;
5114 if (!context
) return IE_INVALID_CONTEXT
;
5115 zfree(context
->long_message
);
5116 if (!expiration
) return IE_INVAL
;
5120 /* Check if connection is established */
5121 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5124 /* Do request and check for success */
5125 err
= build_send_check_dbdummy_request(context
,
5126 BAD_CAST
"GetPasswordInfo",
5127 &response
, NULL
, NULL
, &code
, &message
);
5128 if (err
) goto leave
;
5132 xpath_ctx
= xmlXPathNewContext(response
);
5137 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5142 /* Set context node */
5143 result
= xmlXPathEvalExpression(BAD_CAST
5144 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5149 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5150 isds_log_message(context
,
5151 _("Missing GetPasswordInfoResponse element"));
5155 if (result
->nodesetval
->nodeNr
> 1) {
5156 isds_log_message(context
,
5157 _("Multiple GetPasswordInfoResponse element"));
5161 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5162 xmlXPathFreeObject(result
); result
= NULL
;
5164 /* Extract expiration date */
5165 EXTRACT_STRING("isds:pswExpDate", string
);
5167 /* And convert it if any returned. Otherwise expiration is disabled. */
5168 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5170 char *string_locale
= _isds_utf82locale(string
);
5171 if (err
== IE_DATE
) err
= IE_ISDS
;
5172 isds_printf_message(context
,
5173 _("Could not convert pswExpDate as ISO time: %s"),
5175 free(string_locale
);
5188 xmlXPathFreeObject(result
);
5189 xmlXPathFreeContext(xpath_ctx
);
5193 xmlFreeDoc(response
);
5196 isds_log(ILF_ISDS
, ILL_DEBUG
,
5197 _("GetPasswordInfo request processed by server "
5198 "successfully.\n"));
5199 #else /* not HAVE_LIBCURL */
5208 /* Request delivering new TOTP code from ISDS through side channel before
5209 * changing password.
5210 * @context is session context
5211 * @password is current password.
5212 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5213 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5214 * function for more details.
5215 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5216 * NULL, if you don't care.
5217 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5219 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5220 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5221 isds_error err
= IE_SUCCESS
;
5222 char *saved_url
= NULL
; /* No copy */
5223 #if HAVE_CURL_REAUTHORIZATION_BUG
5224 CURL
*saved_curl
= NULL
; /* No copy */
5226 xmlNsPtr isds_ns
= NULL
;
5227 xmlNodePtr request
= NULL
;
5228 xmlDocPtr response
= NULL
;
5229 xmlChar
*code
= NULL
, *message
= NULL
;
5230 const xmlChar
*codes
[] = {
5235 const char *meanings
[] = {
5236 N_("Unexpected error"),
5237 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5238 N_("One-time code could not been sent. Try later again.")
5240 const isds_otp_resolution resolutions
[] = {
5241 OTP_RESOLUTION_UNKNOWN
,
5242 OTP_RESOLUTION_TO_FAST
,
5243 OTP_RESOLUTION_TOTP_NOT_SENT
5246 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5247 zfree(context
->long_message
);
5248 if (NULL
== password
) {
5249 isds_log_message(context
,
5250 _("Second argument (password) of isds_change_password() "
5255 /* Check if connection is established
5256 * TODO: This check should be done downstairs. */
5257 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5259 if (!context
->otp
) {
5260 isds_log_message(context
, _("This function requires OTP-authenticated "
5262 return IE_INVALID_CONTEXT
;
5265 isds_log_message(context
, _("If one-time password authentication "
5266 "method is in use, requesting new OTP code requires "
5267 "one-time credentials argument either"));
5270 if (otp
->method
!= OTP_TIME
) {
5271 isds_log_message(context
, _("Requesting new time-based OTP code from "
5272 "server requires one-time password authentication "
5276 if (otp
->otp_code
!= NULL
) {
5277 isds_log_message(context
, _("Requesting new time-based OTP code from "
5278 "server requires undefined OTP code member in "
5279 "one-time credentials argument"));
5285 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5287 isds_log_message(context
, _("Could not build SendSMSCode request"));
5290 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5292 isds_log_message(context
, _("Could not create ISDS name space"));
5293 xmlFreeNode(request
);
5296 xmlSetNs(request
, isds_ns
);
5298 /* Change URL temporarily for sending this request only */
5300 char *new_url
= NULL
;
5301 if ((err
= _isds_build_url_from_context(context
,
5302 "%1$.*2$sasws/changePassword", &new_url
))) {
5305 saved_url
= context
->url
;
5306 context
->url
= new_url
;
5309 /* Store credentials for sending this request only */
5310 context
->otp_credentials
= otp
;
5311 _isds_discard_credentials(context
, 0);
5312 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5314 _isds_discard_credentials(context
, 0);
5317 #if HAVE_CURL_REAUTHORIZATION_BUG
5318 saved_curl
= context
->curl
;
5319 context
->curl
= curl_easy_init();
5320 if (NULL
== context
->curl
) {
5324 if (context
->timeout
) {
5325 err
= isds_set_timeout(context
, context
->timeout
);
5326 if (err
) goto leave
;
5330 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5333 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5335 /* Remove temporal credentials */
5336 _isds_discard_credentials(context
, 0);
5337 /* Detach pointer to OTP credentials from context */
5338 context
->otp_credentials
= NULL
;
5339 /* Keep context->otp true to keep signaling this is OTP session */
5341 /* Destroy request */
5342 xmlFreeNode(request
); request
= NULL
;
5345 isds_log(ILF_ISDS
, ILL_DEBUG
,
5346 _("Processing ISDS response on SendSMSCode request failed\n"));
5350 /* Check for response status */
5351 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5352 &code
, &message
, (xmlChar
**)refnumber
);
5354 isds_log(ILF_ISDS
, ILL_DEBUG
,
5355 _("ISDS response on SendSMSCode request is missing "
5360 /* Check for error */
5361 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5362 char *code_locale
= _isds_utf82locale((char*)code
);
5363 char *message_locale
= _isds_utf82locale((char*)message
);
5365 isds_log(ILF_ISDS
, ILL_DEBUG
,
5366 _("Server refused to send new code on SendSMSCode "
5367 "request (code=%s, message=%s)\n"),
5368 code_locale
, message_locale
);
5370 /* Check for known error codes */
5371 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5372 if (!xmlStrcmp(code
, codes
[i
])) break;
5374 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5375 isds_log_message(context
, _(meanings
[i
]));
5376 /* Mimic otp->resolution according to the code, specification does
5377 * prescribe OTP header to be available. */
5378 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5379 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5380 otp
->resolution
= resolutions
[i
];
5382 isds_log_message(context
, message_locale
);
5385 free(message_locale
);
5391 /* Otherwise new code sent successfully */
5392 /* Mimic otp->resolution according to the code, specification does
5393 * prescribe OTP header to be available. */
5394 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5395 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5398 if (NULL
!= saved_url
) {
5399 /* Revert URL to original one */
5400 zfree(context
->url
);
5401 context
->url
= saved_url
;
5403 #if HAVE_CURL_REAUTHORIZATION_BUG
5404 if (NULL
!= saved_curl
) {
5405 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5406 context
->curl
= saved_curl
;
5412 xmlFreeDoc(response
);
5413 xmlFreeNode(request
);
5416 isds_log(ILF_ISDS
, ILL_DEBUG
,
5417 _("New OTP code has been sent successfully on SendSMSCode "
5423 /* Convert response status code to isds_error code and set long message
5424 * @context is context to save long message to
5425 * @map is mapping from codes to errors and messages. Pass NULL for generic
5427 * @code is status code to translate
5428 * @message is non-localized status message to put into long message in case
5429 * of uknown error. It can be NULL if server did not provide any.
5430 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5431 * invalid invocation. */
5432 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5433 const struct code_map_isds_error
*map
,
5434 const xmlChar
*code
, const xmlChar
*message
) {
5436 isds_log_message(context
,
5437 _("NULL status code passed to statuscode2isds_error()"));
5442 /* Check for known error codes */
5443 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5444 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5445 isds_log_message(context
, _(map
->meanings
[i
]));
5446 return map
->errors
[i
];
5452 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5453 char *message_locale
= _isds_utf82locale((char*)message
);
5454 if (NULL
== message_locale
)
5455 isds_log_message(context
, _("ISDS server returned unknown error"));
5457 isds_log_message(context
, message_locale
);
5458 free(message_locale
);
5467 /* Change user password in ISDS.
5468 * User must supply old password, new password will takes effect after some
5469 * time, current session can continue. Password must fulfill some constraints.
5470 * @context is session context
5471 * @old_password is current password.
5472 * @new_password is requested new password
5473 * @otp auxiliary data required if one-time password authentication is in use,
5474 * defines OTP code (if known) and returns fine grade resolution of OTP
5475 * procedure. Pass NULL, if one-time password authentication is not needed.
5476 * Please note the @otp argument must match OTP method used at log-in time. See
5477 * isds_login() function for more details.
5478 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5479 * NULL, if you don't care.
5480 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5481 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5482 * awaiting OTP code that has been delivered by side channel to the user. */
5483 isds_error
isds_change_password(struct isds_ctx
*context
,
5484 const char *old_password
, const char *new_password
,
5485 struct isds_otp
*otp
, char **refnumber
) {
5486 isds_error err
= IE_SUCCESS
;
5488 char *saved_url
= NULL
; /* No copy */
5489 #if HAVE_CURL_REAUTHORIZATION_BUG
5490 CURL
*saved_curl
= NULL
; /* No copy */
5492 xmlNsPtr isds_ns
= NULL
;
5493 xmlNodePtr request
= NULL
, node
;
5494 xmlDocPtr response
= NULL
;
5495 xmlChar
*code
= NULL
, *message
= NULL
;
5496 const xmlChar
*codes
[] = {
5509 const char *meanings
[] = {
5510 N_("Password length must be between 8 and 32 characters"),
5511 N_("Password cannot be reused"), /* Server does not distinguish 1067
5512 and 1091 on ChangePasswordOTP */
5513 N_("Password contains forbidden character"),
5514 N_("Password must contain at least one upper-case letter, "
5515 "one lower-case, and one digit"),
5516 N_("Password cannot contain sequence of three identical characters"),
5517 N_("Password cannot contain user identifier"),
5518 N_("Password is too simmple"),
5519 N_("Old password is not valid"),
5520 N_("Password cannot be reused"),
5521 N_("Unexpected error"),
5522 N_("LDAP update error")
5526 if (!context
) return IE_INVALID_CONTEXT
;
5527 zfree(context
->long_message
);
5528 if (NULL
!= refnumber
)
5530 if (NULL
== old_password
) {
5531 isds_log_message(context
,
5532 _("Second argument (old password) of isds_change_password() "
5536 if (NULL
== otp
&& NULL
== new_password
) {
5537 isds_log_message(context
,
5538 _("Third argument (new password) of isds_change_password() "
5544 /* Check if connection is established
5545 * TODO: This check should be done downstairs. */
5546 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5548 if (context
->otp
&& NULL
== otp
) {
5549 isds_log_message(context
, _("If one-time password authentication "
5550 "method is in use, changing password requires one-time "
5551 "credentials either"));
5555 /* Build ChangeISDSPassword request */
5556 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5557 BAD_CAST
"ChangePasswordOTP");
5559 isds_log_message(context
, (NULL
== otp
) ?
5560 _("Could not build ChangeISDSPassword request") :
5561 _("Could not build ChangePasswordOTP request"));
5564 isds_ns
= xmlNewNs(request
,
5565 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5568 isds_log_message(context
, _("Could not create ISDS name space"));
5569 xmlFreeNode(request
);
5572 xmlSetNs(request
, isds_ns
);
5574 INSERT_STRING(request
, "dbOldPassword", old_password
);
5575 INSERT_STRING(request
, "dbNewPassword", new_password
);
5578 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5579 switch (otp
->method
) {
5581 isds_log(ILF_SEC
, ILL_INFO
,
5582 _("Selected authentication method: "
5583 "HMAC-based one-time password\n"));
5584 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5587 isds_log(ILF_SEC
, ILL_INFO
,
5588 _("Selected authentication method: "
5589 "Time-based one-time password\n"));
5590 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5591 if (otp
->otp_code
== NULL
) {
5592 isds_log(ILF_SEC
, ILL_INFO
,
5593 _("OTP code has not been provided by "
5594 "application, requesting server for "
5596 err
= _isds_request_totp_code(context
, old_password
, otp
,
5598 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5602 isds_log(ILF_SEC
, ILL_INFO
,
5603 _("OTP code has been provided by "
5604 "application, not requesting server "
5609 isds_log_message(context
,
5610 _("Unknown one-time password authentication "
5611 "method requested by application"));
5616 /* Change URL temporarily for sending this request only */
5618 char *new_url
= NULL
;
5619 if ((err
= _isds_build_url_from_context(context
,
5620 "%1$.*2$sasws/changePassword", &new_url
))) {
5623 saved_url
= context
->url
;
5624 context
->url
= new_url
;
5627 /* Store credentials for sending this request only */
5628 context
->otp_credentials
= otp
;
5629 _isds_discard_credentials(context
, 0);
5630 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5631 old_password
, NULL
))) {
5632 _isds_discard_credentials(context
, 0);
5635 #if HAVE_CURL_REAUTHORIZATION_BUG
5636 saved_curl
= context
->curl
;
5637 context
->curl
= curl_easy_init();
5638 if (NULL
== context
->curl
) {
5642 if (context
->timeout
) {
5643 err
= isds_set_timeout(context
, context
->timeout
);
5644 if (err
) goto leave
;
5649 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5650 _("Sending ChangeISDSPassword request to ISDS\n") :
5651 _("Sending ChangePasswordOTP request to ISDS\n"));
5654 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5655 request
, &response
, NULL
, NULL
);
5658 /* Remove temporal credentials */
5659 _isds_discard_credentials(context
, 0);
5660 /* Detach pointer to OTP credentials from context */
5661 context
->otp_credentials
= NULL
;
5662 /* Keep context->otp true to keep signaling this is OTP session */
5665 /* Destroy request */
5666 xmlFreeNode(request
); request
= NULL
;
5669 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5670 _("Processing ISDS response on ChangeISDSPassword "
5671 "request failed\n") :
5672 _("Processing ISDS response on ChangePasswordOTP "
5673 "request failed\n"));
5677 /* Check for response status */
5678 err
= isds_response_status(context
,
5679 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5680 &code
, &message
, (xmlChar
**)refnumber
);
5682 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5683 _("ISDS response on ChangeISDSPassword request is missing "
5685 _("ISDS response on ChangePasswordOTP request is missing "
5690 /* Check for known error codes */
5691 for (int i
=0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5692 if (!xmlStrcmp(code
, codes
[i
])) {
5693 char *code_locale
= _isds_utf82locale((char*)code
);
5694 char *message_locale
= _isds_utf82locale((char*)message
);
5695 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5696 _("Server refused to change password on ChangeISDSPassword "
5697 "request (code=%s, message=%s)\n") :
5698 _("Server refused to change password on ChangePasswordOTP "
5699 "request (code=%s, message=%s)\n"),
5700 code_locale
, message_locale
);
5702 free(message_locale
);
5703 isds_log_message(context
, _(meanings
[i
]));
5710 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5711 char *code_locale
= _isds_utf82locale((char*)code
);
5712 char *message_locale
= _isds_utf82locale((char*)message
);
5713 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5714 _("Server refused to change password on ChangeISDSPassword "
5715 "request (code=%s, message=%s)\n") :
5716 _("Server refused to change password on ChangePasswordOTP "
5717 "request (code=%s, message=%s)\n"),
5718 code_locale
, message_locale
);
5719 isds_log_message(context
, message_locale
);
5721 free(message_locale
);
5726 /* Otherwise password changed successfully */
5729 if (NULL
!= saved_url
) {
5730 /* Revert URL to original one */
5731 zfree(context
->url
);
5732 context
->url
= saved_url
;
5734 #if HAVE_CURL_REAUTHORIZATION_BUG
5735 if (NULL
!= saved_curl
) {
5736 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5737 context
->curl
= saved_curl
;
5743 xmlFreeDoc(response
);
5744 xmlFreeNode(request
);
5747 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5748 _("Password changed successfully on ChangeISDSPassword "
5750 _("Password changed successfully on ChangePasswordOTP "
5752 #else /* not HAVE_LIBCURL */
5761 /* Generic middle part with request sending and response check.
5762 * It sends prepared request and checks for error code.
5763 * @context is ISDS session context.
5764 * @service is ISDS service handler
5765 * @service_name is name in scope of given @service
5766 * @request is XML tree with request. Will be freed to save memory.
5767 * @response is XML document outputting ISDS response.
5768 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5769 * @map is mapping from status code to library error. Pass NULL if no special
5770 * handling is requested.
5771 * NULL, if you don't care. */
5772 static isds_error
send_destroy_request_check_response(
5773 struct isds_ctx
*context
,
5774 const isds_service service
, const xmlChar
*service_name
,
5775 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5776 const struct code_map_isds_error
*map
) {
5777 isds_error err
= IE_SUCCESS
;
5778 char *service_name_locale
= NULL
;
5779 xmlChar
*code
= NULL
, *message
= NULL
;
5782 if (!context
) return IE_INVALID_CONTEXT
;
5783 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5787 /* Check if connection is established
5788 * TODO: This check should be done downstairs. */
5789 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5791 service_name_locale
= _isds_utf82locale((char*) service_name
);
5792 if (!service_name_locale
) {
5797 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5798 service_name_locale
);
5801 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5802 xmlFreeNode(*request
); *request
= NULL
;
5805 isds_log(ILF_ISDS
, ILL_DEBUG
,
5806 _("Processing ISDS response on %s request failed\n"),
5807 service_name_locale
);
5811 /* Check for response status */
5812 err
= isds_response_status(context
, service
, *response
,
5813 &code
, &message
, refnumber
);
5815 isds_log(ILF_ISDS
, ILL_DEBUG
,
5816 _("ISDS response on %s request is missing status\n"),
5817 service_name_locale
);
5821 err
= statuscode2isds_error(context
, map
, code
, message
);
5823 /* Request processed, but server failed */
5824 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5825 char *code_locale
= _isds_utf82locale((char*) code
);
5826 char *message_locale
= _isds_utf82locale((char*) message
);
5827 isds_log(ILF_ISDS
, ILL_DEBUG
,
5828 _("Server refused %s request (code=%s, message=%s)\n"),
5829 service_name_locale
, code_locale
, message_locale
);
5831 free(message_locale
);
5839 if (err
&& *response
) {
5840 xmlFreeDoc(*response
);
5844 xmlFreeNode(*request
);
5847 free(service_name_locale
);
5853 /* Generic bottom half with request sending.
5854 * It sends prepared request, checks for error code, destroys response and
5855 * request and log success or failure.
5856 * @context is ISDS session context.
5857 * @service is ISDS service handler
5858 * @service_name is name in scope of given @service
5859 * @request is XML tree with request. Will be freed to save memory.
5860 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5861 * NULL, if you don't care. */
5862 static isds_error
send_request_check_drop_response(
5863 struct isds_ctx
*context
,
5864 const isds_service service
, const xmlChar
*service_name
,
5865 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5866 isds_error err
= IE_SUCCESS
;
5867 xmlDocPtr response
= NULL
;
5870 if (!context
) return IE_INVALID_CONTEXT
;
5871 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5874 /* Send request and check response*/
5875 err
= send_destroy_request_check_response(context
,
5876 service
, service_name
, request
, &response
, refnumber
, NULL
);
5878 xmlFreeDoc(response
);
5881 xmlFreeNode(*request
);
5886 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5887 isds_log(ILF_ISDS
, ILL_DEBUG
,
5888 _("%s request processed by server successfully.\n"),
5889 service_name_locale
);
5890 free(service_name_locale
);
5897 /* Insert isds_credentials_delivery structure into XML request if not NULL
5898 * @context is session context
5899 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5900 * credentials delivery. The email field is passed.
5901 * @parent is XML element where to insert */
5902 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5903 const struct isds_credentials_delivery
*credentials_delivery
,
5904 xmlNodePtr parent
) {
5905 isds_error err
= IE_SUCCESS
;
5908 if (!context
) return IE_INVALID_CONTEXT
;
5909 if (!parent
) return IE_INVAL
;
5911 if (credentials_delivery
) {
5912 /* Following elements are valid only for services:
5913 * NewAccessData, AddDataBoxUser, CreateDataBox */
5914 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5915 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5923 /* Extract credentials delivery from ISDS response.
5924 * @context is session context
5925 * @credentials_delivery is pointer to valid structure to fill in returned
5926 * user's password (and new log-in name). If NULL, do not extract the data.
5927 * @response is pointer to XML document with ISDS response
5928 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5929 * @return IE_SUCCESS even if new user name has not been found because it's not
5930 * clear whether it's returned always. */
5931 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5932 struct isds_credentials_delivery
*credentials_delivery
,
5933 xmlDocPtr response
, const char *request_name
) {
5934 isds_error err
= IE_SUCCESS
;
5935 xmlXPathContextPtr xpath_ctx
= NULL
;
5936 xmlXPathObjectPtr result
= NULL
;
5937 char *xpath_query
= NULL
;
5939 if (!context
) return IE_INVALID_CONTEXT
;
5940 if (credentials_delivery
) {
5941 zfree(credentials_delivery
->token
);
5942 zfree(credentials_delivery
->new_user_name
);
5944 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5947 /* Extract optional token */
5948 if (credentials_delivery
) {
5949 xpath_ctx
= xmlXPathNewContext(response
);
5954 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5959 /* Verify root element */
5960 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5965 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
5970 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5971 char *request_name_locale
= _isds_utf82locale(request_name
);
5972 isds_log(ILF_ISDS
, ILL_WARNING
,
5973 _("Wrong element in ISDS response for %s request "
5974 "while extracting credentials delivery details\n"),
5975 request_name_locale
);
5976 free(request_name_locale
);
5980 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5983 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5985 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
5987 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
5988 if (!credentials_delivery
->token
) {
5989 char *request_name_locale
= _isds_utf82locale(request_name
);
5990 isds_log(ILF_ISDS
, ILL_ERR
,
5991 _("ISDS did not return token on %s request "
5992 "even if requested\n"), request_name_locale
);
5993 free(request_name_locale
);
6000 xmlXPathFreeObject(result
);
6001 xmlXPathFreeContext(xpath_ctx
);
6007 /* Build XSD:tCreateDBInput request type for box creating.
6008 * @context is session context
6009 * @request outputs built XML tree
6010 * @service_name is request name of SERVICE_DB_MANIPULATION service
6011 * @box is box description to create including single primary user (in case of
6013 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6014 * box, or contact address of PFO box owner)
6015 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6016 * @upper_box_id is optional ID of supper box if currently created box is
6018 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6020 * @credentials_delivery is valid pointer if ISDS should return token that box
6021 * owner can use to obtain his new credentials in on-line way. Then valid email
6022 * member value should be supplied.
6023 * @approval is optional external approval of box manipulation */
6024 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6025 xmlNodePtr
*request
, const xmlChar
*service_name
,
6026 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6027 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6028 const xmlChar
*ceo_label
,
6029 const struct isds_credentials_delivery
*credentials_delivery
,
6030 const struct isds_approval
*approval
) {
6031 isds_error err
= IE_SUCCESS
;
6032 xmlNsPtr isds_ns
= NULL
;
6033 xmlNodePtr node
, dbPrimaryUsers
;
6034 xmlChar
*string
= NULL
;
6035 const struct isds_list
*item
;
6038 if (!context
) return IE_INVALID_CONTEXT
;
6039 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6043 /* Build CreateDataBox-similar request */
6044 *request
= xmlNewNode(NULL
, service_name
);
6046 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6047 isds_printf_message(context
, _("Could build %s request"),
6048 service_name_locale
);
6049 free(service_name_locale
);
6052 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6053 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6055 isds_log_message(context
, _("Could not create ISDS1 name space"));
6056 xmlFreeNode(*request
);
6060 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6062 isds_log_message(context
, _("Could not create ISDS name space"));
6063 xmlFreeNode(*request
);
6067 xmlSetNs(*request
, isds_ns
);
6069 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6070 err
= insert_DbOwnerInfo(context
, box
, node
);
6071 if (err
) goto leave
;
6074 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6075 * verbose documentation allows none dbUserInfo */
6076 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6077 for (item
= users
; item
; item
= item
->next
) {
6079 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6080 err
= insert_DbUserInfo(context
,
6081 (struct isds_DbUserInfo
*) item
->data
, node
);
6082 if (err
) goto leave
;
6086 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6087 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6088 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6090 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6091 if (err
) goto leave
;
6093 err
= insert_GExtApproval(context
, approval
, *request
);
6094 if (err
) goto leave
;
6098 xmlFreeNode(*request
);
6104 #endif /* HAVE_LIBCURL */
6108 * @context is session context
6109 * @box is box description to create including single primary user (in case of
6110 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6111 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6112 * box, or contact address of PFO box owner)
6113 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6114 * @upper_box_id is optional ID of supper box if currently created box is
6116 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6117 * @credentials_delivery is NULL if new password should be delivered off-line
6118 * to box owner. It is valid pointer if owner should obtain new password on-line
6119 * on dedicated web server. Then input @credentials_delivery.email value is
6120 * his e-mail address he must provide to dedicated web server together
6121 * with output reallocated @credentials_delivery.token member. Output
6122 * member @credentials_delivery.new_user_name is unused up on this call.
6123 * @approval is optional external approval of box manipulation
6124 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6125 * NULL, if you don't care.*/
6126 isds_error
isds_add_box(struct isds_ctx
*context
,
6127 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6128 const char *former_names
, const char *upper_box_id
,
6129 const char *ceo_label
,
6130 struct isds_credentials_delivery
*credentials_delivery
,
6131 const struct isds_approval
*approval
, char **refnumber
) {
6132 isds_error err
= IE_SUCCESS
;
6134 xmlNodePtr request
= NULL
;
6135 xmlDocPtr response
= NULL
;
6136 xmlXPathContextPtr xpath_ctx
= NULL
;
6137 xmlXPathObjectPtr result
= NULL
;
6141 if (!context
) return IE_INVALID_CONTEXT
;
6142 zfree(context
->long_message
);
6143 if (credentials_delivery
) {
6144 zfree(credentials_delivery
->token
);
6145 zfree(credentials_delivery
->new_user_name
);
6147 if (!box
) return IE_INVAL
;
6150 /* Scratch box ID */
6153 /* Build CreateDataBox request */
6154 err
= build_CreateDBInput_request(context
,
6155 &request
, BAD_CAST
"CreateDataBox",
6156 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6157 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6158 if (err
) goto leave
;
6160 /* Send it to server and process response */
6161 err
= send_destroy_request_check_response(context
,
6162 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6163 &response
, (xmlChar
**) refnumber
, NULL
);
6165 /* Extract box ID */
6166 xpath_ctx
= xmlXPathNewContext(response
);
6171 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6175 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6177 /* Extract optional token */
6178 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6182 xmlXPathFreeObject(result
);
6183 xmlXPathFreeContext(xpath_ctx
);
6184 xmlFreeDoc(response
);
6185 xmlFreeNode(request
);
6188 isds_log(ILF_ISDS
, ILL_DEBUG
,
6189 _("CreateDataBox request processed by server successfully.\n"));
6191 #else /* not HAVE_LIBCURL */
6199 /* Notify ISDS about new PFO entity.
6200 * This function has no real effect.
6201 * @context is session context
6202 * @box is PFO description including single primary user.
6203 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6204 * @former_names is optional undocumented string. Pass NULL if you don't care.
6205 * @upper_box_id is optional ID of supper box if currently created box is
6207 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6208 * @approval is optional external approval of box manipulation
6209 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6210 * NULL, if you don't care.*/
6211 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6212 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6213 const char *former_names
, const char *upper_box_id
,
6214 const char *ceo_label
, const struct isds_approval
*approval
,
6216 isds_error err
= IE_SUCCESS
;
6218 xmlNodePtr request
= NULL
;
6221 if (!context
) return IE_INVALID_CONTEXT
;
6222 zfree(context
->long_message
);
6223 if (!box
) return IE_INVAL
;
6226 /* Build CreateDataBoxPFOInfo request */
6227 err
= build_CreateDBInput_request(context
,
6228 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6229 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6230 (xmlChar
*) ceo_label
, NULL
, approval
);
6231 if (err
) goto leave
;
6233 /* Send it to server and process response */
6234 err
= send_request_check_drop_response(context
,
6235 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6236 (xmlChar
**) refnumber
);
6237 /* XXX: XML Schema names output dbID element but textual documentation
6238 * states no box identifier is returned. */
6240 xmlFreeNode(request
);
6241 #else /* not HAVE_LIBCURL */
6248 /* Common implementation for removing given box.
6249 * @context is session context
6250 * @service_name is UTF-8 encoded name fo ISDS service
6251 * @box is box description to delete
6252 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6253 * carry sane value. If NULL, do not inject this information into request.
6254 * @approval is optional external approval of box manipulation
6255 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6256 * NULL, if you don't care.*/
6257 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6258 const xmlChar
*service_name
,
6259 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6260 const struct isds_approval
*approval
, char **refnumber
) {
6261 isds_error err
= IE_SUCCESS
;
6263 xmlNsPtr isds_ns
= NULL
;
6264 xmlNodePtr request
= NULL
;
6266 xmlChar
*string
= NULL
;
6270 if (!context
) return IE_INVALID_CONTEXT
;
6271 zfree(context
->long_message
);
6272 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6276 /* Build DeleteDataBox(Promptly) request */
6277 request
= xmlNewNode(NULL
, service_name
);
6279 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6280 isds_printf_message(context
,
6281 _("Could build %s request"), service_name_locale
);
6282 free(service_name_locale
);
6285 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6287 isds_log_message(context
, _("Could not create ISDS name space"));
6288 xmlFreeNode(request
);
6291 xmlSetNs(request
, isds_ns
);
6293 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6294 err
= insert_DbOwnerInfo(context
, box
, node
);
6295 if (err
) goto leave
;
6298 err
= tm2datestring(since
, &string
);
6300 isds_log_message(context
,
6301 _("Could not convert `since' argument to ISO date string"));
6304 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6308 err
= insert_GExtApproval(context
, approval
, request
);
6309 if (err
) goto leave
;
6312 /* Send it to server and process response */
6313 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6314 service_name
, &request
, (xmlChar
**) refnumber
);
6317 xmlFreeNode(request
);
6319 #else /* not HAVE_LIBCURL */
6326 /* Remove given box permanently.
6327 * @context is session context
6328 * @box is box description to delete
6329 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6331 * @approval is optional external approval of box manipulation
6332 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6333 * NULL, if you don't care.*/
6334 isds_error
isds_delete_box(struct isds_ctx
*context
,
6335 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6336 const struct isds_approval
*approval
, char **refnumber
) {
6337 if (!context
) return IE_INVALID_CONTEXT
;
6338 zfree(context
->long_message
);
6339 if (!box
|| !since
) return IE_INVAL
;
6341 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6342 box
, since
, approval
, refnumber
);
6346 /* Undocumented function.
6347 * @context is session context
6348 * @box is box description to delete
6349 * @approval is optional external approval of box manipulation
6350 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6351 * NULL, if you don't care.*/
6352 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6353 const struct isds_DbOwnerInfo
*box
,
6354 const struct isds_approval
*approval
, char **refnumber
) {
6355 if (!context
) return IE_INVALID_CONTEXT
;
6356 zfree(context
->long_message
);
6357 if (!box
) return IE_INVAL
;
6359 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6360 box
, NULL
, approval
, refnumber
);
6364 /* Update data about given box.
6365 * @context is session context
6366 * @old_box current box description
6367 * @new_box are updated data about @old_box
6368 * @approval is optional external approval of box manipulation
6369 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6370 * NULL, if you don't care.*/
6371 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6372 const struct isds_DbOwnerInfo
*old_box
,
6373 const struct isds_DbOwnerInfo
*new_box
,
6374 const struct isds_approval
*approval
, char **refnumber
) {
6375 isds_error err
= IE_SUCCESS
;
6377 xmlNsPtr isds_ns
= NULL
;
6378 xmlNodePtr request
= NULL
;
6383 if (!context
) return IE_INVALID_CONTEXT
;
6384 zfree(context
->long_message
);
6385 if (!old_box
|| !new_box
) return IE_INVAL
;
6389 /* Build UpdateDataBoxDescr request */
6390 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6392 isds_log_message(context
,
6393 _("Could build UpdateDataBoxDescr request"));
6396 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6398 isds_log_message(context
, _("Could not create ISDS name space"));
6399 xmlFreeNode(request
);
6402 xmlSetNs(request
, isds_ns
);
6404 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6405 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6406 if (err
) goto leave
;
6408 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6409 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6410 if (err
) goto leave
;
6412 err
= insert_GExtApproval(context
, approval
, request
);
6413 if (err
) goto leave
;
6416 /* Send it to server and process response */
6417 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6418 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6421 xmlFreeNode(request
);
6422 #else /* not HAVE_LIBCURL */
6431 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6433 * @context is session context
6434 * @service is SOAP service
6435 * @service_name is name of request in @service
6436 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6437 * @box_id is box ID of interest
6438 * @approval is optional external approval of box manipulation
6439 * @response is server SOAP body response as XML document
6440 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6441 * NULL, if you don't care.
6442 * @return error coded from lower layer, context message will be set up
6444 static isds_error
build_send_dbid_request_check_response(
6445 struct isds_ctx
*context
, const isds_service service
,
6446 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6447 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6448 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6450 isds_error err
= IE_SUCCESS
;
6451 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6452 xmlNodePtr request
= NULL
, node
;
6453 xmlNsPtr isds_ns
= NULL
;
6455 if (!context
) return IE_INVALID_CONTEXT
;
6456 if (!service_name
|| !box_id
) return IE_INVAL
;
6457 if (!response
) return IE_INVAL
;
6459 /* Free output argument */
6460 xmlFreeDoc(*response
); *response
= NULL
;
6462 /* Prepare strings */
6463 service_name_locale
= _isds_utf82locale((char*)service_name
);
6464 if (!service_name_locale
) {
6468 box_id_locale
= _isds_utf82locale((char*)box_id
);
6469 if (!box_id_locale
) {
6475 request
= xmlNewNode(NULL
, service_name
);
6477 isds_printf_message(context
,
6478 _("Could not build %s request for %s box"), service_name_locale
,
6483 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6485 isds_log_message(context
, _("Could not create ISDS name space"));
6489 xmlSetNs(request
, isds_ns
);
6491 /* Add XSD:tIdDbInput children */
6492 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6493 INSERT_STRING(request
, box_id_element
, box_id
);
6494 err
= insert_GExtApproval(context
, approval
, request
);
6495 if (err
) goto leave
;
6497 /* Send request and check response*/
6498 err
= send_destroy_request_check_response(context
,
6499 service
, service_name
, &request
, response
, refnumber
, NULL
);
6502 free(service_name_locale
);
6503 free(box_id_locale
);
6504 xmlFreeNode(request
);
6507 #endif /* HAVE_LIBCURL */
6510 /* Get data about all users assigned to given box.
6511 * @context is session context
6513 * @users is automatically reallocated list of struct isds_DbUserInfo */
6514 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6515 struct isds_list
**users
) {
6516 isds_error err
= IE_SUCCESS
;
6518 xmlDocPtr response
= NULL
;
6519 xmlXPathContextPtr xpath_ctx
= NULL
;
6520 xmlXPathObjectPtr result
= NULL
;
6522 struct isds_list
*item
, *prev_item
= NULL
;
6525 if (!context
) return IE_INVALID_CONTEXT
;
6526 zfree(context
->long_message
);
6527 if (!users
|| !box_id
) return IE_INVAL
;
6528 isds_list_free(users
);
6532 /* Do request and check for success */
6533 err
= build_send_dbid_request_check_response(context
,
6534 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6535 BAD_CAST box_id
, NULL
, &response
, NULL
);
6536 if (err
) goto leave
;
6540 /* Prepare structure */
6541 xpath_ctx
= xmlXPathNewContext(response
);
6546 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6551 /* Set context node */
6552 result
= xmlXPathEvalExpression(BAD_CAST
6553 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6559 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6560 /* Iterate over all users */
6561 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6563 /* Prepare structure */
6564 item
= calloc(1, sizeof(*item
));
6569 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6570 if (i
== 0) *users
= item
;
6571 else prev_item
->next
= item
;
6575 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6576 err
= extract_DbUserInfo(context
,
6577 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6578 if (err
) goto leave
;
6584 isds_list_free(users
);
6587 xmlXPathFreeObject(result
);
6588 xmlXPathFreeContext(xpath_ctx
);
6589 xmlFreeDoc(response
);
6592 isds_log(ILF_ISDS
, ILL_DEBUG
,
6593 _("GetDataBoxUsers request processed by server "
6594 "successfully.\n"));
6595 #else /* not HAVE_LIBCURL */
6603 /* Update data about user assigned to given box.
6604 * @context is session context
6605 * @box is box identification
6606 * @old_user identifies user to update
6607 * @new_user are updated data about @old_user
6608 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6609 * NULL, if you don't care.*/
6610 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6611 const struct isds_DbOwnerInfo
*box
,
6612 const struct isds_DbUserInfo
*old_user
,
6613 const struct isds_DbUserInfo
*new_user
,
6615 isds_error err
= IE_SUCCESS
;
6617 xmlNsPtr isds_ns
= NULL
;
6618 xmlNodePtr request
= NULL
;
6623 if (!context
) return IE_INVALID_CONTEXT
;
6624 zfree(context
->long_message
);
6625 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6629 /* Build UpdateDataBoxUser request */
6630 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6632 isds_log_message(context
,
6633 _("Could build UpdateDataBoxUser request"));
6636 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6638 isds_log_message(context
, _("Could not create ISDS name space"));
6639 xmlFreeNode(request
);
6642 xmlSetNs(request
, isds_ns
);
6644 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6645 err
= insert_DbOwnerInfo(context
, box
, node
);
6646 if (err
) goto leave
;
6648 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6649 err
= insert_DbUserInfo(context
, old_user
, node
);
6650 if (err
) goto leave
;
6652 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6653 err
= insert_DbUserInfo(context
, new_user
, node
);
6654 if (err
) goto leave
;
6656 /* Send it to server and process response */
6657 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6658 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6661 xmlFreeNode(request
);
6662 #else /* not HAVE_LIBCURL */
6670 /* Undocumented function.
6671 * @context is session context
6672 * @box_id is UTF-8 encoded box identifier
6673 * @token is UTF-8 encoded temporary password
6674 * @user_id outputs UTF-8 encoded reallocated user identifier
6675 * @password outpus UTF-8 encoded reallocated user password
6676 * Output arguments will be nulled in case of error */
6677 isds_error
isds_activate(struct isds_ctx
*context
,
6678 const char *box_id
, const char *token
,
6679 char **user_id
, char **password
) {
6680 isds_error err
= IE_SUCCESS
;
6682 xmlNsPtr isds_ns
= NULL
;
6683 xmlNodePtr request
= NULL
, node
;
6684 xmlDocPtr response
= NULL
;
6685 xmlXPathContextPtr xpath_ctx
= NULL
;
6686 xmlXPathObjectPtr result
= NULL
;
6690 if (!context
) return IE_INVALID_CONTEXT
;
6691 zfree(context
->long_message
);
6693 if (user_id
) zfree(*user_id
);
6694 if (password
) zfree(*password
);
6696 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6700 /* Build Activate request */
6701 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6703 isds_log_message(context
, _("Could build Activate request"));
6706 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6708 isds_log_message(context
, _("Could not create ISDS name space"));
6709 xmlFreeNode(request
);
6712 xmlSetNs(request
, isds_ns
);
6714 INSERT_STRING(request
, "dbAccessDataId", token
);
6715 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6716 INSERT_STRING(request
, "dbID", box_id
);
6719 /* Send request and check response*/
6720 err
= send_destroy_request_check_response(context
,
6721 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6722 &response
, NULL
, NULL
);
6723 if (err
) goto leave
;
6727 xpath_ctx
= xmlXPathNewContext(response
);
6732 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6736 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6742 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6743 isds_log_message(context
, _("Missing ActivateResponse element"));
6747 if (result
->nodesetval
->nodeNr
> 1) {
6748 isds_log_message(context
, _("Multiple ActivateResponse element"));
6752 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6753 xmlXPathFreeObject(result
); result
= NULL
;
6755 EXTRACT_STRING("isds:userId", *user_id
);
6757 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6758 "but did not return `userId' element.\n"));
6760 EXTRACT_STRING("isds:password", *password
);
6762 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6763 "but did not return `password' element.\n"));
6766 xmlXPathFreeObject(result
);
6767 xmlXPathFreeContext(xpath_ctx
);
6768 xmlFreeDoc(response
);
6769 xmlFreeNode(request
);
6772 isds_log(ILF_ISDS
, ILL_DEBUG
,
6773 _("Activate request processed by server successfully.\n"));
6774 #else /* not HAVE_LIBCURL */
6782 /* Reset credentials of user assigned to given box.
6783 * @context is session context
6784 * @box is box identification
6785 * @user identifies user to reset password
6786 * @fee_paid is true if fee has been paid, false otherwise
6787 * @approval is optional external approval of box manipulation
6788 * @credentials_delivery is NULL if new password should be delivered off-line
6789 * to the user. It is valid pointer if user should obtain new password on-line
6790 * on dedicated web server. Then input @credentials_delivery.email value is
6791 * user's e-mail address user must provide to dedicated web server together
6792 * with @credentials_delivery.token. The output reallocated token user needs
6793 * to use to authorize on the web server to view his new password. Output
6794 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6795 * ISDS changed up on this call. (No reason why server could change the name
6797 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6798 * NULL, if you don't care.*/
6799 isds_error
isds_reset_password(struct isds_ctx
*context
,
6800 const struct isds_DbOwnerInfo
*box
,
6801 const struct isds_DbUserInfo
*user
,
6802 const _Bool fee_paid
, const struct isds_approval
*approval
,
6803 struct isds_credentials_delivery
*credentials_delivery
,
6805 isds_error err
= IE_SUCCESS
;
6807 xmlNsPtr isds_ns
= NULL
;
6808 xmlNodePtr request
= NULL
, node
;
6809 xmlDocPtr response
= NULL
;
6813 if (!context
) return IE_INVALID_CONTEXT
;
6814 zfree(context
->long_message
);
6816 if (credentials_delivery
) {
6817 zfree(credentials_delivery
->token
);
6818 zfree(credentials_delivery
->new_user_name
);
6820 if (!box
|| !user
) return IE_INVAL
;
6824 /* Build NewAccessData request */
6825 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6827 isds_log_message(context
,
6828 _("Could build NewAccessData request"));
6831 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6833 isds_log_message(context
, _("Could not create ISDS name space"));
6834 xmlFreeNode(request
);
6837 xmlSetNs(request
, isds_ns
);
6839 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6840 err
= insert_DbOwnerInfo(context
, box
, node
);
6841 if (err
) goto leave
;
6843 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6844 err
= insert_DbUserInfo(context
, user
, node
);
6845 if (err
) goto leave
;
6847 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6849 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6850 if (err
) goto leave
;
6852 err
= insert_GExtApproval(context
, approval
, request
);
6853 if (err
) goto leave
;
6855 /* Send request and check response*/
6856 err
= send_destroy_request_check_response(context
,
6857 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6858 &response
, (xmlChar
**) refnumber
, NULL
);
6859 if (err
) goto leave
;
6862 /* Extract optional token */
6863 err
= extract_credentials_delivery(context
, credentials_delivery
,
6864 response
, "NewAccessData");
6867 xmlFreeDoc(response
);
6868 xmlFreeNode(request
);
6871 isds_log(ILF_ISDS
, ILL_DEBUG
,
6872 _("NewAccessData request processed by server "
6873 "successfully.\n"));
6874 #else /* not HAVE_LIBCURL */
6882 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6883 * code, destroy response and log success.
6884 * @context is ISDS session context.
6885 * @service_name is name of SERVICE_DB_MANIPULATION service
6886 * @box is box identification
6887 * @user identifies user to remove
6888 * @credentials_delivery is NULL if new user's password should be delivered
6889 * off-line to the user. It is valid pointer if user should obtain new
6890 * password on-line on dedicated web server. Then input
6891 * @credentials_delivery.email value is user's e-mail address user must
6892 * provide to dedicated web server together with @credentials_delivery.token.
6893 * The output reallocated token user needs to use to authorize on the web
6894 * server to view his new password. Output reallocated
6895 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6896 * assingned or changed up on this call.
6897 * @approval is optional external approval of box manipulation
6898 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6899 * NULL, if you don't care. */
6900 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6901 struct isds_ctx
*context
, const xmlChar
*service_name
,
6902 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6903 struct isds_credentials_delivery
*credentials_delivery
,
6904 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6905 isds_error err
= IE_SUCCESS
;
6907 xmlNsPtr isds_ns
= NULL
;
6908 xmlNodePtr request
= NULL
, node
;
6909 xmlDocPtr response
= NULL
;
6913 if (!context
) return IE_INVALID_CONTEXT
;
6914 zfree(context
->long_message
);
6915 if (credentials_delivery
) {
6916 zfree(credentials_delivery
->token
);
6917 zfree(credentials_delivery
->new_user_name
);
6919 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6924 /* Build NewAccessData or similar request */
6925 request
= xmlNewNode(NULL
, service_name
);
6927 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6928 isds_printf_message(context
, _("Could not build %s request"),
6929 service_name_locale
);
6930 free(service_name_locale
);
6933 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6935 isds_log_message(context
, _("Could not create ISDS name space"));
6936 xmlFreeNode(request
);
6939 xmlSetNs(request
, isds_ns
);
6941 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6942 err
= insert_DbOwnerInfo(context
, box
, node
);
6943 if (err
) goto leave
;
6945 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6946 err
= insert_DbUserInfo(context
, user
, node
);
6947 if (err
) goto leave
;
6949 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6950 if (err
) goto leave
;
6952 err
= insert_GExtApproval(context
, approval
, request
);
6953 if (err
) goto leave
;
6956 /* Send request and check response*/
6957 err
= send_destroy_request_check_response(context
,
6958 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6961 xmlFreeNode(request
);
6964 /* Pick up credentials_delivery if requested */
6965 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6966 (char *)service_name
);
6969 xmlFreeDoc(response
);
6970 if (request
) xmlFreeNode(request
);
6973 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6974 isds_log(ILF_ISDS
, ILL_DEBUG
,
6975 _("%s request processed by server successfully.\n"),
6976 service_name_locale
);
6977 free(service_name_locale
);
6979 #else /* not HAVE_LIBCURL */
6987 /* Assign new user to given box.
6988 * @context is session context
6989 * @box is box identification
6990 * @user defines new user to add
6991 * @credentials_delivery is NULL if new user's password should be delivered
6992 * off-line to the user. It is valid pointer if user should obtain new
6993 * password on-line on dedicated web server. Then input
6994 * @credentials_delivery.email value is user's e-mail address user must
6995 * provide to dedicated web server together with @credentials_delivery.token.
6996 * The output reallocated token user needs to use to authorize on the web
6997 * server to view his new password. Output reallocated
6998 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6999 * assingned up on this call.
7000 * @approval is optional external approval of box manipulation
7001 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7002 * NULL, if you don't care.*/
7003 isds_error
isds_add_user(struct isds_ctx
*context
,
7004 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7005 struct isds_credentials_delivery
*credentials_delivery
,
7006 const struct isds_approval
*approval
, char **refnumber
) {
7007 return build_send_manipulationboxuser_request_check_drop_response(context
,
7008 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
7009 approval
, (xmlChar
**) refnumber
);
7013 /* Remove user assigned to given box.
7014 * @context is session context
7015 * @box is box identification
7016 * @user identifies user to remove
7017 * @approval is optional external approval of box manipulation
7018 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7019 * NULL, if you don't care.*/
7020 isds_error
isds_delete_user(struct isds_ctx
*context
,
7021 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7022 const struct isds_approval
*approval
, char **refnumber
) {
7023 return build_send_manipulationboxuser_request_check_drop_response(context
,
7024 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
7025 (xmlChar
**) refnumber
);
7029 /* Get list of boxes in ZIP archive.
7030 * @context is session context
7031 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7032 * System recognizes following values currently: ALL (all boxes), UPG
7033 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
7034 * receiving commercial messages). This argument is a string because
7035 * specification states new values can appear in the future. Not all list
7036 * types are available to all users.
7037 * @buffer is automatically reallocated memory to store the list of boxes. The
7038 * list is zipped CSV file.
7039 * @buffer_length is size of @buffer data in bytes.
7040 * In case of error @buffer will be freed and @buffer_length will be
7042 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7043 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7044 isds_error err
= IE_SUCCESS
;
7046 xmlNsPtr isds_ns
= NULL
;
7047 xmlNodePtr request
= NULL
, node
;
7048 xmlDocPtr response
= NULL
;
7049 xmlXPathContextPtr xpath_ctx
= NULL
;
7050 xmlXPathObjectPtr result
= NULL
;
7051 char *string
= NULL
;
7055 if (!context
) return IE_INVALID_CONTEXT
;
7056 zfree(context
->long_message
);
7057 if (buffer
) zfree(*buffer
);
7058 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7062 /* Check if connection is established
7063 * TODO: This check should be done downstairs. */
7064 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7067 /* Build AuthenticateMessage request */
7068 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7070 isds_log_message(context
,
7071 _("Could not build GetDataBoxList request"));
7074 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7076 isds_log_message(context
, _("Could not create ISDS name space"));
7077 xmlFreeNode(request
);
7080 xmlSetNs(request
, isds_ns
);
7081 INSERT_STRING(request
, "dblType", list_identifier
);
7083 /* Send request to server and process response */
7084 err
= send_destroy_request_check_response(context
,
7085 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7086 &response
, NULL
, NULL
);
7087 if (err
) goto leave
;
7090 /* Extract Base-64 encoded ZIP file */
7091 xpath_ctx
= xmlXPathNewContext(response
);
7096 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7100 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7102 /* Decode non-empty archive */
7103 if (string
&& string
[0] != '\0') {
7104 *buffer_length
= _isds_b64decode(string
, buffer
);
7105 if (*buffer_length
== (size_t) -1) {
7106 isds_printf_message(context
,
7107 _("Error while Base64-decoding box list archive"));
7116 xmlXPathFreeObject(result
);
7117 xmlXPathFreeContext(xpath_ctx
);
7118 xmlFreeDoc(response
);
7119 xmlFreeNode(request
);
7122 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7123 "processed by server successfully.\n"));
7125 #else /* not HAVE_LIBCURL */
7133 /* Find boxes suiting given criteria.
7134 * @criteria is filter. You should fill in at least some members.
7135 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7136 * possibly empty. Input NULL or valid old structure.
7138 * IE_SUCCESS if search succeeded, @boxes contains useful data
7139 * IE_NOEXIST if no such box exists, @boxes will be NULL
7140 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7141 * contains still valid data
7142 * other code if something bad happens. @boxes will be NULL. */
7143 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7144 const struct isds_DbOwnerInfo
*criteria
,
7145 struct isds_list
**boxes
) {
7146 isds_error err
= IE_SUCCESS
;
7148 _Bool truncated
= 0;
7149 xmlNsPtr isds_ns
= NULL
;
7150 xmlNodePtr request
= NULL
;
7151 xmlDocPtr response
= NULL
;
7152 xmlChar
*code
= NULL
, *message
= NULL
;
7153 xmlNodePtr db_owner_info
;
7154 xmlXPathContextPtr xpath_ctx
= NULL
;
7155 xmlXPathObjectPtr result
= NULL
;
7156 xmlChar
*string
= NULL
;
7160 if (!context
) return IE_INVALID_CONTEXT
;
7161 zfree(context
->long_message
);
7162 if (!boxes
) return IE_INVAL
;
7163 isds_list_free(boxes
);
7170 /* Check if connection is established
7171 * TODO: This check should be done downstairs. */
7172 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7175 /* Build FindDataBox request */
7176 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7178 isds_log_message(context
,
7179 _("Could build FindDataBox request"));
7182 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7184 isds_log_message(context
, _("Could not create ISDS name space"));
7185 xmlFreeNode(request
);
7188 xmlSetNs(request
, isds_ns
);
7189 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7190 if (!db_owner_info
) {
7191 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7192 "FindDataBox element"));
7193 xmlFreeNode(request
);
7197 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7198 if (err
) goto leave
;
7201 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7204 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7206 /* Destroy request */
7207 xmlFreeNode(request
); request
= NULL
;
7210 isds_log(ILF_ISDS
, ILL_DEBUG
,
7211 _("Processing ISDS response on FindDataBox "
7212 "request failed\n"));
7216 /* Check for response status */
7217 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7218 &code
, &message
, NULL
);
7220 isds_log(ILF_ISDS
, ILL_DEBUG
,
7221 _("ISDS response on FindDataBox request is missing status\n"));
7225 /* Request processed, but nothing found */
7226 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7227 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7228 char *code_locale
= _isds_utf82locale((char*)code
);
7229 char *message_locale
= _isds_utf82locale((char*)message
);
7230 isds_log(ILF_ISDS
, ILL_DEBUG
,
7231 _("Server did not found any box on FindDataBox request "
7232 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7233 isds_log_message(context
, message_locale
);
7235 free(message_locale
);
7240 /* Warning, not a error */
7241 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7242 char *code_locale
= _isds_utf82locale((char*)code
);
7243 char *message_locale
= _isds_utf82locale((char*)message
);
7244 isds_log(ILF_ISDS
, ILL_DEBUG
,
7245 _("Server truncated response on FindDataBox request "
7246 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7247 isds_log_message(context
, message_locale
);
7249 free(message_locale
);
7254 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7255 char *code_locale
= _isds_utf82locale((char*)code
);
7256 char *message_locale
= _isds_utf82locale((char*)message
);
7257 isds_log(ILF_ISDS
, ILL_DEBUG
,
7258 _("Server refused FindDataBox request "
7259 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7260 isds_log_message(context
, message_locale
);
7262 free(message_locale
);
7267 xpath_ctx
= xmlXPathNewContext(response
);
7272 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7277 /* Extract boxes if they present */
7278 result
= xmlXPathEvalExpression(BAD_CAST
7279 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7285 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7286 struct isds_list
*item
, *prev_item
= NULL
;
7287 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7288 item
= calloc(1, sizeof(*item
));
7294 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7295 if (i
== 0) *boxes
= item
;
7296 else prev_item
->next
= item
;
7299 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7300 err
= extract_DbOwnerInfo(context
,
7301 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7302 if (err
) goto leave
;
7308 isds_list_free(boxes
);
7310 if (truncated
) err
= IE_2BIG
;
7314 xmlFreeNode(request
);
7315 xmlXPathFreeObject(result
);
7316 xmlXPathFreeContext(xpath_ctx
);
7320 xmlFreeDoc(response
);
7323 isds_log(ILF_ISDS
, ILL_DEBUG
,
7324 _("FindDataBox request processed by server successfully.\n"));
7325 #else /* not HAVE_LIBCURL */
7334 /* Convert a string with match markers into a plain string with list of
7335 * pointers to the matches
7336 * @string is an UTF-8 encoded non-constant string with match markers
7337 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7338 * The markers will be removed from the string.
7339 * @starts is a reallocated list of static pointers into the @string pointing
7340 * to places where match start markers occured.
7341 * @ends is a reallocated list of static pointers into the @string pointing
7342 * to places where match end markers occured.
7343 * @return IE_SUCCESS in case of no failure. */
7344 static isds_error
interpret_matches(xmlChar
*string
,
7345 struct isds_list
**starts
, struct isds_list
**ends
) {
7346 isds_error err
= IE_SUCCESS
;
7347 xmlChar
*pointer
, *destination
, *source
;
7348 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7350 isds_list_free(starts
);
7351 isds_list_free(ends
);
7352 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7353 if (NULL
== string
) return IE_SUCCESS
;
7355 for (pointer
= string
; *pointer
!= '\0';) {
7356 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7357 /* Remove the start marker */
7358 for (source
= pointer
+ 14, destination
= pointer
;
7359 *source
!= '\0'; source
++, destination
++) {
7360 *destination
= *source
;
7362 *destination
= '\0';
7363 /* Append the pointer into the list */
7364 item
= calloc(1, sizeof(*item
));
7369 item
->destructor
= (void (*)(void **))NULL
;
7370 item
->data
= pointer
;
7371 if (NULL
== prev_start
) *starts
= item
;
7372 else prev_start
->next
= item
;
7374 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7375 /* Remove the end marker */
7376 for (source
= pointer
+ 12, destination
= pointer
;
7377 *source
!= '\0'; source
++, destination
++) {
7378 *destination
= *source
;
7380 *destination
= '\0';
7381 /* Append the pointer into the list */
7382 item
= calloc(1, sizeof(*item
));
7387 item
->destructor
= (void (*)(void **))NULL
;
7388 item
->data
= pointer
;
7389 if (NULL
== prev_end
) *ends
= item
;
7390 else prev_end
->next
= item
;
7399 isds_list_free(starts
);
7400 isds_list_free(ends
);
7406 /* Convert isds:dbResult XML tree into structure
7407 * @context is ISDS context.
7408 * @fulltext_result is automatically reallocated found box structure.
7409 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7410 * @collect_matches is true to interpret match markers.
7411 * In case of error @result will be freed. */
7412 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7413 struct isds_fulltext_result
**fulltext_result
,
7414 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7415 isds_error err
= IE_SUCCESS
;
7416 xmlXPathObjectPtr result
= NULL
;
7417 char *string
= NULL
;
7419 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7420 if (NULL
== fulltext_result
) return IE_INVAL
;
7421 isds_fulltext_result_free(fulltext_result
);
7422 if (!xpath_ctx
) return IE_INVAL
;
7425 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7426 if (NULL
== *fulltext_result
) {
7432 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7434 EXTRACT_STRING("isds:dbType", string
);
7435 if (NULL
== string
) {
7437 isds_log_message(context
, _("Empty isds:dbType element"));
7440 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7442 if (err
== IE_ENUM
) {
7444 char *string_locale
= _isds_utf82locale(string
);
7445 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7447 free(string_locale
);
7453 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7454 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7456 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7457 if (err
) goto leave
;
7459 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7460 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7461 (*fulltext_result
)->dbEffectiveOVM
);
7463 EXTRACT_STRING("isds:dbSendOptions", string
);
7464 if (NULL
== string
) {
7466 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7469 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7470 (*fulltext_result
)->active
= 1;
7471 (*fulltext_result
)->public_sending
= 1;
7472 (*fulltext_result
)->commercial_sending
= 0;
7473 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7474 (*fulltext_result
)->active
= 1;
7475 (*fulltext_result
)->public_sending
= 1;
7476 (*fulltext_result
)->commercial_sending
= 1;
7477 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7478 (*fulltext_result
)->active
= 1;
7479 (*fulltext_result
)->public_sending
= 0;
7480 (*fulltext_result
)->commercial_sending
= 0;
7481 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7482 (*fulltext_result
)->active
= 1;
7483 (*fulltext_result
)->public_sending
= 0;
7484 (*fulltext_result
)->commercial_sending
= 0;
7485 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7486 (*fulltext_result
)->active
= 0;
7487 (*fulltext_result
)->public_sending
= 0;
7488 (*fulltext_result
)->commercial_sending
= 0;
7491 char *string_locale
= _isds_utf82locale(string
);
7492 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7494 free(string_locale
);
7499 /* Interpret match marks */
7500 if (collect_matches
) {
7501 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7502 &((*fulltext_result
)->name_match_start
),
7503 &((*fulltext_result
)->name_match_end
));
7504 if (err
) goto leave
;
7505 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7506 &((*fulltext_result
)->address_match_start
),
7507 &((*fulltext_result
)->address_match_end
));
7508 if (err
) goto leave
;
7512 if (err
) isds_fulltext_result_free(fulltext_result
);
7514 xmlXPathFreeObject(result
);
7517 #endif /* HAVE_LIBCURL */
7520 /* Find boxes matching a given full-text criteria.
7521 * @context is a session context
7522 * @query is a non-empty string which consists of words to search
7523 * @target selects box attributes to search for @query words. Pass NULL if you
7525 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7526 * to search in all box types. Pass NULL to let server to use default value
7527 * which is DBTYPE_SYSTEM.
7528 * @page_size defines count of boxes to constitute a response page. It counts
7529 * from zero. Pass NULL to let server to use a default value (50 now).
7530 * @page_number defines ordinar number of the response page to return. It
7531 * counts from zero. Pass NULL to let server to use a default value (0 now).
7532 * @track_matches points to true for marking @query words found in the box
7533 * attributes. It points to false for not marking. Pass NULL to let the server
7534 * to use default value (false now).
7535 * @total_matching_boxes outputs number of all boxes matching the query. Pass
7536 * NULL if you don't care.
7537 * @current_page_beginning outputs ordinar number of first box in this @boxes
7538 * page. It counts from zero. Pass NULL if you don't care.
7539 * @current_page_size outputs count of boxes in the this @boxes page. Pass
7540 * NULL if you don't care.
7541 * @last_page outputs true if this page is the last one, false otherwise. Pass
7542 * NULL if you don't care.
7543 * @boxes is automatically reallocated list of isds_fulltext_result structures,
7546 * IE_SUCCESS if search succeeded, @boxes contains useful data
7547 * IE_2BIG if @page_size is too large
7548 * other code if something bad happens. @boxes will be NULL. */
7549 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7551 const isds_fulltext_target
*target
,
7552 const isds_DbType
*box_type
,
7553 const unsigned long int *page_size
,
7554 const unsigned long int *page_number
,
7555 const _Bool
*track_matches
,
7556 unsigned long int *total_matching_boxes
,
7557 unsigned long int *current_page_beginning
,
7558 unsigned long int *current_page_size
,
7560 struct isds_list
**boxes
) {
7561 isds_error err
= IE_SUCCESS
;
7563 xmlNsPtr isds_ns
= NULL
;
7564 xmlNodePtr request
= NULL
;
7565 xmlDocPtr response
= NULL
;
7567 xmlXPathContextPtr xpath_ctx
= NULL
;
7568 xmlXPathObjectPtr result
= NULL
;
7569 const xmlChar
*static_string
= NULL
;
7570 xmlChar
*string
= NULL
;
7572 const xmlChar
*codes
[] = {
7582 const char *meanings
[] = {
7583 N_("You are not allowed to perform the search"),
7584 N_("The query string is empty"),
7585 N_("Searched box ID is malformed"),
7586 N_("Searched organization ID is malformed"),
7587 N_("Invalid input"),
7588 N_("Requested page size is too large"),
7589 N_("Search engine internal error")
7591 const isds_error errors
[] = {
7600 struct code_map_isds_error map
= {
7602 .meanings
= meanings
,
7608 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7609 zfree(context
->long_message
);
7611 if (NULL
== boxes
) return IE_INVAL
;
7612 isds_list_free(boxes
);
7614 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7615 isds_log_message(context
, _("Query string must be non-empty"));
7620 /* Check if connection is established
7621 * TODO: This check should be done downstairs. */
7622 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7624 /* Build FindDataBox request */
7625 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7626 if (NULL
== request
) {
7627 isds_log_message(context
,
7628 _("Could build ISDSSearch2 request"));
7631 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7632 if(NULL
== isds_ns
) {
7633 isds_log_message(context
, _("Could not create ISDS name space"));
7634 xmlFreeNode(request
);
7637 xmlSetNs(request
, isds_ns
);
7639 INSERT_STRING(request
, "searchText", query
);
7641 if (NULL
!= target
) {
7642 static_string
= isds_fulltext_target2string(*(target
));
7643 if (NULL
== static_string
) {
7644 isds_printf_message(context
, _("Invalid target value: %d"),
7650 INSERT_STRING(request
, "searchType", static_string
);
7651 static_string
= NULL
;
7653 if (NULL
!= box_type
) {
7654 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7655 if (DBTYPE_SYSTEM
== *box_type
) {
7656 static_string
= BAD_CAST
"ALL";
7658 static_string
= isds_DbType2string(*(box_type
));
7659 if (NULL
== static_string
) {
7660 isds_printf_message(context
, _("Invalid box type value: %d"),
7667 INSERT_STRING(request
, "searchScope", static_string
);
7668 static_string
= NULL
;
7670 INSERT_ULONGINT(request
, "page", page_number
, string
);
7671 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7672 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7674 /* Send request and check response */
7675 err
= send_destroy_request_check_response(context
,
7676 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7677 &request
, &response
, NULL
, &map
);
7678 if (err
) goto leave
;
7680 /* Parse response */
7681 xpath_ctx
= xmlXPathNewContext(response
);
7682 if (NULL
== xpath_ctx
) {
7686 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7691 /* Extract counters */
7692 if (NULL
!= total_matching_boxes
) {
7693 EXTRACT_ULONGINT("isds:totalCount", total_matching_boxes
, 1);
7695 if (NULL
!= current_page_size
) {
7696 EXTRACT_ULONGINT("isds:currentCount", current_page_size
, 1);
7698 if (NULL
!= current_page_beginning
) {
7699 EXTRACT_ULONGINT("isds:position", current_page_beginning
, 1);
7701 if (NULL
!= last_page
) {
7702 EXTRACT_BOOLEAN("isds:lastPage", last_page
);
7705 /* Extract boxes if they present */
7706 result
= xmlXPathEvalExpression(BAD_CAST
7707 "/isds:ISDSSearch2/isds:dbResults/isds:dbResult", xpath_ctx
);
7708 if (NULL
== result
) {
7712 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7713 struct isds_list
*item
, *prev_item
= NULL
;
7714 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7715 item
= calloc(1, sizeof(*item
));
7721 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7722 if (i
== 0) *boxes
= item
;
7723 else prev_item
->next
= item
;
7726 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7727 err
= extract_dbResult(context
,
7728 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7729 (NULL
== track_matches
) ? 0 : *track_matches
);
7730 if (err
) goto leave
;
7736 isds_list_free(boxes
);
7740 xmlFreeNode(request
);
7741 xmlXPathFreeObject(result
);
7742 xmlXPathFreeContext(xpath_ctx
);
7743 xmlFreeDoc(response
);
7746 isds_log(ILF_ISDS
, ILL_DEBUG
,
7747 _("ISDSSearch2 request processed by server successfully.\n"));
7748 #else /* not HAVE_LIBCURL */
7756 /* Get status of a box.
7757 * @context is ISDS session context.
7758 * @box_id is UTF-8 encoded box identifier as zero terminated string
7759 * @box_status is return value of box status.
7761 * IE_SUCCESS if box has been found and its status retrieved
7762 * IE_NOEXIST if box is not known to ISDS server
7763 * or other appropriate error.
7764 * You can use isds_DbState to enumerate box status. However out of enum
7765 * range value can be returned too. This is feature because ISDS
7766 * specification leaves the set of values open.
7767 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7768 * the box has been deleted, but ISDS still lists its former existence. */
7769 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7770 long int *box_status
) {
7771 isds_error err
= IE_SUCCESS
;
7773 xmlNsPtr isds_ns
= NULL
;
7774 xmlNodePtr request
= NULL
, db_id
;
7775 xmlDocPtr response
= NULL
;
7776 xmlXPathContextPtr xpath_ctx
= NULL
;
7777 xmlXPathObjectPtr result
= NULL
;
7778 xmlChar
*string
= NULL
;
7780 const xmlChar
*codes
[] = {
7786 const char *meanings
[] = {
7787 "The box does not exist",
7788 "Box ID is malformed",
7791 const isds_error errors
[] = {
7796 struct code_map_isds_error map
= {
7798 .meanings
= meanings
,
7803 if (!context
) return IE_INVALID_CONTEXT
;
7804 zfree(context
->long_message
);
7805 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7808 /* Check if connection is established
7809 * TODO: This check should be done downstairs. */
7810 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7813 /* Build CheckDataBox request */
7814 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7816 isds_log_message(context
,
7817 _("Could build CheckDataBox request"));
7820 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7822 isds_log_message(context
, _("Could not create ISDS name space"));
7823 xmlFreeNode(request
);
7826 xmlSetNs(request
, isds_ns
);
7827 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7829 isds_log_message(context
, _("Could not add dbID child to "
7830 "CheckDataBox element"));
7831 xmlFreeNode(request
);
7836 /* Send request and check response*/
7837 err
= send_destroy_request_check_response(context
,
7838 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7839 &request
, &response
, NULL
, &map
);
7840 if (err
) goto leave
;
7844 xpath_ctx
= xmlXPathNewContext(response
);
7849 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7853 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7859 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7860 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7864 if (result
->nodesetval
->nodeNr
> 1) {
7865 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7869 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7870 xmlXPathFreeObject(result
); result
= NULL
;
7872 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7877 xmlXPathFreeObject(result
);
7878 xmlXPathFreeContext(xpath_ctx
);
7880 xmlFreeDoc(response
);
7883 isds_log(ILF_ISDS
, ILL_DEBUG
,
7884 _("CheckDataBox request processed by server successfully.\n"));
7885 #else /* not HAVE_LIBCURL */
7893 /* Get list of permissions to send commercial messages.
7894 * @context is ISDS session context.
7895 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7896 * @permissions is a reallocated list of permissions (struct
7897 * isds_commercial_permission*) to send commercial messages from @box_id. The
7898 * order of permissions is significant as the server applies the permissions
7899 * and associated pre-paid credits in the order. Empty list means no
7902 * IE_SUCCESS if the list has been obtained correctly,
7903 * or other appropriate error. */
7904 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7905 const char *box_id
, struct isds_list
**permissions
) {
7906 isds_error err
= IE_SUCCESS
;
7908 xmlDocPtr response
= NULL
;
7909 xmlXPathContextPtr xpath_ctx
= NULL
;
7910 xmlXPathObjectPtr result
= NULL
;
7913 if (!context
) return IE_INVALID_CONTEXT
;
7914 zfree(context
->long_message
);
7915 if (NULL
== permissions
) return IE_INVAL
;
7916 isds_list_free(permissions
);
7917 if (NULL
== box_id
) return IE_INVAL
;
7920 /* Check if connection is established */
7921 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7923 /* Do request and check for success */
7924 err
= build_send_dbid_request_check_response(context
,
7925 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7926 BAD_CAST box_id
, NULL
, &response
, NULL
);
7928 isds_log(ILF_ISDS
, ILL_DEBUG
,
7929 _("PDZInfo request processed by server successfully.\n"));
7933 /* Prepare structure */
7934 xpath_ctx
= xmlXPathNewContext(response
);
7939 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7944 /* Set context node */
7945 result
= xmlXPathEvalExpression(BAD_CAST
7946 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7952 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7953 struct isds_list
*prev_item
= NULL
;
7955 /* Iterate over all permission records */
7956 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7957 struct isds_list
*item
;
7959 /* Prepare structure */
7960 item
= calloc(1, sizeof(*item
));
7965 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
7966 if (i
== 0) *permissions
= item
;
7967 else prev_item
->next
= item
;
7971 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7972 err
= extract_DbPDZRecord(context
,
7973 (struct isds_commercial_permission
**) (&item
->data
),
7975 if (err
) goto leave
;
7981 isds_list_free(permissions
);
7984 xmlXPathFreeObject(result
);
7985 xmlXPathFreeContext(xpath_ctx
);
7986 xmlFreeDoc(response
);
7988 #else /* not HAVE_LIBCURL */
7996 /* Get details about credit for sending pre-paid commercial messages.
7997 * @context is ISDS session context.
7998 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7999 * @from_date is first day of credit history to return in @history. Only
8000 * tm_year, tm_mon and tm_mday carry sane value.
8001 * @to_date is last day of credit history to return in @history. Only
8002 * tm_year, tm_mon and tm_mday carry sane value.
8003 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8004 * if you don't care. This and all other credit values are integers in
8005 * hundredths of Czech Crowns.
8006 * @email outputs notification e-mail address where notifications about credit
8007 * are sent. This is automatically reallocated string. Pass NULL if you don't
8008 * care. It can return NULL if no address is defined.
8009 * @history outputs auto-reallocated list of pointers to struct
8010 * isds_credit_event. Events in closed interval @from_time to @to_time are
8011 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8012 * are sorted by time.
8014 * IE_SUCCESS if the credit details have been obtained correctly,
8015 * or other appropriate error. Please note that server allows to retrieve
8016 * only limited history of events. */
8017 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8019 const struct tm
*from_date
, const struct tm
*to_date
,
8020 long int *credit
, char **email
, struct isds_list
**history
) {
8021 isds_error err
= IE_SUCCESS
;
8023 char *box_id_locale
= NULL
;
8024 xmlNodePtr request
= NULL
, node
;
8025 xmlNsPtr isds_ns
= NULL
;
8026 xmlChar
*string
= NULL
;
8028 xmlDocPtr response
= NULL
;
8029 xmlXPathContextPtr xpath_ctx
= NULL
;
8030 xmlXPathObjectPtr result
= NULL
;
8032 const xmlChar
*codes
[] = {
8040 const char *meanings
[] = {
8041 "Insufficient priviledges for the box",
8042 "The box does not exist",
8043 "Date is too long (history is not available after 15 months)",
8044 "Interval is too long (limit is 3 months)",
8047 const isds_error errors
[] = {
8054 struct code_map_isds_error map
= {
8056 .meanings
= meanings
,
8061 if (!context
) return IE_INVALID_CONTEXT
;
8062 zfree(context
->long_message
);
8064 /* Free output argument */
8065 if (NULL
!= credit
) *credit
= 0;
8066 if (NULL
!= email
) zfree(*email
);
8067 isds_list_free(history
);
8069 if (NULL
== box_id
) return IE_INVAL
;
8072 /* Check if connection is established */
8073 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8075 box_id_locale
= _isds_utf82locale((char*)box_id
);
8076 if (NULL
== box_id_locale
) {
8082 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8083 if (NULL
== request
) {
8084 isds_printf_message(context
,
8085 _("Could not build DataBoxCreditInfo request for %s box"),
8090 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8092 isds_log_message(context
, _("Could not create ISDS name space"));
8096 xmlSetNs(request
, isds_ns
);
8098 /* Add mandatory XSD:tIdDbInput child */
8099 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8100 /* Add mandatory dates elements with optional values */
8102 err
= tm2datestring(from_date
, &string
);
8104 isds_log_message(context
,
8105 _("Could not convert `from_date' argument to ISO date "
8109 INSERT_STRING(request
, "ciFromDate", string
);
8112 INSERT_STRING(request
, "ciFromDate", NULL
);
8115 err
= tm2datestring(to_date
, &string
);
8117 isds_log_message(context
,
8118 _("Could not convert `to_date' argument to ISO date "
8122 INSERT_STRING(request
, "ciTodate", string
);
8125 INSERT_STRING(request
, "ciTodate", NULL
);
8128 /* Send request and check response*/
8129 err
= send_destroy_request_check_response(context
,
8130 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8131 &request
, &response
, NULL
, &map
);
8132 if (err
) goto leave
;
8136 /* Set context to the root */
8137 xpath_ctx
= xmlXPathNewContext(response
);
8142 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8146 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8152 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8153 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8157 if (result
->nodesetval
->nodeNr
> 1) {
8158 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8162 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8163 xmlXPathFreeObject(result
); result
= NULL
;
8165 /* Extract common data */
8166 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8167 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8169 /* Extract records */
8170 if (NULL
== history
) goto leave
;
8171 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8177 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8178 struct isds_list
*prev_item
= NULL
;
8180 /* Iterate over all records */
8181 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8182 struct isds_list
*item
;
8184 /* Prepare structure */
8185 item
= calloc(1, sizeof(*item
));
8190 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8191 if (i
== 0) *history
= item
;
8192 else prev_item
->next
= item
;
8196 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8197 err
= extract_CiRecord(context
,
8198 (struct isds_credit_event
**) (&item
->data
),
8200 if (err
) goto leave
;
8206 isds_log(ILF_ISDS
, ILL_DEBUG
,
8207 _("DataBoxCreditInfo request processed by server successfully.\n"));
8210 isds_list_free(history
);
8211 if (NULL
!= email
) zfree(*email
)
8214 free(box_id_locale
);
8215 xmlXPathFreeObject(result
);
8216 xmlXPathFreeContext(xpath_ctx
);
8217 xmlFreeDoc(response
);
8219 #else /* not HAVE_LIBCURL */
8227 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8228 * code, destroy response and log success.
8229 * @context is ISDS session context.
8230 * @service_name is name of SERVICE_DB_MANIPULATION service
8231 * @box_id is UTF-8 encoded box identifier as zero terminated string
8232 * @approval is optional external approval of box manipulation
8233 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8234 * NULL, if you don't care. */
8235 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8236 struct isds_ctx
*context
, const xmlChar
*service_name
,
8237 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8238 xmlChar
**refnumber
) {
8239 isds_error err
= IE_SUCCESS
;
8241 xmlDocPtr response
= NULL
;
8244 if (!context
) return IE_INVALID_CONTEXT
;
8245 zfree(context
->long_message
);
8246 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8249 /* Check if connection is established */
8250 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8252 /* Do request and check for success */
8253 err
= build_send_dbid_request_check_response(context
,
8254 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8255 &response
, refnumber
);
8256 xmlFreeDoc(response
);
8259 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8260 isds_log(ILF_ISDS
, ILL_DEBUG
,
8261 _("%s request processed by server successfully.\n"),
8262 service_name_locale
);
8263 free(service_name_locale
);
8265 #else /* not HAVE_LIBCURL */
8273 /* Switch box into state where box can receive commercial messages (off by
8275 * @context is ISDS session context.
8276 * @box_id is UTF-8 encoded box identifier as zero terminated string
8277 * @allow is true for enable, false for disable commercial messages income
8278 * @approval is optional external approval of box manipulation
8279 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8280 * NULL, if you don't care. */
8281 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8282 const char *box_id
, const _Bool allow
,
8283 const struct isds_approval
*approval
, char **refnumber
) {
8284 return build_send_manipulationdbid_request_check_drop_response(context
,
8285 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8286 BAD_CAST
"ClearOpenAddressing",
8287 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8291 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8292 * message acceptance). This is just a box permission. Sender must apply
8293 * such role by sending each message.
8294 * @context is ISDS session context.
8295 * @box_id is UTF-8 encoded box identifier as zero terminated string
8296 * @allow is true for enable, false for disable OVM role permission
8297 * @approval is optional external approval of box manipulation
8298 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8299 * NULL, if you don't care. */
8300 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8301 const char *box_id
, const _Bool allow
,
8302 const struct isds_approval
*approval
, char **refnumber
) {
8303 return build_send_manipulationdbid_request_check_drop_response(context
,
8304 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8305 BAD_CAST
"ClearEffectiveOVM",
8306 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8310 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8311 * code, destroy response and log success.
8312 * @context is ISDS session context.
8313 * @service_name is name of SERVICE_DB_MANIPULATION service
8314 * @owner is structure describing box
8315 * @approval is optional external approval of box manipulation
8316 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8317 * NULL, if you don't care. */
8318 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8319 struct isds_ctx
*context
, const xmlChar
*service_name
,
8320 const struct isds_DbOwnerInfo
*owner
,
8321 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8322 isds_error err
= IE_SUCCESS
;
8324 char *service_name_locale
= NULL
;
8325 xmlNodePtr request
= NULL
, db_owner_info
;
8326 xmlNsPtr isds_ns
= NULL
;
8330 if (!context
) return IE_INVALID_CONTEXT
;
8331 zfree(context
->long_message
);
8332 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8335 service_name_locale
= _isds_utf82locale((char*)service_name
);
8336 if (!service_name_locale
) {
8342 request
= xmlNewNode(NULL
, service_name
);
8344 isds_printf_message(context
,
8345 _("Could not build %s request"), service_name_locale
);
8349 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8351 isds_log_message(context
, _("Could not create ISDS name space"));
8355 xmlSetNs(request
, isds_ns
);
8358 /* Add XSD:tOwnerInfoInput child*/
8359 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8360 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8361 if (err
) goto leave
;
8363 /* Add XSD:gExtApproval*/
8364 err
= insert_GExtApproval(context
, approval
, request
);
8365 if (err
) goto leave
;
8367 /* Send it to server and process response */
8368 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8369 service_name
, &request
, refnumber
);
8372 xmlFreeNode(request
);
8373 free(service_name_locale
);
8374 #else /* not HAVE_LIBCURL */
8382 /* Switch box accessibility state on request of box owner.
8383 * Despite the name, owner must do the request off-line. This function is
8384 * designed for such off-line meeting points (e.g. Czech POINT).
8385 * @context is ISDS session context.
8386 * @box identifies box to switch accessibility state.
8387 * @allow is true for making accessible, false to disallow access.
8388 * @approval is optional external approval of box manipulation
8389 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8390 * NULL, if you don't care. */
8391 isds_error
isds_switch_box_accessibility_on_owner_request(
8392 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8393 const _Bool allow
, const struct isds_approval
*approval
,
8395 return build_send_manipulationdbowner_request_check_drop_response(context
,
8396 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8397 BAD_CAST
"DisableOwnDataBox",
8398 box
, approval
, (xmlChar
**) refnumber
);
8402 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8404 * @context is ISDS session context.
8405 * @box identifies box to switch accessibility state.
8406 * @since is date since accessibility has been denied. This can be past too.
8407 * Only tm_year, tm_mon and tm_mday carry sane value.
8408 * @approval is optional external approval of box manipulation
8409 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8410 * NULL, if you don't care. */
8411 isds_error
isds_disable_box_accessibility_externaly(
8412 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8413 const struct tm
*since
, const struct isds_approval
*approval
,
8415 isds_error err
= IE_SUCCESS
;
8417 char *service_name_locale
= NULL
;
8418 xmlNodePtr request
= NULL
, node
;
8419 xmlNsPtr isds_ns
= NULL
;
8420 xmlChar
*string
= NULL
;
8424 if (!context
) return IE_INVALID_CONTEXT
;
8425 zfree(context
->long_message
);
8426 if (!box
|| !since
) return IE_INVAL
;
8430 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8432 isds_printf_message(context
,
8433 _("Could not build %s request"), "DisableDataBoxExternally");
8437 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8439 isds_log_message(context
, _("Could not create ISDS name space"));
8443 xmlSetNs(request
, isds_ns
);
8446 /* Add @box identification */
8447 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8448 err
= insert_DbOwnerInfo(context
, box
, node
);
8449 if (err
) goto leave
;
8451 /* Add @since date */
8452 err
= tm2datestring(since
, &string
);
8454 isds_log_message(context
,
8455 _("Could not convert `since' argument to ISO date string"));
8458 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8462 err
= insert_GExtApproval(context
, approval
, request
);
8463 if (err
) goto leave
;
8465 /* Send it to server and process response */
8466 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8467 BAD_CAST
"DisableDataBoxExternally", &request
,
8468 (xmlChar
**) refnumber
);
8472 xmlFreeNode(request
);
8473 free(service_name_locale
);
8474 #else /* not HAVE_LIBCURL */
8483 /* Insert struct isds_message data (envelope (recipient data optional) and
8484 * documents into XML tree
8485 * @context is session context
8486 * @outgoing_message is libisds structure with message data
8487 * @create_message is XML CreateMessage or CreateMultipleMessage element
8488 * @process_recipient true for recipient data serialization, false for no
8490 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8491 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8492 const _Bool process_recipient
) {
8494 isds_error err
= IE_SUCCESS
;
8495 xmlNodePtr envelope
, dm_files
, node
;
8496 xmlChar
*string
= NULL
;
8498 if (!context
) return IE_INVALID_CONTEXT
;
8499 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8502 /* Build envelope */
8503 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8505 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8506 "%s element"), create_message
->name
);
8510 if (!outgoing_message
->envelope
) {
8511 isds_log_message(context
, _("Outgoing message is missing envelope"));
8516 /* Insert optional message type */
8517 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8519 if (err
) goto leave
;
8521 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8522 outgoing_message
->envelope
->dmSenderOrgUnit
);
8523 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8524 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8526 if (process_recipient
) {
8527 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8528 isds_log_message(context
,
8529 _("Outgoing message is missing recipient box identifier"));
8533 INSERT_STRING(envelope
, "dbIDRecipient",
8534 outgoing_message
->envelope
->dbIDRecipient
);
8536 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8537 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8538 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8539 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8540 INSERT_STRING(envelope
, "dmToHands",
8541 outgoing_message
->envelope
->dmToHands
);
8544 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8546 INSERT_STRING(envelope
, "dmAnnotation",
8547 outgoing_message
->envelope
->dmAnnotation
);
8549 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8550 0, 50, "dmRecipientRefNumber");
8551 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8552 outgoing_message
->envelope
->dmRecipientRefNumber
);
8554 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8555 0, 50, "dmSenderRefNumber");
8556 INSERT_STRING(envelope
, "dmSenderRefNumber",
8557 outgoing_message
->envelope
->dmSenderRefNumber
);
8559 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8560 0, 50, "dmRecipientIdent");
8561 INSERT_STRING(envelope
, "dmRecipientIdent",
8562 outgoing_message
->envelope
->dmRecipientIdent
);
8564 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8565 0, 50, "dmSenderIdent");
8566 INSERT_STRING(envelope
, "dmSenderIdent",
8567 outgoing_message
->envelope
->dmSenderIdent
);
8569 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8570 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8571 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8572 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8573 INSERT_STRING(envelope
, "dmLegalTitleSect",
8574 outgoing_message
->envelope
->dmLegalTitleSect
);
8575 INSERT_STRING(envelope
, "dmLegalTitlePar",
8576 outgoing_message
->envelope
->dmLegalTitlePar
);
8577 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8578 outgoing_message
->envelope
->dmLegalTitlePoint
);
8580 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8581 outgoing_message
->envelope
->dmPersonalDelivery
);
8582 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8583 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8585 /* ???: Should we require value for dbEffectiveOVM sender?
8586 * ISDS has default as true */
8587 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8588 INSERT_BOOLEAN(envelope
, "dmOVM",
8589 outgoing_message
->envelope
->dmPublishOwnID
);
8592 /* Append dmFiles */
8593 if (!outgoing_message
->documents
) {
8594 isds_log_message(context
,
8595 _("Outgoing message is missing list of documents"));
8599 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8601 isds_printf_message(context
, _("Could not add dmFiles child to "
8602 "%s element"), create_message
->name
);
8607 /* Check for document hierarchy */
8608 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8609 if (err
) goto leave
;
8611 /* Process each document */
8612 for (struct isds_list
*item
=
8613 (struct isds_list
*) outgoing_message
->documents
;
8614 item
; item
= item
->next
) {
8616 isds_log_message(context
,
8617 _("List of documents contains empty item"));
8621 /* FIXME: Check for dmFileMetaType and for document references.
8622 * Only first document can be of MAIN type */
8623 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8626 if (err
) goto leave
;
8633 #endif /* HAVE_LIBCURL */
8636 /* Send a message via ISDS to a recipient
8637 * @context is session context
8638 * @outgoing_message is message to send; Some members are mandatory (like
8639 * dbIDRecipient), some are optional and some are irrelevant (especially data
8640 * about sender). Included pointer to isds_list documents must contain at
8641 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8642 * members will be filled with valid data from ISDS. Exact list of write
8643 * members is subject to change. Currently dmID is changed.
8644 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8645 isds_error
isds_send_message(struct isds_ctx
*context
,
8646 struct isds_message
*outgoing_message
) {
8648 isds_error err
= IE_SUCCESS
;
8650 xmlNsPtr isds_ns
= NULL
;
8651 xmlNodePtr request
= NULL
;
8652 xmlDocPtr response
= NULL
;
8653 xmlChar
*code
= NULL
, *message
= NULL
;
8654 xmlXPathContextPtr xpath_ctx
= NULL
;
8655 xmlXPathObjectPtr result
= NULL
;
8656 /*_Bool message_is_complete = 0;*/
8659 if (!context
) return IE_INVALID_CONTEXT
;
8660 zfree(context
->long_message
);
8661 if (!outgoing_message
) return IE_INVAL
;
8664 /* Check if connection is established
8665 * TODO: This check should be done downstairs. */
8666 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8669 /* Build CreateMessage request */
8670 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8672 isds_log_message(context
,
8673 _("Could not build CreateMessage request"));
8676 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8678 isds_log_message(context
, _("Could not create ISDS name space"));
8679 xmlFreeNode(request
);
8682 xmlSetNs(request
, isds_ns
);
8684 /* Append envelope and files */
8685 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8686 if (err
) goto leave
;
8689 /* Signal we can serialize message since now */
8690 /*message_is_complete = 1;*/
8693 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8696 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8698 /* Don't' destroy request, we want to provide it to application later */
8701 isds_log(ILF_ISDS
, ILL_DEBUG
,
8702 _("Processing ISDS response on CreateMessage "
8703 "request failed\n"));
8707 /* Check for response status */
8708 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8709 &code
, &message
, NULL
);
8711 isds_log(ILF_ISDS
, ILL_DEBUG
,
8712 _("ISDS response on CreateMessage request "
8713 "is missing status\n"));
8717 /* Request processed, but refused by server or server failed */
8718 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8719 char *box_id_locale
=
8720 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8721 char *code_locale
= _isds_utf82locale((char*)code
);
8722 char *message_locale
= _isds_utf82locale((char*)message
);
8723 isds_log(ILF_ISDS
, ILL_DEBUG
,
8724 _("Server did not accept message for %s on CreateMessage "
8725 "request (code=%s, message=%s)\n"),
8726 box_id_locale
, code_locale
, message_locale
);
8727 isds_log_message(context
, message_locale
);
8728 free(box_id_locale
);
8730 free(message_locale
);
8737 xpath_ctx
= xmlXPathNewContext(response
);
8742 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8746 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8752 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8753 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8757 if (result
->nodesetval
->nodeNr
> 1) {
8758 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8762 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8763 xmlXPathFreeObject(result
); result
= NULL
;
8765 if (outgoing_message
->envelope
->dmID
) {
8766 free(outgoing_message
->envelope
->dmID
);
8767 outgoing_message
->envelope
->dmID
= NULL
;
8769 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8770 if (!outgoing_message
->envelope
->dmID
) {
8771 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8772 "but did not return assigned message ID\n"));
8776 /* TODO: Serialize message into structure member raw */
8777 /* XXX: Each web service transport message in different format.
8778 * Therefore it's not possible to save them directly.
8779 * To save them, one must figure out common format.
8780 * We can leave it on application, or we can implement the ESS format. */
8781 /*if (message_is_complete) {
8782 if (outgoing_message->envelope->dmID) {
8784 /* Add assigned message ID as first child*/
8785 /*xmlNodePtr dmid_text = xmlNewText(
8786 (xmlChar *) outgoing_message->envelope->dmID);
8787 if (!dmid_text) goto serialization_failed;
8789 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8791 if (!dmid_element) {
8792 xmlFreeNode(dmid_text);
8793 goto serialization_failed;
8796 xmlNodePtr dmid_element_with_text =
8797 xmlAddChild(dmid_element, dmid_text);
8798 if (!dmid_element_with_text) {
8799 xmlFreeNode(dmid_element);
8800 xmlFreeNode(dmid_text);
8801 goto serialization_failed;
8804 node = xmlAddPrevSibling(envelope->childern,
8805 dmid_element_with_text);
8807 xmlFreeNodeList(dmid_element_with_text);
8808 goto serialization_failed;
8812 /* Serialize message with ID into raw */
8813 /*buffer = serialize_element(envelope)*/
8816 serialization_failed:
8821 xmlXPathFreeObject(result
);
8822 xmlXPathFreeContext(xpath_ctx
);
8826 xmlFreeDoc(response
);
8827 xmlFreeNode(request
);
8830 isds_log(ILF_ISDS
, ILL_DEBUG
,
8831 _("CreateMessage request processed by server "
8832 "successfully.\n"));
8833 #else /* not HAVE_LIBCURL */
8841 /* Send a message via ISDS to a multiple recipients
8842 * @context is session context
8843 * @outgoing_message is message to send; Some members are mandatory,
8844 * some are optional and some are irrelevant (especially data
8845 * about sender). Data about recipient will be substituted by ISDS from
8846 * @copies. Included pointer to isds_list documents must
8847 * contain at least one document of FILEMETATYPE_MAIN.
8848 * @copies is list of isds_message_copy structures addressing all desired
8849 * recipients. This is read-write structure, some members will be filled with
8850 * valid data from ISDS (message IDs, error codes, error descriptions).
8852 * ISDS_SUCCESS if all messages have been sent
8853 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8854 * succeeded messages can be identified by copies->data->error),
8855 * or other error code if something other goes wrong. */
8856 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8857 const struct isds_message
*outgoing_message
,
8858 struct isds_list
*copies
) {
8860 isds_error err
= IE_SUCCESS
;
8862 isds_error append_err
;
8863 xmlNsPtr isds_ns
= NULL
;
8864 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8865 struct isds_list
*item
;
8866 struct isds_message_copy
*copy
;
8867 xmlDocPtr response
= NULL
;
8868 xmlChar
*code
= NULL
, *message
= NULL
;
8869 xmlXPathContextPtr xpath_ctx
= NULL
;
8870 xmlXPathObjectPtr result
= NULL
;
8871 xmlChar
*string
= NULL
;
8875 if (!context
) return IE_INVALID_CONTEXT
;
8876 zfree(context
->long_message
);
8877 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8880 /* Check if connection is established
8881 * TODO: This check should be done downstairs. */
8882 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8885 /* Build CreateMultipleMessage request */
8886 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8888 isds_log_message(context
,
8889 _("Could not build CreateMultipleMessage request"));
8892 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8894 isds_log_message(context
, _("Could not create ISDS name space"));
8895 xmlFreeNode(request
);
8898 xmlSetNs(request
, isds_ns
);
8901 /* Build recipients */
8902 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8904 isds_log_message(context
, _("Could not add dmRecipients child to "
8905 "CreateMultipleMessage element"));
8906 xmlFreeNode(request
);
8910 /* Insert each recipient */
8911 for (item
= copies
; item
; item
= item
->next
) {
8912 copy
= (struct isds_message_copy
*) item
->data
;
8914 isds_log_message(context
,
8915 _("`copies' list item contains empty data"));
8920 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8922 isds_log_message(context
, _("Could not add dmRecipient child to "
8923 "dmRecipients element"));
8928 if (!copy
->dbIDRecipient
) {
8929 isds_log_message(context
,
8930 _("Message copy is missing recipient box identifier"));
8934 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
8935 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
8936 copy
->dmRecipientOrgUnit
);
8937 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
8938 copy
->dmRecipientOrgUnitNum
, string
);
8939 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
8942 /* Append envelope and files */
8943 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
8944 if (err
) goto leave
;
8947 isds_log(ILF_ISDS
, ILL_DEBUG
,
8948 _("Sending CreateMultipleMessage request to ISDS\n"));
8951 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8953 isds_log(ILF_ISDS
, ILL_DEBUG
,
8954 _("Processing ISDS response on CreateMultipleMessage "
8955 "request failed\n"));
8959 /* Check for response status */
8960 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8961 &code
, &message
, NULL
);
8963 isds_log(ILF_ISDS
, ILL_DEBUG
,
8964 _("ISDS response on CreateMultipleMessage request "
8965 "is missing status\n"));
8969 /* Request processed, but some copies failed */
8970 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
8971 char *box_id_locale
=
8972 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8973 char *code_locale
= _isds_utf82locale((char*)code
);
8974 char *message_locale
= _isds_utf82locale((char*)message
);
8975 isds_log(ILF_ISDS
, ILL_DEBUG
,
8976 _("Server did accept message for multiple recipients "
8977 "on CreateMultipleMessage request but delivery to "
8978 "some of them failed (code=%s, message=%s)\n"),
8979 box_id_locale
, code_locale
, message_locale
);
8980 isds_log_message(context
, message_locale
);
8981 free(box_id_locale
);
8983 free(message_locale
);
8984 err
= IE_PARTIAL_SUCCESS
;
8987 /* Request refused by server as whole */
8988 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8989 char *box_id_locale
=
8990 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8991 char *code_locale
= _isds_utf82locale((char*)code
);
8992 char *message_locale
= _isds_utf82locale((char*)message
);
8993 isds_log(ILF_ISDS
, ILL_DEBUG
,
8994 _("Server did not accept message for multiple recipients "
8995 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8996 box_id_locale
, code_locale
, message_locale
);
8997 isds_log_message(context
, message_locale
);
8998 free(box_id_locale
);
9000 free(message_locale
);
9007 xpath_ctx
= xmlXPathNewContext(response
);
9012 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9016 result
= xmlXPathEvalExpression(
9017 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9018 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9024 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9025 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9030 /* Extract message ID and delivery status for each copy */
9031 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9032 item
= item
->next
, i
++) {
9033 copy
= (struct isds_message_copy
*) item
->data
;
9034 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9036 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9042 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9043 isds_printf_message(context
, _("ISDS returned unexpected number of "
9044 "message copy delivery states: %d"),
9045 result
->nodesetval
->nodeNr
);
9054 xmlXPathFreeObject(result
);
9055 xmlXPathFreeContext(xpath_ctx
);
9059 xmlFreeDoc(response
);
9060 xmlFreeNode(request
);
9063 isds_log(ILF_ISDS
, ILL_DEBUG
,
9064 _("CreateMultipleMessageResponse request processed by server "
9065 "successfully.\n"));
9066 #else /* not HAVE_LIBCURL */
9074 /* Get list of messages. This is common core for getting sent or received
9076 * Any criterion argument can be NULL, if you don't care about it.
9077 * @context is session context. Must not be NULL.
9078 * @outgoing_direction is true if you want list of outgoing messages,
9079 * it's false if you want incoming messages.
9080 * @from_time is minimal time and date of message sending inclusive.
9081 * @to_time is maximal time and date of message sending inclusive
9082 * @organization_unit_number is number of sender/recipient respectively.
9083 * @status_filter is bit field of isds_message_status values. Use special
9084 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9085 * all values, you can use bit-wise arithmetic if you want.)
9086 * @offset is index of first message we are interested in. First message is 1.
9087 * Set to 0 (or 1) if you don't care.
9088 * @number is maximal length of list you want to get as input value, outputs
9089 * number of messages matching these criteria. Can be NULL if you don't care
9090 * (applies to output value either).
9091 * @messages is automatically reallocated list of isds_message's. Be ware that
9092 * it returns only brief overview (envelope and some other fields) about each
9093 * message, not the complete message. FIXME: Specify exact fields.
9094 * The list is sorted by delivery time in ascending order.
9095 * Use NULL if you don't care about don't need the data (useful if you want to
9096 * know only the @number). If you provide &NULL, list will be allocated on
9097 * heap, if you provide pointer to non-NULL, list will be freed automatically
9098 * at first. Also in case of error the list will be NULLed.
9099 * @return IE_SUCCESS or appropriate error code. */
9100 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9101 _Bool outgoing_direction
,
9102 const struct timeval
*from_time
, const struct timeval
*to_time
,
9103 const long int *organization_unit_number
,
9104 const unsigned int status_filter
,
9105 const unsigned long int offset
, unsigned long int *number
,
9106 struct isds_list
**messages
) {
9108 isds_error err
= IE_SUCCESS
;
9110 xmlNsPtr isds_ns
= NULL
;
9111 xmlNodePtr request
= NULL
, node
;
9112 xmlDocPtr response
= NULL
;
9113 xmlChar
*code
= NULL
, *message
= NULL
;
9114 xmlXPathContextPtr xpath_ctx
= NULL
;
9115 xmlXPathObjectPtr result
= NULL
;
9116 xmlChar
*string
= NULL
;
9117 long unsigned int count
= 0;
9120 if (!context
) return IE_INVALID_CONTEXT
;
9121 zfree(context
->long_message
);
9123 /* Free former message list if any */
9124 if (messages
) isds_list_free(messages
);
9127 /* Check if connection is established
9128 * TODO: This check should be done downstairs. */
9129 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9131 /* Build GetListOf*Messages request */
9132 request
= xmlNewNode(NULL
,
9133 (outgoing_direction
) ?
9134 BAD_CAST
"GetListOfSentMessages" :
9135 BAD_CAST
"GetListOfReceivedMessages"
9138 isds_log_message(context
,
9139 (outgoing_direction
) ?
9140 _("Could not build GetListOfSentMessages request") :
9141 _("Could not build GetListOfReceivedMessages request")
9145 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9147 isds_log_message(context
, _("Could not create ISDS name space"));
9148 xmlFreeNode(request
);
9151 xmlSetNs(request
, isds_ns
);
9155 err
= timeval2timestring(from_time
, &string
);
9156 if (err
) goto leave
;
9158 INSERT_STRING(request
, "dmFromTime", string
);
9159 free(string
); string
= NULL
;
9162 err
= timeval2timestring(to_time
, &string
);
9163 if (err
) goto leave
;
9165 INSERT_STRING(request
, "dmToTime", string
);
9166 free(string
); string
= NULL
;
9168 if (outgoing_direction
) {
9169 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9170 organization_unit_number
, string
);
9172 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9173 organization_unit_number
, string
);
9176 if (status_filter
> MESSAGESTATE_ANY
) {
9177 isds_printf_message(context
,
9178 _("Invalid message state filter value: %ld"), status_filter
);
9182 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9185 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9187 INSERT_STRING(request
, "dmOffset", "1");
9190 /* number 0 means no limit */
9191 if (number
&& *number
== 0) {
9192 INSERT_STRING(request
, "dmLimit", NULL
);
9194 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9198 isds_log(ILF_ISDS
, ILL_DEBUG
,
9199 (outgoing_direction
) ?
9200 _("Sending GetListOfSentMessages request to ISDS\n") :
9201 _("Sending GetListOfReceivedMessages request to ISDS\n")
9205 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9206 xmlFreeNode(request
); request
= NULL
;
9209 isds_log(ILF_ISDS
, ILL_DEBUG
,
9210 (outgoing_direction
) ?
9211 _("Processing ISDS response on GetListOfSentMessages "
9212 "request failed\n") :
9213 _("Processing ISDS response on GetListOfReceivedMessages "
9219 /* Check for response status */
9220 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9221 &code
, &message
, NULL
);
9223 isds_log(ILF_ISDS
, ILL_DEBUG
,
9224 (outgoing_direction
) ?
9225 _("ISDS response on GetListOfSentMessages request "
9226 "is missing status\n") :
9227 _("ISDS response on GetListOfReceivedMessages request "
9228 "is missing status\n")
9233 /* Request processed, but nothing found */
9234 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9235 char *code_locale
= _isds_utf82locale((char*)code
);
9236 char *message_locale
= _isds_utf82locale((char*)message
);
9237 isds_log(ILF_ISDS
, ILL_DEBUG
,
9238 (outgoing_direction
) ?
9239 _("Server refused GetListOfSentMessages request "
9240 "(code=%s, message=%s)\n") :
9241 _("Server refused GetListOfReceivedMessages request "
9242 "(code=%s, message=%s)\n"),
9243 code_locale
, message_locale
);
9244 isds_log_message(context
, message_locale
);
9246 free(message_locale
);
9253 xpath_ctx
= xmlXPathNewContext(response
);
9258 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9262 result
= xmlXPathEvalExpression(
9263 (outgoing_direction
) ?
9264 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9265 "isds:dmRecords/isds:dmRecord" :
9266 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9267 "isds:dmRecords/isds:dmRecord",
9274 /* Fill output arguments in */
9275 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9276 struct isds_envelope
*envelope
;
9277 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9279 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9280 /* Create new message */
9281 item
= calloc(1, sizeof(*item
));
9286 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9287 item
->data
= calloc(1, sizeof(struct isds_message
));
9289 isds_list_free(&item
);
9294 /* Extract envelope data */
9295 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9297 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9299 isds_list_free(&item
);
9303 /* Attach extracted envelope */
9304 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9306 /* Append new message into the list */
9308 *messages
= last_item
= item
;
9310 last_item
->next
= item
;
9315 if (number
) *number
= count
;
9319 isds_list_free(messages
);
9323 xmlXPathFreeObject(result
);
9324 xmlXPathFreeContext(xpath_ctx
);
9328 xmlFreeDoc(response
);
9329 xmlFreeNode(request
);
9332 isds_log(ILF_ISDS
, ILL_DEBUG
,
9333 (outgoing_direction
) ?
9334 _("GetListOfSentMessages request processed by server "
9335 "successfully.\n") :
9336 _("GetListOfReceivedMessages request processed by server "
9339 #else /* not HAVE_LIBCURL */
9346 /* Get list of outgoing (already sent) messages.
9347 * Any criterion argument can be NULL, if you don't care about it.
9348 * @context is session context. Must not be NULL.
9349 * @from_time is minimal time and date of message sending inclusive.
9350 * @to_time is maximal time and date of message sending inclusive
9351 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9352 * @status_filter is bit field of isds_message_status values. Use special
9353 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9354 * all values, you can use bit-wise arithmetic if you want.)
9355 * @offset is index of first message we are interested in. First message is 1.
9356 * Set to 0 (or 1) if you don't care.
9357 * @number is maximal length of list you want to get as input value, outputs
9358 * number of messages matching these criteria. Can be NULL if you don't care
9359 * (applies to output value either).
9360 * @messages is automatically reallocated list of isds_message's. Be ware that
9361 * it returns only brief overview (envelope and some other fields) about each
9362 * message, not the complete message. FIXME: Specify exact fields.
9363 * The list is sorted by delivery time in ascending order.
9364 * Use NULL if you don't care about the meta data (useful if you want to know
9365 * only the @number). If you provide &NULL, list will be allocated on heap,
9366 * if you provide pointer to non-NULL, list will be freed automatically at
9367 * first. Also in case of error the list will be NULLed.
9368 * @return IE_SUCCESS or appropriate error code. */
9369 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9370 const struct timeval
*from_time
, const struct timeval
*to_time
,
9371 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9372 const unsigned long int offset
, unsigned long int *number
,
9373 struct isds_list
**messages
) {
9375 return isds_get_list_of_messages(
9377 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9383 /* Get list of incoming (addressed to you) messages.
9384 * Any criterion argument can be NULL, if you don't care about it.
9385 * @context is session context. Must not be NULL.
9386 * @from_time is minimal time and date of message sending inclusive.
9387 * @to_time is maximal time and date of message sending inclusive
9388 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9389 * @status_filter is bit field of isds_message_status values. Use special
9390 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9391 * all values, you can use bit-wise arithmetic if you want.)
9392 * @offset is index of first message we are interested in. First message is 1.
9393 * Set to 0 (or 1) if you don't care.
9394 * @number is maximal length of list you want to get as input value, outputs
9395 * number of messages matching these criteria. Can be NULL if you don't care
9396 * (applies to output value either).
9397 * @messages is automatically reallocated list of isds_message's. Be ware that
9398 * it returns only brief overview (envelope and some other fields) about each
9399 * message, not the complete message. FIXME: Specify exact fields.
9400 * Use NULL if you don't care about the meta data (useful if you want to know
9401 * only the @number). If you provide &NULL, list will be allocated on heap,
9402 * if you provide pointer to non-NULL, list will be freed automatically at
9403 * first. Also in case of error the list will be NULLed.
9404 * @return IE_SUCCESS or appropriate error code. */
9405 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9406 const struct timeval
*from_time
, const struct timeval
*to_time
,
9407 const long int *dmRecipientOrgUnitNum
,
9408 const unsigned int status_filter
,
9409 const unsigned long int offset
, unsigned long int *number
,
9410 struct isds_list
**messages
) {
9412 return isds_get_list_of_messages(
9414 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9420 /* Get list of sent message state changes.
9421 * Any criterion argument can be NULL, if you don't care about it.
9422 * @context is session context. Must not be NULL.
9423 * @from_time is minimal time and date of status changes inclusive
9424 * @to_time is maximal time and date of status changes inclusive
9425 * @changed_states is automatically reallocated list of
9426 * isds_message_status_change's. If you provide &NULL, list will be allocated
9427 * on heap, if you provide pointer to non-NULL, list will be freed
9428 * automatically at first. Also in case of error the list will be NULLed.
9429 * XXX: The list item ordering is not specified.
9430 * XXX: Server provides only `recent' changes.
9431 * @return IE_SUCCESS or appropriate error code. */
9432 isds_error
isds_get_list_of_sent_message_state_changes(
9433 struct isds_ctx
*context
,
9434 const struct timeval
*from_time
, const struct timeval
*to_time
,
9435 struct isds_list
**changed_states
) {
9437 isds_error err
= IE_SUCCESS
;
9439 xmlNsPtr isds_ns
= NULL
;
9440 xmlNodePtr request
= NULL
, node
;
9441 xmlDocPtr response
= NULL
;
9442 xmlXPathContextPtr xpath_ctx
= NULL
;
9443 xmlXPathObjectPtr result
= NULL
;
9444 xmlChar
*string
= NULL
;
9445 long unsigned int count
= 0;
9448 if (!context
) return IE_INVALID_CONTEXT
;
9449 zfree(context
->long_message
);
9451 /* Free former message list if any */
9452 isds_list_free(changed_states
);
9455 /* Check if connection is established
9456 * TODO: This check should be done downstairs. */
9457 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9459 /* Build GetMessageStateChanges request */
9460 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9462 isds_log_message(context
,
9463 _("Could not build GetMessageStateChanges request"));
9466 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9468 isds_log_message(context
, _("Could not create ISDS name space"));
9469 xmlFreeNode(request
);
9472 xmlSetNs(request
, isds_ns
);
9476 err
= timeval2timestring(from_time
, &string
);
9477 if (err
) goto leave
;
9479 INSERT_STRING(request
, "dmFromTime", string
);
9483 err
= timeval2timestring(to_time
, &string
);
9484 if (err
) goto leave
;
9486 INSERT_STRING(request
, "dmToTime", string
);
9491 err
= send_destroy_request_check_response(context
,
9492 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9493 &response
, NULL
, NULL
);
9494 if (err
) goto leave
;
9498 xpath_ctx
= xmlXPathNewContext(response
);
9503 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9507 result
= xmlXPathEvalExpression(
9508 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9509 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9515 /* Fill output arguments in */
9516 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9517 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9519 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9520 /* Create new status change */
9521 item
= calloc(1, sizeof(*item
));
9527 (void(*)(void**)) &isds_message_status_change_free
;
9529 /* Extract message status change */
9530 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9531 err
= extract_StateChangesRecord(context
,
9532 (struct isds_message_status_change
**) &item
->data
,
9535 isds_list_free(&item
);
9539 /* Append new message status change into the list */
9540 if (!*changed_states
) {
9541 *changed_states
= last_item
= item
;
9543 last_item
->next
= item
;
9551 isds_list_free(changed_states
);
9555 xmlXPathFreeObject(result
);
9556 xmlXPathFreeContext(xpath_ctx
);
9557 xmlFreeDoc(response
);
9558 xmlFreeNode(request
);
9561 isds_log(ILF_ISDS
, ILL_DEBUG
,
9562 _("GetMessageStateChanges request processed by server "
9563 "successfully.\n"));
9564 #else /* not HAVE_LIBCURL */
9572 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9574 * @context is session context
9575 * @service is ISDS WS service handler
9576 * @service_name is name of SERVICE_DM_OPERATIONS
9577 * @message_id is message ID to send as service argument to ISDS
9578 * @response is reallocated server SOAP body response as XML document
9579 * @raw_response is reallocated bit stream with response body. Use
9580 * NULL if you don't care
9581 * @raw_response_length is size of @raw_response in bytes
9582 * @code is reallocated ISDS status code
9583 * @status_message is reallocated ISDS status message
9584 * @return error coded from lower layer, context message will be set up
9586 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9587 const isds_service service
, const xmlChar
*service_name
,
9588 const char *message_id
,
9589 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9590 xmlChar
**code
, xmlChar
**status_message
) {
9592 isds_error err
= IE_SUCCESS
;
9593 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9594 xmlNodePtr request
= NULL
, node
;
9595 xmlNsPtr isds_ns
= NULL
;
9597 if (!context
) return IE_INVALID_CONTEXT
;
9598 if (!service_name
|| !message_id
) return IE_INVAL
;
9599 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9600 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9602 /* Free output argument */
9603 xmlFreeDoc(*response
); *response
= NULL
;
9604 if (raw_response
) zfree(*raw_response
);
9606 zfree(*status_message
);
9609 /* Check if connection is established
9610 * TODO: This check should be done downstairs. */
9611 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9613 service_name_locale
= _isds_utf82locale((char*)service_name
);
9614 message_id_locale
= _isds_utf82locale(message_id
);
9615 if (!service_name_locale
|| !message_id_locale
) {
9621 request
= xmlNewNode(NULL
, service_name
);
9623 isds_printf_message(context
,
9624 _("Could not build %s request for %s message ID"),
9625 service_name_locale
, message_id_locale
);
9629 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9631 isds_log_message(context
, _("Could not create ISDS name space"));
9635 xmlSetNs(request
, isds_ns
);
9638 /* Add requested ID */
9639 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9640 if (err
) goto leave
;
9641 INSERT_STRING(request
, "dmID", message_id
);
9644 isds_log(ILF_ISDS
, ILL_DEBUG
,
9645 _("Sending %s request for %s message ID to ISDS\n"),
9646 service_name_locale
, message_id_locale
);
9649 err
= _isds(context
, service
, request
, response
,
9650 raw_response
, raw_response_length
);
9651 xmlFreeNode(request
); request
= NULL
;
9654 isds_log(ILF_ISDS
, ILL_DEBUG
,
9655 _("Processing ISDS response on %s request failed\n"),
9656 service_name_locale
);
9660 /* Check for response status */
9661 err
= isds_response_status(context
, service
, *response
,
9662 code
, status_message
, NULL
);
9664 isds_log(ILF_ISDS
, ILL_DEBUG
,
9665 _("ISDS response on %s request is missing status\n"),
9666 service_name_locale
);
9670 /* Request processed, but nothing found */
9671 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9672 char *code_locale
= _isds_utf82locale((char*) *code
);
9673 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9674 isds_log(ILF_ISDS
, ILL_DEBUG
,
9675 _("Server refused %s request for %s message ID "
9676 "(code=%s, message=%s)\n"),
9677 service_name_locale
, message_id_locale
,
9678 code_locale
, status_message_locale
);
9679 isds_log_message(context
, status_message_locale
);
9681 free(status_message_locale
);
9687 free(message_id_locale
);
9688 free(service_name_locale
);
9689 xmlFreeNode(request
);
9694 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9695 * signed data and free ISDS response.
9696 * @context is session context
9697 * @message_id is UTF-8 encoded message ID for logging purpose
9698 * @response is parsed XML document. It will be freed and NULLed in the middle
9699 * of function run to save memory. This is not guaranteed in case of error.
9700 * @request_name is name of ISDS request used to construct response root
9701 * element name and for logging purpose.
9702 * @raw is reallocated output buffer with DER encoded CMS data
9703 * @raw_length is size of @raw buffer in bytes
9704 * @returns standard error codes, in case of error, @raw will be freed and
9705 * NULLed, @response sometimes. */
9706 static isds_error
find_extract_signed_data_free_response(
9707 struct isds_ctx
*context
, const xmlChar
*message_id
,
9708 xmlDocPtr
*response
, const xmlChar
*request_name
,
9709 void **raw
, size_t *raw_length
) {
9711 isds_error err
= IE_SUCCESS
;
9712 char *xpath_expression
= NULL
;
9713 xmlXPathContextPtr xpath_ctx
= NULL
;
9714 xmlXPathObjectPtr result
= NULL
;
9715 char *encoded_structure
= NULL
;
9717 if (!context
) return IE_INVALID_CONTEXT
;
9718 if (!raw
) return IE_INVAL
;
9720 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9723 /* Build XPath expression */
9724 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9725 "Response/isds:dmSignature");
9726 if (!xpath_expression
) return IE_NOMEM
;
9729 xpath_ctx
= xmlXPathNewContext(*response
);
9734 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9738 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9743 /* Empty response */
9744 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9745 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9746 isds_printf_message(context
,
9747 _("Server did not return any signed data for message ID `%s' "
9749 message_id_locale
, request_name
);
9750 free(message_id_locale
);
9754 /* More responses */
9755 if (result
->nodesetval
->nodeNr
> 1) {
9756 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9757 isds_printf_message(context
,
9758 _("Server did return more signed data for message ID `%s' "
9760 message_id_locale
, request_name
);
9761 free(message_id_locale
);
9766 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9768 /* Extract PKCS#7 structure */
9769 EXTRACT_STRING(".", encoded_structure
);
9770 if (!encoded_structure
) {
9771 isds_log_message(context
, _("dmSignature element is empty"));
9774 /* Here we have delivery info as standalone CMS in encoded_structure.
9775 * We don't need any other data, free them: */
9776 xmlXPathFreeObject(result
); result
= NULL
;
9777 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9778 xmlFreeDoc(*response
); *response
= NULL
;
9781 /* Decode PKCS#7 to DER format */
9782 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9783 if (*raw_length
== (size_t) -1) {
9784 isds_log_message(context
,
9785 _("Error while Base64-decoding PKCS#7 structure"));
9796 free(encoded_structure
);
9797 xmlXPathFreeObject(result
);
9798 xmlXPathFreeContext(xpath_ctx
);
9799 free(xpath_expression
);
9803 #endif /* HAVE_LIBCURL */
9806 /* Download incoming message envelope identified by ID.
9807 * @context is session context
9808 * @message_id is message identifier (you can get them from
9809 * isds_get_list_of_received_messages())
9810 * @message is automatically reallocated message retrieved from ISDS.
9811 * It will miss documents per se. Use isds_get_received_message(), if you are
9812 * interested in documents (content) too.
9813 * Returned hash and timestamp require documents to be verifiable. */
9814 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9815 const char *message_id
, struct isds_message
**message
) {
9817 isds_error err
= IE_SUCCESS
;
9819 xmlDocPtr response
= NULL
;
9820 xmlChar
*code
= NULL
, *status_message
= NULL
;
9821 xmlXPathContextPtr xpath_ctx
= NULL
;
9822 xmlXPathObjectPtr result
= NULL
;
9825 if (!context
) return IE_INVALID_CONTEXT
;
9826 zfree(context
->long_message
);
9828 /* Free former message if any */
9829 if (!message
) return IE_INVAL
;
9830 isds_message_free(message
);
9833 /* Do request and check for success */
9834 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9835 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9836 &response
, NULL
, NULL
, &code
, &status_message
);
9837 if (err
) goto leave
;
9840 xpath_ctx
= xmlXPathNewContext(response
);
9845 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9849 result
= xmlXPathEvalExpression(
9850 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9851 "isds:dmReturnedMessageEnvelope",
9857 /* Empty response */
9858 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9859 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9860 isds_printf_message(context
,
9861 _("Server did not return any envelope for ID `%s' "
9862 "on MessageEnvelopeDownload request"), message_id_locale
);
9863 free(message_id_locale
);
9868 if (result
->nodesetval
->nodeNr
> 1) {
9869 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9870 isds_printf_message(context
,
9871 _("Server did return more envelopes for ID `%s' "
9872 "on MessageEnvelopeDownload request"), message_id_locale
);
9873 free(message_id_locale
);
9878 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9880 /* Extract the envelope (= message without documents, hence 0) */
9881 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9882 if (err
) goto leave
;
9885 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9886 &(*message
)->raw_length
);
9890 isds_message_free(message
);
9893 xmlXPathFreeObject(result
);
9894 xmlXPathFreeContext(xpath_ctx
);
9897 free(status_message
);
9898 if (!*message
|| !(*message
)->xml
) {
9899 xmlFreeDoc(response
);
9903 isds_log(ILF_ISDS
, ILL_DEBUG
,
9904 _("MessageEnvelopeDownload request processed by server "
9907 #else /* not HAVE_LIBCURL */
9914 /* Load delivery info of any format from buffer.
9915 * @context is session context
9916 * @raw_type advertises format of @buffer content. Only delivery info types
9918 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9919 * retrieve such data from message->raw after calling
9920 * isds_get_signed_delivery_info().
9921 * @length is length of buffer in bytes.
9922 * @message is automatically reallocated message parsed from @buffer.
9923 * @strategy selects how buffer will be attached into raw isds_message member.
9925 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9926 const isds_raw_type raw_type
,
9927 const void *buffer
, const size_t length
,
9928 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9930 isds_error err
= IE_SUCCESS
;
9931 message_ns_type message_ns
;
9932 xmlDocPtr message_doc
= NULL
;
9933 xmlXPathContextPtr xpath_ctx
= NULL
;
9934 xmlXPathObjectPtr result
= NULL
;
9935 void *xml_stream
= NULL
;
9936 size_t xml_stream_length
= 0;
9938 if (!context
) return IE_INVALID_CONTEXT
;
9939 zfree(context
->long_message
);
9940 if (!message
) return IE_INVAL
;
9941 isds_message_free(message
);
9942 if (!buffer
) return IE_INVAL
;
9945 /* Select buffer format and extract XML from CMS*/
9947 case RAWTYPE_DELIVERYINFO
:
9948 message_ns
= MESSAGE_NS_UNSIGNED
;
9949 xml_stream
= (void *) buffer
;
9950 xml_stream_length
= length
;
9953 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
9954 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9955 xml_stream
= (void *) buffer
;
9956 xml_stream_length
= length
;
9959 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
9960 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9961 err
= _isds_extract_cms_data(context
, buffer
, length
,
9962 &xml_stream
, &xml_stream_length
);
9963 if (err
) goto leave
;
9967 isds_log_message(context
, _("Bad raw delivery representation type"));
9972 isds_log(ILF_ISDS
, ILL_DEBUG
,
9973 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9974 xml_stream_length
, xml_stream
);
9976 /* Convert delivery info XML stream into XPath context */
9977 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9982 xpath_ctx
= xmlXPathNewContext(message_doc
);
9987 /* XXX: Name spaces mangled for signed delivery info:
9988 * http://isds.czechpoint.cz/v20/delivery:
9990 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9992 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9993 * <p:dmID>170272</p:dmID>
9996 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9998 * </q:dmEvents>...</q:dmEvents>
10000 * </q:GetDeliveryInfoResponse>
10002 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10006 result
= xmlXPathEvalExpression(
10007 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10013 /* Empty delivery info */
10014 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10015 isds_printf_message(context
,
10016 _("XML document is not sisds:dmDelivery document"));
10020 /* More delivery info's */
10021 if (result
->nodesetval
->nodeNr
> 1) {
10022 isds_printf_message(context
,
10023 _("XML document has more sisds:dmDelivery elements"));
10027 /* One delivery info */
10028 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10030 /* Extract the envelope (= message without documents, hence 0).
10031 * XXX: extract_TReturnedMessage() can obtain attachments size,
10032 * but delivery info carries none. It's coded as option elements,
10033 * so it should work. */
10034 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10035 if (err
) goto leave
;
10037 /* Extract events */
10038 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10039 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10040 if (err
) { err
= IE_ERROR
; goto leave
; }
10041 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10042 if (err
) goto leave
;
10044 /* Append raw CMS structure into message */
10045 (*message
)->raw_type
= raw_type
;
10046 switch (strategy
) {
10047 case BUFFER_DONT_STORE
:
10050 (*message
)->raw
= malloc(length
);
10051 if (!(*message
)->raw
) {
10055 memcpy((*message
)->raw
, buffer
, length
);
10056 (*message
)->raw_length
= length
;
10059 (*message
)->raw
= (void *) buffer
;
10060 (*message
)->raw_length
= length
;
10069 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10070 isds_message_free(message
);
10073 xmlXPathFreeObject(result
);
10074 xmlXPathFreeContext(xpath_ctx
);
10075 if (!*message
|| !(*message
)->xml
) {
10076 xmlFreeDoc(message_doc
);
10078 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10081 isds_log(ILF_ISDS
, ILL_DEBUG
,
10082 _("Delivery info loaded successfully.\n"));
10087 /* Download signed delivery info-sheet of given message identified by ID.
10088 * @context is session context
10089 * @message_id is message identifier (you can get them from
10090 * isds_get_list_of_{sent,received}_messages())
10091 * @message is automatically reallocated message retrieved from ISDS.
10092 * It will miss documents per se. Use isds_get_signed_received_message(),
10093 * if you are interested in documents (content). OTOH, only this function
10094 * can get list events message has gone through. */
10095 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10096 const char *message_id
, struct isds_message
**message
) {
10098 isds_error err
= IE_SUCCESS
;
10100 xmlDocPtr response
= NULL
;
10101 xmlChar
*code
= NULL
, *status_message
= NULL
;
10103 size_t raw_length
= 0;
10106 if (!context
) return IE_INVALID_CONTEXT
;
10107 zfree(context
->long_message
);
10109 /* Free former message if any */
10110 if (!message
) return IE_INVAL
;
10111 isds_message_free(message
);
10114 /* Do request and check for success */
10115 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10116 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10117 &response
, NULL
, NULL
, &code
, &status_message
);
10118 if (err
) goto leave
;
10120 /* Find signed delivery info, extract it into raw and maybe free
10122 err
= find_extract_signed_data_free_response(context
,
10123 (xmlChar
*)message_id
, &response
,
10124 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10125 if (err
) goto leave
;
10127 /* Parse delivery info */
10128 err
= isds_load_delivery_info(context
,
10129 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10130 message
, BUFFER_MOVE
);
10131 if (err
) goto leave
;
10137 isds_message_free(message
);
10142 free(status_message
);
10143 xmlFreeDoc(response
);
10146 isds_log(ILF_ISDS
, ILL_DEBUG
,
10147 _("GetSignedDeliveryInfo request processed by server "
10150 #else /* not HAVE_LIBCURL */
10157 /* Download delivery info-sheet of given message identified by ID.
10158 * @context is session context
10159 * @message_id is message identifier (you can get them from
10160 * isds_get_list_of_{sent,received}_messages())
10161 * @message is automatically reallocated message retrieved from ISDS.
10162 * It will miss documents per se. Use isds_get_received_message(), if you are
10163 * interested in documents (content). OTOH, only this function can get list
10164 * of events message has gone through. */
10165 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10166 const char *message_id
, struct isds_message
**message
) {
10168 isds_error err
= IE_SUCCESS
;
10170 xmlDocPtr response
= NULL
;
10171 xmlChar
*code
= NULL
, *status_message
= NULL
;
10172 xmlNodePtr delivery_node
= NULL
;
10174 size_t raw_length
= 0;
10177 if (!context
) return IE_INVALID_CONTEXT
;
10178 zfree(context
->long_message
);
10180 /* Free former message if any */
10181 if (!message
) return IE_INVAL
;
10182 isds_message_free(message
);
10185 /* Do request and check for success */
10186 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10187 BAD_CAST
"GetDeliveryInfo", message_id
,
10188 &response
, NULL
, NULL
, &code
, &status_message
);
10189 if (err
) goto leave
;
10192 /* Serialize delivery info */
10193 delivery_node
= xmlDocGetRootElement(response
);
10194 if (!delivery_node
) {
10195 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10196 isds_printf_message(context
,
10197 _("Server did not return any delivery info for ID `%s' "
10198 "on GetDeliveryInfo request"), message_id_locale
);
10199 free(message_id_locale
);
10203 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10204 if (err
) goto leave
;
10206 /* Parse delivery info */
10207 /* TODO: Here we parse the response second time. We could single delivery
10208 * parser from isds_load_delivery_info() to make things faster. */
10209 err
= isds_load_delivery_info(context
,
10210 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10211 message
, BUFFER_MOVE
);
10212 if (err
) goto leave
;
10219 isds_message_free(message
);
10224 free(status_message
);
10225 xmlFreeDoc(response
);
10228 isds_log(ILF_ISDS
, ILL_DEBUG
,
10229 _("GetDeliveryInfo request processed by server "
10232 #else /* not HAVE_LIBCURL */
10239 /* Download incoming message identified by ID.
10240 * @context is session context
10241 * @message_id is message identifier (you can get them from
10242 * isds_get_list_of_received_messages())
10243 * @message is automatically reallocated message retrieved from ISDS */
10244 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10245 const char *message_id
, struct isds_message
**message
) {
10247 isds_error err
= IE_SUCCESS
;
10249 xmlDocPtr response
= NULL
;
10250 void *xml_stream
= NULL
;
10251 size_t xml_stream_length
;
10252 xmlChar
*code
= NULL
, *status_message
= NULL
;
10253 xmlXPathContextPtr xpath_ctx
= NULL
;
10254 xmlXPathObjectPtr result
= NULL
;
10255 char *phys_path
= NULL
;
10256 size_t phys_start
, phys_end
;
10259 if (!context
) return IE_INVALID_CONTEXT
;
10260 zfree(context
->long_message
);
10262 /* Free former message if any */
10263 if (NULL
== message
) return IE_INVAL
;
10264 if (message
) isds_message_free(message
);
10267 /* Do request and check for success */
10268 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10269 BAD_CAST
"MessageDownload", message_id
,
10270 &response
, &xml_stream
, &xml_stream_length
,
10271 &code
, &status_message
);
10272 if (err
) goto leave
;
10275 xpath_ctx
= xmlXPathNewContext(response
);
10280 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10284 result
= xmlXPathEvalExpression(
10285 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10291 /* Empty response */
10292 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10293 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10294 isds_printf_message(context
,
10295 _("Server did not return any message for ID `%s' "
10296 "on MessageDownload request"), message_id_locale
);
10297 free(message_id_locale
);
10301 /* More messages */
10302 if (result
->nodesetval
->nodeNr
> 1) {
10303 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10304 isds_printf_message(context
,
10305 _("Server did return more messages for ID `%s' "
10306 "on MessageDownload request"), message_id_locale
);
10307 free(message_id_locale
);
10312 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10314 /* Extract the message */
10315 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10316 if (err
) goto leave
;
10318 /* Locate raw XML blob */
10319 phys_path
= strdup(
10320 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10321 PHYSXML_ELEMENT_SEPARATOR
10322 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10323 PHYSXML_ELEMENT_SEPARATOR
10324 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10330 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10331 phys_path
, &phys_start
, &phys_end
);
10334 isds_log_message(context
,
10335 _("Substring with isds:MessageDownloadResponse element "
10336 "could not be located in raw SOAP message"));
10339 /* Save XML blob */
10340 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10341 &(*message)->raw_length);*/
10342 /* TODO: Store name space declarations from ancestors */
10343 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10344 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10345 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10346 (*message
)->raw
= malloc((*message
)->raw_length
);
10347 if (!(*message
)->raw
) {
10351 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10356 isds_message_free(message
);
10361 xmlXPathFreeObject(result
);
10362 xmlXPathFreeContext(xpath_ctx
);
10365 free(status_message
);
10367 if (!*message
|| !(*message
)->xml
) {
10368 xmlFreeDoc(response
);
10372 isds_log(ILF_ISDS
, ILL_DEBUG
,
10373 _("MessageDownload request processed by server "
10376 #else /* not HAVE_LIBCURL */
10383 /* Load message of any type from buffer.
10384 * @context is session context
10385 * @raw_type defines content type of @buffer. Only message types are allowed.
10386 * @buffer is message raw representation. Format (CMS, plain signed,
10387 * message direction) is defined in @raw_type. You can retrieve such data
10388 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10389 * @length is length of buffer in bytes.
10390 * @message is automatically reallocated message parsed from @buffer.
10391 * @strategy selects how buffer will be attached into raw isds_message member.
10393 isds_error
isds_load_message(struct isds_ctx
*context
,
10394 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10395 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10397 isds_error err
= IE_SUCCESS
;
10398 void *xml_stream
= NULL
;
10399 size_t xml_stream_length
= 0;
10400 message_ns_type message_ns
;
10401 xmlDocPtr message_doc
= NULL
;
10402 xmlXPathContextPtr xpath_ctx
= NULL
;
10403 xmlXPathObjectPtr result
= NULL
;
10405 if (!context
) return IE_INVALID_CONTEXT
;
10406 zfree(context
->long_message
);
10407 if (!message
) return IE_INVAL
;
10408 isds_message_free(message
);
10409 if (!buffer
) return IE_INVAL
;
10412 /* Select buffer format and extract XML from CMS*/
10413 switch (raw_type
) {
10414 case RAWTYPE_INCOMING_MESSAGE
:
10415 message_ns
= MESSAGE_NS_UNSIGNED
;
10416 xml_stream
= (void *) buffer
;
10417 xml_stream_length
= length
;
10420 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10421 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10422 xml_stream
= (void *) buffer
;
10423 xml_stream_length
= length
;
10426 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10427 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10428 err
= _isds_extract_cms_data(context
, buffer
, length
,
10429 &xml_stream
, &xml_stream_length
);
10430 if (err
) goto leave
;
10433 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10434 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10435 xml_stream
= (void *) buffer
;
10436 xml_stream_length
= length
;
10439 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10440 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10441 err
= _isds_extract_cms_data(context
, buffer
, length
,
10442 &xml_stream
, &xml_stream_length
);
10443 if (err
) goto leave
;
10447 isds_log_message(context
, _("Bad raw message representation type"));
10452 isds_log(ILF_ISDS
, ILL_DEBUG
,
10453 _("Loading message:\n%.*s\nEnd of message\n"),
10454 xml_stream_length
, xml_stream
);
10456 /* Convert messages XML stream into XPath context */
10457 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10458 if (!message_doc
) {
10462 xpath_ctx
= xmlXPathNewContext(message_doc
);
10467 /* XXX: Standard name space for unsigned incoming direction:
10468 * http://isds.czechpoint.cz/v20/
10470 * XXX: Name spaces mangled for signed outgoing direction:
10471 * http://isds.czechpoint.cz/v20/SentMessage:
10473 * <q:MessageDownloadResponse
10474 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10475 * <q:dmReturnedMessage>
10476 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10477 * <p:dmID>151916</p:dmID>
10480 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10482 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10483 * </q:dmReturnedMessage>
10484 * </q:MessageDownloadResponse>
10486 * XXX: Name spaces mangled for signed incoming direction:
10487 * http://isds.czechpoint.cz/v20/message:
10489 * <q:MessageDownloadResponse
10490 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10491 * <q:dmReturnedMessage>
10492 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10493 * <p:dmID>151916</p:dmID>
10496 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10498 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10499 * </q:dmReturnedMessage>
10500 * </q:MessageDownloadResponse>
10502 * Stupidity of ISDS developers is unlimited */
10503 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10507 result
= xmlXPathEvalExpression(
10508 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10514 /* Empty message */
10515 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10516 isds_printf_message(context
,
10517 _("XML document does not contain "
10518 "sisds:dmReturnedMessage element"));
10522 /* More messages */
10523 if (result
->nodesetval
->nodeNr
> 1) {
10524 isds_printf_message(context
,
10525 _("XML document has more sisds:dmReturnedMessage elements"));
10530 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10532 /* Extract the message */
10533 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10534 if (err
) goto leave
;
10536 /* Append raw buffer into message */
10537 (*message
)->raw_type
= raw_type
;
10538 switch (strategy
) {
10539 case BUFFER_DONT_STORE
:
10542 (*message
)->raw
= malloc(length
);
10543 if (!(*message
)->raw
) {
10547 memcpy((*message
)->raw
, buffer
, length
);
10548 (*message
)->raw_length
= length
;
10551 (*message
)->raw
= (void *) buffer
;
10552 (*message
)->raw_length
= length
;
10562 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10563 isds_message_free(message
);
10566 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10567 xmlXPathFreeObject(result
);
10568 xmlXPathFreeContext(xpath_ctx
);
10569 if (!*message
|| !(*message
)->xml
) {
10570 xmlFreeDoc(message_doc
);
10574 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10579 /* Determine type of raw message or delivery info according some heuristics.
10580 * It does not validate the raw blob.
10581 * @context is session context
10582 * @raw_type returns content type of @buffer. Valid only if exit code of this
10583 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10584 * reallocated memory.
10585 * @buffer is message raw representation.
10586 * @length is length of buffer in bytes. */
10587 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10588 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10590 void *xml_stream
= NULL
;
10591 size_t xml_stream_length
= 0;
10592 xmlDocPtr document
= NULL
;
10593 xmlNodePtr root
= NULL
;
10595 if (!context
) return IE_INVALID_CONTEXT
;
10596 zfree(context
->long_message
);
10597 if (length
== 0 || !buffer
) return IE_INVAL
;
10598 if (!raw_type
) return IE_INVAL
;
10601 err
= _isds_extract_cms_data(context
, buffer
, length
,
10602 &xml_stream
, &xml_stream_length
);
10604 xml_stream
= (void *) buffer
;
10605 xml_stream_length
= (size_t) length
;
10610 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10612 isds_printf_message(context
,
10613 _("Could not parse data as XML document"));
10618 /* Get root element */
10619 root
= xmlDocGetRootElement(document
);
10621 isds_printf_message(context
,
10622 _("XML document is missing root element"));
10627 if (!root
->ns
|| !root
->ns
->href
) {
10628 isds_printf_message(context
,
10629 _("Root element does not belong to any name space"));
10634 /* Test name space */
10635 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10636 if (xml_stream
== buffer
)
10637 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10639 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10640 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10641 if (xml_stream
== buffer
)
10642 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10644 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10645 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10646 if (xml_stream
== buffer
)
10647 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10649 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10650 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10651 if (xml_stream
!= buffer
) {
10652 isds_printf_message(context
,
10653 _("Document in ISDS name space is encapsulated into CMS" ));
10655 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10656 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10657 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10658 *raw_type
= RAWTYPE_DELIVERYINFO
;
10660 isds_printf_message(context
,
10661 _("Unknown root element in ISDS name space"));
10665 isds_printf_message(context
,
10666 _("Unknown name space"));
10671 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10672 xmlFreeDoc(document
);
10677 /* Download signed incoming/outgoing message identified by ID.
10678 * @context is session context
10679 * @output is true for outgoing message, false for incoming message
10680 * @message_id is message identifier (you can get them from
10681 * isds_get_list_of_{sent,received}_messages())
10682 * @message is automatically reallocated message retrieved from ISDS. The raw
10683 * member will be filled with PKCS#7 structure in DER format. */
10684 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10685 const _Bool outgoing
, const char *message_id
,
10686 struct isds_message
**message
) {
10688 isds_error err
= IE_SUCCESS
;
10690 xmlDocPtr response
= NULL
;
10691 xmlChar
*code
= NULL
, *status_message
= NULL
;
10692 xmlXPathContextPtr xpath_ctx
= NULL
;
10693 xmlXPathObjectPtr result
= NULL
;
10694 char *encoded_structure
= NULL
;
10696 size_t raw_length
= 0;
10699 if (!context
) return IE_INVALID_CONTEXT
;
10700 zfree(context
->long_message
);
10701 if (!message
) return IE_INVAL
;
10702 isds_message_free(message
);
10705 /* Do request and check for success */
10706 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10707 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10708 BAD_CAST
"SignedMessageDownload",
10709 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10710 if (err
) goto leave
;
10712 /* Find signed message, extract it into raw and maybe free
10714 err
= find_extract_signed_data_free_response(context
,
10715 (xmlChar
*)message_id
, &response
,
10716 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10717 BAD_CAST
"SignedMessageDownload",
10718 &raw
, &raw_length
);
10719 if (err
) goto leave
;
10721 /* Parse message */
10722 err
= isds_load_message(context
,
10723 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10724 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10725 raw
, raw_length
, message
, BUFFER_MOVE
);
10726 if (err
) goto leave
;
10732 isds_message_free(message
);
10735 free(encoded_structure
);
10736 xmlXPathFreeObject(result
);
10737 xmlXPathFreeContext(xpath_ctx
);
10741 free(status_message
);
10742 xmlFreeDoc(response
);
10745 isds_log(ILF_ISDS
, ILL_DEBUG
,
10747 _("SignedSentMessageDownload request processed by server "
10748 "successfully.\n") :
10749 _("SignedMessageDownload request processed by server "
10752 #else /* not HAVE_LIBCURL */
10759 /* Download signed incoming message identified by ID.
10760 * @context is session context
10761 * @message_id is message identifier (you can get them from
10762 * isds_get_list_of_received_messages())
10763 * @message is automatically reallocated message retrieved from ISDS. The raw
10764 * member will be filled with PKCS#7 structure in DER format. */
10765 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10766 const char *message_id
, struct isds_message
**message
) {
10767 return isds_get_signed_message(context
, 0, message_id
, message
);
10771 /* Download signed outgoing message identified by ID.
10772 * @context is session context
10773 * @message_id is message identifier (you can get them from
10774 * isds_get_list_of_sent_messages())
10775 * @message is automatically reallocated message retrieved from ISDS. The raw
10776 * member will be filled with PKCS#7 structure in DER format. */
10777 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10778 const char *message_id
, struct isds_message
**message
) {
10779 return isds_get_signed_message(context
, 1, message_id
, message
);
10783 /* Get type and name of user who sent a message identified by ID.
10784 * @context is session context
10785 * @message_id is message identifier
10786 * @sender_type is pointer to automatically allocated type of sender detected
10787 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10788 * library or to the server, NULL will be returned. Pass NULL if you don't
10790 * @raw_sender_type is automatically reallocated UTF-8 string describing
10791 * sender type or NULL if not known to server. Pass NULL if you don't care.
10792 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10793 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10794 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10795 const char *message_id
, isds_sender_type
**sender_type
,
10796 char **raw_sender_type
, char **sender_name
) {
10797 isds_error err
= IE_SUCCESS
;
10799 xmlDocPtr response
= NULL
;
10800 xmlChar
*code
= NULL
, *status_message
= NULL
;
10801 xmlXPathContextPtr xpath_ctx
= NULL
;
10802 xmlXPathObjectPtr result
= NULL
;
10803 char *type_string
= NULL
;
10806 if (!context
) return IE_INVALID_CONTEXT
;
10807 zfree(context
->long_message
);
10808 if (sender_type
) zfree(*sender_type
);
10809 if (raw_sender_type
) zfree(*raw_sender_type
);
10810 if (sender_name
) zfree(*sender_name
);
10811 if (!message_id
) return IE_INVAL
;
10814 /* Do request and check for success */
10815 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10816 BAD_CAST
"GetMessageAuthor",
10817 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10818 if (err
) goto leave
;
10821 xpath_ctx
= xmlXPathNewContext(response
);
10826 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10830 result
= xmlXPathEvalExpression(
10831 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10836 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10837 isds_log_message(context
,
10838 _("Missing GetMessageAuthorResponse element"));
10842 if (result
->nodesetval
->nodeNr
> 1) {
10843 isds_log_message(context
,
10844 _("Multiple GetMessageAuthorResponse element"));
10848 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10849 xmlXPathFreeObject(result
); result
= NULL
;
10851 /* Fill output arguments in */
10852 EXTRACT_STRING("isds:userType", type_string
);
10853 if (NULL
!= type_string
) {
10854 if (NULL
!= sender_type
) {
10855 *sender_type
= calloc(1, sizeof(**sender_type
));
10856 if (NULL
== *sender_type
) {
10861 err
= string2isds_sender_type((xmlChar
*)type_string
,
10864 zfree(*sender_type
);
10865 if (err
== IE_ENUM
) {
10867 char *type_string_locale
= _isds_utf82locale(type_string
);
10868 isds_log(ILF_ISDS
, ILL_WARNING
,
10869 _("Unknown isds:userType value: %s"),
10870 type_string_locale
);
10871 free(type_string_locale
);
10876 if (NULL
!= sender_name
)
10877 EXTRACT_STRING("isds:authorName", *sender_name
);
10881 if (NULL
!= sender_type
) zfree(*sender_type
);
10882 zfree(type_string
);
10883 if (NULL
!= sender_name
) zfree(*sender_name
);
10885 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10887 xmlXPathFreeObject(result
);
10888 xmlXPathFreeContext(xpath_ctx
);
10891 free(status_message
);
10892 xmlFreeDoc(response
);
10895 isds_log(ILF_ISDS
, ILL_DEBUG
,
10896 _("GetMessageAuthor request processed by server "
10897 "successfully.\n"));
10898 #else /* not HAVE_LIBCURL */
10905 /* Retrieve hash of message identified by ID stored in ISDS.
10906 * @context is session context
10907 * @message_id is message identifier
10908 * @hash is automatically reallocated message hash downloaded from ISDS.
10909 * Message must exist in system and must not be deleted. */
10910 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10911 const char *message_id
, struct isds_hash
**hash
) {
10913 isds_error err
= IE_SUCCESS
;
10915 xmlDocPtr response
= NULL
;
10916 xmlChar
*code
= NULL
, *status_message
= NULL
;
10917 xmlXPathContextPtr xpath_ctx
= NULL
;
10918 xmlXPathObjectPtr result
= NULL
;
10921 if (!context
) return IE_INVALID_CONTEXT
;
10922 zfree(context
->long_message
);
10924 isds_hash_free(hash
);
10927 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10928 BAD_CAST
"VerifyMessage", message_id
,
10929 &response
, NULL
, NULL
, &code
, &status_message
);
10930 if (err
) goto leave
;
10934 xpath_ctx
= xmlXPathNewContext(response
);
10939 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10943 result
= xmlXPathEvalExpression(
10944 BAD_CAST
"/isds:VerifyMessageResponse",
10950 /* Empty response */
10951 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10952 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10953 isds_printf_message(context
,
10954 _("Server did not return any response for ID `%s' "
10955 "on VerifyMessage request"), message_id_locale
);
10956 free(message_id_locale
);
10960 /* More responses */
10961 if (result
->nodesetval
->nodeNr
> 1) {
10962 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10963 isds_printf_message(context
,
10964 _("Server did return more responses for ID `%s' "
10965 "on VerifyMessage request"), message_id_locale
);
10966 free(message_id_locale
);
10971 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10973 /* Extract the hash */
10974 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
10978 isds_hash_free(hash
);
10981 xmlXPathFreeObject(result
);
10982 xmlXPathFreeContext(xpath_ctx
);
10985 free(status_message
);
10986 xmlFreeDoc(response
);
10989 isds_log(ILF_ISDS
, ILL_DEBUG
,
10990 _("VerifyMessage request processed by server "
10993 #else /* not HAVE_LIBCURL */
11000 /* Erase message specified by @message_id from long term storage. Other
11001 * message cannot be erased on user request.
11002 * @context is session context
11003 * @message_id is message identifier.
11004 * @incoming is true for incoming message, false for outgoing message.
11006 * IE_SUCCESS if message has ben removed
11007 * IE_INVAL if message does not exist in long term storage or message
11008 * belongs to different box
11009 * TODO: IE_NOEPRM if user has no permission to erase a message */
11010 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11011 const char *message_id
, _Bool incoming
) {
11012 isds_error err
= IE_SUCCESS
;
11014 xmlNodePtr request
= NULL
, node
;
11015 xmlNsPtr isds_ns
= NULL
;
11016 xmlDocPtr response
= NULL
;
11017 xmlChar
*code
= NULL
, *status_message
= NULL
;
11020 if (!context
) return IE_INVALID_CONTEXT
;
11021 zfree(context
->long_message
);
11022 if (NULL
== message_id
) return IE_INVAL
;
11024 /* Check if connection is established
11025 * TODO: This check should be done downstairs. */
11026 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11029 /* Build request */
11030 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11032 isds_log_message(context
,
11033 _("Could build EraseMessage request"));
11036 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11038 isds_log_message(context
, _("Could not create ISDS name space"));
11039 xmlFreeNode(request
);
11042 xmlSetNs(request
, isds_ns
);
11044 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11045 if (err
) goto leave
;
11046 INSERT_STRING(request
, "dmID", message_id
);
11048 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11052 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11053 "message ID %s to ISDS\n"), message_id
);
11054 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11055 xmlFreeNode(request
); request
= NULL
;
11058 isds_log(ILF_ISDS
, ILL_DEBUG
,
11059 _("Processing ISDS response on EraseMessage request "
11064 /* Check for response status */
11065 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11066 &code
, &status_message
, NULL
);
11068 isds_log(ILF_ISDS
, ILL_DEBUG
,
11069 _("ISDS response on EraseMessage request is missing "
11074 /* Check server status code */
11075 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11076 isds_log_message(context
, _("Message to erase belongs to other box"));
11078 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11079 isds_log_message(context
, _("Message to erase is not saved in "
11080 "long term storage or the direction does not match"));
11082 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11083 char *code_locale
= _isds_utf82locale((char*) code
);
11084 char *message_locale
= _isds_utf82locale((char*) status_message
);
11085 isds_log(ILF_ISDS
, ILL_DEBUG
,
11086 _("Server refused EraseMessage request "
11087 "(code=%s, message=%s)\n"),
11088 code_locale
, message_locale
);
11089 isds_log_message(context
, message_locale
);
11091 free(message_locale
);
11098 free(status_message
);
11099 xmlFreeDoc(response
);
11100 xmlFreeNode(request
);
11103 isds_log(ILF_ISDS
, ILL_DEBUG
,
11104 _("EraseMessage request processed by server "
11107 #else /* not HAVE_LIBCURL */
11114 /* Mark message as read. This is a transactional commit function to acknowledge
11115 * to ISDS the message has been downloaded and processed by client properly.
11116 * @context is session context
11117 * @message_id is message identifier. */
11118 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11119 const char *message_id
) {
11121 isds_error err
= IE_SUCCESS
;
11123 xmlDocPtr response
= NULL
;
11124 xmlChar
*code
= NULL
, *status_message
= NULL
;
11127 if (!context
) return IE_INVALID_CONTEXT
;
11128 zfree(context
->long_message
);
11131 /* Do request and check for success */
11132 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11133 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11134 &response
, NULL
, NULL
, &code
, &status_message
);
11137 free(status_message
);
11138 xmlFreeDoc(response
);
11141 isds_log(ILF_ISDS
, ILL_DEBUG
,
11142 _("MarkMessageAsDownloaded request processed by server "
11145 #else /* not HAVE_LIBCURL */
11152 /* Mark message as received by recipient. This is applicable only to
11153 * commercial message. Use envelope->dmType message member to distinguish
11154 * commercial message from government message. Government message is
11155 * received automatically (by law), commercial message on recipient request.
11156 * @context is session context
11157 * @message_id is message identifier. */
11158 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11159 const char *message_id
) {
11161 isds_error err
= IE_SUCCESS
;
11163 xmlDocPtr response
= NULL
;
11164 xmlChar
*code
= NULL
, *status_message
= NULL
;
11167 if (!context
) return IE_INVALID_CONTEXT
;
11168 zfree(context
->long_message
);
11171 /* Do request and check for success */
11172 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11173 BAD_CAST
"ConfirmDelivery", message_id
,
11174 &response
, NULL
, NULL
, &code
, &status_message
);
11177 free(status_message
);
11178 xmlFreeDoc(response
);
11181 isds_log(ILF_ISDS
, ILL_DEBUG
,
11182 _("ConfirmDelivery request processed by server "
11185 #else /* not HAVE_LIBCURL */
11192 /* Send document for authorized conversion into Czech POINT system.
11193 * This is public anonymous service, no log-in necessary. Special context is
11194 * used to reuse keep-a-live HTTPS connection.
11195 * @context is Czech POINT session context. DO NOT use context connected to
11196 * ISDS server. Use new context or context used by this function previously.
11197 * @document is document to convert. Only data, data_length, dmFileDescr and
11198 * is_xml members are significant. Be ware that not all document formats can be
11199 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11200 * @id is reallocated identifier assigned by Czech POINT system to
11201 * your document on submit. Use is to tell it to Czech POINT officer.
11202 * @date is reallocated document submit date (submitted documents
11203 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11205 isds_error
czp_convert_document(struct isds_ctx
*context
,
11206 const struct isds_document
*document
,
11207 char **id
, struct tm
**date
) {
11208 isds_error err
= IE_SUCCESS
;
11210 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11211 xmlNodePtr request
= NULL
, node
;
11212 xmlDocPtr response
= NULL
;
11214 xmlXPathContextPtr xpath_ctx
= NULL
;
11215 xmlXPathObjectPtr result
= NULL
;
11216 long int status
= -1;
11217 long int *status_ptr
= &status
;
11218 char *string
= NULL
;
11222 if (!context
) return IE_INVALID_CONTEXT
;
11223 zfree(context
->long_message
);
11224 if (!document
|| !id
|| !date
) return IE_INVAL
;
11226 if (document
->is_xml
) {
11227 isds_log_message(context
,
11228 _("XML documents cannot be submitted to conversion"));
11232 /* Free output arguments */
11237 /* Store configuration */
11238 context
->type
= CTX_TYPE_CZP
;
11239 free(context
->url
);
11240 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11241 if (!(context
->url
))
11244 /* Prepare CURL handle if not yet connected */
11245 if (!context
->curl
) {
11246 context
->curl
= curl_easy_init();
11247 if (!(context
->curl
))
11251 /* Build conversion request */
11252 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11254 isds_log_message(context
,
11255 _("Could not build Czech POINT conversion request"));
11258 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11260 isds_log_message(context
,
11261 _("Could not create Czech POINT deposit name space"));
11262 xmlFreeNode(request
);
11265 xmlSetNs(request
, deposit_ns
);
11267 /* Insert children. They are in empty namespace! */
11268 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11270 isds_log_message(context
, _("Could not create empty name space"));
11274 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11275 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11276 document
->dmFileDescr
);
11278 /* Document encoded in Base64 */
11279 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11280 document
->data
, document
->data_length
);
11281 if (err
) goto leave
;
11283 isds_log(ILF_ISDS
, ILL_DEBUG
,
11284 _("Submitting document for conversion into Czech POINT deposit"));
11286 /* Send conversion request */
11287 err
= _czp_czpdeposit(context
, request
, &response
);
11288 xmlFreeNode(request
); request
= NULL
;
11291 czp_do_close_connection(context
);
11296 /* Extract response */
11297 xpath_ctx
= xmlXPathNewContext(response
);
11302 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11306 result
= xmlXPathEvalExpression(
11307 BAD_CAST
"/deposit:saveDocumentResponse/return",
11313 /* Empty response */
11314 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11315 isds_printf_message(context
,
11316 _("Missing `return' element in Czech POINT deposit response"));
11320 /* More responses */
11321 if (result
->nodesetval
->nodeNr
> 1) {
11322 isds_printf_message(context
,
11323 _("Multiple `return' element in Czech POINT deposit response"));
11328 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11331 EXTRACT_LONGINT("status", status_ptr
, 1);
11333 EXTRACT_STRING("statusMsg", string
);
11334 char *string_locale
= _isds_utf82locale(string
);
11335 isds_printf_message(context
,
11336 _("Czech POINT deposit refused document for conversion "
11337 "(code=%ld, message=%s)"),
11338 status
, string_locale
);
11339 free(string_locale
);
11344 /* Get document ID */
11345 EXTRACT_STRING("documentID", *id
);
11347 /* Get submit date */
11348 EXTRACT_STRING("dateInserted", string
);
11350 *date
= calloc(1, sizeof(**date
));
11355 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11357 if (err
== IE_NOTSUP
) {
11359 char *string_locale
= _isds_utf82locale(string
);
11360 isds_printf_message(context
,
11361 _("Invalid dateInserted value: %s"), string_locale
);
11362 free(string_locale
);
11370 xmlXPathFreeObject(result
);
11371 xmlXPathFreeContext(xpath_ctx
);
11373 xmlFreeDoc(response
);
11374 xmlFreeNode(request
);
11377 char *id_locale
= _isds_utf82locale((char *) *id
);
11378 isds_log(ILF_ISDS
, ILL_DEBUG
,
11379 _("Document %s has been submitted for conversion "
11380 "to server successfully\n"), id_locale
);
11383 #else /* not HAVE_LIBCURL */
11390 /* Close possibly opened connection to Czech POINT document deposit.
11391 * @context is Czech POINT session context. */
11392 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11393 if (!context
) return IE_INVALID_CONTEXT
;
11394 zfree(context
->long_message
);
11396 return czp_do_close_connection(context
);
11403 /* Send request for new box creation in testing ISDS instance.
11404 * It's not possible to request for a production box currently, as it
11405 * communicates via e-mail.
11406 * XXX: This function does not work either. Server complains about invalid
11408 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11410 * @context is special session context for box creation request. DO NOT use
11411 * standard context as it could reveal your password. Use fresh new context or
11412 * context previously used by this function.
11413 * @box is box description to create including single primary user (in case of
11414 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11415 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11416 * box, or contact address of PFO box owner). The email member is mandatory as
11417 * it will be used to deliver credentials.
11418 * @former_names is former name of box owner. Pass NULL if you don't care.
11419 * @approval is optional external approval of box manipulation
11420 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11421 * NULL, if you don't care.*/
11422 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11423 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11424 const char *former_names
, const struct isds_approval
*approval
,
11425 char **refnumber
) {
11426 isds_error err
= IE_SUCCESS
;
11428 xmlNodePtr request
= NULL
;
11429 xmlDocPtr response
= NULL
;
11430 xmlXPathContextPtr xpath_ctx
= NULL
;
11431 xmlXPathObjectPtr result
= NULL
;
11435 if (!context
) return IE_INVALID_CONTEXT
;
11436 zfree(context
->long_message
);
11437 if (!box
) return IE_INVAL
;
11440 if (!box
->email
|| box
->email
[0] == '\0') {
11441 isds_log_message(context
, _("E-mail field is mandatory"));
11445 /* Scratch box ID */
11448 /* Store configuration */
11449 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11450 free(context
->url
);
11451 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11452 if (!(context
->url
))
11455 /* Prepare CURL handle if not yet connected */
11456 if (!context
->curl
) {
11457 context
->curl
= curl_easy_init();
11458 if (!(context
->curl
))
11462 /* Build CreateDataBox request */
11463 err
= build_CreateDBInput_request(context
,
11464 &request
, BAD_CAST
"CreateDataBox",
11465 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11466 if (err
) goto leave
;
11468 /* Send it to server and process response */
11469 err
= send_destroy_request_check_response(context
,
11470 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11471 &response
, (xmlChar
**) refnumber
, NULL
);
11472 if (err
) goto leave
;
11474 /* Extract box ID */
11475 xpath_ctx
= xmlXPathNewContext(response
);
11480 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11484 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11487 xmlXPathFreeObject(result
);
11488 xmlXPathFreeContext(xpath_ctx
);
11489 xmlFreeDoc(response
);
11490 xmlFreeNode(request
);
11493 isds_log(ILF_ISDS
, ILL_DEBUG
,
11494 _("CreateDataBox request processed by server successfully.\n"));
11496 #else /* not HAVE_LIBCURL */
11504 /* Submit CMS signed message to ISDS to verify its originality. This is
11505 * stronger form of isds_verify_message_hash() because ISDS does more checks
11506 * than simple one (potentialy old weak) hash comparison.
11507 * @context is session context
11508 * @message is memory with raw CMS signed message bit stream
11509 * @length is @message size in bytes
11511 * IE_SUCCESS if message originates in ISDS
11512 * IE_NOTEQUAL if message is unknown to ISDS
11513 * other code for other errors */
11514 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11515 const void *message
, size_t length
) {
11516 isds_error err
= IE_SUCCESS
;
11518 xmlNsPtr isds_ns
= NULL
;
11519 xmlNodePtr request
= NULL
;
11520 xmlDocPtr response
= NULL
;
11521 xmlXPathContextPtr xpath_ctx
= NULL
;
11522 xmlXPathObjectPtr result
= NULL
;
11523 _Bool
*authentic
= NULL
;
11526 if (!context
) return IE_INVALID_CONTEXT
;
11527 zfree(context
->long_message
);
11528 if (!message
|| length
== 0) return IE_INVAL
;
11531 /* Check if connection is established
11532 * TODO: This check should be done downstairs. */
11533 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11536 /* Build AuthenticateMessage request */
11537 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11539 isds_log_message(context
,
11540 _("Could not build AuthenticateMessage request"));
11543 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11545 isds_log_message(context
, _("Could not create ISDS name space"));
11546 xmlFreeNode(request
);
11549 xmlSetNs(request
, isds_ns
);
11551 /* Insert Base64 encoded message */
11552 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11554 if (err
) goto leave
;
11556 /* Send request to server and process response */
11557 err
= send_destroy_request_check_response(context
,
11558 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11559 &response
, NULL
, NULL
);
11560 if (err
) goto leave
;
11563 /* ISDS has decided */
11564 xpath_ctx
= xmlXPathNewContext(response
);
11569 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11574 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11577 isds_log_message(context
,
11578 _("Server did not return any response on "
11579 "AuthenticateMessage request"));
11584 isds_log(ILF_ISDS
, ILL_DEBUG
,
11585 _("ISDS authenticated the message successfully\n"));
11587 isds_log_message(context
, _("ISDS does not know the message"));
11594 xmlXPathFreeObject(result
);
11595 xmlXPathFreeContext(xpath_ctx
);
11597 xmlFreeDoc(response
);
11598 xmlFreeNode(request
);
11599 #else /* not HAVE_LIBCURL */
11607 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11608 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11610 * @context is session context
11611 * @input_data is memory with raw CMS signed message or delivery info bit
11612 * stream to re-sign
11613 * @input_length is @input_data size in bytes
11614 * @output_data is pointer to auto-allocated memory where to store re-signed
11615 * input data blob. Caller must free it.
11616 * @output_data is pointer where to store @output_data size in bytes
11617 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11618 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11620 * IE_SUCCESS if CMS blob has been re-signed successfully
11621 * other code for other errors */
11622 isds_error
isds_resign_message(struct isds_ctx
*context
,
11623 const void *input_data
, size_t input_length
,
11624 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11625 isds_error err
= IE_SUCCESS
;
11627 xmlNsPtr isds_ns
= NULL
;
11628 xmlNodePtr request
= NULL
;
11629 xmlDocPtr response
= NULL
;
11630 xmlXPathContextPtr xpath_ctx
= NULL
;
11631 xmlXPathObjectPtr result
= NULL
;
11632 char *string
= NULL
;
11633 const xmlChar
*codes
[] = {
11640 const char *meanings
[] = {
11642 "Message is not original",
11643 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11644 "Time stamp could not been generated in time"
11646 const isds_error errors
[] = {
11652 struct code_map_isds_error map
= {
11654 .meanings
= meanings
,
11659 if (NULL
!= output_data
) *output_data
= NULL
;
11660 if (NULL
!= output_length
) *output_length
= 0;
11661 if (NULL
!= valid_to
) *valid_to
= NULL
;
11663 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11664 zfree(context
->long_message
);
11665 if (NULL
== input_data
|| 0 == input_length
) {
11666 isds_log_message(context
, _("Empty CMS blob on input"));
11669 if (NULL
== output_data
|| NULL
== output_length
) {
11670 isds_log_message(context
,
11671 _("NULL pointer provided for output CMS blob"));
11676 /* Check if connection is established
11677 * TODO: This check should be done downstairs. */
11678 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11681 /* Build Re-signISDSDocument request */
11682 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11684 isds_log_message(context
,
11685 _("Could not build Re-signISDSDocument request"));
11688 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11690 isds_log_message(context
, _("Could not create ISDS name space"));
11691 xmlFreeNode(request
);
11694 xmlSetNs(request
, isds_ns
);
11696 /* Insert Base64 encoded CMS blob */
11697 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11698 input_data
, input_length
);
11699 if (err
) goto leave
;
11701 /* Send request to server and process response */
11702 err
= send_destroy_request_check_response(context
,
11703 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11704 &response
, NULL
, &map
);
11705 if (err
) goto leave
;
11708 /* Extract re-signed data */
11709 xpath_ctx
= xmlXPathNewContext(response
);
11714 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11718 result
= xmlXPathEvalExpression(
11719 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11724 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11725 isds_log_message(context
,
11726 _("Missing Re-signISDSDocumentResponse element"));
11730 if (result
->nodesetval
->nodeNr
> 1) {
11731 isds_log_message(context
,
11732 _("Multiple Re-signISDSDocumentResponse element"));
11736 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11737 xmlXPathFreeObject(result
); result
= NULL
;
11739 EXTRACT_STRING("isds:dmResultDoc", string
);
11740 /* Decode non-empty data */
11741 if (NULL
!= string
&& string
[0] != '\0') {
11742 *output_length
= _isds_b64decode(string
, output_data
);
11743 if (*output_length
== (size_t) -1) {
11744 isds_log_message(context
,
11745 _("Error while Base64-decoding re-signed data"));
11750 isds_log_message(context
, _("Server did not send re-signed data"));
11756 if (NULL
!= valid_to
) {
11757 /* Get time stamp expiration date */
11758 EXTRACT_STRING("isds:dmValidTo", string
);
11759 if (NULL
!= string
) {
11760 *valid_to
= calloc(1, sizeof(**valid_to
));
11765 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11767 if (err
== IE_NOTSUP
) {
11769 char *string_locale
= _isds_utf82locale(string
);
11770 isds_printf_message(context
,
11771 _("Invalid dmValidTo value: %s"), string_locale
);
11772 free(string_locale
);
11782 xmlXPathFreeObject(result
);
11783 xmlXPathFreeContext(xpath_ctx
);
11785 xmlFreeDoc(response
);
11786 xmlFreeNode(request
);
11787 #else /* not HAVE_LIBCURL */
11794 #undef INSERT_ELEMENT
11795 #undef CHECK_FOR_STRING_LENGTH
11796 #undef INSERT_STRING_ATTRIBUTE
11797 #undef INSERT_ULONGINTNOPTR
11798 #undef INSERT_ULONGINT
11799 #undef INSERT_LONGINT
11800 #undef INSERT_BOOLEAN
11801 #undef INSERT_SCALAR_BOOLEAN
11802 #undef INSERT_STRING
11803 #undef INSERT_STRING_WITH_NS
11804 #undef EXTRACT_STRING_ATTRIBUTE
11805 #undef EXTRACT_ULONGINT
11806 #undef EXTRACT_LONGINT
11807 #undef EXTRACT_BOOLEAN
11808 #undef EXTRACT_STRING
11811 /* Compute hash of message from raw representation and store it into envelope.
11812 * Original hash structure will be destroyed in envelope.
11813 * @context is session context
11814 * @message is message carrying raw XML message blob
11815 * @algorithm is desired hash algorithm to use */
11816 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11817 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11818 isds_error err
= IE_SUCCESS
;
11820 void *xml_stream
= NULL
;
11821 size_t xml_stream_length
;
11822 size_t phys_start
, phys_end
;
11823 char *phys_path
= NULL
;
11824 struct isds_hash
*new_hash
= NULL
;
11827 if (!context
) return IE_INVALID_CONTEXT
;
11828 zfree(context
->long_message
);
11829 if (!message
) return IE_INVAL
;
11831 if (!message
->raw
) {
11832 isds_log_message(context
,
11833 _("Message does not carry raw representation"));
11837 switch (message
->raw_type
) {
11838 case RAWTYPE_INCOMING_MESSAGE
:
11840 xml_stream
= message
->raw
;
11841 xml_stream_length
= message
->raw_length
;
11844 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11845 nsuri
= SISDS_INCOMING_NS
;
11846 xml_stream
= message
->raw
;
11847 xml_stream_length
= message
->raw_length
;
11850 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11851 nsuri
= SISDS_INCOMING_NS
;
11852 err
= _isds_extract_cms_data(context
,
11853 message
->raw
, message
->raw_length
,
11854 &xml_stream
, &xml_stream_length
);
11855 if (err
) goto leave
;
11858 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11859 nsuri
= SISDS_OUTGOING_NS
;
11860 xml_stream
= message
->raw
;
11861 xml_stream_length
= message
->raw_length
;
11864 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11865 nsuri
= SISDS_OUTGOING_NS
;
11866 err
= _isds_extract_cms_data(context
,
11867 message
->raw
, message
->raw_length
,
11868 &xml_stream
, &xml_stream_length
);
11869 if (err
) goto leave
;
11873 isds_log_message(context
, _("Bad raw representation type"));
11879 /* XXX: Hash is computed from original string representing isds:dmDm
11880 * subtree. That means no encoding, white space, xmlns attributes changes.
11881 * In other words, input for hash can be invalid XML stream. */
11882 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11883 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11884 PHYSXML_ELEMENT_SEPARATOR
,
11885 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11886 PHYSXML_ELEMENT_SEPARATOR
11887 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11891 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11892 phys_path
, &phys_start
, &phys_end
);
11895 isds_log_message(context
,
11896 _("Substring with isds:dmDM element could not be located "
11897 "in raw message"));
11903 new_hash
= calloc(1, sizeof(*new_hash
));
11908 new_hash
->algorithm
= algorithm
;
11909 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11912 isds_log_message(context
, _("Could not compute message hash"));
11916 /* Save computed hash */
11917 if (!message
->envelope
) {
11918 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11919 if (!message
->envelope
) {
11924 isds_hash_free(&message
->envelope
->hash
);
11925 message
->envelope
->hash
= new_hash
;
11929 isds_hash_free(&new_hash
);
11933 if (xml_stream
!= message
->raw
) free(xml_stream
);
11938 /* Compare two hashes.
11939 * @h1 is first hash
11940 * @h2 is another hash
11942 * IE_SUCCESS if hashes equal
11943 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11944 * IE_ENUM if not comparable, but both structures defined
11945 * IE_INVAL if some of the structures are undefined (NULL)
11946 * IE_ERROR if internal error occurs */
11947 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
11948 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
11949 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
11950 if (h1
->length
!= h2
->length
) return IE_ERROR
;
11951 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
11952 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
11954 for (int i
= 0; i
< h1
->length
; i
++) {
11955 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
11956 return IE_NOTEQUAL
;
11962 /* Check message has gone through ISDS by comparing message hash stored in
11963 * ISDS and locally computed hash. You must provide message with valid raw
11964 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11965 * This is convenient wrapper for isds_download_message_hash(),
11966 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11967 * @context is session context
11968 * @message is message with valid raw and envelope member; envelope->hash
11969 * member will be changed during function run. Use envelope on heap only.
11971 * IE_SUCCESS if message originates in ISDS
11972 * IE_NOTEQUAL if message is unknown to ISDS
11973 * other code for other errors */
11974 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
11975 struct isds_message
*message
) {
11976 isds_error err
= IE_SUCCESS
;
11977 struct isds_hash
*downloaded_hash
= NULL
;
11979 if (!context
) return IE_INVALID_CONTEXT
;
11980 zfree(context
->long_message
);
11981 if (!message
) return IE_INVAL
;
11983 if (!message
->envelope
) {
11984 isds_log_message(context
,
11985 _("Given message structure is missing envelope"));
11988 if (!message
->raw
) {
11989 isds_log_message(context
,
11990 _("Given message structure is missing raw representation"));
11994 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
11996 if (err
) goto leave
;
11998 err
= isds_compute_message_hash(context
, message
,
11999 downloaded_hash
->algorithm
);
12000 if (err
) goto leave
;
12002 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12005 isds_hash_free(&downloaded_hash
);
12010 /* Search for document by document ID in list of documents. IDs are compared
12012 * @documents is list of isds_documents
12013 * @id is document identifier
12014 * @return first matching document or NULL. */
12015 const struct isds_document
*isds_find_document_by_id(
12016 const struct isds_list
*documents
, const char *id
) {
12017 const struct isds_list
*item
;
12018 const struct isds_document
*document
;
12020 for (item
= documents
; item
; item
= item
->next
) {
12021 document
= (struct isds_document
*) item
->data
;
12022 if (!document
) continue;
12024 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12032 /* Normalize @mime_type to be proper MIME type.
12033 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12034 * guess regular MIME type (e.g. "application/pdf").
12035 * @mime_type is UTF-8 encoded MIME type to fix
12036 * @return original @mime_type if no better interpretation exists, or
12037 * constant static UTF-8 encoded string with proper MIME type. */
12038 const char *isds_normalize_mime_type(const char *mime_type
) {
12039 if (!mime_type
) return NULL
;
12041 for (int offset
= 0;
12042 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12044 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12045 extension_map_mime
[offset
]))
12046 return (const char *) extension_map_mime
[offset
+ 1];
12053 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12054 struct isds_message **message);
12055 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12056 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12057 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12058 struct isds_address **address);
12060 int isds_message_free(struct isds_message **message);
12061 int isds_address_free(struct isds_address **address);
12065 /* Makes known all relevant namespaces to given XPath context
12066 * @xpath_ctx is XPath context
12067 * @message_ns selects proper message name space. Unsigned and signed
12068 * messages and delivery info's differ in prefix and URI. */
12069 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12070 const message_ns_type message_ns
) {
12071 const xmlChar
*message_namespace
= NULL
;
12073 if (!xpath_ctx
) return IE_ERROR
;
12075 switch(message_ns
) {
12077 message_namespace
= BAD_CAST ISDS1_NS
; break;
12078 case MESSAGE_NS_UNSIGNED
:
12079 message_namespace
= BAD_CAST ISDS_NS
; break;
12080 case MESSAGE_NS_SIGNED_INCOMING
:
12081 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12082 case MESSAGE_NS_SIGNED_OUTGOING
:
12083 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12084 case MESSAGE_NS_SIGNED_DELIVERY
:
12085 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12090 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12092 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12094 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12096 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12098 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12100 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))