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
)) {
7690 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7696 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7697 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7701 if (result
->nodesetval
->nodeNr
> 1) {
7702 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7706 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7707 xmlXPathFreeObject(result
); result
= NULL
;
7710 /* Extract counters */
7711 if (NULL
!= total_matching_boxes
) {
7712 EXTRACT_ULONGINT("isds:totalCount", total_matching_boxes
, 1);
7714 if (NULL
!= current_page_size
) {
7715 EXTRACT_ULONGINT("isds:currentCount", current_page_size
, 1);
7717 if (NULL
!= current_page_beginning
) {
7718 EXTRACT_ULONGINT("isds:position", current_page_beginning
, 1);
7720 if (NULL
!= last_page
) {
7721 EXTRACT_BOOLEAN("isds:lastPage", last_page
);
7724 /* Extract boxes if they present */
7725 result
= xmlXPathEvalExpression(BAD_CAST
7726 "isds:dbResults/isds:dbResult", xpath_ctx
);
7727 if (NULL
== result
) {
7731 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7732 struct isds_list
*item
, *prev_item
= NULL
;
7733 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7734 item
= calloc(1, sizeof(*item
));
7740 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7741 if (i
== 0) *boxes
= item
;
7742 else prev_item
->next
= item
;
7745 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7746 err
= extract_dbResult(context
,
7747 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7748 (NULL
== track_matches
) ? 0 : *track_matches
);
7749 if (err
) goto leave
;
7755 isds_list_free(boxes
);
7759 xmlFreeNode(request
);
7760 xmlXPathFreeObject(result
);
7761 xmlXPathFreeContext(xpath_ctx
);
7762 xmlFreeDoc(response
);
7765 isds_log(ILF_ISDS
, ILL_DEBUG
,
7766 _("ISDSSearch2 request processed by server successfully.\n"));
7767 #else /* not HAVE_LIBCURL */
7775 /* Get status of a box.
7776 * @context is ISDS session context.
7777 * @box_id is UTF-8 encoded box identifier as zero terminated string
7778 * @box_status is return value of box status.
7780 * IE_SUCCESS if box has been found and its status retrieved
7781 * IE_NOEXIST if box is not known to ISDS server
7782 * or other appropriate error.
7783 * You can use isds_DbState to enumerate box status. However out of enum
7784 * range value can be returned too. This is feature because ISDS
7785 * specification leaves the set of values open.
7786 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7787 * the box has been deleted, but ISDS still lists its former existence. */
7788 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7789 long int *box_status
) {
7790 isds_error err
= IE_SUCCESS
;
7792 xmlNsPtr isds_ns
= NULL
;
7793 xmlNodePtr request
= NULL
, db_id
;
7794 xmlDocPtr response
= NULL
;
7795 xmlXPathContextPtr xpath_ctx
= NULL
;
7796 xmlXPathObjectPtr result
= NULL
;
7797 xmlChar
*string
= NULL
;
7799 const xmlChar
*codes
[] = {
7805 const char *meanings
[] = {
7806 "The box does not exist",
7807 "Box ID is malformed",
7810 const isds_error errors
[] = {
7815 struct code_map_isds_error map
= {
7817 .meanings
= meanings
,
7822 if (!context
) return IE_INVALID_CONTEXT
;
7823 zfree(context
->long_message
);
7824 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7827 /* Check if connection is established
7828 * TODO: This check should be done downstairs. */
7829 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7832 /* Build CheckDataBox request */
7833 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7835 isds_log_message(context
,
7836 _("Could build CheckDataBox request"));
7839 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7841 isds_log_message(context
, _("Could not create ISDS name space"));
7842 xmlFreeNode(request
);
7845 xmlSetNs(request
, isds_ns
);
7846 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7848 isds_log_message(context
, _("Could not add dbID child to "
7849 "CheckDataBox element"));
7850 xmlFreeNode(request
);
7855 /* Send request and check response*/
7856 err
= send_destroy_request_check_response(context
,
7857 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7858 &request
, &response
, NULL
, &map
);
7859 if (err
) goto leave
;
7863 xpath_ctx
= xmlXPathNewContext(response
);
7868 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7872 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7878 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7879 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7883 if (result
->nodesetval
->nodeNr
> 1) {
7884 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7888 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7889 xmlXPathFreeObject(result
); result
= NULL
;
7891 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7896 xmlXPathFreeObject(result
);
7897 xmlXPathFreeContext(xpath_ctx
);
7899 xmlFreeDoc(response
);
7902 isds_log(ILF_ISDS
, ILL_DEBUG
,
7903 _("CheckDataBox request processed by server successfully.\n"));
7904 #else /* not HAVE_LIBCURL */
7912 /* Get list of permissions to send commercial messages.
7913 * @context is ISDS session context.
7914 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7915 * @permissions is a reallocated list of permissions (struct
7916 * isds_commercial_permission*) to send commercial messages from @box_id. The
7917 * order of permissions is significant as the server applies the permissions
7918 * and associated pre-paid credits in the order. Empty list means no
7921 * IE_SUCCESS if the list has been obtained correctly,
7922 * or other appropriate error. */
7923 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7924 const char *box_id
, struct isds_list
**permissions
) {
7925 isds_error err
= IE_SUCCESS
;
7927 xmlDocPtr response
= NULL
;
7928 xmlXPathContextPtr xpath_ctx
= NULL
;
7929 xmlXPathObjectPtr result
= NULL
;
7932 if (!context
) return IE_INVALID_CONTEXT
;
7933 zfree(context
->long_message
);
7934 if (NULL
== permissions
) return IE_INVAL
;
7935 isds_list_free(permissions
);
7936 if (NULL
== box_id
) return IE_INVAL
;
7939 /* Check if connection is established */
7940 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7942 /* Do request and check for success */
7943 err
= build_send_dbid_request_check_response(context
,
7944 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7945 BAD_CAST box_id
, NULL
, &response
, NULL
);
7947 isds_log(ILF_ISDS
, ILL_DEBUG
,
7948 _("PDZInfo request processed by server successfully.\n"));
7952 /* Prepare structure */
7953 xpath_ctx
= xmlXPathNewContext(response
);
7958 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7963 /* Set context node */
7964 result
= xmlXPathEvalExpression(BAD_CAST
7965 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7971 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7972 struct isds_list
*prev_item
= NULL
;
7974 /* Iterate over all permission records */
7975 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7976 struct isds_list
*item
;
7978 /* Prepare structure */
7979 item
= calloc(1, sizeof(*item
));
7984 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
7985 if (i
== 0) *permissions
= item
;
7986 else prev_item
->next
= item
;
7990 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7991 err
= extract_DbPDZRecord(context
,
7992 (struct isds_commercial_permission
**) (&item
->data
),
7994 if (err
) goto leave
;
8000 isds_list_free(permissions
);
8003 xmlXPathFreeObject(result
);
8004 xmlXPathFreeContext(xpath_ctx
);
8005 xmlFreeDoc(response
);
8007 #else /* not HAVE_LIBCURL */
8015 /* Get details about credit for sending pre-paid commercial messages.
8016 * @context is ISDS session context.
8017 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8018 * @from_date is first day of credit history to return in @history. Only
8019 * tm_year, tm_mon and tm_mday carry sane value.
8020 * @to_date is last day of credit history to return in @history. Only
8021 * tm_year, tm_mon and tm_mday carry sane value.
8022 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8023 * if you don't care. This and all other credit values are integers in
8024 * hundredths of Czech Crowns.
8025 * @email outputs notification e-mail address where notifications about credit
8026 * are sent. This is automatically reallocated string. Pass NULL if you don't
8027 * care. It can return NULL if no address is defined.
8028 * @history outputs auto-reallocated list of pointers to struct
8029 * isds_credit_event. Events in closed interval @from_time to @to_time are
8030 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8031 * are sorted by time.
8033 * IE_SUCCESS if the credit details have been obtained correctly,
8034 * or other appropriate error. Please note that server allows to retrieve
8035 * only limited history of events. */
8036 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8038 const struct tm
*from_date
, const struct tm
*to_date
,
8039 long int *credit
, char **email
, struct isds_list
**history
) {
8040 isds_error err
= IE_SUCCESS
;
8042 char *box_id_locale
= NULL
;
8043 xmlNodePtr request
= NULL
, node
;
8044 xmlNsPtr isds_ns
= NULL
;
8045 xmlChar
*string
= NULL
;
8047 xmlDocPtr response
= NULL
;
8048 xmlXPathContextPtr xpath_ctx
= NULL
;
8049 xmlXPathObjectPtr result
= NULL
;
8051 const xmlChar
*codes
[] = {
8059 const char *meanings
[] = {
8060 "Insufficient priviledges for the box",
8061 "The box does not exist",
8062 "Date is too long (history is not available after 15 months)",
8063 "Interval is too long (limit is 3 months)",
8066 const isds_error errors
[] = {
8073 struct code_map_isds_error map
= {
8075 .meanings
= meanings
,
8080 if (!context
) return IE_INVALID_CONTEXT
;
8081 zfree(context
->long_message
);
8083 /* Free output argument */
8084 if (NULL
!= credit
) *credit
= 0;
8085 if (NULL
!= email
) zfree(*email
);
8086 isds_list_free(history
);
8088 if (NULL
== box_id
) return IE_INVAL
;
8091 /* Check if connection is established */
8092 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8094 box_id_locale
= _isds_utf82locale((char*)box_id
);
8095 if (NULL
== box_id_locale
) {
8101 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8102 if (NULL
== request
) {
8103 isds_printf_message(context
,
8104 _("Could not build DataBoxCreditInfo request for %s box"),
8109 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8111 isds_log_message(context
, _("Could not create ISDS name space"));
8115 xmlSetNs(request
, isds_ns
);
8117 /* Add mandatory XSD:tIdDbInput child */
8118 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8119 /* Add mandatory dates elements with optional values */
8121 err
= tm2datestring(from_date
, &string
);
8123 isds_log_message(context
,
8124 _("Could not convert `from_date' argument to ISO date "
8128 INSERT_STRING(request
, "ciFromDate", string
);
8131 INSERT_STRING(request
, "ciFromDate", NULL
);
8134 err
= tm2datestring(to_date
, &string
);
8136 isds_log_message(context
,
8137 _("Could not convert `to_date' argument to ISO date "
8141 INSERT_STRING(request
, "ciTodate", string
);
8144 INSERT_STRING(request
, "ciTodate", NULL
);
8147 /* Send request and check response*/
8148 err
= send_destroy_request_check_response(context
,
8149 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8150 &request
, &response
, NULL
, &map
);
8151 if (err
) goto leave
;
8155 /* Set context to the root */
8156 xpath_ctx
= xmlXPathNewContext(response
);
8161 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8165 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8171 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8172 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8176 if (result
->nodesetval
->nodeNr
> 1) {
8177 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8181 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8182 xmlXPathFreeObject(result
); result
= NULL
;
8184 /* Extract common data */
8185 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8186 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8188 /* Extract records */
8189 if (NULL
== history
) goto leave
;
8190 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8196 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8197 struct isds_list
*prev_item
= NULL
;
8199 /* Iterate over all records */
8200 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8201 struct isds_list
*item
;
8203 /* Prepare structure */
8204 item
= calloc(1, sizeof(*item
));
8209 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8210 if (i
== 0) *history
= item
;
8211 else prev_item
->next
= item
;
8215 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8216 err
= extract_CiRecord(context
,
8217 (struct isds_credit_event
**) (&item
->data
),
8219 if (err
) goto leave
;
8225 isds_log(ILF_ISDS
, ILL_DEBUG
,
8226 _("DataBoxCreditInfo request processed by server successfully.\n"));
8229 isds_list_free(history
);
8230 if (NULL
!= email
) zfree(*email
)
8233 free(box_id_locale
);
8234 xmlXPathFreeObject(result
);
8235 xmlXPathFreeContext(xpath_ctx
);
8236 xmlFreeDoc(response
);
8238 #else /* not HAVE_LIBCURL */
8246 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8247 * code, destroy response and log success.
8248 * @context is ISDS session context.
8249 * @service_name is name of SERVICE_DB_MANIPULATION service
8250 * @box_id is UTF-8 encoded box identifier as zero terminated string
8251 * @approval is optional external approval of box manipulation
8252 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8253 * NULL, if you don't care. */
8254 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8255 struct isds_ctx
*context
, const xmlChar
*service_name
,
8256 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8257 xmlChar
**refnumber
) {
8258 isds_error err
= IE_SUCCESS
;
8260 xmlDocPtr response
= NULL
;
8263 if (!context
) return IE_INVALID_CONTEXT
;
8264 zfree(context
->long_message
);
8265 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8268 /* Check if connection is established */
8269 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8271 /* Do request and check for success */
8272 err
= build_send_dbid_request_check_response(context
,
8273 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8274 &response
, refnumber
);
8275 xmlFreeDoc(response
);
8278 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8279 isds_log(ILF_ISDS
, ILL_DEBUG
,
8280 _("%s request processed by server successfully.\n"),
8281 service_name_locale
);
8282 free(service_name_locale
);
8284 #else /* not HAVE_LIBCURL */
8292 /* Switch box into state where box can receive commercial messages (off by
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 commercial messages income
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_commercial_receiving(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
"SetOpenAddressing" :
8305 BAD_CAST
"ClearOpenAddressing",
8306 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8310 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8311 * message acceptance). This is just a box permission. Sender must apply
8312 * such role by sending each message.
8313 * @context is ISDS session context.
8314 * @box_id is UTF-8 encoded box identifier as zero terminated string
8315 * @allow is true for enable, false for disable OVM role permission
8316 * @approval is optional external approval of box manipulation
8317 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8318 * NULL, if you don't care. */
8319 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8320 const char *box_id
, const _Bool allow
,
8321 const struct isds_approval
*approval
, char **refnumber
) {
8322 return build_send_manipulationdbid_request_check_drop_response(context
,
8323 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8324 BAD_CAST
"ClearEffectiveOVM",
8325 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8329 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8330 * code, destroy response and log success.
8331 * @context is ISDS session context.
8332 * @service_name is name of SERVICE_DB_MANIPULATION service
8333 * @owner is structure describing box
8334 * @approval is optional external approval of box manipulation
8335 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8336 * NULL, if you don't care. */
8337 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8338 struct isds_ctx
*context
, const xmlChar
*service_name
,
8339 const struct isds_DbOwnerInfo
*owner
,
8340 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8341 isds_error err
= IE_SUCCESS
;
8343 char *service_name_locale
= NULL
;
8344 xmlNodePtr request
= NULL
, db_owner_info
;
8345 xmlNsPtr isds_ns
= NULL
;
8349 if (!context
) return IE_INVALID_CONTEXT
;
8350 zfree(context
->long_message
);
8351 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8354 service_name_locale
= _isds_utf82locale((char*)service_name
);
8355 if (!service_name_locale
) {
8361 request
= xmlNewNode(NULL
, service_name
);
8363 isds_printf_message(context
,
8364 _("Could not build %s request"), service_name_locale
);
8368 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8370 isds_log_message(context
, _("Could not create ISDS name space"));
8374 xmlSetNs(request
, isds_ns
);
8377 /* Add XSD:tOwnerInfoInput child*/
8378 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8379 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8380 if (err
) goto leave
;
8382 /* Add XSD:gExtApproval*/
8383 err
= insert_GExtApproval(context
, approval
, request
);
8384 if (err
) goto leave
;
8386 /* Send it to server and process response */
8387 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8388 service_name
, &request
, refnumber
);
8391 xmlFreeNode(request
);
8392 free(service_name_locale
);
8393 #else /* not HAVE_LIBCURL */
8401 /* Switch box accessibility state on request of box owner.
8402 * Despite the name, owner must do the request off-line. This function is
8403 * designed for such off-line meeting points (e.g. Czech POINT).
8404 * @context is ISDS session context.
8405 * @box identifies box to switch accessibility state.
8406 * @allow is true for making accessible, false to disallow access.
8407 * @approval is optional external approval of box manipulation
8408 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8409 * NULL, if you don't care. */
8410 isds_error
isds_switch_box_accessibility_on_owner_request(
8411 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8412 const _Bool allow
, const struct isds_approval
*approval
,
8414 return build_send_manipulationdbowner_request_check_drop_response(context
,
8415 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8416 BAD_CAST
"DisableOwnDataBox",
8417 box
, approval
, (xmlChar
**) refnumber
);
8421 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8423 * @context is ISDS session context.
8424 * @box identifies box to switch accessibility state.
8425 * @since is date since accessibility has been denied. This can be past too.
8426 * Only tm_year, tm_mon and tm_mday carry sane value.
8427 * @approval is optional external approval of box manipulation
8428 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8429 * NULL, if you don't care. */
8430 isds_error
isds_disable_box_accessibility_externaly(
8431 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8432 const struct tm
*since
, const struct isds_approval
*approval
,
8434 isds_error err
= IE_SUCCESS
;
8436 char *service_name_locale
= NULL
;
8437 xmlNodePtr request
= NULL
, node
;
8438 xmlNsPtr isds_ns
= NULL
;
8439 xmlChar
*string
= NULL
;
8443 if (!context
) return IE_INVALID_CONTEXT
;
8444 zfree(context
->long_message
);
8445 if (!box
|| !since
) return IE_INVAL
;
8449 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8451 isds_printf_message(context
,
8452 _("Could not build %s request"), "DisableDataBoxExternally");
8456 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8458 isds_log_message(context
, _("Could not create ISDS name space"));
8462 xmlSetNs(request
, isds_ns
);
8465 /* Add @box identification */
8466 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8467 err
= insert_DbOwnerInfo(context
, box
, node
);
8468 if (err
) goto leave
;
8470 /* Add @since date */
8471 err
= tm2datestring(since
, &string
);
8473 isds_log_message(context
,
8474 _("Could not convert `since' argument to ISO date string"));
8477 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8481 err
= insert_GExtApproval(context
, approval
, request
);
8482 if (err
) goto leave
;
8484 /* Send it to server and process response */
8485 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8486 BAD_CAST
"DisableDataBoxExternally", &request
,
8487 (xmlChar
**) refnumber
);
8491 xmlFreeNode(request
);
8492 free(service_name_locale
);
8493 #else /* not HAVE_LIBCURL */
8502 /* Insert struct isds_message data (envelope (recipient data optional) and
8503 * documents into XML tree
8504 * @context is session context
8505 * @outgoing_message is libisds structure with message data
8506 * @create_message is XML CreateMessage or CreateMultipleMessage element
8507 * @process_recipient true for recipient data serialization, false for no
8509 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8510 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8511 const _Bool process_recipient
) {
8513 isds_error err
= IE_SUCCESS
;
8514 xmlNodePtr envelope
, dm_files
, node
;
8515 xmlChar
*string
= NULL
;
8517 if (!context
) return IE_INVALID_CONTEXT
;
8518 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8521 /* Build envelope */
8522 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8524 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8525 "%s element"), create_message
->name
);
8529 if (!outgoing_message
->envelope
) {
8530 isds_log_message(context
, _("Outgoing message is missing envelope"));
8535 /* Insert optional message type */
8536 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8538 if (err
) goto leave
;
8540 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8541 outgoing_message
->envelope
->dmSenderOrgUnit
);
8542 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8543 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8545 if (process_recipient
) {
8546 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8547 isds_log_message(context
,
8548 _("Outgoing message is missing recipient box identifier"));
8552 INSERT_STRING(envelope
, "dbIDRecipient",
8553 outgoing_message
->envelope
->dbIDRecipient
);
8555 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8556 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8557 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8558 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8559 INSERT_STRING(envelope
, "dmToHands",
8560 outgoing_message
->envelope
->dmToHands
);
8563 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8565 INSERT_STRING(envelope
, "dmAnnotation",
8566 outgoing_message
->envelope
->dmAnnotation
);
8568 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8569 0, 50, "dmRecipientRefNumber");
8570 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8571 outgoing_message
->envelope
->dmRecipientRefNumber
);
8573 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8574 0, 50, "dmSenderRefNumber");
8575 INSERT_STRING(envelope
, "dmSenderRefNumber",
8576 outgoing_message
->envelope
->dmSenderRefNumber
);
8578 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8579 0, 50, "dmRecipientIdent");
8580 INSERT_STRING(envelope
, "dmRecipientIdent",
8581 outgoing_message
->envelope
->dmRecipientIdent
);
8583 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8584 0, 50, "dmSenderIdent");
8585 INSERT_STRING(envelope
, "dmSenderIdent",
8586 outgoing_message
->envelope
->dmSenderIdent
);
8588 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8589 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8590 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8591 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8592 INSERT_STRING(envelope
, "dmLegalTitleSect",
8593 outgoing_message
->envelope
->dmLegalTitleSect
);
8594 INSERT_STRING(envelope
, "dmLegalTitlePar",
8595 outgoing_message
->envelope
->dmLegalTitlePar
);
8596 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8597 outgoing_message
->envelope
->dmLegalTitlePoint
);
8599 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8600 outgoing_message
->envelope
->dmPersonalDelivery
);
8601 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8602 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8604 /* ???: Should we require value for dbEffectiveOVM sender?
8605 * ISDS has default as true */
8606 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8607 INSERT_BOOLEAN(envelope
, "dmOVM",
8608 outgoing_message
->envelope
->dmPublishOwnID
);
8611 /* Append dmFiles */
8612 if (!outgoing_message
->documents
) {
8613 isds_log_message(context
,
8614 _("Outgoing message is missing list of documents"));
8618 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8620 isds_printf_message(context
, _("Could not add dmFiles child to "
8621 "%s element"), create_message
->name
);
8626 /* Check for document hierarchy */
8627 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8628 if (err
) goto leave
;
8630 /* Process each document */
8631 for (struct isds_list
*item
=
8632 (struct isds_list
*) outgoing_message
->documents
;
8633 item
; item
= item
->next
) {
8635 isds_log_message(context
,
8636 _("List of documents contains empty item"));
8640 /* FIXME: Check for dmFileMetaType and for document references.
8641 * Only first document can be of MAIN type */
8642 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8645 if (err
) goto leave
;
8652 #endif /* HAVE_LIBCURL */
8655 /* Send a message via ISDS to a recipient
8656 * @context is session context
8657 * @outgoing_message is message to send; Some members are mandatory (like
8658 * dbIDRecipient), some are optional and some are irrelevant (especially data
8659 * about sender). Included pointer to isds_list documents must contain at
8660 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8661 * members will be filled with valid data from ISDS. Exact list of write
8662 * members is subject to change. Currently dmID is changed.
8663 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8664 isds_error
isds_send_message(struct isds_ctx
*context
,
8665 struct isds_message
*outgoing_message
) {
8667 isds_error err
= IE_SUCCESS
;
8669 xmlNsPtr isds_ns
= NULL
;
8670 xmlNodePtr request
= NULL
;
8671 xmlDocPtr response
= NULL
;
8672 xmlChar
*code
= NULL
, *message
= NULL
;
8673 xmlXPathContextPtr xpath_ctx
= NULL
;
8674 xmlXPathObjectPtr result
= NULL
;
8675 /*_Bool message_is_complete = 0;*/
8678 if (!context
) return IE_INVALID_CONTEXT
;
8679 zfree(context
->long_message
);
8680 if (!outgoing_message
) return IE_INVAL
;
8683 /* Check if connection is established
8684 * TODO: This check should be done downstairs. */
8685 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8688 /* Build CreateMessage request */
8689 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8691 isds_log_message(context
,
8692 _("Could not build CreateMessage request"));
8695 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8697 isds_log_message(context
, _("Could not create ISDS name space"));
8698 xmlFreeNode(request
);
8701 xmlSetNs(request
, isds_ns
);
8703 /* Append envelope and files */
8704 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8705 if (err
) goto leave
;
8708 /* Signal we can serialize message since now */
8709 /*message_is_complete = 1;*/
8712 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8715 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8717 /* Don't' destroy request, we want to provide it to application later */
8720 isds_log(ILF_ISDS
, ILL_DEBUG
,
8721 _("Processing ISDS response on CreateMessage "
8722 "request failed\n"));
8726 /* Check for response status */
8727 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8728 &code
, &message
, NULL
);
8730 isds_log(ILF_ISDS
, ILL_DEBUG
,
8731 _("ISDS response on CreateMessage request "
8732 "is missing status\n"));
8736 /* Request processed, but refused by server or server failed */
8737 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8738 char *box_id_locale
=
8739 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8740 char *code_locale
= _isds_utf82locale((char*)code
);
8741 char *message_locale
= _isds_utf82locale((char*)message
);
8742 isds_log(ILF_ISDS
, ILL_DEBUG
,
8743 _("Server did not accept message for %s on CreateMessage "
8744 "request (code=%s, message=%s)\n"),
8745 box_id_locale
, code_locale
, message_locale
);
8746 isds_log_message(context
, message_locale
);
8747 free(box_id_locale
);
8749 free(message_locale
);
8756 xpath_ctx
= xmlXPathNewContext(response
);
8761 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8765 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8771 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8772 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8776 if (result
->nodesetval
->nodeNr
> 1) {
8777 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8781 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8782 xmlXPathFreeObject(result
); result
= NULL
;
8784 if (outgoing_message
->envelope
->dmID
) {
8785 free(outgoing_message
->envelope
->dmID
);
8786 outgoing_message
->envelope
->dmID
= NULL
;
8788 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8789 if (!outgoing_message
->envelope
->dmID
) {
8790 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8791 "but did not return assigned message ID\n"));
8795 /* TODO: Serialize message into structure member raw */
8796 /* XXX: Each web service transport message in different format.
8797 * Therefore it's not possible to save them directly.
8798 * To save them, one must figure out common format.
8799 * We can leave it on application, or we can implement the ESS format. */
8800 /*if (message_is_complete) {
8801 if (outgoing_message->envelope->dmID) {
8803 /* Add assigned message ID as first child*/
8804 /*xmlNodePtr dmid_text = xmlNewText(
8805 (xmlChar *) outgoing_message->envelope->dmID);
8806 if (!dmid_text) goto serialization_failed;
8808 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8810 if (!dmid_element) {
8811 xmlFreeNode(dmid_text);
8812 goto serialization_failed;
8815 xmlNodePtr dmid_element_with_text =
8816 xmlAddChild(dmid_element, dmid_text);
8817 if (!dmid_element_with_text) {
8818 xmlFreeNode(dmid_element);
8819 xmlFreeNode(dmid_text);
8820 goto serialization_failed;
8823 node = xmlAddPrevSibling(envelope->childern,
8824 dmid_element_with_text);
8826 xmlFreeNodeList(dmid_element_with_text);
8827 goto serialization_failed;
8831 /* Serialize message with ID into raw */
8832 /*buffer = serialize_element(envelope)*/
8835 serialization_failed:
8840 xmlXPathFreeObject(result
);
8841 xmlXPathFreeContext(xpath_ctx
);
8845 xmlFreeDoc(response
);
8846 xmlFreeNode(request
);
8849 isds_log(ILF_ISDS
, ILL_DEBUG
,
8850 _("CreateMessage request processed by server "
8851 "successfully.\n"));
8852 #else /* not HAVE_LIBCURL */
8860 /* Send a message via ISDS to a multiple recipients
8861 * @context is session context
8862 * @outgoing_message is message to send; Some members are mandatory,
8863 * some are optional and some are irrelevant (especially data
8864 * about sender). Data about recipient will be substituted by ISDS from
8865 * @copies. Included pointer to isds_list documents must
8866 * contain at least one document of FILEMETATYPE_MAIN.
8867 * @copies is list of isds_message_copy structures addressing all desired
8868 * recipients. This is read-write structure, some members will be filled with
8869 * valid data from ISDS (message IDs, error codes, error descriptions).
8871 * ISDS_SUCCESS if all messages have been sent
8872 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8873 * succeeded messages can be identified by copies->data->error),
8874 * or other error code if something other goes wrong. */
8875 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8876 const struct isds_message
*outgoing_message
,
8877 struct isds_list
*copies
) {
8879 isds_error err
= IE_SUCCESS
;
8881 isds_error append_err
;
8882 xmlNsPtr isds_ns
= NULL
;
8883 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8884 struct isds_list
*item
;
8885 struct isds_message_copy
*copy
;
8886 xmlDocPtr response
= NULL
;
8887 xmlChar
*code
= NULL
, *message
= NULL
;
8888 xmlXPathContextPtr xpath_ctx
= NULL
;
8889 xmlXPathObjectPtr result
= NULL
;
8890 xmlChar
*string
= NULL
;
8894 if (!context
) return IE_INVALID_CONTEXT
;
8895 zfree(context
->long_message
);
8896 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8899 /* Check if connection is established
8900 * TODO: This check should be done downstairs. */
8901 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8904 /* Build CreateMultipleMessage request */
8905 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8907 isds_log_message(context
,
8908 _("Could not build CreateMultipleMessage request"));
8911 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8913 isds_log_message(context
, _("Could not create ISDS name space"));
8914 xmlFreeNode(request
);
8917 xmlSetNs(request
, isds_ns
);
8920 /* Build recipients */
8921 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8923 isds_log_message(context
, _("Could not add dmRecipients child to "
8924 "CreateMultipleMessage element"));
8925 xmlFreeNode(request
);
8929 /* Insert each recipient */
8930 for (item
= copies
; item
; item
= item
->next
) {
8931 copy
= (struct isds_message_copy
*) item
->data
;
8933 isds_log_message(context
,
8934 _("`copies' list item contains empty data"));
8939 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8941 isds_log_message(context
, _("Could not add dmRecipient child to "
8942 "dmRecipients element"));
8947 if (!copy
->dbIDRecipient
) {
8948 isds_log_message(context
,
8949 _("Message copy is missing recipient box identifier"));
8953 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
8954 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
8955 copy
->dmRecipientOrgUnit
);
8956 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
8957 copy
->dmRecipientOrgUnitNum
, string
);
8958 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
8961 /* Append envelope and files */
8962 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
8963 if (err
) goto leave
;
8966 isds_log(ILF_ISDS
, ILL_DEBUG
,
8967 _("Sending CreateMultipleMessage request to ISDS\n"));
8970 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8972 isds_log(ILF_ISDS
, ILL_DEBUG
,
8973 _("Processing ISDS response on CreateMultipleMessage "
8974 "request failed\n"));
8978 /* Check for response status */
8979 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8980 &code
, &message
, NULL
);
8982 isds_log(ILF_ISDS
, ILL_DEBUG
,
8983 _("ISDS response on CreateMultipleMessage request "
8984 "is missing status\n"));
8988 /* Request processed, but some copies failed */
8989 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
8990 char *box_id_locale
=
8991 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8992 char *code_locale
= _isds_utf82locale((char*)code
);
8993 char *message_locale
= _isds_utf82locale((char*)message
);
8994 isds_log(ILF_ISDS
, ILL_DEBUG
,
8995 _("Server did accept message for multiple recipients "
8996 "on CreateMultipleMessage request but delivery to "
8997 "some of them failed (code=%s, message=%s)\n"),
8998 box_id_locale
, code_locale
, message_locale
);
8999 isds_log_message(context
, message_locale
);
9000 free(box_id_locale
);
9002 free(message_locale
);
9003 err
= IE_PARTIAL_SUCCESS
;
9006 /* Request refused by server as whole */
9007 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9008 char *box_id_locale
=
9009 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9010 char *code_locale
= _isds_utf82locale((char*)code
);
9011 char *message_locale
= _isds_utf82locale((char*)message
);
9012 isds_log(ILF_ISDS
, ILL_DEBUG
,
9013 _("Server did not accept message for multiple recipients "
9014 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9015 box_id_locale
, code_locale
, message_locale
);
9016 isds_log_message(context
, message_locale
);
9017 free(box_id_locale
);
9019 free(message_locale
);
9026 xpath_ctx
= xmlXPathNewContext(response
);
9031 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9035 result
= xmlXPathEvalExpression(
9036 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9037 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9043 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9044 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9049 /* Extract message ID and delivery status for each copy */
9050 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9051 item
= item
->next
, i
++) {
9052 copy
= (struct isds_message_copy
*) item
->data
;
9053 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9055 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9061 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9062 isds_printf_message(context
, _("ISDS returned unexpected number of "
9063 "message copy delivery states: %d"),
9064 result
->nodesetval
->nodeNr
);
9073 xmlXPathFreeObject(result
);
9074 xmlXPathFreeContext(xpath_ctx
);
9078 xmlFreeDoc(response
);
9079 xmlFreeNode(request
);
9082 isds_log(ILF_ISDS
, ILL_DEBUG
,
9083 _("CreateMultipleMessageResponse request processed by server "
9084 "successfully.\n"));
9085 #else /* not HAVE_LIBCURL */
9093 /* Get list of messages. This is common core for getting sent or received
9095 * Any criterion argument can be NULL, if you don't care about it.
9096 * @context is session context. Must not be NULL.
9097 * @outgoing_direction is true if you want list of outgoing messages,
9098 * it's false if you want incoming messages.
9099 * @from_time is minimal time and date of message sending inclusive.
9100 * @to_time is maximal time and date of message sending inclusive
9101 * @organization_unit_number is number of sender/recipient respectively.
9102 * @status_filter is bit field of isds_message_status values. Use special
9103 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9104 * all values, you can use bit-wise arithmetic if you want.)
9105 * @offset is index of first message we are interested in. First message is 1.
9106 * Set to 0 (or 1) if you don't care.
9107 * @number is maximal length of list you want to get as input value, outputs
9108 * number of messages matching these criteria. Can be NULL if you don't care
9109 * (applies to output value either).
9110 * @messages is automatically reallocated list of isds_message's. Be ware that
9111 * it returns only brief overview (envelope and some other fields) about each
9112 * message, not the complete message. FIXME: Specify exact fields.
9113 * The list is sorted by delivery time in ascending order.
9114 * Use NULL if you don't care about don't need the data (useful if you want to
9115 * know only the @number). If you provide &NULL, list will be allocated on
9116 * heap, if you provide pointer to non-NULL, list will be freed automatically
9117 * at first. Also in case of error the list will be NULLed.
9118 * @return IE_SUCCESS or appropriate error code. */
9119 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9120 _Bool outgoing_direction
,
9121 const struct timeval
*from_time
, const struct timeval
*to_time
,
9122 const long int *organization_unit_number
,
9123 const unsigned int status_filter
,
9124 const unsigned long int offset
, unsigned long int *number
,
9125 struct isds_list
**messages
) {
9127 isds_error err
= IE_SUCCESS
;
9129 xmlNsPtr isds_ns
= NULL
;
9130 xmlNodePtr request
= NULL
, node
;
9131 xmlDocPtr response
= NULL
;
9132 xmlChar
*code
= NULL
, *message
= NULL
;
9133 xmlXPathContextPtr xpath_ctx
= NULL
;
9134 xmlXPathObjectPtr result
= NULL
;
9135 xmlChar
*string
= NULL
;
9136 long unsigned int count
= 0;
9139 if (!context
) return IE_INVALID_CONTEXT
;
9140 zfree(context
->long_message
);
9142 /* Free former message list if any */
9143 if (messages
) isds_list_free(messages
);
9146 /* Check if connection is established
9147 * TODO: This check should be done downstairs. */
9148 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9150 /* Build GetListOf*Messages request */
9151 request
= xmlNewNode(NULL
,
9152 (outgoing_direction
) ?
9153 BAD_CAST
"GetListOfSentMessages" :
9154 BAD_CAST
"GetListOfReceivedMessages"
9157 isds_log_message(context
,
9158 (outgoing_direction
) ?
9159 _("Could not build GetListOfSentMessages request") :
9160 _("Could not build GetListOfReceivedMessages request")
9164 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9166 isds_log_message(context
, _("Could not create ISDS name space"));
9167 xmlFreeNode(request
);
9170 xmlSetNs(request
, isds_ns
);
9174 err
= timeval2timestring(from_time
, &string
);
9175 if (err
) goto leave
;
9177 INSERT_STRING(request
, "dmFromTime", string
);
9178 free(string
); string
= NULL
;
9181 err
= timeval2timestring(to_time
, &string
);
9182 if (err
) goto leave
;
9184 INSERT_STRING(request
, "dmToTime", string
);
9185 free(string
); string
= NULL
;
9187 if (outgoing_direction
) {
9188 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9189 organization_unit_number
, string
);
9191 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9192 organization_unit_number
, string
);
9195 if (status_filter
> MESSAGESTATE_ANY
) {
9196 isds_printf_message(context
,
9197 _("Invalid message state filter value: %ld"), status_filter
);
9201 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9204 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9206 INSERT_STRING(request
, "dmOffset", "1");
9209 /* number 0 means no limit */
9210 if (number
&& *number
== 0) {
9211 INSERT_STRING(request
, "dmLimit", NULL
);
9213 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9217 isds_log(ILF_ISDS
, ILL_DEBUG
,
9218 (outgoing_direction
) ?
9219 _("Sending GetListOfSentMessages request to ISDS\n") :
9220 _("Sending GetListOfReceivedMessages request to ISDS\n")
9224 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9225 xmlFreeNode(request
); request
= NULL
;
9228 isds_log(ILF_ISDS
, ILL_DEBUG
,
9229 (outgoing_direction
) ?
9230 _("Processing ISDS response on GetListOfSentMessages "
9231 "request failed\n") :
9232 _("Processing ISDS response on GetListOfReceivedMessages "
9238 /* Check for response status */
9239 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9240 &code
, &message
, NULL
);
9242 isds_log(ILF_ISDS
, ILL_DEBUG
,
9243 (outgoing_direction
) ?
9244 _("ISDS response on GetListOfSentMessages request "
9245 "is missing status\n") :
9246 _("ISDS response on GetListOfReceivedMessages request "
9247 "is missing status\n")
9252 /* Request processed, but nothing found */
9253 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9254 char *code_locale
= _isds_utf82locale((char*)code
);
9255 char *message_locale
= _isds_utf82locale((char*)message
);
9256 isds_log(ILF_ISDS
, ILL_DEBUG
,
9257 (outgoing_direction
) ?
9258 _("Server refused GetListOfSentMessages request "
9259 "(code=%s, message=%s)\n") :
9260 _("Server refused GetListOfReceivedMessages request "
9261 "(code=%s, message=%s)\n"),
9262 code_locale
, message_locale
);
9263 isds_log_message(context
, message_locale
);
9265 free(message_locale
);
9272 xpath_ctx
= xmlXPathNewContext(response
);
9277 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9281 result
= xmlXPathEvalExpression(
9282 (outgoing_direction
) ?
9283 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9284 "isds:dmRecords/isds:dmRecord" :
9285 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9286 "isds:dmRecords/isds:dmRecord",
9293 /* Fill output arguments in */
9294 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9295 struct isds_envelope
*envelope
;
9296 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9298 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9299 /* Create new message */
9300 item
= calloc(1, sizeof(*item
));
9305 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9306 item
->data
= calloc(1, sizeof(struct isds_message
));
9308 isds_list_free(&item
);
9313 /* Extract envelope data */
9314 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9316 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9318 isds_list_free(&item
);
9322 /* Attach extracted envelope */
9323 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9325 /* Append new message into the list */
9327 *messages
= last_item
= item
;
9329 last_item
->next
= item
;
9334 if (number
) *number
= count
;
9338 isds_list_free(messages
);
9342 xmlXPathFreeObject(result
);
9343 xmlXPathFreeContext(xpath_ctx
);
9347 xmlFreeDoc(response
);
9348 xmlFreeNode(request
);
9351 isds_log(ILF_ISDS
, ILL_DEBUG
,
9352 (outgoing_direction
) ?
9353 _("GetListOfSentMessages request processed by server "
9354 "successfully.\n") :
9355 _("GetListOfReceivedMessages request processed by server "
9358 #else /* not HAVE_LIBCURL */
9365 /* Get list of outgoing (already sent) messages.
9366 * Any criterion argument can be NULL, if you don't care about it.
9367 * @context is session context. Must not be NULL.
9368 * @from_time is minimal time and date of message sending inclusive.
9369 * @to_time is maximal time and date of message sending inclusive
9370 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9371 * @status_filter is bit field of isds_message_status values. Use special
9372 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9373 * all values, you can use bit-wise arithmetic if you want.)
9374 * @offset is index of first message we are interested in. First message is 1.
9375 * Set to 0 (or 1) if you don't care.
9376 * @number is maximal length of list you want to get as input value, outputs
9377 * number of messages matching these criteria. Can be NULL if you don't care
9378 * (applies to output value either).
9379 * @messages is automatically reallocated list of isds_message's. Be ware that
9380 * it returns only brief overview (envelope and some other fields) about each
9381 * message, not the complete message. FIXME: Specify exact fields.
9382 * The list is sorted by delivery time in ascending order.
9383 * Use NULL if you don't care about the meta data (useful if you want to know
9384 * only the @number). If you provide &NULL, list will be allocated on heap,
9385 * if you provide pointer to non-NULL, list will be freed automatically at
9386 * first. Also in case of error the list will be NULLed.
9387 * @return IE_SUCCESS or appropriate error code. */
9388 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9389 const struct timeval
*from_time
, const struct timeval
*to_time
,
9390 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9391 const unsigned long int offset
, unsigned long int *number
,
9392 struct isds_list
**messages
) {
9394 return isds_get_list_of_messages(
9396 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9402 /* Get list of incoming (addressed to you) messages.
9403 * Any criterion argument can be NULL, if you don't care about it.
9404 * @context is session context. Must not be NULL.
9405 * @from_time is minimal time and date of message sending inclusive.
9406 * @to_time is maximal time and date of message sending inclusive
9407 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9408 * @status_filter is bit field of isds_message_status values. Use special
9409 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9410 * all values, you can use bit-wise arithmetic if you want.)
9411 * @offset is index of first message we are interested in. First message is 1.
9412 * Set to 0 (or 1) if you don't care.
9413 * @number is maximal length of list you want to get as input value, outputs
9414 * number of messages matching these criteria. Can be NULL if you don't care
9415 * (applies to output value either).
9416 * @messages is automatically reallocated list of isds_message's. Be ware that
9417 * it returns only brief overview (envelope and some other fields) about each
9418 * message, not the complete message. FIXME: Specify exact fields.
9419 * Use NULL if you don't care about the meta data (useful if you want to know
9420 * only the @number). If you provide &NULL, list will be allocated on heap,
9421 * if you provide pointer to non-NULL, list will be freed automatically at
9422 * first. Also in case of error the list will be NULLed.
9423 * @return IE_SUCCESS or appropriate error code. */
9424 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9425 const struct timeval
*from_time
, const struct timeval
*to_time
,
9426 const long int *dmRecipientOrgUnitNum
,
9427 const unsigned int status_filter
,
9428 const unsigned long int offset
, unsigned long int *number
,
9429 struct isds_list
**messages
) {
9431 return isds_get_list_of_messages(
9433 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9439 /* Get list of sent message state changes.
9440 * Any criterion argument can be NULL, if you don't care about it.
9441 * @context is session context. Must not be NULL.
9442 * @from_time is minimal time and date of status changes inclusive
9443 * @to_time is maximal time and date of status changes inclusive
9444 * @changed_states is automatically reallocated list of
9445 * isds_message_status_change's. If you provide &NULL, list will be allocated
9446 * on heap, if you provide pointer to non-NULL, list will be freed
9447 * automatically at first. Also in case of error the list will be NULLed.
9448 * XXX: The list item ordering is not specified.
9449 * XXX: Server provides only `recent' changes.
9450 * @return IE_SUCCESS or appropriate error code. */
9451 isds_error
isds_get_list_of_sent_message_state_changes(
9452 struct isds_ctx
*context
,
9453 const struct timeval
*from_time
, const struct timeval
*to_time
,
9454 struct isds_list
**changed_states
) {
9456 isds_error err
= IE_SUCCESS
;
9458 xmlNsPtr isds_ns
= NULL
;
9459 xmlNodePtr request
= NULL
, node
;
9460 xmlDocPtr response
= NULL
;
9461 xmlXPathContextPtr xpath_ctx
= NULL
;
9462 xmlXPathObjectPtr result
= NULL
;
9463 xmlChar
*string
= NULL
;
9464 long unsigned int count
= 0;
9467 if (!context
) return IE_INVALID_CONTEXT
;
9468 zfree(context
->long_message
);
9470 /* Free former message list if any */
9471 isds_list_free(changed_states
);
9474 /* Check if connection is established
9475 * TODO: This check should be done downstairs. */
9476 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9478 /* Build GetMessageStateChanges request */
9479 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9481 isds_log_message(context
,
9482 _("Could not build GetMessageStateChanges request"));
9485 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9487 isds_log_message(context
, _("Could not create ISDS name space"));
9488 xmlFreeNode(request
);
9491 xmlSetNs(request
, isds_ns
);
9495 err
= timeval2timestring(from_time
, &string
);
9496 if (err
) goto leave
;
9498 INSERT_STRING(request
, "dmFromTime", string
);
9502 err
= timeval2timestring(to_time
, &string
);
9503 if (err
) goto leave
;
9505 INSERT_STRING(request
, "dmToTime", string
);
9510 err
= send_destroy_request_check_response(context
,
9511 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9512 &response
, NULL
, NULL
);
9513 if (err
) goto leave
;
9517 xpath_ctx
= xmlXPathNewContext(response
);
9522 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9526 result
= xmlXPathEvalExpression(
9527 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9528 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9534 /* Fill output arguments in */
9535 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9536 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9538 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9539 /* Create new status change */
9540 item
= calloc(1, sizeof(*item
));
9546 (void(*)(void**)) &isds_message_status_change_free
;
9548 /* Extract message status change */
9549 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9550 err
= extract_StateChangesRecord(context
,
9551 (struct isds_message_status_change
**) &item
->data
,
9554 isds_list_free(&item
);
9558 /* Append new message status change into the list */
9559 if (!*changed_states
) {
9560 *changed_states
= last_item
= item
;
9562 last_item
->next
= item
;
9570 isds_list_free(changed_states
);
9574 xmlXPathFreeObject(result
);
9575 xmlXPathFreeContext(xpath_ctx
);
9576 xmlFreeDoc(response
);
9577 xmlFreeNode(request
);
9580 isds_log(ILF_ISDS
, ILL_DEBUG
,
9581 _("GetMessageStateChanges request processed by server "
9582 "successfully.\n"));
9583 #else /* not HAVE_LIBCURL */
9591 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9593 * @context is session context
9594 * @service is ISDS WS service handler
9595 * @service_name is name of SERVICE_DM_OPERATIONS
9596 * @message_id is message ID to send as service argument to ISDS
9597 * @response is reallocated server SOAP body response as XML document
9598 * @raw_response is reallocated bit stream with response body. Use
9599 * NULL if you don't care
9600 * @raw_response_length is size of @raw_response in bytes
9601 * @code is reallocated ISDS status code
9602 * @status_message is reallocated ISDS status message
9603 * @return error coded from lower layer, context message will be set up
9605 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9606 const isds_service service
, const xmlChar
*service_name
,
9607 const char *message_id
,
9608 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9609 xmlChar
**code
, xmlChar
**status_message
) {
9611 isds_error err
= IE_SUCCESS
;
9612 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9613 xmlNodePtr request
= NULL
, node
;
9614 xmlNsPtr isds_ns
= NULL
;
9616 if (!context
) return IE_INVALID_CONTEXT
;
9617 if (!service_name
|| !message_id
) return IE_INVAL
;
9618 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9619 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9621 /* Free output argument */
9622 xmlFreeDoc(*response
); *response
= NULL
;
9623 if (raw_response
) zfree(*raw_response
);
9625 zfree(*status_message
);
9628 /* Check if connection is established
9629 * TODO: This check should be done downstairs. */
9630 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9632 service_name_locale
= _isds_utf82locale((char*)service_name
);
9633 message_id_locale
= _isds_utf82locale(message_id
);
9634 if (!service_name_locale
|| !message_id_locale
) {
9640 request
= xmlNewNode(NULL
, service_name
);
9642 isds_printf_message(context
,
9643 _("Could not build %s request for %s message ID"),
9644 service_name_locale
, message_id_locale
);
9648 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9650 isds_log_message(context
, _("Could not create ISDS name space"));
9654 xmlSetNs(request
, isds_ns
);
9657 /* Add requested ID */
9658 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9659 if (err
) goto leave
;
9660 INSERT_STRING(request
, "dmID", message_id
);
9663 isds_log(ILF_ISDS
, ILL_DEBUG
,
9664 _("Sending %s request for %s message ID to ISDS\n"),
9665 service_name_locale
, message_id_locale
);
9668 err
= _isds(context
, service
, request
, response
,
9669 raw_response
, raw_response_length
);
9670 xmlFreeNode(request
); request
= NULL
;
9673 isds_log(ILF_ISDS
, ILL_DEBUG
,
9674 _("Processing ISDS response on %s request failed\n"),
9675 service_name_locale
);
9679 /* Check for response status */
9680 err
= isds_response_status(context
, service
, *response
,
9681 code
, status_message
, NULL
);
9683 isds_log(ILF_ISDS
, ILL_DEBUG
,
9684 _("ISDS response on %s request is missing status\n"),
9685 service_name_locale
);
9689 /* Request processed, but nothing found */
9690 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9691 char *code_locale
= _isds_utf82locale((char*) *code
);
9692 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9693 isds_log(ILF_ISDS
, ILL_DEBUG
,
9694 _("Server refused %s request for %s message ID "
9695 "(code=%s, message=%s)\n"),
9696 service_name_locale
, message_id_locale
,
9697 code_locale
, status_message_locale
);
9698 isds_log_message(context
, status_message_locale
);
9700 free(status_message_locale
);
9706 free(message_id_locale
);
9707 free(service_name_locale
);
9708 xmlFreeNode(request
);
9713 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9714 * signed data and free ISDS response.
9715 * @context is session context
9716 * @message_id is UTF-8 encoded message ID for logging purpose
9717 * @response is parsed XML document. It will be freed and NULLed in the middle
9718 * of function run to save memory. This is not guaranteed in case of error.
9719 * @request_name is name of ISDS request used to construct response root
9720 * element name and for logging purpose.
9721 * @raw is reallocated output buffer with DER encoded CMS data
9722 * @raw_length is size of @raw buffer in bytes
9723 * @returns standard error codes, in case of error, @raw will be freed and
9724 * NULLed, @response sometimes. */
9725 static isds_error
find_extract_signed_data_free_response(
9726 struct isds_ctx
*context
, const xmlChar
*message_id
,
9727 xmlDocPtr
*response
, const xmlChar
*request_name
,
9728 void **raw
, size_t *raw_length
) {
9730 isds_error err
= IE_SUCCESS
;
9731 char *xpath_expression
= NULL
;
9732 xmlXPathContextPtr xpath_ctx
= NULL
;
9733 xmlXPathObjectPtr result
= NULL
;
9734 char *encoded_structure
= NULL
;
9736 if (!context
) return IE_INVALID_CONTEXT
;
9737 if (!raw
) return IE_INVAL
;
9739 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9742 /* Build XPath expression */
9743 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9744 "Response/isds:dmSignature");
9745 if (!xpath_expression
) return IE_NOMEM
;
9748 xpath_ctx
= xmlXPathNewContext(*response
);
9753 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9757 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9762 /* Empty response */
9763 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9764 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9765 isds_printf_message(context
,
9766 _("Server did not return any signed data for message ID `%s' "
9768 message_id_locale
, request_name
);
9769 free(message_id_locale
);
9773 /* More responses */
9774 if (result
->nodesetval
->nodeNr
> 1) {
9775 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9776 isds_printf_message(context
,
9777 _("Server did return more signed data for message ID `%s' "
9779 message_id_locale
, request_name
);
9780 free(message_id_locale
);
9785 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9787 /* Extract PKCS#7 structure */
9788 EXTRACT_STRING(".", encoded_structure
);
9789 if (!encoded_structure
) {
9790 isds_log_message(context
, _("dmSignature element is empty"));
9793 /* Here we have delivery info as standalone CMS in encoded_structure.
9794 * We don't need any other data, free them: */
9795 xmlXPathFreeObject(result
); result
= NULL
;
9796 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9797 xmlFreeDoc(*response
); *response
= NULL
;
9800 /* Decode PKCS#7 to DER format */
9801 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9802 if (*raw_length
== (size_t) -1) {
9803 isds_log_message(context
,
9804 _("Error while Base64-decoding PKCS#7 structure"));
9815 free(encoded_structure
);
9816 xmlXPathFreeObject(result
);
9817 xmlXPathFreeContext(xpath_ctx
);
9818 free(xpath_expression
);
9822 #endif /* HAVE_LIBCURL */
9825 /* Download incoming message envelope identified by ID.
9826 * @context is session context
9827 * @message_id is message identifier (you can get them from
9828 * isds_get_list_of_received_messages())
9829 * @message is automatically reallocated message retrieved from ISDS.
9830 * It will miss documents per se. Use isds_get_received_message(), if you are
9831 * interested in documents (content) too.
9832 * Returned hash and timestamp require documents to be verifiable. */
9833 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9834 const char *message_id
, struct isds_message
**message
) {
9836 isds_error err
= IE_SUCCESS
;
9838 xmlDocPtr response
= NULL
;
9839 xmlChar
*code
= NULL
, *status_message
= NULL
;
9840 xmlXPathContextPtr xpath_ctx
= NULL
;
9841 xmlXPathObjectPtr result
= NULL
;
9844 if (!context
) return IE_INVALID_CONTEXT
;
9845 zfree(context
->long_message
);
9847 /* Free former message if any */
9848 if (!message
) return IE_INVAL
;
9849 isds_message_free(message
);
9852 /* Do request and check for success */
9853 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9854 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9855 &response
, NULL
, NULL
, &code
, &status_message
);
9856 if (err
) goto leave
;
9859 xpath_ctx
= xmlXPathNewContext(response
);
9864 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9868 result
= xmlXPathEvalExpression(
9869 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9870 "isds:dmReturnedMessageEnvelope",
9876 /* Empty response */
9877 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9878 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9879 isds_printf_message(context
,
9880 _("Server did not return any envelope for ID `%s' "
9881 "on MessageEnvelopeDownload request"), message_id_locale
);
9882 free(message_id_locale
);
9887 if (result
->nodesetval
->nodeNr
> 1) {
9888 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9889 isds_printf_message(context
,
9890 _("Server did return more envelopes for ID `%s' "
9891 "on MessageEnvelopeDownload request"), message_id_locale
);
9892 free(message_id_locale
);
9897 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9899 /* Extract the envelope (= message without documents, hence 0) */
9900 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9901 if (err
) goto leave
;
9904 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9905 &(*message
)->raw_length
);
9909 isds_message_free(message
);
9912 xmlXPathFreeObject(result
);
9913 xmlXPathFreeContext(xpath_ctx
);
9916 free(status_message
);
9917 if (!*message
|| !(*message
)->xml
) {
9918 xmlFreeDoc(response
);
9922 isds_log(ILF_ISDS
, ILL_DEBUG
,
9923 _("MessageEnvelopeDownload request processed by server "
9926 #else /* not HAVE_LIBCURL */
9933 /* Load delivery info of any format from buffer.
9934 * @context is session context
9935 * @raw_type advertises format of @buffer content. Only delivery info types
9937 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9938 * retrieve such data from message->raw after calling
9939 * isds_get_signed_delivery_info().
9940 * @length is length of buffer in bytes.
9941 * @message is automatically reallocated message parsed from @buffer.
9942 * @strategy selects how buffer will be attached into raw isds_message member.
9944 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9945 const isds_raw_type raw_type
,
9946 const void *buffer
, const size_t length
,
9947 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9949 isds_error err
= IE_SUCCESS
;
9950 message_ns_type message_ns
;
9951 xmlDocPtr message_doc
= NULL
;
9952 xmlXPathContextPtr xpath_ctx
= NULL
;
9953 xmlXPathObjectPtr result
= NULL
;
9954 void *xml_stream
= NULL
;
9955 size_t xml_stream_length
= 0;
9957 if (!context
) return IE_INVALID_CONTEXT
;
9958 zfree(context
->long_message
);
9959 if (!message
) return IE_INVAL
;
9960 isds_message_free(message
);
9961 if (!buffer
) return IE_INVAL
;
9964 /* Select buffer format and extract XML from CMS*/
9966 case RAWTYPE_DELIVERYINFO
:
9967 message_ns
= MESSAGE_NS_UNSIGNED
;
9968 xml_stream
= (void *) buffer
;
9969 xml_stream_length
= length
;
9972 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
9973 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9974 xml_stream
= (void *) buffer
;
9975 xml_stream_length
= length
;
9978 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
9979 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9980 err
= _isds_extract_cms_data(context
, buffer
, length
,
9981 &xml_stream
, &xml_stream_length
);
9982 if (err
) goto leave
;
9986 isds_log_message(context
, _("Bad raw delivery representation type"));
9991 isds_log(ILF_ISDS
, ILL_DEBUG
,
9992 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9993 xml_stream_length
, xml_stream
);
9995 /* Convert delivery info XML stream into XPath context */
9996 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10001 xpath_ctx
= xmlXPathNewContext(message_doc
);
10006 /* XXX: Name spaces mangled for signed delivery info:
10007 * http://isds.czechpoint.cz/v20/delivery:
10009 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10011 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10012 * <p:dmID>170272</p:dmID>
10015 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10017 * </q:dmEvents>...</q:dmEvents>
10019 * </q:GetDeliveryInfoResponse>
10021 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10025 result
= xmlXPathEvalExpression(
10026 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10032 /* Empty delivery info */
10033 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10034 isds_printf_message(context
,
10035 _("XML document is not sisds:dmDelivery document"));
10039 /* More delivery info's */
10040 if (result
->nodesetval
->nodeNr
> 1) {
10041 isds_printf_message(context
,
10042 _("XML document has more sisds:dmDelivery elements"));
10046 /* One delivery info */
10047 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10049 /* Extract the envelope (= message without documents, hence 0).
10050 * XXX: extract_TReturnedMessage() can obtain attachments size,
10051 * but delivery info carries none. It's coded as option elements,
10052 * so it should work. */
10053 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10054 if (err
) goto leave
;
10056 /* Extract events */
10057 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10058 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10059 if (err
) { err
= IE_ERROR
; goto leave
; }
10060 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10061 if (err
) goto leave
;
10063 /* Append raw CMS structure into message */
10064 (*message
)->raw_type
= raw_type
;
10065 switch (strategy
) {
10066 case BUFFER_DONT_STORE
:
10069 (*message
)->raw
= malloc(length
);
10070 if (!(*message
)->raw
) {
10074 memcpy((*message
)->raw
, buffer
, length
);
10075 (*message
)->raw_length
= length
;
10078 (*message
)->raw
= (void *) buffer
;
10079 (*message
)->raw_length
= length
;
10088 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10089 isds_message_free(message
);
10092 xmlXPathFreeObject(result
);
10093 xmlXPathFreeContext(xpath_ctx
);
10094 if (!*message
|| !(*message
)->xml
) {
10095 xmlFreeDoc(message_doc
);
10097 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10100 isds_log(ILF_ISDS
, ILL_DEBUG
,
10101 _("Delivery info loaded successfully.\n"));
10106 /* Download signed delivery info-sheet of given message identified by ID.
10107 * @context is session context
10108 * @message_id is message identifier (you can get them from
10109 * isds_get_list_of_{sent,received}_messages())
10110 * @message is automatically reallocated message retrieved from ISDS.
10111 * It will miss documents per se. Use isds_get_signed_received_message(),
10112 * if you are interested in documents (content). OTOH, only this function
10113 * can get list events message has gone through. */
10114 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10115 const char *message_id
, struct isds_message
**message
) {
10117 isds_error err
= IE_SUCCESS
;
10119 xmlDocPtr response
= NULL
;
10120 xmlChar
*code
= NULL
, *status_message
= NULL
;
10122 size_t raw_length
= 0;
10125 if (!context
) return IE_INVALID_CONTEXT
;
10126 zfree(context
->long_message
);
10128 /* Free former message if any */
10129 if (!message
) return IE_INVAL
;
10130 isds_message_free(message
);
10133 /* Do request and check for success */
10134 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10135 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10136 &response
, NULL
, NULL
, &code
, &status_message
);
10137 if (err
) goto leave
;
10139 /* Find signed delivery info, extract it into raw and maybe free
10141 err
= find_extract_signed_data_free_response(context
,
10142 (xmlChar
*)message_id
, &response
,
10143 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10144 if (err
) goto leave
;
10146 /* Parse delivery info */
10147 err
= isds_load_delivery_info(context
,
10148 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10149 message
, BUFFER_MOVE
);
10150 if (err
) goto leave
;
10156 isds_message_free(message
);
10161 free(status_message
);
10162 xmlFreeDoc(response
);
10165 isds_log(ILF_ISDS
, ILL_DEBUG
,
10166 _("GetSignedDeliveryInfo request processed by server "
10169 #else /* not HAVE_LIBCURL */
10176 /* Download delivery info-sheet of given message identified by ID.
10177 * @context is session context
10178 * @message_id is message identifier (you can get them from
10179 * isds_get_list_of_{sent,received}_messages())
10180 * @message is automatically reallocated message retrieved from ISDS.
10181 * It will miss documents per se. Use isds_get_received_message(), if you are
10182 * interested in documents (content). OTOH, only this function can get list
10183 * of events message has gone through. */
10184 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10185 const char *message_id
, struct isds_message
**message
) {
10187 isds_error err
= IE_SUCCESS
;
10189 xmlDocPtr response
= NULL
;
10190 xmlChar
*code
= NULL
, *status_message
= NULL
;
10191 xmlNodePtr delivery_node
= NULL
;
10193 size_t raw_length
= 0;
10196 if (!context
) return IE_INVALID_CONTEXT
;
10197 zfree(context
->long_message
);
10199 /* Free former message if any */
10200 if (!message
) return IE_INVAL
;
10201 isds_message_free(message
);
10204 /* Do request and check for success */
10205 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10206 BAD_CAST
"GetDeliveryInfo", message_id
,
10207 &response
, NULL
, NULL
, &code
, &status_message
);
10208 if (err
) goto leave
;
10211 /* Serialize delivery info */
10212 delivery_node
= xmlDocGetRootElement(response
);
10213 if (!delivery_node
) {
10214 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10215 isds_printf_message(context
,
10216 _("Server did not return any delivery info for ID `%s' "
10217 "on GetDeliveryInfo request"), message_id_locale
);
10218 free(message_id_locale
);
10222 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10223 if (err
) goto leave
;
10225 /* Parse delivery info */
10226 /* TODO: Here we parse the response second time. We could single delivery
10227 * parser from isds_load_delivery_info() to make things faster. */
10228 err
= isds_load_delivery_info(context
,
10229 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10230 message
, BUFFER_MOVE
);
10231 if (err
) goto leave
;
10238 isds_message_free(message
);
10243 free(status_message
);
10244 xmlFreeDoc(response
);
10247 isds_log(ILF_ISDS
, ILL_DEBUG
,
10248 _("GetDeliveryInfo request processed by server "
10251 #else /* not HAVE_LIBCURL */
10258 /* Download incoming message identified by ID.
10259 * @context is session context
10260 * @message_id is message identifier (you can get them from
10261 * isds_get_list_of_received_messages())
10262 * @message is automatically reallocated message retrieved from ISDS */
10263 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10264 const char *message_id
, struct isds_message
**message
) {
10266 isds_error err
= IE_SUCCESS
;
10268 xmlDocPtr response
= NULL
;
10269 void *xml_stream
= NULL
;
10270 size_t xml_stream_length
;
10271 xmlChar
*code
= NULL
, *status_message
= NULL
;
10272 xmlXPathContextPtr xpath_ctx
= NULL
;
10273 xmlXPathObjectPtr result
= NULL
;
10274 char *phys_path
= NULL
;
10275 size_t phys_start
, phys_end
;
10278 if (!context
) return IE_INVALID_CONTEXT
;
10279 zfree(context
->long_message
);
10281 /* Free former message if any */
10282 if (NULL
== message
) return IE_INVAL
;
10283 if (message
) isds_message_free(message
);
10286 /* Do request and check for success */
10287 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10288 BAD_CAST
"MessageDownload", message_id
,
10289 &response
, &xml_stream
, &xml_stream_length
,
10290 &code
, &status_message
);
10291 if (err
) goto leave
;
10294 xpath_ctx
= xmlXPathNewContext(response
);
10299 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10303 result
= xmlXPathEvalExpression(
10304 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10310 /* Empty response */
10311 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10312 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10313 isds_printf_message(context
,
10314 _("Server did not return any message for ID `%s' "
10315 "on MessageDownload request"), message_id_locale
);
10316 free(message_id_locale
);
10320 /* More messages */
10321 if (result
->nodesetval
->nodeNr
> 1) {
10322 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10323 isds_printf_message(context
,
10324 _("Server did return more messages for ID `%s' "
10325 "on MessageDownload request"), message_id_locale
);
10326 free(message_id_locale
);
10331 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10333 /* Extract the message */
10334 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10335 if (err
) goto leave
;
10337 /* Locate raw XML blob */
10338 phys_path
= strdup(
10339 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10340 PHYSXML_ELEMENT_SEPARATOR
10341 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10342 PHYSXML_ELEMENT_SEPARATOR
10343 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10349 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10350 phys_path
, &phys_start
, &phys_end
);
10353 isds_log_message(context
,
10354 _("Substring with isds:MessageDownloadResponse element "
10355 "could not be located in raw SOAP message"));
10358 /* Save XML blob */
10359 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10360 &(*message)->raw_length);*/
10361 /* TODO: Store name space declarations from ancestors */
10362 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10363 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10364 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10365 (*message
)->raw
= malloc((*message
)->raw_length
);
10366 if (!(*message
)->raw
) {
10370 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10375 isds_message_free(message
);
10380 xmlXPathFreeObject(result
);
10381 xmlXPathFreeContext(xpath_ctx
);
10384 free(status_message
);
10386 if (!*message
|| !(*message
)->xml
) {
10387 xmlFreeDoc(response
);
10391 isds_log(ILF_ISDS
, ILL_DEBUG
,
10392 _("MessageDownload request processed by server "
10395 #else /* not HAVE_LIBCURL */
10402 /* Load message of any type from buffer.
10403 * @context is session context
10404 * @raw_type defines content type of @buffer. Only message types are allowed.
10405 * @buffer is message raw representation. Format (CMS, plain signed,
10406 * message direction) is defined in @raw_type. You can retrieve such data
10407 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10408 * @length is length of buffer in bytes.
10409 * @message is automatically reallocated message parsed from @buffer.
10410 * @strategy selects how buffer will be attached into raw isds_message member.
10412 isds_error
isds_load_message(struct isds_ctx
*context
,
10413 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10414 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10416 isds_error err
= IE_SUCCESS
;
10417 void *xml_stream
= NULL
;
10418 size_t xml_stream_length
= 0;
10419 message_ns_type message_ns
;
10420 xmlDocPtr message_doc
= NULL
;
10421 xmlXPathContextPtr xpath_ctx
= NULL
;
10422 xmlXPathObjectPtr result
= NULL
;
10424 if (!context
) return IE_INVALID_CONTEXT
;
10425 zfree(context
->long_message
);
10426 if (!message
) return IE_INVAL
;
10427 isds_message_free(message
);
10428 if (!buffer
) return IE_INVAL
;
10431 /* Select buffer format and extract XML from CMS*/
10432 switch (raw_type
) {
10433 case RAWTYPE_INCOMING_MESSAGE
:
10434 message_ns
= MESSAGE_NS_UNSIGNED
;
10435 xml_stream
= (void *) buffer
;
10436 xml_stream_length
= length
;
10439 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10440 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10441 xml_stream
= (void *) buffer
;
10442 xml_stream_length
= length
;
10445 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10446 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10447 err
= _isds_extract_cms_data(context
, buffer
, length
,
10448 &xml_stream
, &xml_stream_length
);
10449 if (err
) goto leave
;
10452 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10453 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10454 xml_stream
= (void *) buffer
;
10455 xml_stream_length
= length
;
10458 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10459 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10460 err
= _isds_extract_cms_data(context
, buffer
, length
,
10461 &xml_stream
, &xml_stream_length
);
10462 if (err
) goto leave
;
10466 isds_log_message(context
, _("Bad raw message representation type"));
10471 isds_log(ILF_ISDS
, ILL_DEBUG
,
10472 _("Loading message:\n%.*s\nEnd of message\n"),
10473 xml_stream_length
, xml_stream
);
10475 /* Convert messages XML stream into XPath context */
10476 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10477 if (!message_doc
) {
10481 xpath_ctx
= xmlXPathNewContext(message_doc
);
10486 /* XXX: Standard name space for unsigned incoming direction:
10487 * http://isds.czechpoint.cz/v20/
10489 * XXX: Name spaces mangled for signed outgoing direction:
10490 * http://isds.czechpoint.cz/v20/SentMessage:
10492 * <q:MessageDownloadResponse
10493 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10494 * <q:dmReturnedMessage>
10495 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10496 * <p:dmID>151916</p:dmID>
10499 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10501 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10502 * </q:dmReturnedMessage>
10503 * </q:MessageDownloadResponse>
10505 * XXX: Name spaces mangled for signed incoming direction:
10506 * http://isds.czechpoint.cz/v20/message:
10508 * <q:MessageDownloadResponse
10509 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10510 * <q:dmReturnedMessage>
10511 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10512 * <p:dmID>151916</p:dmID>
10515 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10517 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10518 * </q:dmReturnedMessage>
10519 * </q:MessageDownloadResponse>
10521 * Stupidity of ISDS developers is unlimited */
10522 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10526 result
= xmlXPathEvalExpression(
10527 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10533 /* Empty message */
10534 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10535 isds_printf_message(context
,
10536 _("XML document does not contain "
10537 "sisds:dmReturnedMessage element"));
10541 /* More messages */
10542 if (result
->nodesetval
->nodeNr
> 1) {
10543 isds_printf_message(context
,
10544 _("XML document has more sisds:dmReturnedMessage elements"));
10549 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10551 /* Extract the message */
10552 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10553 if (err
) goto leave
;
10555 /* Append raw buffer into message */
10556 (*message
)->raw_type
= raw_type
;
10557 switch (strategy
) {
10558 case BUFFER_DONT_STORE
:
10561 (*message
)->raw
= malloc(length
);
10562 if (!(*message
)->raw
) {
10566 memcpy((*message
)->raw
, buffer
, length
);
10567 (*message
)->raw_length
= length
;
10570 (*message
)->raw
= (void *) buffer
;
10571 (*message
)->raw_length
= length
;
10581 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10582 isds_message_free(message
);
10585 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10586 xmlXPathFreeObject(result
);
10587 xmlXPathFreeContext(xpath_ctx
);
10588 if (!*message
|| !(*message
)->xml
) {
10589 xmlFreeDoc(message_doc
);
10593 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10598 /* Determine type of raw message or delivery info according some heuristics.
10599 * It does not validate the raw blob.
10600 * @context is session context
10601 * @raw_type returns content type of @buffer. Valid only if exit code of this
10602 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10603 * reallocated memory.
10604 * @buffer is message raw representation.
10605 * @length is length of buffer in bytes. */
10606 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10607 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10609 void *xml_stream
= NULL
;
10610 size_t xml_stream_length
= 0;
10611 xmlDocPtr document
= NULL
;
10612 xmlNodePtr root
= NULL
;
10614 if (!context
) return IE_INVALID_CONTEXT
;
10615 zfree(context
->long_message
);
10616 if (length
== 0 || !buffer
) return IE_INVAL
;
10617 if (!raw_type
) return IE_INVAL
;
10620 err
= _isds_extract_cms_data(context
, buffer
, length
,
10621 &xml_stream
, &xml_stream_length
);
10623 xml_stream
= (void *) buffer
;
10624 xml_stream_length
= (size_t) length
;
10629 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10631 isds_printf_message(context
,
10632 _("Could not parse data as XML document"));
10637 /* Get root element */
10638 root
= xmlDocGetRootElement(document
);
10640 isds_printf_message(context
,
10641 _("XML document is missing root element"));
10646 if (!root
->ns
|| !root
->ns
->href
) {
10647 isds_printf_message(context
,
10648 _("Root element does not belong to any name space"));
10653 /* Test name space */
10654 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10655 if (xml_stream
== buffer
)
10656 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10658 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10659 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10660 if (xml_stream
== buffer
)
10661 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10663 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10664 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10665 if (xml_stream
== buffer
)
10666 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10668 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10669 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10670 if (xml_stream
!= buffer
) {
10671 isds_printf_message(context
,
10672 _("Document in ISDS name space is encapsulated into CMS" ));
10674 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10675 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10676 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10677 *raw_type
= RAWTYPE_DELIVERYINFO
;
10679 isds_printf_message(context
,
10680 _("Unknown root element in ISDS name space"));
10684 isds_printf_message(context
,
10685 _("Unknown name space"));
10690 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10691 xmlFreeDoc(document
);
10696 /* Download signed incoming/outgoing message identified by ID.
10697 * @context is session context
10698 * @output is true for outgoing message, false for incoming message
10699 * @message_id is message identifier (you can get them from
10700 * isds_get_list_of_{sent,received}_messages())
10701 * @message is automatically reallocated message retrieved from ISDS. The raw
10702 * member will be filled with PKCS#7 structure in DER format. */
10703 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10704 const _Bool outgoing
, const char *message_id
,
10705 struct isds_message
**message
) {
10707 isds_error err
= IE_SUCCESS
;
10709 xmlDocPtr response
= NULL
;
10710 xmlChar
*code
= NULL
, *status_message
= NULL
;
10711 xmlXPathContextPtr xpath_ctx
= NULL
;
10712 xmlXPathObjectPtr result
= NULL
;
10713 char *encoded_structure
= NULL
;
10715 size_t raw_length
= 0;
10718 if (!context
) return IE_INVALID_CONTEXT
;
10719 zfree(context
->long_message
);
10720 if (!message
) return IE_INVAL
;
10721 isds_message_free(message
);
10724 /* Do request and check for success */
10725 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10726 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10727 BAD_CAST
"SignedMessageDownload",
10728 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10729 if (err
) goto leave
;
10731 /* Find signed message, extract it into raw and maybe free
10733 err
= find_extract_signed_data_free_response(context
,
10734 (xmlChar
*)message_id
, &response
,
10735 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10736 BAD_CAST
"SignedMessageDownload",
10737 &raw
, &raw_length
);
10738 if (err
) goto leave
;
10740 /* Parse message */
10741 err
= isds_load_message(context
,
10742 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10743 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10744 raw
, raw_length
, message
, BUFFER_MOVE
);
10745 if (err
) goto leave
;
10751 isds_message_free(message
);
10754 free(encoded_structure
);
10755 xmlXPathFreeObject(result
);
10756 xmlXPathFreeContext(xpath_ctx
);
10760 free(status_message
);
10761 xmlFreeDoc(response
);
10764 isds_log(ILF_ISDS
, ILL_DEBUG
,
10766 _("SignedSentMessageDownload request processed by server "
10767 "successfully.\n") :
10768 _("SignedMessageDownload request processed by server "
10771 #else /* not HAVE_LIBCURL */
10778 /* Download signed incoming message identified by ID.
10779 * @context is session context
10780 * @message_id is message identifier (you can get them from
10781 * isds_get_list_of_received_messages())
10782 * @message is automatically reallocated message retrieved from ISDS. The raw
10783 * member will be filled with PKCS#7 structure in DER format. */
10784 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10785 const char *message_id
, struct isds_message
**message
) {
10786 return isds_get_signed_message(context
, 0, message_id
, message
);
10790 /* Download signed outgoing message identified by ID.
10791 * @context is session context
10792 * @message_id is message identifier (you can get them from
10793 * isds_get_list_of_sent_messages())
10794 * @message is automatically reallocated message retrieved from ISDS. The raw
10795 * member will be filled with PKCS#7 structure in DER format. */
10796 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10797 const char *message_id
, struct isds_message
**message
) {
10798 return isds_get_signed_message(context
, 1, message_id
, message
);
10802 /* Get type and name of user who sent a message identified by ID.
10803 * @context is session context
10804 * @message_id is message identifier
10805 * @sender_type is pointer to automatically allocated type of sender detected
10806 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10807 * library or to the server, NULL will be returned. Pass NULL if you don't
10809 * @raw_sender_type is automatically reallocated UTF-8 string describing
10810 * sender type or NULL if not known to server. Pass NULL if you don't care.
10811 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10812 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10813 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10814 const char *message_id
, isds_sender_type
**sender_type
,
10815 char **raw_sender_type
, char **sender_name
) {
10816 isds_error err
= IE_SUCCESS
;
10818 xmlDocPtr response
= NULL
;
10819 xmlChar
*code
= NULL
, *status_message
= NULL
;
10820 xmlXPathContextPtr xpath_ctx
= NULL
;
10821 xmlXPathObjectPtr result
= NULL
;
10822 char *type_string
= NULL
;
10825 if (!context
) return IE_INVALID_CONTEXT
;
10826 zfree(context
->long_message
);
10827 if (sender_type
) zfree(*sender_type
);
10828 if (raw_sender_type
) zfree(*raw_sender_type
);
10829 if (sender_name
) zfree(*sender_name
);
10830 if (!message_id
) return IE_INVAL
;
10833 /* Do request and check for success */
10834 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10835 BAD_CAST
"GetMessageAuthor",
10836 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10837 if (err
) goto leave
;
10840 xpath_ctx
= xmlXPathNewContext(response
);
10845 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10849 result
= xmlXPathEvalExpression(
10850 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10855 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10856 isds_log_message(context
,
10857 _("Missing GetMessageAuthorResponse element"));
10861 if (result
->nodesetval
->nodeNr
> 1) {
10862 isds_log_message(context
,
10863 _("Multiple GetMessageAuthorResponse element"));
10867 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10868 xmlXPathFreeObject(result
); result
= NULL
;
10870 /* Fill output arguments in */
10871 EXTRACT_STRING("isds:userType", type_string
);
10872 if (NULL
!= type_string
) {
10873 if (NULL
!= sender_type
) {
10874 *sender_type
= calloc(1, sizeof(**sender_type
));
10875 if (NULL
== *sender_type
) {
10880 err
= string2isds_sender_type((xmlChar
*)type_string
,
10883 zfree(*sender_type
);
10884 if (err
== IE_ENUM
) {
10886 char *type_string_locale
= _isds_utf82locale(type_string
);
10887 isds_log(ILF_ISDS
, ILL_WARNING
,
10888 _("Unknown isds:userType value: %s"),
10889 type_string_locale
);
10890 free(type_string_locale
);
10895 if (NULL
!= sender_name
)
10896 EXTRACT_STRING("isds:authorName", *sender_name
);
10900 if (NULL
!= sender_type
) zfree(*sender_type
);
10901 zfree(type_string
);
10902 if (NULL
!= sender_name
) zfree(*sender_name
);
10904 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10906 xmlXPathFreeObject(result
);
10907 xmlXPathFreeContext(xpath_ctx
);
10910 free(status_message
);
10911 xmlFreeDoc(response
);
10914 isds_log(ILF_ISDS
, ILL_DEBUG
,
10915 _("GetMessageAuthor request processed by server "
10916 "successfully.\n"));
10917 #else /* not HAVE_LIBCURL */
10924 /* Retrieve hash of message identified by ID stored in ISDS.
10925 * @context is session context
10926 * @message_id is message identifier
10927 * @hash is automatically reallocated message hash downloaded from ISDS.
10928 * Message must exist in system and must not be deleted. */
10929 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10930 const char *message_id
, struct isds_hash
**hash
) {
10932 isds_error err
= IE_SUCCESS
;
10934 xmlDocPtr response
= NULL
;
10935 xmlChar
*code
= NULL
, *status_message
= NULL
;
10936 xmlXPathContextPtr xpath_ctx
= NULL
;
10937 xmlXPathObjectPtr result
= NULL
;
10940 if (!context
) return IE_INVALID_CONTEXT
;
10941 zfree(context
->long_message
);
10943 isds_hash_free(hash
);
10946 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10947 BAD_CAST
"VerifyMessage", message_id
,
10948 &response
, NULL
, NULL
, &code
, &status_message
);
10949 if (err
) goto leave
;
10953 xpath_ctx
= xmlXPathNewContext(response
);
10958 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10962 result
= xmlXPathEvalExpression(
10963 BAD_CAST
"/isds:VerifyMessageResponse",
10969 /* Empty response */
10970 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10971 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10972 isds_printf_message(context
,
10973 _("Server did not return any response for ID `%s' "
10974 "on VerifyMessage request"), message_id_locale
);
10975 free(message_id_locale
);
10979 /* More responses */
10980 if (result
->nodesetval
->nodeNr
> 1) {
10981 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10982 isds_printf_message(context
,
10983 _("Server did return more responses for ID `%s' "
10984 "on VerifyMessage request"), message_id_locale
);
10985 free(message_id_locale
);
10990 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10992 /* Extract the hash */
10993 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
10997 isds_hash_free(hash
);
11000 xmlXPathFreeObject(result
);
11001 xmlXPathFreeContext(xpath_ctx
);
11004 free(status_message
);
11005 xmlFreeDoc(response
);
11008 isds_log(ILF_ISDS
, ILL_DEBUG
,
11009 _("VerifyMessage request processed by server "
11012 #else /* not HAVE_LIBCURL */
11019 /* Erase message specified by @message_id from long term storage. Other
11020 * message cannot be erased on user request.
11021 * @context is session context
11022 * @message_id is message identifier.
11023 * @incoming is true for incoming message, false for outgoing message.
11025 * IE_SUCCESS if message has ben removed
11026 * IE_INVAL if message does not exist in long term storage or message
11027 * belongs to different box
11028 * TODO: IE_NOEPRM if user has no permission to erase a message */
11029 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11030 const char *message_id
, _Bool incoming
) {
11031 isds_error err
= IE_SUCCESS
;
11033 xmlNodePtr request
= NULL
, node
;
11034 xmlNsPtr isds_ns
= NULL
;
11035 xmlDocPtr response
= NULL
;
11036 xmlChar
*code
= NULL
, *status_message
= NULL
;
11039 if (!context
) return IE_INVALID_CONTEXT
;
11040 zfree(context
->long_message
);
11041 if (NULL
== message_id
) return IE_INVAL
;
11043 /* Check if connection is established
11044 * TODO: This check should be done downstairs. */
11045 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11048 /* Build request */
11049 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11051 isds_log_message(context
,
11052 _("Could build EraseMessage request"));
11055 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11057 isds_log_message(context
, _("Could not create ISDS name space"));
11058 xmlFreeNode(request
);
11061 xmlSetNs(request
, isds_ns
);
11063 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11064 if (err
) goto leave
;
11065 INSERT_STRING(request
, "dmID", message_id
);
11067 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11071 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11072 "message ID %s to ISDS\n"), message_id
);
11073 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11074 xmlFreeNode(request
); request
= NULL
;
11077 isds_log(ILF_ISDS
, ILL_DEBUG
,
11078 _("Processing ISDS response on EraseMessage request "
11083 /* Check for response status */
11084 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11085 &code
, &status_message
, NULL
);
11087 isds_log(ILF_ISDS
, ILL_DEBUG
,
11088 _("ISDS response on EraseMessage request is missing "
11093 /* Check server status code */
11094 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11095 isds_log_message(context
, _("Message to erase belongs to other box"));
11097 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11098 isds_log_message(context
, _("Message to erase is not saved in "
11099 "long term storage or the direction does not match"));
11101 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11102 char *code_locale
= _isds_utf82locale((char*) code
);
11103 char *message_locale
= _isds_utf82locale((char*) status_message
);
11104 isds_log(ILF_ISDS
, ILL_DEBUG
,
11105 _("Server refused EraseMessage request "
11106 "(code=%s, message=%s)\n"),
11107 code_locale
, message_locale
);
11108 isds_log_message(context
, message_locale
);
11110 free(message_locale
);
11117 free(status_message
);
11118 xmlFreeDoc(response
);
11119 xmlFreeNode(request
);
11122 isds_log(ILF_ISDS
, ILL_DEBUG
,
11123 _("EraseMessage request processed by server "
11126 #else /* not HAVE_LIBCURL */
11133 /* Mark message as read. This is a transactional commit function to acknowledge
11134 * to ISDS the message has been downloaded and processed by client properly.
11135 * @context is session context
11136 * @message_id is message identifier. */
11137 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11138 const char *message_id
) {
11140 isds_error err
= IE_SUCCESS
;
11142 xmlDocPtr response
= NULL
;
11143 xmlChar
*code
= NULL
, *status_message
= NULL
;
11146 if (!context
) return IE_INVALID_CONTEXT
;
11147 zfree(context
->long_message
);
11150 /* Do request and check for success */
11151 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11152 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11153 &response
, NULL
, NULL
, &code
, &status_message
);
11156 free(status_message
);
11157 xmlFreeDoc(response
);
11160 isds_log(ILF_ISDS
, ILL_DEBUG
,
11161 _("MarkMessageAsDownloaded request processed by server "
11164 #else /* not HAVE_LIBCURL */
11171 /* Mark message as received by recipient. This is applicable only to
11172 * commercial message. Use envelope->dmType message member to distinguish
11173 * commercial message from government message. Government message is
11174 * received automatically (by law), commercial message on recipient request.
11175 * @context is session context
11176 * @message_id is message identifier. */
11177 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11178 const char *message_id
) {
11180 isds_error err
= IE_SUCCESS
;
11182 xmlDocPtr response
= NULL
;
11183 xmlChar
*code
= NULL
, *status_message
= NULL
;
11186 if (!context
) return IE_INVALID_CONTEXT
;
11187 zfree(context
->long_message
);
11190 /* Do request and check for success */
11191 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11192 BAD_CAST
"ConfirmDelivery", message_id
,
11193 &response
, NULL
, NULL
, &code
, &status_message
);
11196 free(status_message
);
11197 xmlFreeDoc(response
);
11200 isds_log(ILF_ISDS
, ILL_DEBUG
,
11201 _("ConfirmDelivery request processed by server "
11204 #else /* not HAVE_LIBCURL */
11211 /* Send document for authorized conversion into Czech POINT system.
11212 * This is public anonymous service, no log-in necessary. Special context is
11213 * used to reuse keep-a-live HTTPS connection.
11214 * @context is Czech POINT session context. DO NOT use context connected to
11215 * ISDS server. Use new context or context used by this function previously.
11216 * @document is document to convert. Only data, data_length, dmFileDescr and
11217 * is_xml members are significant. Be ware that not all document formats can be
11218 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11219 * @id is reallocated identifier assigned by Czech POINT system to
11220 * your document on submit. Use is to tell it to Czech POINT officer.
11221 * @date is reallocated document submit date (submitted documents
11222 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11224 isds_error
czp_convert_document(struct isds_ctx
*context
,
11225 const struct isds_document
*document
,
11226 char **id
, struct tm
**date
) {
11227 isds_error err
= IE_SUCCESS
;
11229 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11230 xmlNodePtr request
= NULL
, node
;
11231 xmlDocPtr response
= NULL
;
11233 xmlXPathContextPtr xpath_ctx
= NULL
;
11234 xmlXPathObjectPtr result
= NULL
;
11235 long int status
= -1;
11236 long int *status_ptr
= &status
;
11237 char *string
= NULL
;
11241 if (!context
) return IE_INVALID_CONTEXT
;
11242 zfree(context
->long_message
);
11243 if (!document
|| !id
|| !date
) return IE_INVAL
;
11245 if (document
->is_xml
) {
11246 isds_log_message(context
,
11247 _("XML documents cannot be submitted to conversion"));
11251 /* Free output arguments */
11256 /* Store configuration */
11257 context
->type
= CTX_TYPE_CZP
;
11258 free(context
->url
);
11259 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11260 if (!(context
->url
))
11263 /* Prepare CURL handle if not yet connected */
11264 if (!context
->curl
) {
11265 context
->curl
= curl_easy_init();
11266 if (!(context
->curl
))
11270 /* Build conversion request */
11271 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11273 isds_log_message(context
,
11274 _("Could not build Czech POINT conversion request"));
11277 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11279 isds_log_message(context
,
11280 _("Could not create Czech POINT deposit name space"));
11281 xmlFreeNode(request
);
11284 xmlSetNs(request
, deposit_ns
);
11286 /* Insert children. They are in empty namespace! */
11287 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11289 isds_log_message(context
, _("Could not create empty name space"));
11293 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11294 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11295 document
->dmFileDescr
);
11297 /* Document encoded in Base64 */
11298 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11299 document
->data
, document
->data_length
);
11300 if (err
) goto leave
;
11302 isds_log(ILF_ISDS
, ILL_DEBUG
,
11303 _("Submitting document for conversion into Czech POINT deposit"));
11305 /* Send conversion request */
11306 err
= _czp_czpdeposit(context
, request
, &response
);
11307 xmlFreeNode(request
); request
= NULL
;
11310 czp_do_close_connection(context
);
11315 /* Extract response */
11316 xpath_ctx
= xmlXPathNewContext(response
);
11321 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11325 result
= xmlXPathEvalExpression(
11326 BAD_CAST
"/deposit:saveDocumentResponse/return",
11332 /* Empty response */
11333 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11334 isds_printf_message(context
,
11335 _("Missing `return' element in Czech POINT deposit response"));
11339 /* More responses */
11340 if (result
->nodesetval
->nodeNr
> 1) {
11341 isds_printf_message(context
,
11342 _("Multiple `return' element in Czech POINT deposit response"));
11347 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11350 EXTRACT_LONGINT("status", status_ptr
, 1);
11352 EXTRACT_STRING("statusMsg", string
);
11353 char *string_locale
= _isds_utf82locale(string
);
11354 isds_printf_message(context
,
11355 _("Czech POINT deposit refused document for conversion "
11356 "(code=%ld, message=%s)"),
11357 status
, string_locale
);
11358 free(string_locale
);
11363 /* Get document ID */
11364 EXTRACT_STRING("documentID", *id
);
11366 /* Get submit date */
11367 EXTRACT_STRING("dateInserted", string
);
11369 *date
= calloc(1, sizeof(**date
));
11374 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11376 if (err
== IE_NOTSUP
) {
11378 char *string_locale
= _isds_utf82locale(string
);
11379 isds_printf_message(context
,
11380 _("Invalid dateInserted value: %s"), string_locale
);
11381 free(string_locale
);
11389 xmlXPathFreeObject(result
);
11390 xmlXPathFreeContext(xpath_ctx
);
11392 xmlFreeDoc(response
);
11393 xmlFreeNode(request
);
11396 char *id_locale
= _isds_utf82locale((char *) *id
);
11397 isds_log(ILF_ISDS
, ILL_DEBUG
,
11398 _("Document %s has been submitted for conversion "
11399 "to server successfully\n"), id_locale
);
11402 #else /* not HAVE_LIBCURL */
11409 /* Close possibly opened connection to Czech POINT document deposit.
11410 * @context is Czech POINT session context. */
11411 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11412 if (!context
) return IE_INVALID_CONTEXT
;
11413 zfree(context
->long_message
);
11415 return czp_do_close_connection(context
);
11422 /* Send request for new box creation in testing ISDS instance.
11423 * It's not possible to request for a production box currently, as it
11424 * communicates via e-mail.
11425 * XXX: This function does not work either. Server complains about invalid
11427 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11429 * @context is special session context for box creation request. DO NOT use
11430 * standard context as it could reveal your password. Use fresh new context or
11431 * context previously used by this function.
11432 * @box is box description to create including single primary user (in case of
11433 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11434 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11435 * box, or contact address of PFO box owner). The email member is mandatory as
11436 * it will be used to deliver credentials.
11437 * @former_names is former name of box owner. Pass NULL if you don't care.
11438 * @approval is optional external approval of box manipulation
11439 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11440 * NULL, if you don't care.*/
11441 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11442 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11443 const char *former_names
, const struct isds_approval
*approval
,
11444 char **refnumber
) {
11445 isds_error err
= IE_SUCCESS
;
11447 xmlNodePtr request
= NULL
;
11448 xmlDocPtr response
= NULL
;
11449 xmlXPathContextPtr xpath_ctx
= NULL
;
11450 xmlXPathObjectPtr result
= NULL
;
11454 if (!context
) return IE_INVALID_CONTEXT
;
11455 zfree(context
->long_message
);
11456 if (!box
) return IE_INVAL
;
11459 if (!box
->email
|| box
->email
[0] == '\0') {
11460 isds_log_message(context
, _("E-mail field is mandatory"));
11464 /* Scratch box ID */
11467 /* Store configuration */
11468 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11469 free(context
->url
);
11470 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11471 if (!(context
->url
))
11474 /* Prepare CURL handle if not yet connected */
11475 if (!context
->curl
) {
11476 context
->curl
= curl_easy_init();
11477 if (!(context
->curl
))
11481 /* Build CreateDataBox request */
11482 err
= build_CreateDBInput_request(context
,
11483 &request
, BAD_CAST
"CreateDataBox",
11484 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11485 if (err
) goto leave
;
11487 /* Send it to server and process response */
11488 err
= send_destroy_request_check_response(context
,
11489 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11490 &response
, (xmlChar
**) refnumber
, NULL
);
11491 if (err
) goto leave
;
11493 /* Extract box ID */
11494 xpath_ctx
= xmlXPathNewContext(response
);
11499 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11503 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11506 xmlXPathFreeObject(result
);
11507 xmlXPathFreeContext(xpath_ctx
);
11508 xmlFreeDoc(response
);
11509 xmlFreeNode(request
);
11512 isds_log(ILF_ISDS
, ILL_DEBUG
,
11513 _("CreateDataBox request processed by server successfully.\n"));
11515 #else /* not HAVE_LIBCURL */
11523 /* Submit CMS signed message to ISDS to verify its originality. This is
11524 * stronger form of isds_verify_message_hash() because ISDS does more checks
11525 * than simple one (potentialy old weak) hash comparison.
11526 * @context is session context
11527 * @message is memory with raw CMS signed message bit stream
11528 * @length is @message size in bytes
11530 * IE_SUCCESS if message originates in ISDS
11531 * IE_NOTEQUAL if message is unknown to ISDS
11532 * other code for other errors */
11533 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11534 const void *message
, size_t length
) {
11535 isds_error err
= IE_SUCCESS
;
11537 xmlNsPtr isds_ns
= NULL
;
11538 xmlNodePtr request
= NULL
;
11539 xmlDocPtr response
= NULL
;
11540 xmlXPathContextPtr xpath_ctx
= NULL
;
11541 xmlXPathObjectPtr result
= NULL
;
11542 _Bool
*authentic
= NULL
;
11545 if (!context
) return IE_INVALID_CONTEXT
;
11546 zfree(context
->long_message
);
11547 if (!message
|| length
== 0) return IE_INVAL
;
11550 /* Check if connection is established
11551 * TODO: This check should be done downstairs. */
11552 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11555 /* Build AuthenticateMessage request */
11556 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11558 isds_log_message(context
,
11559 _("Could not build AuthenticateMessage request"));
11562 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11564 isds_log_message(context
, _("Could not create ISDS name space"));
11565 xmlFreeNode(request
);
11568 xmlSetNs(request
, isds_ns
);
11570 /* Insert Base64 encoded message */
11571 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11573 if (err
) goto leave
;
11575 /* Send request to server and process response */
11576 err
= send_destroy_request_check_response(context
,
11577 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11578 &response
, NULL
, NULL
);
11579 if (err
) goto leave
;
11582 /* ISDS has decided */
11583 xpath_ctx
= xmlXPathNewContext(response
);
11588 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11593 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11596 isds_log_message(context
,
11597 _("Server did not return any response on "
11598 "AuthenticateMessage request"));
11603 isds_log(ILF_ISDS
, ILL_DEBUG
,
11604 _("ISDS authenticated the message successfully\n"));
11606 isds_log_message(context
, _("ISDS does not know the message"));
11613 xmlXPathFreeObject(result
);
11614 xmlXPathFreeContext(xpath_ctx
);
11616 xmlFreeDoc(response
);
11617 xmlFreeNode(request
);
11618 #else /* not HAVE_LIBCURL */
11626 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11627 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11629 * @context is session context
11630 * @input_data is memory with raw CMS signed message or delivery info bit
11631 * stream to re-sign
11632 * @input_length is @input_data size in bytes
11633 * @output_data is pointer to auto-allocated memory where to store re-signed
11634 * input data blob. Caller must free it.
11635 * @output_data is pointer where to store @output_data size in bytes
11636 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11637 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11639 * IE_SUCCESS if CMS blob has been re-signed successfully
11640 * other code for other errors */
11641 isds_error
isds_resign_message(struct isds_ctx
*context
,
11642 const void *input_data
, size_t input_length
,
11643 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11644 isds_error err
= IE_SUCCESS
;
11646 xmlNsPtr isds_ns
= NULL
;
11647 xmlNodePtr request
= NULL
;
11648 xmlDocPtr response
= NULL
;
11649 xmlXPathContextPtr xpath_ctx
= NULL
;
11650 xmlXPathObjectPtr result
= NULL
;
11651 char *string
= NULL
;
11652 const xmlChar
*codes
[] = {
11659 const char *meanings
[] = {
11661 "Message is not original",
11662 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11663 "Time stamp could not been generated in time"
11665 const isds_error errors
[] = {
11671 struct code_map_isds_error map
= {
11673 .meanings
= meanings
,
11678 if (NULL
!= output_data
) *output_data
= NULL
;
11679 if (NULL
!= output_length
) *output_length
= 0;
11680 if (NULL
!= valid_to
) *valid_to
= NULL
;
11682 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11683 zfree(context
->long_message
);
11684 if (NULL
== input_data
|| 0 == input_length
) {
11685 isds_log_message(context
, _("Empty CMS blob on input"));
11688 if (NULL
== output_data
|| NULL
== output_length
) {
11689 isds_log_message(context
,
11690 _("NULL pointer provided for output CMS blob"));
11695 /* Check if connection is established
11696 * TODO: This check should be done downstairs. */
11697 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11700 /* Build Re-signISDSDocument request */
11701 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11703 isds_log_message(context
,
11704 _("Could not build Re-signISDSDocument request"));
11707 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11709 isds_log_message(context
, _("Could not create ISDS name space"));
11710 xmlFreeNode(request
);
11713 xmlSetNs(request
, isds_ns
);
11715 /* Insert Base64 encoded CMS blob */
11716 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11717 input_data
, input_length
);
11718 if (err
) goto leave
;
11720 /* Send request to server and process response */
11721 err
= send_destroy_request_check_response(context
,
11722 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11723 &response
, NULL
, &map
);
11724 if (err
) goto leave
;
11727 /* Extract re-signed data */
11728 xpath_ctx
= xmlXPathNewContext(response
);
11733 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11737 result
= xmlXPathEvalExpression(
11738 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11743 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11744 isds_log_message(context
,
11745 _("Missing Re-signISDSDocumentResponse element"));
11749 if (result
->nodesetval
->nodeNr
> 1) {
11750 isds_log_message(context
,
11751 _("Multiple Re-signISDSDocumentResponse element"));
11755 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11756 xmlXPathFreeObject(result
); result
= NULL
;
11758 EXTRACT_STRING("isds:dmResultDoc", string
);
11759 /* Decode non-empty data */
11760 if (NULL
!= string
&& string
[0] != '\0') {
11761 *output_length
= _isds_b64decode(string
, output_data
);
11762 if (*output_length
== (size_t) -1) {
11763 isds_log_message(context
,
11764 _("Error while Base64-decoding re-signed data"));
11769 isds_log_message(context
, _("Server did not send re-signed data"));
11775 if (NULL
!= valid_to
) {
11776 /* Get time stamp expiration date */
11777 EXTRACT_STRING("isds:dmValidTo", string
);
11778 if (NULL
!= string
) {
11779 *valid_to
= calloc(1, sizeof(**valid_to
));
11784 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11786 if (err
== IE_NOTSUP
) {
11788 char *string_locale
= _isds_utf82locale(string
);
11789 isds_printf_message(context
,
11790 _("Invalid dmValidTo value: %s"), string_locale
);
11791 free(string_locale
);
11801 xmlXPathFreeObject(result
);
11802 xmlXPathFreeContext(xpath_ctx
);
11804 xmlFreeDoc(response
);
11805 xmlFreeNode(request
);
11806 #else /* not HAVE_LIBCURL */
11813 #undef INSERT_ELEMENT
11814 #undef CHECK_FOR_STRING_LENGTH
11815 #undef INSERT_STRING_ATTRIBUTE
11816 #undef INSERT_ULONGINTNOPTR
11817 #undef INSERT_ULONGINT
11818 #undef INSERT_LONGINT
11819 #undef INSERT_BOOLEAN
11820 #undef INSERT_SCALAR_BOOLEAN
11821 #undef INSERT_STRING
11822 #undef INSERT_STRING_WITH_NS
11823 #undef EXTRACT_STRING_ATTRIBUTE
11824 #undef EXTRACT_ULONGINT
11825 #undef EXTRACT_LONGINT
11826 #undef EXTRACT_BOOLEAN
11827 #undef EXTRACT_STRING
11830 /* Compute hash of message from raw representation and store it into envelope.
11831 * Original hash structure will be destroyed in envelope.
11832 * @context is session context
11833 * @message is message carrying raw XML message blob
11834 * @algorithm is desired hash algorithm to use */
11835 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11836 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11837 isds_error err
= IE_SUCCESS
;
11839 void *xml_stream
= NULL
;
11840 size_t xml_stream_length
;
11841 size_t phys_start
, phys_end
;
11842 char *phys_path
= NULL
;
11843 struct isds_hash
*new_hash
= NULL
;
11846 if (!context
) return IE_INVALID_CONTEXT
;
11847 zfree(context
->long_message
);
11848 if (!message
) return IE_INVAL
;
11850 if (!message
->raw
) {
11851 isds_log_message(context
,
11852 _("Message does not carry raw representation"));
11856 switch (message
->raw_type
) {
11857 case RAWTYPE_INCOMING_MESSAGE
:
11859 xml_stream
= message
->raw
;
11860 xml_stream_length
= message
->raw_length
;
11863 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11864 nsuri
= SISDS_INCOMING_NS
;
11865 xml_stream
= message
->raw
;
11866 xml_stream_length
= message
->raw_length
;
11869 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11870 nsuri
= SISDS_INCOMING_NS
;
11871 err
= _isds_extract_cms_data(context
,
11872 message
->raw
, message
->raw_length
,
11873 &xml_stream
, &xml_stream_length
);
11874 if (err
) goto leave
;
11877 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11878 nsuri
= SISDS_OUTGOING_NS
;
11879 xml_stream
= message
->raw
;
11880 xml_stream_length
= message
->raw_length
;
11883 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11884 nsuri
= SISDS_OUTGOING_NS
;
11885 err
= _isds_extract_cms_data(context
,
11886 message
->raw
, message
->raw_length
,
11887 &xml_stream
, &xml_stream_length
);
11888 if (err
) goto leave
;
11892 isds_log_message(context
, _("Bad raw representation type"));
11898 /* XXX: Hash is computed from original string representing isds:dmDm
11899 * subtree. That means no encoding, white space, xmlns attributes changes.
11900 * In other words, input for hash can be invalid XML stream. */
11901 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11902 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11903 PHYSXML_ELEMENT_SEPARATOR
,
11904 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11905 PHYSXML_ELEMENT_SEPARATOR
11906 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11910 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11911 phys_path
, &phys_start
, &phys_end
);
11914 isds_log_message(context
,
11915 _("Substring with isds:dmDM element could not be located "
11916 "in raw message"));
11922 new_hash
= calloc(1, sizeof(*new_hash
));
11927 new_hash
->algorithm
= algorithm
;
11928 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11931 isds_log_message(context
, _("Could not compute message hash"));
11935 /* Save computed hash */
11936 if (!message
->envelope
) {
11937 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11938 if (!message
->envelope
) {
11943 isds_hash_free(&message
->envelope
->hash
);
11944 message
->envelope
->hash
= new_hash
;
11948 isds_hash_free(&new_hash
);
11952 if (xml_stream
!= message
->raw
) free(xml_stream
);
11957 /* Compare two hashes.
11958 * @h1 is first hash
11959 * @h2 is another hash
11961 * IE_SUCCESS if hashes equal
11962 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11963 * IE_ENUM if not comparable, but both structures defined
11964 * IE_INVAL if some of the structures are undefined (NULL)
11965 * IE_ERROR if internal error occurs */
11966 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
11967 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
11968 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
11969 if (h1
->length
!= h2
->length
) return IE_ERROR
;
11970 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
11971 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
11973 for (int i
= 0; i
< h1
->length
; i
++) {
11974 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
11975 return IE_NOTEQUAL
;
11981 /* Check message has gone through ISDS by comparing message hash stored in
11982 * ISDS and locally computed hash. You must provide message with valid raw
11983 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11984 * This is convenient wrapper for isds_download_message_hash(),
11985 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11986 * @context is session context
11987 * @message is message with valid raw and envelope member; envelope->hash
11988 * member will be changed during function run. Use envelope on heap only.
11990 * IE_SUCCESS if message originates in ISDS
11991 * IE_NOTEQUAL if message is unknown to ISDS
11992 * other code for other errors */
11993 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
11994 struct isds_message
*message
) {
11995 isds_error err
= IE_SUCCESS
;
11996 struct isds_hash
*downloaded_hash
= NULL
;
11998 if (!context
) return IE_INVALID_CONTEXT
;
11999 zfree(context
->long_message
);
12000 if (!message
) return IE_INVAL
;
12002 if (!message
->envelope
) {
12003 isds_log_message(context
,
12004 _("Given message structure is missing envelope"));
12007 if (!message
->raw
) {
12008 isds_log_message(context
,
12009 _("Given message structure is missing raw representation"));
12013 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12015 if (err
) goto leave
;
12017 err
= isds_compute_message_hash(context
, message
,
12018 downloaded_hash
->algorithm
);
12019 if (err
) goto leave
;
12021 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12024 isds_hash_free(&downloaded_hash
);
12029 /* Search for document by document ID in list of documents. IDs are compared
12031 * @documents is list of isds_documents
12032 * @id is document identifier
12033 * @return first matching document or NULL. */
12034 const struct isds_document
*isds_find_document_by_id(
12035 const struct isds_list
*documents
, const char *id
) {
12036 const struct isds_list
*item
;
12037 const struct isds_document
*document
;
12039 for (item
= documents
; item
; item
= item
->next
) {
12040 document
= (struct isds_document
*) item
->data
;
12041 if (!document
) continue;
12043 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12051 /* Normalize @mime_type to be proper MIME type.
12052 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12053 * guess regular MIME type (e.g. "application/pdf").
12054 * @mime_type is UTF-8 encoded MIME type to fix
12055 * @return original @mime_type if no better interpretation exists, or
12056 * constant static UTF-8 encoded string with proper MIME type. */
12057 const char *isds_normalize_mime_type(const char *mime_type
) {
12058 if (!mime_type
) return NULL
;
12060 for (int offset
= 0;
12061 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12063 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12064 extension_map_mime
[offset
]))
12065 return (const char *) extension_map_mime
[offset
+ 1];
12072 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12073 struct isds_message **message);
12074 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12075 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12076 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12077 struct isds_address **address);
12079 int isds_message_free(struct isds_message **message);
12080 int isds_address_free(struct isds_address **address);
12084 /* Makes known all relevant namespaces to given XPath context
12085 * @xpath_ctx is XPath context
12086 * @message_ns selects proper message name space. Unsigned and signed
12087 * messages and delivery info's differ in prefix and URI. */
12088 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12089 const message_ns_type message_ns
) {
12090 const xmlChar
*message_namespace
= NULL
;
12092 if (!xpath_ctx
) return IE_ERROR
;
12094 switch(message_ns
) {
12096 message_namespace
= BAD_CAST ISDS1_NS
; break;
12097 case MESSAGE_NS_UNSIGNED
:
12098 message_namespace
= BAD_CAST ISDS_NS
; break;
12099 case MESSAGE_NS_SIGNED_INCOMING
:
12100 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12101 case MESSAGE_NS_SIGNED_OUTGOING
:
12102 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12103 case MESSAGE_NS_SIGNED_DELIVERY
:
12104 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12109 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12111 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12113 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12115 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12117 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12119 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))