7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include <inttypes.h> /* For PRIdMAX formatting macro */
14 #include "validator.h"
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities
;
22 isds_log_level log_level
;
23 isds_log_callback log_callback
;
24 void *log_callback_data
;
25 const char *version_gpgme
= N_("n/a");
26 const char *version_gcrypt
= N_("n/a");
27 const char *version_openssl
= N_("n/a");
28 const char *version_expat
= N_("n/a");
31 /* Base URL of production ISDS instance */
32 const char isds_locator
[] = "https://ws1.mojedatovaschranka.cz/";
33 const char isds_cert_locator
[] = "https://ws1c.mojedatovaschranka.cz/";
34 const char isds_otp_locator
[] = "https://www.mojedatovaschranka.cz/";
36 /* Base URL of production ISDS instance */
37 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
38 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
39 const char isds_otp_testing_locator
[] = "https://www.czebox.cz/";
41 /* Extension to MIME type map */
42 static const xmlChar
*extension_map_mime
[] = {
43 BAD_CAST
"cer", BAD_CAST
"application/x-x509-ca-cert",
44 BAD_CAST
"crt", BAD_CAST
"application/x-x509-ca-cert",
45 BAD_CAST
"der", BAD_CAST
"application/x-x509-ca-cert",
46 BAD_CAST
"doc", BAD_CAST
"application/msword",
47 BAD_CAST
"docx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
48 "wordprocessingml.document",
49 BAD_CAST
"dbf", BAD_CAST
"application/octet-stream",
50 BAD_CAST
"prj", BAD_CAST
"application/octet-stream",
51 BAD_CAST
"qix", BAD_CAST
"application/octet-stream",
52 BAD_CAST
"sbn", BAD_CAST
"application/octet-stream",
53 BAD_CAST
"sbx", BAD_CAST
"application/octet-stream",
54 BAD_CAST
"shp", BAD_CAST
"application/octet-stream",
55 BAD_CAST
"shx", BAD_CAST
"application/octet-stream",
56 BAD_CAST
"dgn", BAD_CAST
"application/octet-stream",
57 BAD_CAST
"dwg", BAD_CAST
"image/vnd.dwg",
58 BAD_CAST
"edi", BAD_CAST
"application/edifact",
59 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.form+xml",
60 BAD_CAST
"gfs", BAD_CAST
"application/xml",
61 BAD_CAST
"gml", BAD_CAST
"application/xml",
62 BAD_CAST
"gif", BAD_CAST
"image/gif",
63 BAD_CAST
"htm", BAD_CAST
"text/html",
64 BAD_CAST
"html", BAD_CAST
"text/html",
65 BAD_CAST
"isdoc", BAD_CAST
"text/isdoc",
66 BAD_CAST
"isdocx", BAD_CAST
"text/isdocx",
67 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
68 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
69 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
70 BAD_CAST
"mpeg", BAD_CAST
"video/mpeg",
71 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
72 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg",
73 BAD_CAST
"mpg", BAD_CAST
"video/mpeg",
74 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
75 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
76 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
77 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
78 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
79 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
80 BAD_CAST
"p7b", BAD_CAST
"application/pkcs7-certificates",
81 BAD_CAST
"p7c", BAD_CAST
"application/pkcs7-mime",
82 BAD_CAST
"p7m", BAD_CAST
"application/pkcs7-mime",
83 BAD_CAST
"p7f", BAD_CAST
"application/pkcs7-signature",
84 BAD_CAST
"p7s", BAD_CAST
"application/pkcs7-signature",
85 BAD_CAST
"pk7", BAD_CAST
"application/pkcs7-mime",
86 BAD_CAST
"png", BAD_CAST
"image/png",
87 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
88 BAD_CAST
"pptx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
89 "presentationml.presentation",
90 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
91 BAD_CAST
"tif", BAD_CAST
"image/tiff",
92 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
93 BAD_CAST
"tsr", BAD_CAST
"application/timestamp-reply",
94 BAD_CAST
"tst", BAD_CAST
"application/timestamp-reply",
95 BAD_CAST
"txt", BAD_CAST
"text/plain",
96 BAD_CAST
"wav", BAD_CAST
"audio/wav",
97 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
98 BAD_CAST
"xlsx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
99 "spreadsheetml.sheet",
100 BAD_CAST
"xml", BAD_CAST
"application/xml",
101 BAD_CAST
"xsd", BAD_CAST
"application/xml",
102 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.form-xml-zip"
105 /* Structure type to hold conversion table from status code to isds_error and
107 struct code_map_isds_error
{
108 const xmlChar
**codes
; /* NULL terminated array of status codes */
109 const char **meanings
; /* Mapping to non-localized long messages */
110 const isds_error
*errors
; /* Mapping to isds_error code */
113 /* Deallocate structure isds_pki_credentials and NULL it.
114 * Pass-phrase is discarded.
115 * @pki credentials to to free */
116 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
117 if(!pki
|| !*pki
) return;
119 free((*pki
)->engine
);
120 free((*pki
)->certificate
);
123 if ((*pki
)->passphrase
) {
124 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
125 free((*pki
)->passphrase
);
132 /* Free isds_list with all member data.
133 * @list list to free, on return will be NULL */
134 void isds_list_free(struct isds_list
**list
) {
135 struct isds_list
*item
, *next_item
;
137 if (!list
|| !*list
) return;
139 for(item
= *list
; item
; item
= next_item
) {
140 if (item
->destructor
) (item
->destructor
)(&(item
->data
));
141 next_item
= item
->next
;
149 /* Deallocate structure isds_hash and NULL it.
150 * @hash hash to to free */
151 void isds_hash_free(struct isds_hash
**hash
) {
152 if(!hash
|| !*hash
) return;
153 free((*hash
)->value
);
158 /* Deallocate structure isds_PersonName recursively and NULL it */
159 void isds_PersonName_free(struct isds_PersonName
**person_name
) {
160 if (!person_name
|| !*person_name
) return;
162 free((*person_name
)->pnFirstName
);
163 free((*person_name
)->pnMiddleName
);
164 free((*person_name
)->pnLastName
);
165 free((*person_name
)->pnLastNameAtBirth
);
172 /* Deallocate structure isds_BirthInfo recursively and NULL it */
173 void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
174 if (!birth_info
|| !*birth_info
) return;
176 free((*birth_info
)->biDate
);
177 free((*birth_info
)->biCity
);
178 free((*birth_info
)->biCounty
);
179 free((*birth_info
)->biState
);
186 /* Deallocate structure isds_Address recursively and NULL it */
187 void isds_Address_free(struct isds_Address
**address
) {
188 if (!address
|| !*address
) return;
190 free((*address
)->adCity
);
191 free((*address
)->adStreet
);
192 free((*address
)->adNumberInStreet
);
193 free((*address
)->adNumberInMunicipality
);
194 free((*address
)->adZipCode
);
195 free((*address
)->adState
);
202 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
203 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
204 if (!db_owner_info
|| !*db_owner_info
) return;
206 free((*db_owner_info
)->dbID
);
207 free((*db_owner_info
)->dbType
);
208 free((*db_owner_info
)->ic
);
209 isds_PersonName_free(&((*db_owner_info
)->personName
));
210 free((*db_owner_info
)->firmName
);
211 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
212 isds_Address_free(&((*db_owner_info
)->address
));
213 free((*db_owner_info
)->nationality
);
214 free((*db_owner_info
)->email
);
215 free((*db_owner_info
)->telNumber
);
216 free((*db_owner_info
)->identifier
);
217 free((*db_owner_info
)->registryCode
);
218 free((*db_owner_info
)->dbState
);
219 free((*db_owner_info
)->dbEffectiveOVM
);
220 free((*db_owner_info
)->dbOpenAddressing
);
222 free(*db_owner_info
);
223 *db_owner_info
= NULL
;
226 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
227 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
228 if (!db_user_info
|| !*db_user_info
) return;
230 free((*db_user_info
)->userID
);
231 free((*db_user_info
)->userType
);
232 free((*db_user_info
)->userPrivils
);
233 isds_PersonName_free(&((*db_user_info
)->personName
));
234 isds_Address_free(&((*db_user_info
)->address
));
235 free((*db_user_info
)->biDate
);
236 free((*db_user_info
)->ic
);
237 free((*db_user_info
)->firmName
);
238 free((*db_user_info
)->caStreet
);
239 free((*db_user_info
)->caCity
);
240 free((*db_user_info
)->caZipCode
);
241 free((*db_user_info
)->caState
);
243 zfree(*db_user_info
);
247 /* Deallocate struct isds_event recursively and NULL it */
248 void isds_event_free(struct isds_event
**event
) {
249 if (!event
|| !*event
) return;
251 free((*event
)->time
);
252 free((*event
)->type
);
253 free((*event
)->description
);
258 /* Deallocate struct isds_envelope recursively and NULL it */
259 void isds_envelope_free(struct isds_envelope
**envelope
) {
260 if (!envelope
|| !*envelope
) return;
262 free((*envelope
)->dmID
);
263 free((*envelope
)->dbIDSender
);
264 free((*envelope
)->dmSender
);
265 free((*envelope
)->dmSenderAddress
);
266 free((*envelope
)->dmSenderType
);
267 free((*envelope
)->dmRecipient
);
268 free((*envelope
)->dmRecipientAddress
);
269 free((*envelope
)->dmAmbiguousRecipient
);
270 free((*envelope
)->dmType
);
272 free((*envelope
)->dmOrdinal
);
273 free((*envelope
)->dmMessageStatus
);
274 free((*envelope
)->dmDeliveryTime
);
275 free((*envelope
)->dmAcceptanceTime
);
276 isds_hash_free(&(*envelope
)->hash
);
277 free((*envelope
)->timestamp
);
278 isds_list_free(&(*envelope
)->events
);
280 free((*envelope
)->dmSenderOrgUnit
);
281 free((*envelope
)->dmSenderOrgUnitNum
);
282 free((*envelope
)->dbIDRecipient
);
283 free((*envelope
)->dmRecipientOrgUnit
);
284 free((*envelope
)->dmRecipientOrgUnitNum
);
285 free((*envelope
)->dmToHands
);
286 free((*envelope
)->dmAnnotation
);
287 free((*envelope
)->dmRecipientRefNumber
);
288 free((*envelope
)->dmSenderRefNumber
);
289 free((*envelope
)->dmRecipientIdent
);
290 free((*envelope
)->dmSenderIdent
);
292 free((*envelope
)->dmLegalTitleLaw
);
293 free((*envelope
)->dmLegalTitleYear
);
294 free((*envelope
)->dmLegalTitleSect
);
295 free((*envelope
)->dmLegalTitlePar
);
296 free((*envelope
)->dmLegalTitlePoint
);
298 free((*envelope
)->dmPersonalDelivery
);
299 free((*envelope
)->dmAllowSubstDelivery
);
301 free((*envelope
)->dmOVM
);
302 free((*envelope
)->dmPublishOwnID
);
309 /* Deallocate struct isds_message recursively and NULL it */
310 void isds_message_free(struct isds_message
**message
) {
311 if (!message
|| !*message
) return;
313 free((*message
)->raw
);
314 isds_envelope_free(&((*message
)->envelope
));
315 isds_list_free(&((*message
)->documents
));
316 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
323 /* Deallocate struct isds_document recursively and NULL it */
324 void isds_document_free(struct isds_document
**document
) {
325 if (!document
|| !*document
) return;
327 if (!(*document
)->is_xml
) {
328 free((*document
)->data
);
330 free((*document
)->dmMimeType
);
331 free((*document
)->dmFileGuid
);
332 free((*document
)->dmUpFileGuid
);
333 free((*document
)->dmFileDescr
);
334 free((*document
)->dmFormat
);
341 /* Deallocate struct isds_message_copy recursively and NULL it */
342 void isds_message_copy_free(struct isds_message_copy
**copy
) {
343 if (!copy
|| !*copy
) return;
345 free((*copy
)->dbIDRecipient
);
346 free((*copy
)->dmRecipientOrgUnit
);
347 free((*copy
)->dmRecipientOrgUnitNum
);
348 free((*copy
)->dmToHands
);
350 free((*copy
)->dmStatus
);
357 /* Deallocate struct isds_message_status_change recursively and NULL it */
358 void isds_message_status_change_free(
359 struct isds_message_status_change
**message_status_change
) {
360 if (!message_status_change
|| !*message_status_change
) return;
362 free((*message_status_change
)->dmID
);
363 free((*message_status_change
)->time
);
364 free((*message_status_change
)->dmMessageStatus
);
366 zfree(*message_status_change
);
370 /* Deallocate struct isds_approval recursively and NULL it */
371 void isds_approval_free(struct isds_approval
**approval
) {
372 if (!approval
|| !*approval
) return;
374 free((*approval
)->refference
);
380 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
381 * The email string is deallocated too. */
382 void isds_credentials_delivery_free(
383 struct isds_credentials_delivery
**credentials_delivery
) {
384 if (!credentials_delivery
|| !*credentials_delivery
) return;
386 free((*credentials_delivery
)->email
);
387 free((*credentials_delivery
)->token
);
388 free((*credentials_delivery
)->new_user_name
);
390 zfree(*credentials_delivery
);
394 /* Deallocate struct isds_commercial_permission recursively and NULL it */
395 void isds_commercial_permission_free(
396 struct isds_commercial_permission
**permission
) {
397 if (NULL
== permission
|| NULL
== *permission
) return;
399 free((*permission
)->recipient
);
400 free((*permission
)->payer
);
401 free((*permission
)->expiration
);
402 free((*permission
)->count
);
403 free((*permission
)->reply_identifier
);
409 /* Deallocate struct isds_credit_event recursively and NULL it */
410 void isds_credit_event_free(struct isds_credit_event
**event
) {
411 if (NULL
== event
|| NULL
== *event
) return;
413 free((*event
)->time
);
414 switch ((*event
)->type
) {
415 case ISDS_CREDIT_CHARGED
:
416 free((*event
)->details
.charged
.transaction
);
418 case ISDS_CREDIT_DISCHARGED
:
419 free((*event
)->details
.discharged
.transaction
);
421 case ISDS_CREDIT_MESSAGE_SENT
:
422 free((*event
)->details
.message_sent
.recipient
);
423 free((*event
)->details
.message_sent
.message_id
);
425 case ISDS_CREDIT_STORAGE_SET
:
426 free((*event
)->details
.storage_set
.new_valid_from
);
427 free((*event
)->details
.storage_set
.new_valid_to
);
428 free((*event
)->details
.storage_set
.old_capacity
);
429 free((*event
)->details
.storage_set
.old_valid_from
);
430 free((*event
)->details
.storage_set
.old_valid_to
);
431 free((*event
)->details
.storage_set
.initiator
);
433 case ISDS_CREDIT_EXPIRED
:
441 /* Deallocate struct isds_fulltext_result recursively and NULL it */
442 void isds_fulltext_result_free(
443 struct isds_fulltext_result
**result
) {
444 if (NULL
== result
|| NULL
== *result
) return;
446 free((*result
)->dbID
);
447 free((*result
)->name
);
448 isds_list_free(&((*result
)->name_match_start
));
449 isds_list_free(&((*result
)->name_match_end
));
450 free((*result
)->address
);
451 isds_list_free(&((*result
)->address_match_start
));
452 isds_list_free(&((*result
)->address_match_end
));
454 free((*result
)->biDate
);
460 /* *DUP_OR_ERROR macros needs error label */
461 #define STRDUP_OR_ERROR(new, template) { \
465 (new) = strdup(template); \
466 if (!new) goto error; \
470 #define FLATDUP_OR_ERROR(new, template) { \
474 (new) = malloc(sizeof(*(new))); \
475 if (!new) goto error; \
476 memcpy((new), (template), sizeof(*(template))); \
480 /* Copy structure isds_pki_credentials recursively. */
481 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
482 const struct isds_pki_credentials
*template) {
483 struct isds_pki_credentials
*new = NULL
;
485 if(!template) return NULL
;
487 new = calloc(1, sizeof(*new));
488 if (!new) return NULL
;
490 STRDUP_OR_ERROR(new->engine
, template->engine
);
491 new->certificate_format
= template->certificate_format
;
492 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
493 new->key_format
= template->key_format
;
494 STRDUP_OR_ERROR(new->key
, template->key
);
495 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
500 isds_pki_credentials_free(&new);
505 /* Copy structure isds_PersonName recursively */
506 struct isds_PersonName
*isds_PersonName_duplicate(
507 const struct isds_PersonName
*src
) {
508 struct isds_PersonName
*new = NULL
;
510 if (!src
) return NULL
;
512 new = calloc(1, sizeof(*new));
513 if (!new) return NULL
;
515 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
516 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
517 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
518 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
523 isds_PersonName_free(&new);
528 /* Copy structure isds_BirthInfo recursively */
529 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
530 const struct isds_BirthInfo
*template) {
531 struct isds_BirthInfo
*new = NULL
;
533 if (!template) return NULL
;
535 new = calloc(1, sizeof(*new));
536 if (!new) return NULL
;
538 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
539 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
540 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
541 STRDUP_OR_ERROR(new->biState
, template->biState
);
546 isds_BirthInfo_free(&new);
551 /* Copy structure isds_Address recursively */
552 struct isds_Address
*isds_Address_duplicate(
553 const struct isds_Address
*src
) {
554 struct isds_Address
*new = NULL
;
556 if (!src
) return NULL
;
558 new = calloc(1, sizeof(*new));
559 if (!new) return NULL
;
561 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
562 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
563 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
564 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
565 src
->adNumberInMunicipality
);
566 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
567 STRDUP_OR_ERROR(new->adState
, src
->adState
);
572 isds_Address_free(&new);
577 /* Copy structure isds_DbOwnerInfo recursively */
578 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
579 const struct isds_DbOwnerInfo
*src
) {
580 struct isds_DbOwnerInfo
*new = NULL
;
581 if (!src
) return NULL
;
583 new = calloc(1, sizeof(*new));
584 if (!new) return NULL
;
586 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
587 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
588 STRDUP_OR_ERROR(new->ic
, src
->ic
);
590 if (src
->personName
) {
591 if (!(new->personName
=
592 isds_PersonName_duplicate(src
->personName
)))
596 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
598 if (src
->birthInfo
) {
599 if (!(new->birthInfo
=
600 isds_BirthInfo_duplicate(src
->birthInfo
)))
605 if (!(new->address
= isds_Address_duplicate(src
->address
)))
609 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
610 STRDUP_OR_ERROR(new->email
, src
->email
);
611 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
612 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
613 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
614 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
615 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
616 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
621 isds_DbOwnerInfo_free(&new);
626 /* Copy structure isds_DbUserInfo recursively */
627 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
628 const struct isds_DbUserInfo
*src
) {
629 struct isds_DbUserInfo
*new = NULL
;
630 if (!src
) return NULL
;
632 new = calloc(1, sizeof(*new));
633 if (!new) return NULL
;
635 STRDUP_OR_ERROR(new->userID
, src
->userID
);
636 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
637 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
639 if (src
->personName
) {
640 if (!(new->personName
=
641 isds_PersonName_duplicate(src
->personName
)))
646 if (!(new->address
= isds_Address_duplicate(src
->address
)))
650 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
651 STRDUP_OR_ERROR(new->ic
, src
->ic
);
652 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
653 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
654 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
655 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
656 STRDUP_OR_ERROR(new->caState
, src
->caState
);
661 isds_DbUserInfo_free(&new);
665 #undef FLATDUP_OR_ERROR
666 #undef STRDUP_OR_ERROR
669 /* Logs libxml2 errors. Should be registered to libxml2 library.
670 * @ctx is unused currently
671 * @msg is printf-like formated message from libxml2 (UTF-8?)
672 * @... are variadic arguments for @msg */
673 static void log_xml(void *ctx
, const char *msg
, ...) {
677 /* Silent warning for unused function argument.
678 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
684 isds_vasprintf(&text
, msg
, ap
);
688 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
693 /* Initialize ISDS library.
694 * Global function, must be called before other functions.
695 * If it fails you can not use ISDS library and must call isds_cleanup() to
696 * free partially initialized global variables. */
697 isds_error
isds_init(void) {
698 /* NULL global variables */
699 log_facilities
= ILF_ALL
;
700 log_level
= ILL_WARNING
;
702 log_callback_data
= NULL
;
705 /* Initialize gettext */
706 bindtextdomain(PACKAGE
, LOCALEDIR
);
710 /* Initialize CURL */
711 if (curl_global_init(CURL_GLOBAL_ALL
)) {
712 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
715 #endif /* HAVE_LIBCURL */
717 /* Initialise cryptographic back-ends. */
718 if (IE_SUCCESS
!= _isds_init_crypto()) {
719 isds_log(ILF_ISDS
, ILL_CRIT
,
720 _("Initialization of cryptographic back-end failed\n"));
724 /* This can _exit() current program. Find not so assertive check. */
726 xmlSetGenericErrorFunc(NULL
, log_xml
);
729 if (_isds_init_expat(&version_expat
)) {
730 isds_log(ILF_ISDS
, ILL_CRIT
,
731 _("expat library initialization failed\n"));
735 /* Allocate global variables */
742 /* Deinitialize ISDS library.
743 * Global function, must be called as last library function. */
744 isds_error
isds_cleanup(void) {
750 curl_global_cleanup();
757 /* Return version string of this library. Version of dependencies can be
758 * embedded. Do no try to parse it. You must free it. */
759 char *isds_version(void) {
762 isds_asprintf(&buffer
,
764 # ifndef USE_OPENSSL_BACKEND
765 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
767 _("%s (%s, %s, %s, libxml2 %s)"),
770 # ifndef USE_OPENSSL_BACKEND
771 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
773 _("%s (%s, %s, libxml2 %s)"),
780 #ifndef USE_OPENSSL_BACKEND
781 version_gpgme
, version_gcrypt
,
785 version_expat
, xmlParserVersion
);
790 /* Return text description of ISDS error */
791 const char *isds_strerror(const isds_error error
) {
794 return(_("Success")); break;
796 return(_("Unspecified error")); break;
798 return(_("Not supported")); break;
800 return(_("Invalid value")); break;
801 case IE_INVALID_CONTEXT
:
802 return(_("Invalid context")); break;
803 case IE_NOT_LOGGED_IN
:
804 return(_("Not logged in")); break;
805 case IE_CONNECTION_CLOSED
:
806 return(_("Connection closed")); break;
808 return(_("Timed out")); break;
810 return(_("Not exist")); break;
812 return(_("Out of memory")); break;
814 return(_("Network problem")); break;
816 return(_("HTTP problem")); break;
818 return(_("SOAP problem")); break;
820 return(_("XML problem")); break;
822 return(_("ISDS server problem")); break;
824 return(_("Invalid enum value")); break;
826 return(_("Invalid date value")); break;
828 return(_("Too big")); break;
830 return(_("Too small")); break;
832 return(_("Value not unique")); break;
834 return(_("Values not equal")); break;
835 case IE_PARTIAL_SUCCESS
:
836 return(_("Some suboperations failed")); break;
838 return(_("Operation aborted")); break;
840 return(_("Security problem")); break;
842 return(_("Unknown error"));
847 /* Create ISDS context.
848 * Each context can be used for different sessions to (possibly) different
849 * ISDS server with different credentials. */
850 struct isds_ctx
*isds_ctx_create(void) {
851 struct isds_ctx
*context
;
852 context
= malloc(sizeof(*context
));
853 if (context
) memset(context
, 0, sizeof(*context
));
858 /* Close possibly opened connection to Czech POINT document deposit without
859 * resetting long_message buffer.
860 * XXX: Do not use czp_close_connection() if you do not want to destroy log
862 * @context is Czech POINT session context. */
863 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
864 if (!context
) return IE_INVALID_CONTEXT
;
865 _isds_close_connection(context
);
870 /* Discard credentials.
871 * @context is ISDS context
872 * @discard_saved_username is true for removing saved username, false for
874 * Only that. It does not cause log out, connection close or similar. */
875 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
876 _Bool discard_saved_username
) {
877 if(!context
) return IE_INVALID_CONTEXT
;
879 if (context
->username
) {
880 memset(context
->username
, 0, strlen(context
->username
));
881 zfree(context
->username
);
883 if (context
->password
) {
884 memset(context
->password
, 0, strlen(context
->password
));
885 zfree(context
->password
);
887 isds_pki_credentials_free(&context
->pki_credentials
);
888 if (discard_saved_username
&& context
->saved_username
) {
889 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
890 zfree(context
->saved_username
);
895 #endif /* HAVE_LIBCURL */
898 /* Destroy ISDS context and free memory.
899 * @context will be NULLed on success. */
900 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
901 if (!context
|| !*context
) {
902 return IE_INVALID_CONTEXT
;
906 /* Discard credentials and close connection */
907 switch ((*context
)->type
) {
908 case CTX_TYPE_NONE
: break;
909 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
911 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
912 czp_do_close_connection(*context
); break;
916 _isds_discard_credentials(*context
, 1);
918 /* Free other structures */
919 free((*context
)->url
);
920 free((*context
)->tls_verify_server
);
921 free((*context
)->tls_ca_file
);
922 free((*context
)->tls_ca_dir
);
923 free((*context
)->tls_crl_file
);
924 #endif /* HAVE_LIBCURL */
925 free((*context
)->long_message
);
933 /* Return long message text produced by library function, e.g. detailed error
934 * message. Returned pointer is only valid until new library function is
935 * called for the same context. Could be NULL, especially if NULL context is
936 * supplied. Return string is locale encoded. */
937 char *isds_long_message(const struct isds_ctx
*context
) {
938 if (!context
) return NULL
;
939 return context
->long_message
;
943 /* Stores message into context' long_message buffer.
944 * Application can pick the message up using isds_long_message().
945 * NULL @message truncates the buffer but does not deallocate it.
946 * @message is coded in locale encoding */
947 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
948 const char *message
) {
952 if (!context
) return IE_INVALID_CONTEXT
;
954 /* FIXME: Check for integer overflow */
955 length
= 1 + ((message
) ? strlen(message
) : 0);
956 buffer
= realloc(context
->long_message
, length
);
957 if (!buffer
) return IE_NOMEM
;
960 strcpy(buffer
, message
);
964 context
->long_message
= buffer
;
969 /* Appends message into context' long_message buffer.
970 * Application can pick the message up using isds_long_message().
971 * NULL message has void effect. */
972 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
973 const char *message
) {
975 size_t old_length
, length
;
977 if (!context
) return IE_INVALID_CONTEXT
;
978 if (!message
) return IE_SUCCESS
;
979 if (!context
->long_message
)
980 return isds_log_message(context
, message
);
982 old_length
= strlen(context
->long_message
);
983 /* FIXME: Check for integer overflow */
984 length
= 1 + old_length
+ strlen(message
);
985 buffer
= realloc(context
->long_message
, length
);
986 if (!buffer
) return IE_NOMEM
;
988 strcpy(buffer
+ old_length
, message
);
990 context
->long_message
= buffer
;
995 /* Stores formatted message into context' long_message buffer.
996 * Application can pick the message up using isds_long_message(). */
997 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
998 const char *format
, ...) {
1002 if (!context
) return IE_INVALID_CONTEXT
;
1003 va_start(ap
, format
);
1004 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
1007 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
1012 * @facilities is bit mask of isds_log_facility values,
1013 * @level is verbosity level. */
1014 void isds_set_logging(const unsigned int facilities
,
1015 const isds_log_level level
) {
1016 log_facilities
= facilities
;
1021 /* Register callback function libisds calls when new global log message is
1022 * produced by library. Library logs to stderr by default.
1023 * @callback is function provided by application libisds will call. See type
1024 * definition for @callback argument explanation. Pass NULL to revert logging to
1025 * default behaviour.
1026 * @data is application specific data @callback gets as last argument */
1027 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1028 log_callback
= callback
;
1029 log_callback_data
= data
;
1033 /* Log @message in class @facility with log @level into global log. @message
1034 * is printf(3) formatting string, variadic arguments may be necessary.
1035 * For debugging purposes. */
1036 _hidden isds_error
isds_log(const isds_log_facility facility
,
1037 const isds_log_level level
, const char *message
, ...) {
1039 char *buffer
= NULL
;
1042 if (level
> log_level
) return IE_SUCCESS
;
1043 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1044 if (!message
) return IE_INVAL
;
1047 /* Pass message to application supplied callback function */
1048 va_start(ap
, message
);
1049 length
= isds_vasprintf(&buffer
, message
, ap
);
1056 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1060 /* Default: Log it to stderr */
1061 va_start(ap
, message
);
1062 vfprintf(stderr
, message
, ap
);
1064 /* Line buffered printf is default.
1072 /* Set timeout in milliseconds for each network job like connecting to server
1073 * or sending message. Use 0 to disable timeout limits. */
1074 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1075 const unsigned int timeout
) {
1076 if (!context
) return IE_INVALID_CONTEXT
;
1077 zfree(context
->long_message
);
1080 context
->timeout
= timeout
;
1082 if (context
->curl
) {
1085 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1087 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1088 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1091 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1092 context
->timeout
/ 1000);
1093 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1094 if (curl_err
) return IE_ERROR
;
1098 #else /* not HAVE_LIBCURL */
1104 /* Register callback function libisds calls periodically during HTTP data
1106 * @context is session context
1107 * @callback is function provided by application libisds will call. See type
1108 * definition for @callback argument explanation.
1109 * @data is application specific data @callback gets as last argument */
1110 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1111 isds_progress_callback callback
, void *data
) {
1112 if (!context
) return IE_INVALID_CONTEXT
;
1113 zfree(context
->long_message
);
1116 context
->progress_callback
= callback
;
1117 context
->progress_callback_data
= data
;
1120 #else /* not HAVE_LIBCURL */
1126 /* Change context settings.
1127 * @context is context which setting will be applied to
1128 * @option is name of option. It determines the type of last argument. See
1129 * isds_option definition for more info.
1130 * @... is value of new setting. Type is determined by @option
1132 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1134 isds_error err
= IE_SUCCESS
;
1137 char *pointer
, *string
;
1140 if (!context
) return IE_INVALID_CONTEXT
;
1141 zfree(context
->long_message
);
1143 va_start(ap
, option
);
1145 #define REPLACE_VA_BOOLEAN(destination) { \
1146 if (!(destination)) { \
1147 (destination) = malloc(sizeof(*(destination))); \
1148 if (!(destination)) { \
1149 err = IE_NOMEM; goto leave; \
1152 *(destination) = (_Bool) !!va_arg(ap, int); \
1155 #define REPLACE_VA_STRING(destination) { \
1156 string = va_arg(ap, char *); \
1158 pointer = realloc((destination), 1 + strlen(string)); \
1159 if (!pointer) { err = IE_NOMEM; goto leave; } \
1160 strcpy(pointer, string); \
1161 (destination) = pointer; \
1163 free(destination); \
1164 (destination) = NULL; \
1169 case IOPT_TLS_VERIFY_SERVER
:
1171 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1173 err
= IE_NOTSUP
; goto leave
;
1176 case IOPT_TLS_CA_FILE
:
1178 REPLACE_VA_STRING(context
->tls_ca_file
);
1180 err
= IE_NOTSUP
; goto leave
;
1183 case IOPT_TLS_CA_DIRECTORY
:
1185 REPLACE_VA_STRING(context
->tls_ca_dir
);
1187 err
= IE_NOTSUP
; goto leave
;
1190 case IOPT_TLS_CRL_FILE
:
1192 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1193 REPLACE_VA_STRING(context
->tls_crl_file
);
1195 isds_log_message(context
,
1196 _("Curl library does not support CRL definition"));
1198 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1200 err
= IE_NOTSUP
; goto leave
;
1201 #endif /* not HAVE_LIBCURL */
1203 case IOPT_NORMALIZE_MIME_TYPE
:
1204 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1208 err
= IE_ENUM
; goto leave
;
1211 #undef REPLACE_VA_STRING
1212 #undef REPLACE_VA_BOOLEAN
1221 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1222 * Destination for NULL argument will not be touched.
1223 * Destination pointers must be freed before calling this function.
1224 * If @username is @context->saved_username, the saved_username will not be
1225 * replaced. The saved_username is clobbered only if context has set otp
1227 * Return IE_SUCCESS on success. */
1228 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1229 const char *username
, const char *password
,
1230 const struct isds_pki_credentials
*pki_credentials
) {
1231 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1233 /* FIXME: mlock password
1234 * (I have a library) */
1237 context
->username
= strdup(username
);
1238 if (context
->otp
&& context
->saved_username
!= username
)
1239 context
->saved_username
= strdup(username
);
1242 if (NULL
== context
->otp_credentials
)
1243 context
->password
= strdup(password
);
1245 context
->password
= _isds_astrcat(password
,
1246 context
->otp_credentials
->otp_code
);
1248 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1250 if ((NULL
!= username
&& NULL
== context
->username
) ||
1251 (NULL
!= password
&& NULL
== context
->password
) ||
1252 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1253 (context
->otp
&& NULL
!= context
->username
&&
1254 NULL
== context
->saved_username
)) {
1263 /* Connect and log into ISDS server.
1264 * All required arguments will be copied, you do not have to keep them after
1266 * ISDS supports six different authentication methods. Exact method is
1267 * selected on @username, @password, @pki_credentials, and @otp arguments:
1268 * - If @pki_credentials == NULL, @username and @password must be supplied
1270 * - If @otp == NULL, simple authentication by username and password will
1272 * - If @otp != NULL, authentication by username and password and OTP
1274 * - If @pki_credentials != NULL, then
1275 * - If @username == NULL, only certificate will be used
1276 * - If @username != NULL, then
1277 * - If @password == NULL, then certificate will be used and
1278 * @username shifts meaning to box ID. This is used for hosted
1280 * - Otherwise all three arguments will be used.
1281 * Please note, that different cases require different certificate type
1282 * (system qualified one or commercial non qualified one). This library
1283 * does not check such political issues. Please see ISDS Specification
1285 * @url is base address of ISDS web service. Pass extern isds_locator
1286 * variable to use production ISDS instance without client certificate
1287 * authentication (or extern isds_cert_locator with client certificate
1288 * authentication or extern isds_otp_locators with OTP authentication).
1289 * Passing NULL has the same effect, autoselection between isds_locator,
1290 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1291 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1292 * isds_otp_testing_locator) variable to select testing instance.
1293 * @username is user name of ISDS user or box ID
1294 * @password is user's secret password
1295 * @pki_credentials defines public key cryptographic material to use in client
1297 * @otp selects one-time password authentication method to use, defines OTP
1298 * code (if known) and returns fine grade resolution of OTP procedure.
1300 * IE_SUCCESS if authentication succeeds
1301 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1302 * requested, fine grade reason will be set into @otp->resolution. Error
1303 * message from server can be obtained by isds_long_message() call.
1304 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1305 * server has sent OTP code through side channel. Application is expected to
1306 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1307 * this call to complete second phase of TOTP authentication;
1308 * or other appropriate error. */
1309 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1310 const char *username
, const char *password
,
1311 const struct isds_pki_credentials
*pki_credentials
,
1312 struct isds_otp
*otp
) {
1314 isds_error err
= IE_NOT_LOGGED_IN
;
1315 isds_error soap_err
;
1316 xmlNsPtr isds_ns
= NULL
;
1317 xmlNodePtr request
= NULL
;
1318 #endif /* HAVE_LIBCURL */
1320 if (!context
) return IE_INVALID_CONTEXT
;
1321 zfree(context
->long_message
);
1324 /* Close connection if already logged in */
1325 if (context
->curl
) {
1326 _isds_close_connection(context
);
1329 /* Store configuration */
1330 context
->type
= CTX_TYPE_ISDS
;
1331 zfree(context
->url
);
1333 /* Mangle base URI according to requested authentication method */
1334 if (NULL
== pki_credentials
) {
1335 isds_log(ILF_SEC
, ILL_INFO
,
1336 _("Selected authentication method: no certificate, "
1337 "username and password\n"));
1338 if (!username
|| !password
) {
1339 isds_log_message(context
,
1340 _("Both username and password must be supplied"));
1343 context
->otp_credentials
= otp
;
1344 context
->otp
= (NULL
!= context
->otp_credentials
);
1346 if (!context
->otp
) {
1347 /* Default locator is official system (without certificate or
1349 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1351 const char *authenticator_uri
= NULL
;
1352 if (!url
) url
= isds_otp_locator
;
1353 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1354 switch (context
->otp_credentials
->method
) {
1356 isds_log(ILF_SEC
, ILL_INFO
,
1357 _("Selected authentication method: "
1358 "HMAC-based one-time password\n"));
1360 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1363 isds_log(ILF_SEC
, ILL_INFO
,
1364 _("Selected authentication method: "
1365 "Time-based one-time password\n"));
1366 if (context
->otp_credentials
->otp_code
== NULL
) {
1367 isds_log(ILF_SEC
, ILL_INFO
,
1368 _("OTP code has not been provided by "
1369 "application, requesting server for "
1372 "%1$sas/processLogin?type=totp&sendSms=true&"
1375 isds_log(ILF_SEC
, ILL_INFO
,
1376 _("OTP code has been provided by "
1377 "application, not requesting server "
1380 "%1$sas/processLogin?type=totp&"
1385 isds_log_message(context
,
1386 _("Unknown one-time password authentication "
1387 "method requested by application"));
1390 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1394 /* Default locator is official system (with client certificate) */
1396 context
->otp_credentials
= NULL
;
1397 if (!url
) url
= isds_cert_locator
;
1400 isds_log(ILF_SEC
, ILL_INFO
,
1401 _("Selected authentication method: system certificate, "
1402 "no username and no password\n"));
1404 context
->url
= _isds_astrcat(url
, "cert/");
1407 isds_log(ILF_SEC
, ILL_INFO
,
1408 _("Selected authentication method: system certificate, "
1409 "box ID and no password\n"));
1410 context
->url
= _isds_astrcat(url
, "hspis/");
1412 isds_log(ILF_SEC
, ILL_INFO
,
1413 _("Selected authentication method: commercial "
1414 "certificate, username and password\n"));
1415 context
->url
= _isds_astrcat(url
, "certds/");
1419 if (!(context
->url
))
1422 /* Prepare CURL handle */
1423 context
->curl
= curl_easy_init();
1424 if (!(context
->curl
))
1427 /* Build log-in request */
1428 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1430 isds_log_message(context
, _("Could not build ISDS log-in request"));
1433 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1435 isds_log_message(context
, _("Could not create ISDS name space"));
1436 xmlFreeNode(request
);
1439 xmlSetNs(request
, isds_ns
);
1441 /* Store credentials */
1442 _isds_discard_credentials(context
, 1);
1443 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1444 _isds_discard_credentials(context
, 1);
1445 xmlFreeNode(request
);
1449 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1452 /* XXX: ISDS documentation does not specify response body for
1453 * DummyOperation request. However real server sends back
1454 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1455 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1456 * SOAP body content, e.g. the dmStatus element. */
1458 /* Send log-in request */
1459 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1462 /* Revert context URL from OTP authentication service URL to OTP web
1463 * service base URL for subsequent calls. Potenial isds_login() retry
1464 * will re-set context URL again. */
1465 zfree(context
->url
);
1466 context
->url
= _isds_astrcat(url
, "apps/");
1467 if (context
->url
== NULL
) {
1468 soap_err
= IE_NOMEM
;
1470 /* Detach pointer to OTP credentials from context */
1471 context
->otp_credentials
= NULL
;
1474 /* Remove credentials */
1475 _isds_discard_credentials(context
, 0);
1477 /* Destroy log-in request */
1478 xmlFreeNode(request
);
1481 _isds_close_connection(context
);
1485 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1486 * authentication succeeded if soap_err == IE_SUCCESS */
1490 isds_log(ILF_ISDS
, ILL_DEBUG
,
1491 _("User %s has been logged into server %s successfully\n"),
1494 #else /* not HAVE_LIBCURL */
1500 /* Log out from ISDS server discards credentials and connection configuration. */
1501 isds_error
isds_logout(struct isds_ctx
*context
) {
1502 if (!context
) return IE_INVALID_CONTEXT
;
1503 zfree(context
->long_message
);
1506 if (context
->curl
) {
1508 isds_error err
= _isds_invalidate_otp_cookie(context
);
1509 if (err
) return err
;
1512 /* Close connection */
1513 _isds_close_connection(context
);
1515 /* Discard credentials for sure. They should not survive isds_login(),
1516 * even successful .*/
1517 _isds_discard_credentials(context
, 1);
1519 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1521 _isds_discard_credentials(context
, 1);
1523 zfree(context
->url
);
1525 #else /* not HAVE_LIBCURL */
1531 /* Verify connection to ISDS is alive and server is responding.
1532 * Send dummy request to ISDS and expect dummy response. */
1533 isds_error
isds_ping(struct isds_ctx
*context
) {
1535 isds_error soap_err
;
1536 xmlNsPtr isds_ns
= NULL
;
1537 xmlNodePtr request
= NULL
;
1538 #endif /* HAVE_LIBCURL */
1540 if (!context
) return IE_INVALID_CONTEXT
;
1541 zfree(context
->long_message
);
1544 /* Check if connection is established */
1545 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1548 /* Build dummy request */
1549 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1551 isds_log_message(context
, _("Could build ISDS dummy request"));
1554 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1556 isds_log_message(context
, _("Could not create ISDS name space"));
1557 xmlFreeNode(request
);
1560 xmlSetNs(request
, isds_ns
);
1562 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1564 /* XXX: ISDS documentation does not specify response body for
1565 * DummyOperation request. However real server sends back
1566 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1567 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1568 * SOAP body content, e.g. the dmStatus element. */
1570 /* Send dummy request */
1571 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1573 /* Destroy log-in request */
1574 xmlFreeNode(request
);
1577 isds_log(ILF_ISDS
, ILL_DEBUG
,
1578 _("ISDS server could not be contacted\n"));
1582 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1583 * authentication succeeded if soap_err == IE_SUCCESS */
1586 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1589 #else /* not HAVE_LIBCURL */
1595 /* Send bogus request to ISDS.
1596 * Just for test purposes */
1597 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1600 xmlNsPtr isds_ns
= NULL
;
1601 xmlNodePtr request
= NULL
;
1602 xmlDocPtr response
= NULL
;
1603 xmlChar
*code
= NULL
, *message
= NULL
;
1606 if (!context
) return IE_INVALID_CONTEXT
;
1607 zfree(context
->long_message
);
1610 /* Check if connection is established */
1611 if (!context
->curl
) {
1612 /* Testing printf message */
1613 isds_printf_message(context
, "%s", _("I said connection closed"));
1614 return IE_CONNECTION_CLOSED
;
1618 /* Build dummy request */
1619 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1621 isds_log_message(context
, _("Could build ISDS bogus request"));
1624 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1626 isds_log_message(context
, _("Could not create ISDS name space"));
1627 xmlFreeNode(request
);
1630 xmlSetNs(request
, isds_ns
);
1632 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1634 /* Sent bogus request */
1635 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1637 /* Destroy request */
1638 xmlFreeNode(request
);
1641 isds_log(ILF_ISDS
, ILL_DEBUG
,
1642 _("Processing ISDS response on bogus request failed\n"));
1643 xmlFreeDoc(response
);
1647 /* Check for response status */
1648 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1649 &code
, &message
, NULL
);
1651 isds_log(ILF_ISDS
, ILL_DEBUG
,
1652 _("ISDS response on bogus request is missing status\n"));
1655 xmlFreeDoc(response
);
1658 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1659 char *code_locale
= _isds_utf82locale((char*)code
);
1660 char *message_locale
= _isds_utf82locale((char*)message
);
1661 isds_log(ILF_ISDS
, ILL_DEBUG
,
1662 _("Server refused bogus request (code=%s, message=%s)\n"),
1663 code_locale
, message_locale
);
1664 /* XXX: Literal error messages from ISDS are Czech messages
1665 * (English sometimes) in UTF-8. It's hard to catch them for
1666 * translation. Successfully gettextized would return in locale
1667 * encoding, unsuccessfully translated would pass in UTF-8. */
1668 isds_log_message(context
, message_locale
);
1670 free(message_locale
);
1673 xmlFreeDoc(response
);
1680 xmlFreeDoc(response
);
1682 isds_log(ILF_ISDS
, ILL_DEBUG
,
1683 _("Bogus message accepted by server. This should not happen.\n"));
1686 #else /* not HAVE_LIBCURL */
1693 /* Serialize XML subtree to buffer preserving XML indentation.
1694 * @context is session context
1695 * @subtree is XML element to be serialized (with children)
1696 * @buffer is automatically reallocated buffer where serialize to
1697 * @length is size of serialized stream in bytes
1698 * @return standard error code, free @buffer in case of error */
1699 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1700 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1701 isds_error err
= IE_SUCCESS
;
1702 xmlBufferPtr xml_buffer
= NULL
;
1703 xmlSaveCtxtPtr save_ctx
= NULL
;
1704 xmlDocPtr subtree_doc
= NULL
;
1705 xmlNodePtr subtree_copy
;
1709 if (!context
) return IE_INVALID_CONTEXT
;
1710 if (!buffer
) return IE_INVAL
;
1712 if (!subtree
|| !length
) return IE_INVAL
;
1714 /* Make temporary XML document with @subtree root element */
1715 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1716 * It can result in not well-formed on invalid XML tree (e.g. name space
1717 * prefix definition can miss. */
1720 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1722 isds_log_message(context
, _("Could not build temporary document"));
1727 /* XXX: Copy subtree and attach the copy to document.
1728 * One node can not bee attached into more document at the same time.
1729 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1731 * XXX: Check xmlSaveTree() too. */
1732 subtree_copy
= xmlCopyNodeList(subtree
);
1733 if (!subtree_copy
) {
1734 isds_log_message(context
, _("Could not copy subtree"));
1738 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1740 /* Only this way we get namespace definition as @xmlns:isds,
1741 * otherwise we get namespace prefix without definition */
1742 /* FIXME: Don't overwrite original default namespace */
1743 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1745 isds_log_message(context
, _("Could not create ISDS name space"));
1749 xmlSetNs(subtree_copy
, isds_ns
);
1752 /* Serialize the document into buffer */
1753 xml_buffer
= xmlBufferCreate();
1755 isds_log_message(context
, _("Could not create xmlBuffer"));
1759 /* Last argument 0 means to not format the XML tree */
1760 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1762 isds_log_message(context
, _("Could not create XML serializer"));
1766 /* XXX: According LibXML documentation, this function does not return
1767 * meaningful value yet */
1768 xmlSaveDoc(save_ctx
, subtree_doc
);
1769 if (-1 == xmlSaveFlush(save_ctx
)) {
1770 isds_log_message(context
,
1771 _("Could not serialize XML subtree"));
1775 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1776 * even after xmlSaveFlush(). Thus close it here */
1777 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1780 /* Store and detach buffer from xml_buffer */
1781 *buffer
= xml_buffer
->content
;
1782 *length
= xml_buffer
->use
;
1783 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1786 new_buffer
= realloc(*buffer
, *length
);
1787 if (new_buffer
) *buffer
= new_buffer
;
1795 xmlSaveClose(save_ctx
);
1796 xmlBufferFree(xml_buffer
);
1797 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1800 #endif /* HAVE_LIBCURL */
1804 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1805 * @context is session context
1806 * @document is original document where @nodeset points to
1807 * @nodeset is XPath node set to dump (recursively)
1808 * @buffer is automatically reallocated buffer where serialize to
1809 * @length is size of serialized stream in bytes
1810 * @return standard error code, free @buffer in case of error */
1811 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1812 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1813 void **buffer
, size_t *length
) {
1814 isds_error err
= IE_SUCCESS
;
1815 xmlBufferPtr xml_buffer
= NULL
;
1818 if (!context
) return IE_INVALID_CONTEXT
;
1819 if (!buffer
) return IE_INVAL
;
1821 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1824 /* Empty node set results into NULL buffer */
1825 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1829 /* Resulting the document into buffer */
1830 xml_buffer
= xmlBufferCreate();
1832 isds_log_message(context
, _("Could not create xmlBuffer"));
1837 /* Iterate over all nodes */
1838 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1840 * XXX: xmlNodeDump() appends to xml_buffer. */
1842 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1843 isds_log_message(context
, _("Could not dump XML node"));
1849 /* Store and detach buffer from xml_buffer */
1850 *buffer
= xml_buffer
->content
;
1851 *length
= xml_buffer
->use
;
1852 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1855 new_buffer
= realloc(*buffer
, *length
);
1856 if (new_buffer
) *buffer
= new_buffer
;
1865 xmlBufferFree(xml_buffer
);
1871 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1872 * @context is session context
1873 * @document is original document where @nodeset points to
1874 * @nodeset is XPath node set to dump (recursively)
1875 * @buffer is automatically reallocated buffer where serialize to
1876 * @length is size of serialized stream in bytes
1877 * @return standard error code, free @buffer in case of error */
1878 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1879 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1880 void **buffer
, size_t *length
) {
1881 isds_error err
= IE_SUCCESS
;
1882 xmlBufferPtr xml_buffer
= NULL
;
1883 xmlSaveCtxtPtr save_ctx
= NULL
;
1886 if (!context
) return IE_INVALID_CONTEXT
;
1887 if (!buffer
) return IE_INVAL
;
1889 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1892 /* Empty node set results into NULL buffer */
1893 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1897 /* Resulting the document into buffer */
1898 xml_buffer
= xmlBufferCreate();
1900 isds_log_message(context
, _("Could not create xmlBuffer"));
1904 if (xmlSubstituteEntitiesDefault(1)) {
1905 isds_log_message(context
, _("Could not disable attribute escaping"));
1909 /* Last argument means:
1910 * 0 to not format the XML tree
1911 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1912 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1913 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1915 isds_log_message(context
, _("Could not create XML serializer"));
1919 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1920 isds_log_message(context, _("Could not disable attribute escaping"));
1926 /* Iterate over all nodes */
1927 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1929 * XXX: xmlNodeDump() appends to xml_buffer. */
1931 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1933 /* XXX: According LibXML documentation, this function does not return
1934 * meaningful value yet */
1935 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1936 if (-1 == xmlSaveFlush(save_ctx
)) {
1937 isds_log_message(context
,
1938 _("Could not serialize XML subtree"));
1944 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1945 * even after xmlSaveFlush(). Thus close it here */
1946 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1948 /* Store and detach buffer from xml_buffer */
1949 *buffer
= xml_buffer
->content
;
1950 *length
= xml_buffer
->use
;
1951 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1954 new_buffer
= realloc(*buffer
, *length
);
1955 if (new_buffer
) *buffer
= new_buffer
;
1963 xmlSaveClose(save_ctx
);
1964 xmlBufferFree(xml_buffer
);
1971 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1972 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1973 if (!string
|| !type
) return IE_INVAL
;
1975 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1977 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1979 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1980 *type
= DBTYPE_PFO_ADVOK
;
1981 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1982 *type
= DBTYPE_PFO_DANPOR
;
1983 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1984 *type
= DBTYPE_PFO_INSSPR
;
1985 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1987 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1988 *type
= DBTYPE_PO_ZAK
;
1989 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1990 *type
= DBTYPE_PO_REQ
;
1991 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1993 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1994 *type
= DBTYPE_OVM_NOTAR
;
1995 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1996 *type
= DBTYPE_OVM_EXEKUT
;
1997 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1998 *type
= DBTYPE_OVM_REQ
;
2005 /* Convert ISDS dbType enum @type to UTF-8 string.
2006 * @Return pointer to static string, or NULL if unknown enum value */
2007 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2009 /* DBTYPE_SYSTEM is invalid value from point of view of public
2010 * SOAP interface. */
2011 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2012 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2013 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2014 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2015 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2016 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2017 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2018 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2019 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2020 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2021 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2022 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2023 default: return NULL
; break;
2028 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2029 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2030 if (!string
|| !type
) return IE_INVAL
;
2032 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2033 *type
= USERTYPE_PRIMARY
;
2034 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2035 *type
= USERTYPE_ENTRUSTED
;
2036 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2037 *type
= USERTYPE_ADMINISTRATOR
;
2038 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2039 *type
= USERTYPE_OFFICIAL
;
2040 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2041 *type
= USERTYPE_OFFICIAL_CERT
;
2042 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2043 *type
= USERTYPE_LIQUIDATOR
;
2050 /* Convert ISDS userType enum @type to UTF-8 string.
2051 * @Return pointer to static string, or NULL if unknown enum value */
2052 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2054 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2055 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2056 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2057 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2058 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2059 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2060 default: return NULL
; break;
2065 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2066 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2067 isds_sender_type
*type
) {
2068 if (!string
|| !type
) return IE_INVAL
;
2070 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2071 *type
= SENDERTYPE_PRIMARY
;
2072 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2073 *type
= SENDERTYPE_ENTRUSTED
;
2074 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2075 *type
= SENDERTYPE_ADMINISTRATOR
;
2076 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2077 *type
= SENDERTYPE_OFFICIAL
;
2078 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2079 *type
= SENDERTYPE_VIRTUAL
;
2080 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2081 *type
= SENDERTYPE_OFFICIAL_CERT
;
2082 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2083 *type
= SENDERTYPE_LIQUIDATOR
;
2090 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2091 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2092 isds_payment_type
*type
) {
2093 if (!string
|| !type
) return IE_INVAL
;
2095 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2096 *type
= PAYMENT_SENDER
;
2097 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2098 *type
= PAYMENT_RESPONSE
;
2099 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2100 *type
= PAYMENT_SPONSOR
;
2101 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2102 *type
= PAYMENT_SPONSOR_LIMITED
;
2103 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2104 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2105 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2106 *type
= PAYMENT_STAMP
;
2113 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2114 * ciEventType is integer but we convert it from string representation
2116 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2117 isds_credit_event_type
*type
) {
2118 if (!string
|| !type
) return IE_INVAL
;
2120 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2121 *type
= ISDS_CREDIT_CHARGED
;
2122 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2123 *type
= ISDS_CREDIT_DISCHARGED
;
2124 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2125 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2126 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2127 *type
= ISDS_CREDIT_STORAGE_SET
;
2128 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2129 *type
= ISDS_CREDIT_EXPIRED
;
2136 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2137 * @Return pointer to static string, or NULL if unknown enum value */
2138 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2140 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2141 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2142 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2143 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2144 default: return NULL
; break;
2149 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2150 * ISDSSearch2/searchType value.
2151 * @Return pointer to static string, or NULL if unknown enum value */
2152 static const xmlChar
*isds_fulltext_target2string(
2153 const isds_fulltext_target type
) {
2155 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2156 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2157 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2158 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2159 default: return NULL
; break;
2162 #endif /* HAVE_LIBCURL */
2165 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2166 * @Return IE_ENUM if @string is not valid enum member */
2167 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2168 isds_FileMetaType
*type
) {
2169 if (!string
|| !type
) return IE_INVAL
;
2171 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2172 *type
= FILEMETATYPE_MAIN
;
2173 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2174 *type
= FILEMETATYPE_ENCLOSURE
;
2175 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2176 *type
= FILEMETATYPE_SIGNATURE
;
2177 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2178 *type
= FILEMETATYPE_META
;
2185 /* Convert UTF-8 @string to ISDS hash @algorithm.
2186 * @Return IE_ENUM if @string is not valid enum member */
2187 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2188 isds_hash_algorithm
*algorithm
) {
2189 if (!string
|| !algorithm
) return IE_INVAL
;
2191 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2192 *algorithm
= HASH_ALGORITHM_MD5
;
2193 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2194 *algorithm
= HASH_ALGORITHM_SHA_1
;
2195 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2196 *algorithm
= HASH_ALGORITHM_SHA_224
;
2197 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2198 *algorithm
= HASH_ALGORITHM_SHA_256
;
2199 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2200 *algorithm
= HASH_ALGORITHM_SHA_384
;
2201 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2202 *algorithm
= HASH_ALGORITHM_SHA_512
;
2210 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2211 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2212 if (!time
|| !string
) return IE_INVAL
;
2214 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2215 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2222 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2223 * respects the @time microseconds too. */
2224 static isds_error
timeval2timestring(const struct timeval
*time
,
2228 if (!time
|| !string
) return IE_INVAL
;
2230 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2231 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2233 /* TODO: small negative year should be formatted as "-0012". This is not
2234 * true for glibc "%04d". We should implement it.
2235 * time->tv_usec type is su_seconds_t which is required to be signed
2236 * integer to accomodate values from range [-1, 1000000].
2237 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2238 if (-1 == isds_asprintf((char **) string
,
2239 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX
,
2240 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2241 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2242 (intmax_t)time
->tv_usec
))
2247 #endif /* HAVE_LIBCURL */
2250 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2251 * It respects microseconds too. Microseconds are rounded half up.
2252 * In case of error, @time will be freed. */
2253 static isds_error
timestring2timeval(const xmlChar
*string
,
2254 struct timeval
**time
) {
2256 char *offset
, *delim
, *endptr
;
2257 const int subsecond_resolution
= 6;
2258 char subseconds
[subsecond_resolution
+ 1];
2260 int offset_hours
, offset_minutes
;
2262 long int long_number
;
2267 if (!time
) return IE_INVAL
;
2273 memset(&broken
, 0, sizeof(broken
));
2276 *time
= calloc(1, sizeof(**time
));
2277 if (!*time
) return IE_NOMEM
;
2279 memset(*time
, 0, sizeof(**time
));
2283 /* xsd:date is ISO 8601 string, thus ASCII */
2284 /*TODO: negative year */
2288 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2289 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2290 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2296 broken
.tm_year
-= 1900;
2298 offset
= (char*)string
+ i
;
2300 /* Parse date and time without subseconds and offset */
2301 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2308 /* Get subseconds */
2309 if (*offset
== '.' ) {
2312 /* Copy first 6 digits, pad it with zeros.
2313 * Current server implementation uses only millisecond resolution. */
2314 /* TODO: isdigit() is locale sensitive */
2316 i
< subsecond_resolution
&& isdigit(*offset
);
2318 subseconds
[i
] = *offset
;
2320 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2321 /* Check 7th digit for rounding */
2322 if (*offset
>= '5') round_up
= 1;
2325 for (; i
< subsecond_resolution
; i
++) {
2326 subseconds
[i
] = '0';
2328 subseconds
[subsecond_resolution
] = '\0';
2330 /* Convert it into integer */
2331 long_number
= strtol(subseconds
, &endptr
, 10);
2332 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2333 long_number
== LONG_MAX
) {
2337 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2338 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2339 * microseconds" and "the type shall be a signed integer capable of
2340 * storing values at least in the range [-1, 1000000]. */
2341 if (long_number
< -1 || long_number
>= 1000000) {
2345 (*time
)->tv_usec
= long_number
;
2347 /* Round the subseconds */
2349 if (999999 == (*time
)->tv_usec
) {
2350 (*time
)->tv_usec
= 0;
2357 /* move to the zone offset delimiter or signal NULL*/
2358 delim
= strchr(offset
, '-');
2360 delim
= strchr(offset
, '+');
2362 delim
= strchr(offset
, 'Z');
2366 /* Get zone offset */
2367 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2368 * "" equals to "Z" and it means UTC zone. */
2369 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2370 * colon separator */
2371 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2372 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2376 if (*offset
== '+') {
2377 broken
.tm_hour
-= offset_hours
;
2378 broken
.tm_min
-= offset_minutes
;
2380 broken
.tm_hour
+= offset_hours
;
2381 broken
.tm_min
+= offset_minutes
;
2385 /* Convert to time_t */
2386 (*time
)->tv_sec
= _isds_timegm(&broken
);
2387 if ((*time
)->tv_sec
== (time_t) -1) {
2396 /* Convert unsigned int into isds_message_status.
2397 * @context is session context
2398 * @number is pointer to number value. NULL will be treated as invalid value.
2399 * @status is automatically reallocated status
2400 * @return IE_SUCCESS, or error code and free status */
2401 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2402 const unsigned long int *number
, isds_message_status
**status
) {
2403 if (!context
) return IE_INVALID_CONTEXT
;
2404 if (!status
) return IE_INVAL
;
2406 free(*status
); *status
= NULL
;
2407 if (!number
) return IE_INVAL
;
2409 if (*number
< 1 || *number
> 10) {
2410 isds_printf_message(context
, _("Invalid message status value: %lu"),
2415 *status
= malloc(sizeof(**status
));
2416 if (!*status
) return IE_NOMEM
;
2418 **status
= 1 << *number
;
2423 /* Convert event description string into isds_event members type and
2425 * @string is raw event description starting with event prefix
2426 * @event is structure where to store type and stripped description to
2427 * @return standard error code, unknown prefix is not classified as an error.
2429 static isds_error
eventstring2event(const xmlChar
*string
,
2430 struct isds_event
* event
) {
2431 const xmlChar
*known_prefixes
[] = {
2442 const isds_event_type types
[] = {
2443 EVENT_ENTERED_SYSTEM
,
2444 EVENT_ACCEPTED_BY_RECIPIENT
,
2445 EVENT_ACCEPTED_BY_FICTION
,
2446 EVENT_UNDELIVERABLE
,
2447 EVENT_COMMERCIAL_ACCEPTED
,
2449 EVENT_PRIMARY_LOGIN
,
2450 EVENT_ENTRUSTED_LOGIN
,
2456 if (!string
|| !event
) return IE_INVAL
;
2459 event
->type
= malloc(sizeof(*event
->type
));
2460 if (!(event
->type
)) return IE_NOMEM
;
2462 zfree(event
->description
);
2464 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2466 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2468 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2469 /* Prefix is known */
2470 *event
->type
= types
[index
];
2472 /* Strip prefix from description and spaces */
2473 /* TODO: Recognize all white spaces from UCS blank class and
2474 * operate on UTF-8 chars. */
2475 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2476 event
->description
= strdup((char *) (string
+ length
));
2477 if (!(event
->description
)) return IE_NOMEM
;
2483 /* Unknown event prefix.
2484 * XSD allows any string */
2485 char *string_locale
= _isds_utf82locale((char *) string
);
2486 isds_log(ILF_ISDS
, ILL_WARNING
,
2487 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2488 free(string_locale
);
2490 *event
->type
= EVENT_UKNOWN
;
2491 event
->description
= strdup((char *) string
);
2492 if (!(event
->description
)) return IE_NOMEM
;
2498 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2499 * and leave label */
2500 #define EXTRACT_STRING(element, string) { \
2501 xmlXPathFreeObject(result); \
2502 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2503 if (NULL == (result)) { \
2507 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2508 if (result->nodesetval->nodeNr > 1) { \
2509 isds_printf_message(context, _("Multiple %s element"), element); \
2513 (string) = (char *) \
2514 xmlXPathCastNodeSetToString(result->nodesetval); \
2515 if (NULL == (string)) { \
2522 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2524 char *string = NULL; \
2525 EXTRACT_STRING(element, string); \
2528 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2529 if (!(booleanPtr)) { \
2535 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2536 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2537 *(booleanPtr) = 1; \
2538 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2539 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2540 *(booleanPtr) = 0; \
2542 char *string_locale = _isds_utf82locale((char*)string); \
2543 isds_printf_message(context, \
2544 _("%s value is not valid boolean: %s"), \
2545 element, string_locale); \
2546 free(string_locale); \
2556 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2558 char *string = NULL; \
2559 EXTRACT_STRING(element, string); \
2561 if (NULL == string) { \
2562 isds_printf_message(context, _("%s element is empty"), element); \
2566 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2567 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2569 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2570 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2573 char *string_locale = _isds_utf82locale((char*)string); \
2574 isds_printf_message(context, \
2575 _("%s value is not valid boolean: %s"), \
2576 element, string_locale); \
2577 free(string_locale); \
2586 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2588 char *string = NULL; \
2589 EXTRACT_STRING(element, string); \
2594 number = strtol((char*)string, &endptr, 10); \
2596 if (*endptr != '\0') { \
2597 char *string_locale = _isds_utf82locale((char *)string); \
2598 isds_printf_message(context, \
2599 _("%s is not valid integer: %s"), \
2600 element, string_locale); \
2601 free(string_locale); \
2607 if (number == LONG_MIN || number == LONG_MAX) { \
2608 char *string_locale = _isds_utf82locale((char *)string); \
2609 isds_printf_message(context, \
2610 _("%s value out of range of long int: %s"), \
2611 element, string_locale); \
2612 free(string_locale); \
2618 free(string); string = NULL; \
2620 if (!(preallocated)) { \
2621 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2622 if (!(longintPtr)) { \
2627 *(longintPtr) = number; \
2631 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2633 char *string = NULL; \
2634 EXTRACT_STRING(element, string); \
2639 number = strtol((char*)string, &endptr, 10); \
2641 if (*endptr != '\0') { \
2642 char *string_locale = _isds_utf82locale((char *)string); \
2643 isds_printf_message(context, \
2644 _("%s is not valid integer: %s"), \
2645 element, string_locale); \
2646 free(string_locale); \
2652 if (number == LONG_MIN || number == LONG_MAX) { \
2653 char *string_locale = _isds_utf82locale((char *)string); \
2654 isds_printf_message(context, \
2655 _("%s value out of range of long int: %s"), \
2656 element, string_locale); \
2657 free(string_locale); \
2663 free(string); string = NULL; \
2665 isds_printf_message(context, \
2666 _("%s value is negative: %ld"), element, number); \
2671 if (!(preallocated)) { \
2672 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2673 if (!(ulongintPtr)) { \
2678 *(ulongintPtr) = number; \
2682 #define EXTRACT_DATE(element, tmPtr) { \
2683 char *string = NULL; \
2684 EXTRACT_STRING(element, string); \
2685 if (NULL != string) { \
2686 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2687 if (NULL == (tmPtr)) { \
2692 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2694 if (err == IE_NOTSUP) { \
2696 char *string_locale = _isds_utf82locale(string); \
2697 char *element_locale = _isds_utf82locale(element); \
2698 isds_printf_message(context, _("Invalid %s value: %s"), \
2699 element_locale, string_locale); \
2700 free(string_locale); \
2701 free(element_locale); \
2710 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2711 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2713 if ((required) && (!string)) { \
2714 char *attribute_locale = _isds_utf82locale(attribute); \
2715 char *element_locale = \
2716 _isds_utf82locale((char *)xpath_ctx->node->name); \
2717 isds_printf_message(context, \
2718 _("Could not extract required %s attribute value from " \
2719 "%s element"), attribute_locale, element_locale); \
2720 free(element_locale); \
2721 free(attribute_locale); \
2728 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2730 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2731 (xmlChar *) (string)); \
2733 isds_printf_message(context, \
2734 _("Could not add %s child to %s element"), \
2735 element, (parent)->name); \
2741 #define INSERT_STRING(parent, element, string) \
2742 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2744 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2746 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2747 else { INSERT_STRING(parent, element, "false"); } \
2750 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2753 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2755 INSERT_STRING(parent, element, NULL); \
2759 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2760 if ((longintPtr)) { \
2761 /* FIXME: locale sensitive */ \
2762 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2766 INSERT_STRING(parent, element, buffer) \
2767 free(buffer); (buffer) = NULL; \
2768 } else { INSERT_STRING(parent, element, NULL) } \
2771 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2772 if ((ulongintPtr)) { \
2773 /* FIXME: locale sensitive */ \
2774 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2778 INSERT_STRING(parent, element, buffer) \
2779 free(buffer); (buffer) = NULL; \
2780 } else { INSERT_STRING(parent, element, NULL) } \
2783 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2785 /* FIXME: locale sensitive */ \
2786 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2790 INSERT_STRING(parent, element, buffer) \
2791 free(buffer); (buffer) = NULL; \
2794 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2796 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2798 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2799 (xmlChar *) (string)); \
2800 if (!attribute_node) { \
2801 isds_printf_message(context, _("Could not add %s " \
2802 "attribute to %s element"), \
2803 (attribute), (parent)->name); \
2809 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2811 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2812 if (length > (maximum)) { \
2813 isds_printf_message(context, \
2814 ngettext("%s has more than %d characters", \
2815 "%s has more than %d characters", (maximum)), \
2816 (name), (maximum)); \
2820 if (length < (minimum)) { \
2821 isds_printf_message(context, \
2822 ngettext("%s has less than %d characters", \
2823 "%s has less than %d characters", (minimum)), \
2824 (name), (minimum)); \
2831 #define INSERT_ELEMENT(child, parent, element) \
2833 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2835 isds_printf_message(context, \
2836 _("Could not add %s child to %s element"), \
2837 (element), (parent)->name); \
2844 /* Find child element by name in given XPath context and switch context onto
2845 * it. The child must be uniq and must exist. Otherwise fails.
2846 * @context is ISDS context
2847 * @child is child element name
2848 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2849 * into it child. In error case, the @xpath_ctx keeps original value. */
2850 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2851 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2852 isds_error err
= IE_SUCCESS
;
2853 xmlXPathObjectPtr result
= NULL
;
2855 if (!context
) return IE_INVALID_CONTEXT
;
2856 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2859 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2866 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2867 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2868 char *child_locale
= _isds_utf82locale((char*) child
);
2869 isds_printf_message(context
,
2870 _("%s element does not contain %s child"),
2871 parent_locale
, child_locale
);
2873 free(parent_locale
);
2879 if (result
->nodesetval
->nodeNr
> 1) {
2880 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2881 char *child_locale
= _isds_utf82locale((char*) child
);
2882 isds_printf_message(context
,
2883 _("%s element contains multiple %s children"),
2884 parent_locale
, child_locale
);
2886 free(parent_locale
);
2891 /* Switch context */
2892 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2895 xmlXPathFreeObject(result
);
2902 /* Find and convert XSD:gPersonName group in current node into structure
2903 * @context is ISDS context
2904 * @personName is automatically reallocated person name structure. If no member
2905 * value is found, will be freed.
2906 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2908 * In case of error @personName will be freed. */
2909 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2910 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2911 isds_error err
= IE_SUCCESS
;
2912 xmlXPathObjectPtr result
= NULL
;
2914 if (!context
) return IE_INVALID_CONTEXT
;
2915 if (!personName
) return IE_INVAL
;
2916 isds_PersonName_free(personName
);
2917 if (!xpath_ctx
) return IE_INVAL
;
2920 *personName
= calloc(1, sizeof(**personName
));
2926 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2927 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2928 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2929 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2931 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2932 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2933 isds_PersonName_free(personName
);
2936 if (err
) isds_PersonName_free(personName
);
2937 xmlXPathFreeObject(result
);
2942 /* Find and convert XSD:gAddress group in current node into structure
2943 * @context is ISDS context
2944 * @address is automatically reallocated address structure. If no member
2945 * value is found, will be freed.
2946 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2948 * In case of error @address will be freed. */
2949 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2950 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2951 isds_error err
= IE_SUCCESS
;
2952 xmlXPathObjectPtr result
= NULL
;
2954 if (!context
) return IE_INVALID_CONTEXT
;
2955 if (!address
) return IE_INVAL
;
2956 isds_Address_free(address
);
2957 if (!xpath_ctx
) return IE_INVAL
;
2960 *address
= calloc(1, sizeof(**address
));
2966 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2967 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2968 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2969 EXTRACT_STRING("isds:adNumberInMunicipality",
2970 (*address
)->adNumberInMunicipality
);
2971 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2972 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2974 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2975 !(*address
)->adNumberInStreet
&&
2976 !(*address
)->adNumberInMunicipality
&&
2977 !(*address
)->adZipCode
&& !(*address
)->adState
)
2978 isds_Address_free(address
);
2981 if (err
) isds_Address_free(address
);
2982 xmlXPathFreeObject(result
);
2987 /* Find and convert isds:biDate element in current node into structure
2988 * @context is ISDS context
2989 * @biDate is automatically reallocated birth date structure. If no member
2990 * value is found, will be freed.
2991 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2993 * In case of error @biDate will be freed. */
2994 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2995 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2996 isds_error err
= IE_SUCCESS
;
2997 xmlXPathObjectPtr result
= NULL
;
2998 char *string
= NULL
;
3000 if (!context
) return IE_INVALID_CONTEXT
;
3001 if (!biDate
) return IE_INVAL
;
3003 if (!xpath_ctx
) return IE_INVAL
;
3005 EXTRACT_STRING("isds:biDate", string
);
3007 *biDate
= calloc(1, sizeof(**biDate
));
3012 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3014 if (err
== IE_NOTSUP
) {
3016 char *string_locale
= _isds_utf82locale(string
);
3017 isds_printf_message(context
,
3018 _("Invalid isds:biDate value: %s"), string_locale
);
3019 free(string_locale
);
3026 if (err
) zfree(*biDate
);
3028 xmlXPathFreeObject(result
);
3033 /* Convert isds:dBOwnerInfo XML tree into structure
3034 * @context is ISDS context
3035 * @db_owner_info is automatically reallocated box owner info structure
3036 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3037 * In case of error @db_owner_info will be freed. */
3038 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3039 struct isds_DbOwnerInfo
**db_owner_info
,
3040 xmlXPathContextPtr xpath_ctx
) {
3041 isds_error err
= IE_SUCCESS
;
3042 xmlXPathObjectPtr result
= NULL
;
3043 char *string
= NULL
;
3045 if (!context
) return IE_INVALID_CONTEXT
;
3046 if (!db_owner_info
) return IE_INVAL
;
3047 isds_DbOwnerInfo_free(db_owner_info
);
3048 if (!xpath_ctx
) return IE_INVAL
;
3051 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3052 if (!*db_owner_info
) {
3057 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3059 EXTRACT_STRING("isds:dbType", string
);
3061 (*db_owner_info
)->dbType
=
3062 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3063 if (!(*db_owner_info
)->dbType
) {
3067 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3069 zfree((*db_owner_info
)->dbType
);
3070 if (err
== IE_ENUM
) {
3072 char *string_locale
= _isds_utf82locale(string
);
3073 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3075 free(string_locale
);
3082 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3084 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3086 if (err
) goto leave
;
3088 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3090 (*db_owner_info
)->birthInfo
=
3091 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3092 if (!(*db_owner_info
)->birthInfo
) {
3096 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3098 if (err
) goto leave
;
3099 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3100 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3101 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3102 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3103 !(*db_owner_info
)->birthInfo
->biCity
&&
3104 !(*db_owner_info
)->birthInfo
->biCounty
&&
3105 !(*db_owner_info
)->birthInfo
->biState
)
3106 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3108 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3109 if (err
) goto leave
;
3111 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3112 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3113 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3114 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3115 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3117 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3119 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3120 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3121 (*db_owner_info
)->dbOpenAddressing
);
3124 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3126 xmlXPathFreeObject(result
);
3131 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3132 * @context is session context
3133 * @owner is libisds structure with box description
3134 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3135 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3136 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3138 isds_error err
= IE_SUCCESS
;
3140 xmlChar
*string
= NULL
;
3142 if (!context
) return IE_INVALID_CONTEXT
;
3143 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3146 /* Build XSD:tDbOwnerInfo */
3147 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3148 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3151 if (owner
->dbType
) {
3152 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3154 isds_printf_message(context
, _("Invalid dbType value: %d"),
3159 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3161 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3162 if (owner
->personName
) {
3163 INSERT_STRING(db_owner_info
, "pnFirstName",
3164 owner
->personName
->pnFirstName
);
3165 INSERT_STRING(db_owner_info
, "pnMiddleName",
3166 owner
->personName
->pnMiddleName
);
3167 INSERT_STRING(db_owner_info
, "pnLastName",
3168 owner
->personName
->pnLastName
);
3169 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3170 owner
->personName
->pnLastNameAtBirth
);
3172 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3173 if (owner
->birthInfo
) {
3174 if (owner
->birthInfo
->biDate
) {
3175 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3176 INSERT_STRING(db_owner_info
, "biDate", string
);
3177 free(string
); string
= NULL
;
3179 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3180 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3181 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3183 if (owner
->address
) {
3184 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3185 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3186 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3187 owner
->address
->adNumberInStreet
);
3188 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3189 owner
->address
->adNumberInMunicipality
);
3190 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3191 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3193 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3194 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3195 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3197 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3198 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3200 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3201 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3203 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3205 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3206 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3207 owner
->dbOpenAddressing
);
3215 /* Convert XSD:tDbUserInfo XML tree into structure
3216 * @context is ISDS context
3217 * @db_user_info is automatically reallocated user info structure
3218 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3219 * In case of error @db_user_info will be freed. */
3220 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3221 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3222 isds_error err
= IE_SUCCESS
;
3223 xmlXPathObjectPtr result
= NULL
;
3224 char *string
= NULL
;
3226 if (!context
) return IE_INVALID_CONTEXT
;
3227 if (!db_user_info
) return IE_INVAL
;
3228 isds_DbUserInfo_free(db_user_info
);
3229 if (!xpath_ctx
) return IE_INVAL
;
3232 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3233 if (!*db_user_info
) {
3238 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3240 EXTRACT_STRING("isds:userType", string
);
3242 (*db_user_info
)->userType
=
3243 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3244 if (!(*db_user_info
)->userType
) {
3248 err
= string2isds_UserType((xmlChar
*)string
,
3249 (*db_user_info
)->userType
);
3251 zfree((*db_user_info
)->userType
);
3252 if (err
== IE_ENUM
) {
3254 char *string_locale
= _isds_utf82locale(string
);
3255 isds_printf_message(context
,
3256 _("Unknown isds:userType value: %s"), string_locale
);
3257 free(string_locale
);
3264 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3266 (*db_user_info
)->personName
=
3267 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3268 if (!(*db_user_info
)->personName
) {
3273 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3275 if (err
) goto leave
;
3277 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3278 if (err
) goto leave
;
3280 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3281 if (err
) goto leave
;
3283 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3284 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3286 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3287 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3288 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3290 /* ???: Default value is "CZ" according specification. Should we provide
3292 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3295 if (err
) isds_DbUserInfo_free(db_user_info
);
3297 xmlXPathFreeObject(result
);
3302 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3303 * @context is session context
3304 * @user is libisds structure with user description
3305 * @db_user_info is XML element of XSD:tDbUserInfo */
3306 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3307 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3309 isds_error err
= IE_SUCCESS
;
3311 xmlChar
*string
= NULL
;
3313 if (!context
) return IE_INVALID_CONTEXT
;
3314 if (!user
|| !db_user_info
) return IE_INVAL
;
3316 /* Build XSD:tDbUserInfo */
3317 if (user
->personName
) {
3318 INSERT_STRING(db_user_info
, "pnFirstName",
3319 user
->personName
->pnFirstName
);
3320 INSERT_STRING(db_user_info
, "pnMiddleName",
3321 user
->personName
->pnMiddleName
);
3322 INSERT_STRING(db_user_info
, "pnLastName",
3323 user
->personName
->pnLastName
);
3324 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3325 user
->personName
->pnLastNameAtBirth
);
3327 if (user
->address
) {
3328 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3329 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3330 INSERT_STRING(db_user_info
, "adNumberInStreet",
3331 user
->address
->adNumberInStreet
);
3332 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3333 user
->address
->adNumberInMunicipality
);
3334 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3335 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3338 if (!tm2datestring(user
->biDate
, &string
))
3339 INSERT_STRING(db_user_info
, "biDate", string
);
3342 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3343 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3346 if (user
->userType
) {
3347 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3349 isds_printf_message(context
, _("Invalid userType value: %d"),
3354 INSERT_STRING(db_user_info
, "userType", type_string
);
3357 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3358 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3359 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3360 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3361 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3362 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3363 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3364 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3365 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3373 /* Convert XSD:tPDZRec XML tree into structure
3374 * @context is ISDS context
3375 * @permission is automatically reallocated commercial permission structure
3376 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3377 * In case of error @permission will be freed. */
3378 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3379 struct isds_commercial_permission
**permission
,
3380 xmlXPathContextPtr xpath_ctx
) {
3381 isds_error err
= IE_SUCCESS
;
3382 xmlXPathObjectPtr result
= NULL
;
3383 char *string
= NULL
;
3385 if (!context
) return IE_INVALID_CONTEXT
;
3386 if (!permission
) return IE_INVAL
;
3387 isds_commercial_permission_free(permission
);
3388 if (!xpath_ctx
) return IE_INVAL
;
3391 *permission
= calloc(1, sizeof(**permission
));
3397 EXTRACT_STRING("isds:PDZType", string
);
3399 err
= string2isds_payment_type((xmlChar
*)string
,
3400 &(*permission
)->type
);
3402 if (err
== IE_ENUM
) {
3404 char *string_locale
= _isds_utf82locale(string
);
3405 isds_printf_message(context
,
3406 _("Unknown isds:PDZType value: %s"), string_locale
);
3407 free(string_locale
);
3414 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3415 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3417 EXTRACT_STRING("isds:PDZExpire", string
);
3419 err
= timestring2timeval((xmlChar
*) string
,
3420 &((*permission
)->expiration
));
3422 char *string_locale
= _isds_utf82locale(string
);
3423 if (err
== IE_DATE
) err
= IE_ISDS
;
3424 isds_printf_message(context
,
3425 _("Could not convert PDZExpire as ISO time: %s"),
3427 free(string_locale
);
3433 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3434 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3437 if (err
) isds_commercial_permission_free(permission
);
3439 xmlXPathFreeObject(result
);
3444 /* Convert XSD:tCiRecord XML tree into structure
3445 * @context is ISDS context
3446 * @event is automatically reallocated commercial credit event structure
3447 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3448 * In case of error @event will be freed. */
3449 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3450 struct isds_credit_event
**event
,
3451 xmlXPathContextPtr xpath_ctx
) {
3452 isds_error err
= IE_SUCCESS
;
3453 xmlXPathObjectPtr result
= NULL
;
3454 char *string
= NULL
;
3455 long int *number_ptr
;
3457 if (!context
) return IE_INVALID_CONTEXT
;
3458 if (!event
) return IE_INVAL
;
3459 isds_credit_event_free(event
);
3460 if (!xpath_ctx
) return IE_INVAL
;
3463 *event
= calloc(1, sizeof(**event
));
3469 EXTRACT_STRING("isds:ciEventTime", string
);
3471 err
= timestring2timeval((xmlChar
*) string
,
3474 char *string_locale
= _isds_utf82locale(string
);
3475 if (err
== IE_DATE
) err
= IE_ISDS
;
3476 isds_printf_message(context
,
3477 _("Could not convert ciEventTime as ISO time: %s"),
3479 free(string_locale
);
3485 EXTRACT_STRING("isds:ciEventType", string
);
3487 err
= string2isds_credit_event_type((xmlChar
*)string
,
3490 if (err
== IE_ENUM
) {
3492 char *string_locale
= _isds_utf82locale(string
);
3493 isds_printf_message(context
,
3494 _("Unknown isds:ciEventType value: %s"), string_locale
);
3495 free(string_locale
);
3502 number_ptr
= &((*event
)->credit_change
);
3503 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3504 number_ptr
= &(*event
)->new_credit
;
3505 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3507 switch((*event
)->type
) {
3508 case ISDS_CREDIT_CHARGED
:
3509 EXTRACT_STRING("isds:ciTransID",
3510 (*event
)->details
.charged
.transaction
);
3512 case ISDS_CREDIT_DISCHARGED
:
3513 EXTRACT_STRING("isds:ciTransID",
3514 (*event
)->details
.discharged
.transaction
);
3516 case ISDS_CREDIT_MESSAGE_SENT
:
3517 EXTRACT_STRING("isds:ciRecipientID",
3518 (*event
)->details
.message_sent
.recipient
);
3519 EXTRACT_STRING("isds:ciPDZID",
3520 (*event
)->details
.message_sent
.message_id
);
3522 case ISDS_CREDIT_STORAGE_SET
:
3523 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3524 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3525 EXTRACT_DATE("isds:ciNewFrom",
3526 (*event
)->details
.storage_set
.new_valid_from
);
3527 EXTRACT_DATE("isds:ciNewTo",
3528 (*event
)->details
.storage_set
.new_valid_to
);
3529 EXTRACT_LONGINT("isds:ciOldCapacity",
3530 (*event
)->details
.storage_set
.old_capacity
, 0);
3531 EXTRACT_DATE("isds:ciOldFrom",
3532 (*event
)->details
.storage_set
.old_valid_from
);
3533 EXTRACT_DATE("isds:ciOldTo",
3534 (*event
)->details
.storage_set
.old_valid_to
);
3535 EXTRACT_STRING("isds:ciDoneBy",
3536 (*event
)->details
.storage_set
.initiator
);
3538 case ISDS_CREDIT_EXPIRED
:
3543 if (err
) isds_credit_event_free(event
);
3545 xmlXPathFreeObject(result
);
3550 #endif /* HAVE_LIBCURL */
3553 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3554 * isds_envelope structure. The envelope is automatically allocated but not
3555 * reallocated. The date are just appended into envelope structure.
3556 * @context is ISDS context
3557 * @envelope is automatically allocated message envelope structure
3558 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3559 * In case of error @envelope will be freed. */
3560 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3561 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3562 isds_error err
= IE_SUCCESS
;
3563 xmlXPathObjectPtr result
= NULL
;
3565 if (!context
) return IE_INVALID_CONTEXT
;
3566 if (!envelope
) return IE_INVAL
;
3567 if (!xpath_ctx
) return IE_INVAL
;
3571 /* Allocate envelope */
3572 *envelope
= calloc(1, sizeof(**envelope
));
3578 /* Else free former data */
3579 zfree((*envelope
)->dmSenderOrgUnit
);
3580 zfree((*envelope
)->dmSenderOrgUnitNum
);
3581 zfree((*envelope
)->dbIDRecipient
);
3582 zfree((*envelope
)->dmRecipientOrgUnit
);
3583 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3584 zfree((*envelope
)->dmToHands
);
3585 zfree((*envelope
)->dmAnnotation
);
3586 zfree((*envelope
)->dmRecipientRefNumber
);
3587 zfree((*envelope
)->dmSenderRefNumber
);
3588 zfree((*envelope
)->dmRecipientIdent
);
3589 zfree((*envelope
)->dmSenderIdent
);
3590 zfree((*envelope
)->dmLegalTitleLaw
);
3591 zfree((*envelope
)->dmLegalTitleYear
);
3592 zfree((*envelope
)->dmLegalTitleSect
);
3593 zfree((*envelope
)->dmLegalTitlePar
);
3594 zfree((*envelope
)->dmLegalTitlePoint
);
3595 zfree((*envelope
)->dmPersonalDelivery
);
3596 zfree((*envelope
)->dmAllowSubstDelivery
);
3599 /* Extract envelope elements added by sender or ISDS
3600 * (XSD: gMessageEnvelopeSub type) */
3601 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3602 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3603 (*envelope
)->dmSenderOrgUnitNum
, 0);
3604 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3605 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3606 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3607 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3608 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3609 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3610 EXTRACT_STRING("isds:dmRecipientRefNumber",
3611 (*envelope
)->dmRecipientRefNumber
);
3612 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3613 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3614 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3616 /* Extract envelope elements regarding law reference */
3617 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3618 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3619 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3620 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3621 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3623 /* Extract envelope other elements */
3624 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3625 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3626 (*envelope
)->dmAllowSubstDelivery
);
3629 if (err
) isds_envelope_free(envelope
);
3630 xmlXPathFreeObject(result
);
3636 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3637 * isds_envelope structure. The envelope is automatically allocated but not
3638 * reallocated. The date are just appended into envelope structure.
3639 * @context is ISDS context
3640 * @envelope is automatically allocated message envelope structure
3641 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3642 * In case of error @envelope will be freed. */
3643 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3644 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3645 isds_error err
= IE_SUCCESS
;
3646 xmlXPathObjectPtr result
= NULL
;
3648 if (!context
) return IE_INVALID_CONTEXT
;
3649 if (!envelope
) return IE_INVAL
;
3650 if (!xpath_ctx
) return IE_INVAL
;
3654 /* Allocate envelope */
3655 *envelope
= calloc(1, sizeof(**envelope
));
3661 /* Else free former data */
3662 zfree((*envelope
)->dmID
);
3663 zfree((*envelope
)->dbIDSender
);
3664 zfree((*envelope
)->dmSender
);
3665 zfree((*envelope
)->dmSenderAddress
);
3666 zfree((*envelope
)->dmSenderType
);
3667 zfree((*envelope
)->dmRecipient
);
3668 zfree((*envelope
)->dmRecipientAddress
);
3669 zfree((*envelope
)->dmAmbiguousRecipient
);
3672 /* Extract envelope elements added by ISDS
3673 * (XSD: gMessageEnvelope type) */
3674 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3675 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3676 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3677 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3678 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3679 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3680 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3681 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3682 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3683 (*envelope
)->dmAmbiguousRecipient
);
3685 /* Extract envelope elements added by sender and ISDS
3686 * (XSD: gMessageEnvelope type) */
3687 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3688 if (err
) goto leave
;
3691 if (err
) isds_envelope_free(envelope
);
3692 xmlXPathFreeObject(result
);
3697 /* Convert other envelope elements from XML tree into isds_envelope structure:
3698 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3699 * The envelope is automatically allocated but not reallocated.
3700 * The data are just appended into envelope structure.
3701 * @context is ISDS context
3702 * @envelope is automatically allocated message envelope structure
3703 * @xpath_ctx is XPath context with current node as parent desired elements
3704 * In case of error @envelope will be freed. */
3705 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3706 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3707 isds_error err
= IE_SUCCESS
;
3708 xmlXPathObjectPtr result
= NULL
;
3709 char *string
= NULL
;
3710 unsigned long int *unumber
= NULL
;
3712 if (!context
) return IE_INVALID_CONTEXT
;
3713 if (!envelope
) return IE_INVAL
;
3714 if (!xpath_ctx
) return IE_INVAL
;
3719 *envelope
= calloc(1, sizeof(**envelope
));
3726 zfree((*envelope
)->dmMessageStatus
);
3727 zfree((*envelope
)->dmAttachmentSize
);
3728 zfree((*envelope
)->dmDeliveryTime
);
3729 zfree((*envelope
)->dmAcceptanceTime
);
3733 /* dmMessageStatus element is mandatory */
3734 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3736 isds_log_message(context
,
3737 _("Missing mandatory sisds:dmMessageStatus integer"));
3741 err
= uint2isds_message_status(context
, unumber
,
3742 &((*envelope
)->dmMessageStatus
));
3744 if (err
== IE_ENUM
) err
= IE_ISDS
;
3747 free(unumber
); unumber
= NULL
;
3749 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3752 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3754 err
= timestring2timeval((xmlChar
*) string
,
3755 &((*envelope
)->dmDeliveryTime
));
3757 char *string_locale
= _isds_utf82locale(string
);
3758 if (err
== IE_DATE
) err
= IE_ISDS
;
3759 isds_printf_message(context
,
3760 _("Could not convert dmDeliveryTime as ISO time: %s"),
3762 free(string_locale
);
3768 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3770 err
= timestring2timeval((xmlChar
*) string
,
3771 &((*envelope
)->dmAcceptanceTime
));
3773 char *string_locale
= _isds_utf82locale(string
);
3774 if (err
== IE_DATE
) err
= IE_ISDS
;
3775 isds_printf_message(context
,
3776 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3778 free(string_locale
);
3785 if (err
) isds_envelope_free(envelope
);
3788 xmlXPathFreeObject(result
);
3793 /* Convert message type attribute of current element into isds_envelope
3795 * TODO: This function can be incorporated into append_status_size_times() as
3796 * they are called always together.
3797 * The envelope is automatically allocated but not reallocated.
3798 * The data are just appended into envelope structure.
3799 * @context is ISDS context
3800 * @envelope is automatically allocated message envelope structure
3801 * @xpath_ctx is XPath context with current node as parent of attribute
3802 * carrying message type
3803 * In case of error @envelope will be freed. */
3804 static isds_error
append_message_type(struct isds_ctx
*context
,
3805 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3806 isds_error err
= IE_SUCCESS
;
3808 if (!context
) return IE_INVALID_CONTEXT
;
3809 if (!envelope
) return IE_INVAL
;
3810 if (!xpath_ctx
) return IE_INVAL
;
3815 *envelope
= calloc(1, sizeof(**envelope
));
3822 zfree((*envelope
)->dmType
);
3826 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3828 if (!(*envelope
)->dmType
) {
3829 /* Use default value */
3830 (*envelope
)->dmType
= strdup("V");
3831 if (!(*envelope
)->dmType
) {
3835 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3836 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3837 isds_printf_message(context
,
3838 _("Message type in dmType attribute is not 1 character long: "
3847 if (err
) isds_envelope_free(envelope
);
3853 /* Convert dmType isds_envelope member into XML attribute and append it to
3855 * @context is ISDS context
3856 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3857 * @dm_envelope is XML element the resulting attribute will be appended to.
3858 * @return error code, in case of error context' message is filled. */
3859 static isds_error
insert_message_type(struct isds_ctx
*context
,
3860 const char *type
, xmlNodePtr dm_envelope
) {
3861 isds_error err
= IE_SUCCESS
;
3862 xmlAttrPtr attribute_node
;
3864 if (!context
) return IE_INVALID_CONTEXT
;
3865 if (!dm_envelope
) return IE_INVAL
;
3867 /* Insert optional message type */
3869 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3870 char *type_locale
= _isds_utf82locale(type
);
3871 isds_printf_message(context
,
3872 _("Message type in envelope is not 1 character long: %s"),
3878 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3884 #endif /* HAVE_LIBCURL */
3887 /* Extract message document into reallocated document structure
3888 * @context is ISDS context
3889 * @document is automatically reallocated message documents structure
3890 * @xpath_ctx is XPath context with current node as isds:dmFile
3891 * In case of error @document will be freed. */
3892 static isds_error
extract_document(struct isds_ctx
*context
,
3893 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3894 isds_error err
= IE_SUCCESS
;
3895 xmlXPathObjectPtr result
= NULL
;
3896 xmlNodePtr file_node
;
3897 char *string
= NULL
;
3899 if (!context
) return IE_INVALID_CONTEXT
;
3900 if (!document
) return IE_INVAL
;
3901 isds_document_free(document
);
3902 if (!xpath_ctx
) return IE_INVAL
;
3903 file_node
= xpath_ctx
->node
;
3905 *document
= calloc(1, sizeof(**document
));
3911 /* Extract document meta data */
3912 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3913 if (context
->normalize_mime_type
) {
3914 const char *normalized_type
=
3915 isds_normalize_mime_type((*document
)->dmMimeType
);
3916 if (NULL
!= normalized_type
&&
3917 normalized_type
!= (*document
)->dmMimeType
) {
3918 char *new_type
= strdup(normalized_type
);
3919 if (NULL
== new_type
) {
3920 isds_printf_message(context
,
3921 _("Not enough memory to normalize document MIME type"));
3925 free((*document
)->dmMimeType
);
3926 (*document
)->dmMimeType
= new_type
;
3930 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3931 err
= string2isds_FileMetaType((xmlChar
*)string
,
3932 &((*document
)->dmFileMetaType
));
3934 char *meta_type_locale
= _isds_utf82locale(string
);
3935 isds_printf_message(context
,
3936 _("Document has invalid dmFileMetaType attribute value: %s"),
3938 free(meta_type_locale
);
3944 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3945 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3946 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3947 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3950 /* Extract document data.
3951 * Base64 encoded blob or XML subtree must be presented. */
3953 /* Check for dmEncodedContent */
3954 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3961 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3962 /* Here we have Base64 blob */
3963 (*document
)->is_xml
= 0;
3965 if (result
->nodesetval
->nodeNr
> 1) {
3966 isds_printf_message(context
,
3967 _("Document has more dmEncodedContent elements"));
3972 xmlXPathFreeObject(result
); result
= NULL
;
3973 EXTRACT_STRING("isds:dmEncodedContent", string
);
3975 /* Decode non-empty document */
3976 if (string
&& string
[0] != '\0') {
3977 (*document
)->data_length
=
3978 _isds_b64decode(string
, &((*document
)->data
));
3979 if ((*document
)->data_length
== (size_t) -1) {
3980 isds_printf_message(context
,
3981 _("Error while Base64-decoding document content"));
3987 /* No Base64 blob, try XML document */
3988 xmlXPathFreeObject(result
); result
= NULL
;
3989 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3996 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3997 /* Here we have XML document */
3998 (*document
)->is_xml
= 1;
4000 if (result
->nodesetval
->nodeNr
> 1) {
4001 isds_printf_message(context
,
4002 _("Document has more dmXMLContent elements"));
4007 /* XXX: We cannot serialize the content simply because:
4008 * - XML document may point out of its scope (e.g. to message
4010 * - isds:dmXMLContent can contain more elements, no element,
4012 * - it's not the XML way
4013 * Thus we provide the only right solution: XML DOM. Let's
4014 * application to cope with this hot potato :) */
4015 (*document
)->xml_node_list
=
4016 result
->nodesetval
->nodeTab
[0]->children
;
4018 /* No base64 blob, nor XML document */
4019 isds_printf_message(context
,
4020 _("Document has no dmEncodedContent, nor dmXMLContent "
4029 if (err
) isds_document_free(document
);
4031 xmlXPathFreeObject(result
);
4032 xpath_ctx
->node
= file_node
;
4038 /* Extract message documents into reallocated list of documents
4039 * @context is ISDS context
4040 * @documents is automatically reallocated message documents list structure
4041 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4042 * In case of error @documents will be freed. */
4043 static isds_error
extract_documents(struct isds_ctx
*context
,
4044 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4045 isds_error err
= IE_SUCCESS
;
4046 xmlXPathObjectPtr result
= NULL
;
4047 xmlNodePtr files_node
;
4048 struct isds_list
*document
, *prev_document
= NULL
;
4050 if (!context
) return IE_INVALID_CONTEXT
;
4051 if (!documents
) return IE_INVAL
;
4052 isds_list_free(documents
);
4053 if (!xpath_ctx
) return IE_INVAL
;
4054 files_node
= xpath_ctx
->node
;
4056 /* Find documents */
4057 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4064 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4065 isds_printf_message(context
,
4066 _("Message does not contain any document"));
4072 /* Iterate over documents */
4073 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4075 /* Allocate and append list item */
4076 document
= calloc(1, sizeof(*document
));
4081 document
->destructor
= (void (*)(void **))isds_document_free
;
4082 if (i
== 0) *documents
= document
;
4083 else prev_document
->next
= document
;
4084 prev_document
= document
;
4086 /* Extract document */
4087 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4088 err
= extract_document(context
,
4089 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4090 if (err
) goto leave
;
4095 if (err
) isds_list_free(documents
);
4096 xmlXPathFreeObject(result
);
4097 xpath_ctx
->node
= files_node
;
4103 /* Convert isds:dmRecord XML tree into structure
4104 * @context is ISDS context
4105 * @envelope is automatically reallocated message envelope structure
4106 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4107 * In case of error @envelope will be freed. */
4108 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4109 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4110 isds_error err
= IE_SUCCESS
;
4111 xmlXPathObjectPtr result
= NULL
;
4113 if (!context
) return IE_INVALID_CONTEXT
;
4114 if (!envelope
) return IE_INVAL
;
4115 isds_envelope_free(envelope
);
4116 if (!xpath_ctx
) return IE_INVAL
;
4119 *envelope
= calloc(1, sizeof(**envelope
));
4126 /* Extract tRecord data */
4127 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4129 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4130 * dmAcceptanceTime. */
4131 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4132 if (err
) goto leave
;
4134 /* Extract envelope elements added by sender and ISDS
4135 * (XSD: gMessageEnvelope type) */
4136 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4137 if (err
) goto leave
;
4139 /* Get message type */
4140 err
= append_message_type(context
, envelope
, xpath_ctx
);
4141 if (err
) goto leave
;
4145 if (err
) isds_envelope_free(envelope
);
4146 xmlXPathFreeObject(result
);
4151 /* Convert XSD:tStateChangesRecord type XML tree into structure
4152 * @context is ISDS context
4153 * @changed_status is automatically reallocated message state change structure
4154 * @xpath_ctx is XPath context with current node as element of
4155 * XSD:tStateChangesRecord type
4156 * In case of error @changed_status will be freed. */
4157 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4158 struct isds_message_status_change
**changed_status
,
4159 xmlXPathContextPtr xpath_ctx
) {
4160 isds_error err
= IE_SUCCESS
;
4161 xmlXPathObjectPtr result
= NULL
;
4162 unsigned long int *unumber
= NULL
;
4163 char *string
= NULL
;
4165 if (!context
) return IE_INVALID_CONTEXT
;
4166 if (!changed_status
) return IE_INVAL
;
4167 isds_message_status_change_free(changed_status
);
4168 if (!xpath_ctx
) return IE_INVAL
;
4171 *changed_status
= calloc(1, sizeof(**changed_status
));
4172 if (!*changed_status
) {
4178 /* Extract tGetStateChangesInput data */
4179 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4181 /* dmEventTime is mandatory */
4182 EXTRACT_STRING("isds:dmEventTime", string
);
4184 err
= timestring2timeval((xmlChar
*) string
,
4185 &((*changed_status
)->time
));
4187 char *string_locale
= _isds_utf82locale(string
);
4188 if (err
== IE_DATE
) err
= IE_ISDS
;
4189 isds_printf_message(context
,
4190 _("Could not convert dmEventTime as ISO time: %s"),
4192 free(string_locale
);
4198 /* dmMessageStatus element is mandatory */
4199 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4201 isds_log_message(context
,
4202 _("Missing mandatory isds:dmMessageStatus integer"));
4206 err
= uint2isds_message_status(context
, unumber
,
4207 &((*changed_status
)->dmMessageStatus
));
4209 if (err
== IE_ENUM
) err
= IE_ISDS
;
4218 if (err
) isds_message_status_change_free(changed_status
);
4219 xmlXPathFreeObject(result
);
4222 #endif /* HAVE_LIBCURL */
4225 /* Find and convert isds:dmHash XML tree into structure
4226 * @context is ISDS context
4227 * @envelope is automatically reallocated message hash structure
4228 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4229 * In case of error @hash will be freed. */
4230 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4231 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4232 isds_error err
= IE_SUCCESS
;
4233 xmlNodePtr old_ctx_node
;
4234 xmlXPathObjectPtr result
= NULL
;
4235 char *string
= NULL
;
4237 if (!context
) return IE_INVALID_CONTEXT
;
4238 if (!hash
) return IE_INVAL
;
4239 isds_hash_free(hash
);
4240 if (!xpath_ctx
) return IE_INVAL
;
4242 old_ctx_node
= xpath_ctx
->node
;
4244 *hash
= calloc(1, sizeof(**hash
));
4251 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4252 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4261 /* Get hash algorithm */
4262 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4263 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4265 if (err
== IE_ENUM
) {
4266 char *string_locale
= _isds_utf82locale(string
);
4267 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4269 free(string_locale
);
4275 /* Get hash value */
4276 EXTRACT_STRING(".", string
);
4278 isds_printf_message(context
,
4279 _("sisds:dmHash element is missing hash value"));
4283 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4284 if ((*hash
)->length
== (size_t) -1) {
4285 isds_printf_message(context
,
4286 _("Error while Base64-decoding hash value"));
4292 if (err
) isds_hash_free(hash
);
4294 xmlXPathFreeObject(result
);
4295 xpath_ctx
->node
= old_ctx_node
;
4300 /* Find and append isds:dmQTimestamp XML tree into envelope.
4301 * Because one service is allowed to miss time-stamp content, and we think
4302 * other could too (flaw in specification), this function is deliberated and
4303 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4304 * @context is ISDS context
4305 * @envelope is automatically allocated envelope structure
4306 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4308 * In case of error @envelope will be freed. */
4309 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4310 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4311 isds_error err
= IE_SUCCESS
;
4312 xmlXPathObjectPtr result
= NULL
;
4313 char *string
= NULL
;
4315 if (!context
) return IE_INVALID_CONTEXT
;
4316 if (!envelope
) return IE_INVAL
;
4318 isds_envelope_free(envelope
);
4323 *envelope
= calloc(1, sizeof(**envelope
));
4329 zfree((*envelope
)->timestamp
);
4330 (*envelope
)->timestamp_length
= 0;
4333 /* Get dmQTimestamp */
4334 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4336 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4339 (*envelope
)->timestamp_length
=
4340 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4341 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4342 isds_printf_message(context
,
4343 _("Error while Base64-decoding time stamp value"));
4349 if (err
) isds_envelope_free(envelope
);
4351 xmlXPathFreeObject(result
);
4356 /* Convert XSD tReturnedMessage XML tree into message structure.
4357 * It does not store serialized XML tree into message->raw.
4358 * It does store (pointer to) parsed XML tree into message->xml if needed.
4359 * @context is ISDS context
4360 * @include_documents Use true if documents must be extracted
4361 * (tReturnedMessage XSD type), use false if documents shall be omitted
4362 * (tReturnedMessageEnvelope).
4363 * @message is automatically reallocated message structure
4364 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4366 * In case of error @message will be freed. */
4367 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4368 const _Bool include_documents
, struct isds_message
**message
,
4369 xmlXPathContextPtr xpath_ctx
) {
4370 isds_error err
= IE_SUCCESS
;
4371 xmlNodePtr message_node
;
4373 if (!context
) return IE_INVALID_CONTEXT
;
4374 if (!message
) return IE_INVAL
;
4375 isds_message_free(message
);
4376 if (!xpath_ctx
) return IE_INVAL
;
4379 *message
= calloc(1, sizeof(**message
));
4385 /* Save message XPATH context node */
4386 message_node
= xpath_ctx
->node
;
4390 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4391 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4392 if (err
) { err
= IE_ERROR
; goto leave
; }
4393 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4394 if (err
) goto leave
;
4396 if (include_documents
) {
4397 struct isds_list
*item
;
4399 /* Extract dmFiles */
4400 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4402 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4403 err
= IE_ISDS
; goto leave
;
4405 if (err
) { err
= IE_ERROR
; goto leave
; }
4406 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4407 if (err
) goto leave
;
4409 /* Store xmlDoc of this message if needed */
4410 /* Only if we got a XML document in all the documents. */
4411 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4412 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4413 (*message
)->xml
= xpath_ctx
->doc
;
4420 /* Restore context to message */
4421 xpath_ctx
->node
= message_node
;
4423 /* Extract dmHash */
4424 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4426 if (err
) goto leave
;
4428 /* Extract dmQTimestamp, */
4429 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4431 if (err
) goto leave
;
4433 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4434 * dmAcceptanceTime. */
4435 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4436 if (err
) goto leave
;
4438 /* Get message type */
4439 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4440 if (err
) goto leave
;
4443 if (err
) isds_message_free(message
);
4448 /* Extract message event into reallocated isds_event structure
4449 * @context is ISDS context
4450 * @event is automatically reallocated message event structure
4451 * @xpath_ctx is XPath context with current node as isds:dmEvent
4452 * In case of error @event will be freed. */
4453 static isds_error
extract_event(struct isds_ctx
*context
,
4454 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4455 isds_error err
= IE_SUCCESS
;
4456 xmlXPathObjectPtr result
= NULL
;
4457 xmlNodePtr event_node
;
4458 char *string
= NULL
;
4460 if (!context
) return IE_INVALID_CONTEXT
;
4461 if (!event
) return IE_INVAL
;
4462 isds_event_free(event
);
4463 if (!xpath_ctx
) return IE_INVAL
;
4464 event_node
= xpath_ctx
->node
;
4466 *event
= calloc(1, sizeof(**event
));
4472 /* Extract event data.
4473 * All elements are optional according XSD. That's funny. */
4474 EXTRACT_STRING("sisds:dmEventTime", string
);
4476 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4478 char *string_locale
= _isds_utf82locale(string
);
4479 if (err
== IE_DATE
) err
= IE_ISDS
;
4480 isds_printf_message(context
,
4481 _("Could not convert dmEventTime as ISO time: %s"),
4483 free(string_locale
);
4489 /* dmEventDescr element has prefix and the rest */
4490 EXTRACT_STRING("sisds:dmEventDescr", string
);
4492 err
= eventstring2event((xmlChar
*) string
, *event
);
4493 if (err
) goto leave
;
4498 if (err
) isds_event_free(event
);
4500 xmlXPathFreeObject(result
);
4501 xpath_ctx
->node
= event_node
;
4506 /* Convert element of XSD tEventsArray type from XML tree into
4507 * isds_list of isds_event's structure. The list is automatically reallocated.
4508 * @context is ISDS context
4509 * @events is automatically reallocated list of event structures
4510 * @xpath_ctx is XPath context with current node as tEventsArray
4511 * In case of error @events will be freed. */
4512 static isds_error
extract_events(struct isds_ctx
*context
,
4513 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4514 isds_error err
= IE_SUCCESS
;
4515 xmlXPathObjectPtr result
= NULL
;
4516 xmlNodePtr events_node
;
4517 struct isds_list
*event
, *prev_event
= NULL
;
4519 if (!context
) return IE_INVALID_CONTEXT
;
4520 if (!events
) return IE_INVAL
;
4521 if (!xpath_ctx
) return IE_INVAL
;
4522 events_node
= xpath_ctx
->node
;
4525 isds_list_free(events
);
4528 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4535 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4536 isds_printf_message(context
,
4537 _("Delivery info does not contain any event"));
4543 /* Iterate over events */
4544 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4546 /* Allocate and append list item */
4547 event
= calloc(1, sizeof(*event
));
4552 event
->destructor
= (void (*)(void **))isds_event_free
;
4553 if (i
== 0) *events
= event
;
4554 else prev_event
->next
= event
;
4558 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4559 err
= extract_event(context
,
4560 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4561 if (err
) goto leave
;
4566 if (err
) isds_list_free(events
);
4567 xmlXPathFreeObject(result
);
4568 xpath_ctx
->node
= events_node
;
4574 /* Insert Base64 encoded data as element with text child.
4575 * @context is session context
4576 * @parent is XML node to append @element with @data as child
4577 * @ns is XML namespace of @element, use NULL to inherit from @parent
4578 * @element is UTF-8 encoded name of new element
4579 * @data is bit stream to encode into @element
4580 * @length is size of @data in bytes
4581 * @return standard error code and fill long error message if needed */
4582 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4583 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4584 const void *data
, size_t length
) {
4585 isds_error err
= IE_SUCCESS
;
4588 if (!context
) return IE_INVALID_CONTEXT
;
4589 if (!data
&& length
> 0) return IE_INVAL
;
4590 if (!parent
|| !element
) return IE_INVAL
;
4592 xmlChar
*base64data
= NULL
;
4593 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4595 isds_printf_message(context
,
4596 ngettext("Not enough memory to encode %zd byte into Base64",
4597 "Not enough memory to encode %zd bytes into Base64",
4603 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4611 /* Convert isds_document structure into XML tree and append to dmFiles node.
4612 * @context is session context
4613 * @document is ISDS document
4614 * @dm_files is XML element the resulting tree will be appended to as a child.
4615 * @return error code, in case of error context' message is filled. */
4616 static isds_error
insert_document(struct isds_ctx
*context
,
4617 struct isds_document
*document
, xmlNodePtr dm_files
) {
4618 isds_error err
= IE_SUCCESS
;
4619 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4620 xmlAttrPtr attribute_node
;
4622 if (!context
) return IE_INVALID_CONTEXT
;
4623 if (!document
|| !dm_files
) return IE_INVAL
;
4625 /* Allocate new dmFile */
4626 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4628 isds_printf_message(context
, _("Could not allocate main dmFile"));
4632 /* Append the new dmFile.
4633 * XXX: Main document must go first */
4634 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4635 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4637 file
= xmlAddChild(dm_files
, new_file
);
4640 xmlFreeNode(new_file
); new_file
= NULL
;
4641 isds_printf_message(context
, _("Could not add dmFile child to "
4642 "%s element"), dm_files
->name
);
4647 /* @dmMimeType is required */
4648 if (!document
->dmMimeType
) {
4649 isds_log_message(context
,
4650 _("Document is missing mandatory MIME type definition"));
4654 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4656 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4658 isds_printf_message(context
,
4659 _("Document has unknown dmFileMetaType: %ld"),
4660 document
->dmFileMetaType
);
4664 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4666 if (document
->dmFileGuid
) {
4667 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4669 if (document
->dmUpFileGuid
) {
4670 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4673 /* @dmFileDescr is required */
4674 if (!document
->dmFileDescr
) {
4675 isds_log_message(context
,
4676 _("Document is missing mandatory description (title)"));
4680 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4682 if (document
->dmFormat
) {
4683 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4687 /* Insert content (body) of the document. */
4688 if (document
->is_xml
) {
4689 /* XML document requested */
4691 /* Allocate new dmXMLContent */
4692 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4694 isds_printf_message(context
,
4695 _("Could not allocate dmXMLContent element"));
4700 node
= xmlAddChild(file
, xmlcontent
);
4702 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4703 isds_printf_message(context
,
4704 _("Could not add dmXMLContent child to %s element"),
4710 /* Copy non-empty node list */
4711 if (document
->xml_node_list
) {
4712 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4713 document
->xml_node_list
);
4715 isds_printf_message(context
,
4716 _("Not enough memory to copy XML document"));
4721 if (!xmlAddChildList(node
, content
)) {
4722 xmlFreeNodeList(content
);
4723 isds_printf_message(context
,
4724 _("Error while adding XML document into dmXMLContent"));
4728 /* XXX: We cannot free the content here because it's part of node's
4729 * document since now. It will be freed with it automatically. */
4732 /* Binary document requested */
4733 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4734 document
->data
, document
->data_length
);
4735 if (err
) goto leave
;
4743 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4744 * The copy must be preallocated, the date are just appended into structure.
4745 * @context is ISDS context
4746 * @copy is message copy structure
4747 * @xpath_ctx is XPath context with current node as tMStatus */
4748 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4749 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4750 isds_error err
= IE_SUCCESS
;
4751 xmlXPathObjectPtr result
= NULL
;
4752 char *code
= NULL
, *message
= NULL
;
4754 if (!context
) return IE_INVALID_CONTEXT
;
4755 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4757 /* Free old values */
4758 zfree(copy
->dmStatus
);
4761 /* Get error specific to this copy */
4762 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4764 isds_log_message(context
,
4765 _("Missing isds:dmStatusCode under "
4766 "XSD:tMStatus type element"));
4771 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4772 /* This copy failed */
4773 copy
->error
= IE_ISDS
;
4774 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4776 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4777 if (!copy
->dmStatus
) {
4778 copy
->dmStatus
= code
;
4782 copy
->dmStatus
= code
;
4786 /* This copy succeeded. In this case only, message ID is valid */
4787 copy
->error
= IE_SUCCESS
;
4789 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4791 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4792 "but did not returned assigned message ID\n"));
4800 xmlXPathFreeObject(result
);
4805 /* Insert struct isds_approval data (box approval) into XML tree
4806 * @context is session context
4807 * @approval is libisds structure with approval description. NULL is
4809 * @parent is XML element to append @approval to */
4810 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4811 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4813 isds_error err
= IE_SUCCESS
;
4816 if (!context
) return IE_INVALID_CONTEXT
;
4817 if (!parent
) return IE_INVAL
;
4819 if (!approval
) return IE_SUCCESS
;
4821 /* Build XSD:gExtApproval */
4822 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4823 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4830 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4832 * @context is session context
4833 * @service_name is name of SERVICE_DB_ACCESS
4834 * @response is reallocated server SOAP body response as XML document
4835 * @raw_response is reallocated bit stream with response body. Use
4836 * NULL if you don't care
4837 * @raw_response_length is size of @raw_response in bytes
4838 * @code is reallocated ISDS status code
4839 * @status_message is reallocated ISDS status message
4840 * @return error coded from lower layer, context message will be set up
4842 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4843 const xmlChar
*service_name
,
4844 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4845 xmlChar
**code
, xmlChar
**status_message
) {
4847 isds_error err
= IE_SUCCESS
;
4848 char *service_name_locale
= NULL
;
4849 xmlNodePtr request
= NULL
, node
;
4850 xmlNsPtr isds_ns
= NULL
;
4852 if (!context
) return IE_INVALID_CONTEXT
;
4853 if (!service_name
) return IE_INVAL
;
4854 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4855 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4857 /* Free output argument */
4858 xmlFreeDoc(*response
); *response
= NULL
;
4859 if (raw_response
) zfree(*raw_response
);
4861 zfree(*status_message
);
4864 /* Check if connection is established
4865 * TODO: This check should be done downstairs. */
4866 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4868 service_name_locale
= _isds_utf82locale((char*)service_name
);
4869 if (!service_name_locale
) {
4875 request
= xmlNewNode(NULL
, service_name
);
4877 isds_printf_message(context
,
4878 _("Could not build %s request"), service_name_locale
);
4882 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4884 isds_log_message(context
, _("Could not create ISDS name space"));
4888 xmlSetNs(request
, isds_ns
);
4891 /* Add XSD:tDummyInput child */
4892 INSERT_STRING(request
, "dbDummy", NULL
);
4895 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4896 service_name_locale
);
4899 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4900 raw_response
, raw_response_length
);
4901 xmlFreeNode(request
); request
= NULL
;
4904 isds_log(ILF_ISDS
, ILL_DEBUG
,
4905 _("Processing ISDS response on %s request failed\n"),
4906 service_name_locale
);
4910 /* Check for response status */
4911 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4912 code
, status_message
, NULL
);
4914 isds_log(ILF_ISDS
, ILL_DEBUG
,
4915 _("ISDS response on %s request is missing status\n"),
4916 service_name_locale
);
4920 /* Request processed, but nothing found */
4921 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4922 char *code_locale
= _isds_utf82locale((char*) *code
);
4923 char *status_message_locale
=
4924 _isds_utf82locale((char*) *status_message
);
4925 isds_log(ILF_ISDS
, ILL_DEBUG
,
4926 _("Server refused %s request (code=%s, message=%s)\n"),
4927 service_name_locale
, code_locale
, status_message_locale
);
4928 isds_log_message(context
, status_message_locale
);
4930 free(status_message_locale
);
4936 free(service_name_locale
);
4937 xmlFreeNode(request
);
4943 /* Get data about logged in user and his box. */
4944 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4945 struct isds_DbOwnerInfo
**db_owner_info
) {
4946 isds_error err
= IE_SUCCESS
;
4948 xmlDocPtr response
= NULL
;
4949 xmlChar
*code
= NULL
, *message
= NULL
;
4950 xmlXPathContextPtr xpath_ctx
= NULL
;
4951 xmlXPathObjectPtr result
= NULL
;
4952 char *string
= NULL
;
4955 if (!context
) return IE_INVALID_CONTEXT
;
4956 zfree(context
->long_message
);
4957 if (!db_owner_info
) return IE_INVAL
;
4958 isds_DbOwnerInfo_free(db_owner_info
);
4961 /* Check if connection is established */
4962 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4965 /* Do request and check for success */
4966 err
= build_send_check_dbdummy_request(context
,
4967 BAD_CAST
"GetOwnerInfoFromLogin",
4968 &response
, NULL
, NULL
, &code
, &message
);
4969 if (err
) goto leave
;
4973 /* Prepare structure */
4974 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4975 if (!*db_owner_info
) {
4979 xpath_ctx
= xmlXPathNewContext(response
);
4984 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4989 /* Set context node */
4990 result
= xmlXPathEvalExpression(BAD_CAST
4991 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4996 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4997 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5001 if (result
->nodesetval
->nodeNr
> 1) {
5002 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5006 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5007 xmlXPathFreeObject(result
); result
= NULL
;
5010 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5015 isds_DbOwnerInfo_free(db_owner_info
);
5019 xmlXPathFreeObject(result
);
5020 xmlXPathFreeContext(xpath_ctx
);
5024 xmlFreeDoc(response
);
5027 isds_log(ILF_ISDS
, ILL_DEBUG
,
5028 _("GetOwnerInfoFromLogin request processed by server "
5029 "successfully.\n"));
5030 #else /* not HAVE_LIBCURL */
5038 /* Get data about logged in user. */
5039 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5040 struct isds_DbUserInfo
**db_user_info
) {
5041 isds_error err
= IE_SUCCESS
;
5043 xmlDocPtr response
= NULL
;
5044 xmlChar
*code
= NULL
, *message
= NULL
;
5045 xmlXPathContextPtr xpath_ctx
= NULL
;
5046 xmlXPathObjectPtr result
= NULL
;
5049 if (!context
) return IE_INVALID_CONTEXT
;
5050 zfree(context
->long_message
);
5051 if (!db_user_info
) return IE_INVAL
;
5052 isds_DbUserInfo_free(db_user_info
);
5055 /* Check if connection is established */
5056 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5059 /* Do request and check for success */
5060 err
= build_send_check_dbdummy_request(context
,
5061 BAD_CAST
"GetUserInfoFromLogin",
5062 &response
, NULL
, NULL
, &code
, &message
);
5063 if (err
) goto leave
;
5067 /* Prepare structure */
5068 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5069 if (!*db_user_info
) {
5073 xpath_ctx
= xmlXPathNewContext(response
);
5078 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5083 /* Set context node */
5084 result
= xmlXPathEvalExpression(BAD_CAST
5085 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5090 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5091 isds_log_message(context
, _("Missing dbUserInfo element"));
5095 if (result
->nodesetval
->nodeNr
> 1) {
5096 isds_log_message(context
, _("Multiple dbUserInfo element"));
5100 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5101 xmlXPathFreeObject(result
); result
= NULL
;
5104 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5108 isds_DbUserInfo_free(db_user_info
);
5111 xmlXPathFreeObject(result
);
5112 xmlXPathFreeContext(xpath_ctx
);
5116 xmlFreeDoc(response
);
5119 isds_log(ILF_ISDS
, ILL_DEBUG
,
5120 _("GetUserInfoFromLogin request processed by server "
5121 "successfully.\n"));
5122 #else /* not HAVE_LIBCURL */
5130 /* Get expiration time of current password
5131 * @context is session context
5132 * @expiration is automatically reallocated time when password expires. If
5133 * password expiration is disabled, NULL will be returned. In case of error
5134 * it will be nulled too. */
5135 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5136 struct timeval
**expiration
) {
5137 isds_error err
= IE_SUCCESS
;
5139 xmlDocPtr response
= NULL
;
5140 xmlChar
*code
= NULL
, *message
= NULL
;
5141 xmlXPathContextPtr xpath_ctx
= NULL
;
5142 xmlXPathObjectPtr result
= NULL
;
5143 char *string
= NULL
;
5146 if (!context
) return IE_INVALID_CONTEXT
;
5147 zfree(context
->long_message
);
5148 if (!expiration
) return IE_INVAL
;
5152 /* Check if connection is established */
5153 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5156 /* Do request and check for success */
5157 err
= build_send_check_dbdummy_request(context
,
5158 BAD_CAST
"GetPasswordInfo",
5159 &response
, NULL
, NULL
, &code
, &message
);
5160 if (err
) goto leave
;
5164 xpath_ctx
= xmlXPathNewContext(response
);
5169 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5174 /* Set context node */
5175 result
= xmlXPathEvalExpression(BAD_CAST
5176 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5181 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5182 isds_log_message(context
,
5183 _("Missing GetPasswordInfoResponse element"));
5187 if (result
->nodesetval
->nodeNr
> 1) {
5188 isds_log_message(context
,
5189 _("Multiple GetPasswordInfoResponse element"));
5193 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5194 xmlXPathFreeObject(result
); result
= NULL
;
5196 /* Extract expiration date */
5197 EXTRACT_STRING("isds:pswExpDate", string
);
5199 /* And convert it if any returned. Otherwise expiration is disabled. */
5200 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5202 char *string_locale
= _isds_utf82locale(string
);
5203 if (err
== IE_DATE
) err
= IE_ISDS
;
5204 isds_printf_message(context
,
5205 _("Could not convert pswExpDate as ISO time: %s"),
5207 free(string_locale
);
5220 xmlXPathFreeObject(result
);
5221 xmlXPathFreeContext(xpath_ctx
);
5225 xmlFreeDoc(response
);
5228 isds_log(ILF_ISDS
, ILL_DEBUG
,
5229 _("GetPasswordInfo request processed by server "
5230 "successfully.\n"));
5231 #else /* not HAVE_LIBCURL */
5240 /* Request delivering new TOTP code from ISDS through side channel before
5241 * changing password.
5242 * @context is session context
5243 * @password is current password.
5244 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5245 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5246 * function for more details.
5247 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5248 * NULL, if you don't care.
5249 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5251 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5252 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5253 isds_error err
= IE_SUCCESS
;
5254 char *saved_url
= NULL
; /* No copy */
5255 #if HAVE_CURL_REAUTHORIZATION_BUG
5256 CURL
*saved_curl
= NULL
; /* No copy */
5258 xmlNsPtr isds_ns
= NULL
;
5259 xmlNodePtr request
= NULL
;
5260 xmlDocPtr response
= NULL
;
5261 xmlChar
*code
= NULL
, *message
= NULL
;
5262 const xmlChar
*codes
[] = {
5267 const char *meanings
[] = {
5268 N_("Unexpected error"),
5269 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5270 N_("One-time code could not been sent. Try later again.")
5272 const isds_otp_resolution resolutions
[] = {
5273 OTP_RESOLUTION_UNKNOWN
,
5274 OTP_RESOLUTION_TO_FAST
,
5275 OTP_RESOLUTION_TOTP_NOT_SENT
5278 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5279 zfree(context
->long_message
);
5280 if (NULL
== password
) {
5281 isds_log_message(context
,
5282 _("Second argument (password) of isds_change_password() "
5287 /* Check if connection is established
5288 * TODO: This check should be done downstairs. */
5289 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5291 if (!context
->otp
) {
5292 isds_log_message(context
, _("This function requires OTP-authenticated "
5294 return IE_INVALID_CONTEXT
;
5297 isds_log_message(context
, _("If one-time password authentication "
5298 "method is in use, requesting new OTP code requires "
5299 "one-time credentials argument either"));
5302 if (otp
->method
!= OTP_TIME
) {
5303 isds_log_message(context
, _("Requesting new time-based OTP code from "
5304 "server requires one-time password authentication "
5308 if (otp
->otp_code
!= NULL
) {
5309 isds_log_message(context
, _("Requesting new time-based OTP code from "
5310 "server requires undefined OTP code member in "
5311 "one-time credentials argument"));
5317 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5319 isds_log_message(context
, _("Could not build SendSMSCode request"));
5322 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5324 isds_log_message(context
, _("Could not create ISDS name space"));
5325 xmlFreeNode(request
);
5328 xmlSetNs(request
, isds_ns
);
5330 /* Change URL temporarily for sending this request only */
5332 char *new_url
= NULL
;
5333 if ((err
= _isds_build_url_from_context(context
,
5334 "%1$.*2$sasws/changePassword", &new_url
))) {
5337 saved_url
= context
->url
;
5338 context
->url
= new_url
;
5341 /* Store credentials for sending this request only */
5342 context
->otp_credentials
= otp
;
5343 _isds_discard_credentials(context
, 0);
5344 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5346 _isds_discard_credentials(context
, 0);
5349 #if HAVE_CURL_REAUTHORIZATION_BUG
5350 saved_curl
= context
->curl
;
5351 context
->curl
= curl_easy_init();
5352 if (NULL
== context
->curl
) {
5356 if (context
->timeout
) {
5357 err
= isds_set_timeout(context
, context
->timeout
);
5358 if (err
) goto leave
;
5362 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5365 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5367 /* Remove temporal credentials */
5368 _isds_discard_credentials(context
, 0);
5369 /* Detach pointer to OTP credentials from context */
5370 context
->otp_credentials
= NULL
;
5371 /* Keep context->otp true to keep signaling this is OTP session */
5373 /* Destroy request */
5374 xmlFreeNode(request
); request
= NULL
;
5377 isds_log(ILF_ISDS
, ILL_DEBUG
,
5378 _("Processing ISDS response on SendSMSCode request failed\n"));
5382 /* Check for response status */
5383 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5384 &code
, &message
, (xmlChar
**)refnumber
);
5386 isds_log(ILF_ISDS
, ILL_DEBUG
,
5387 _("ISDS response on SendSMSCode request is missing "
5392 /* Check for error */
5393 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5394 char *code_locale
= _isds_utf82locale((char*)code
);
5395 char *message_locale
= _isds_utf82locale((char*)message
);
5397 isds_log(ILF_ISDS
, ILL_DEBUG
,
5398 _("Server refused to send new code on SendSMSCode "
5399 "request (code=%s, message=%s)\n"),
5400 code_locale
, message_locale
);
5402 /* Check for known error codes */
5403 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5404 if (!xmlStrcmp(code
, codes
[i
])) break;
5406 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5407 isds_log_message(context
, _(meanings
[i
]));
5408 /* Mimic otp->resolution according to the code, specification does
5409 * prescribe OTP header to be available. */
5410 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5411 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5412 otp
->resolution
= resolutions
[i
];
5414 isds_log_message(context
, message_locale
);
5417 free(message_locale
);
5423 /* Otherwise new code sent successfully */
5424 /* Mimic otp->resolution according to the code, specification does
5425 * prescribe OTP header to be available. */
5426 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5427 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5430 if (NULL
!= saved_url
) {
5431 /* Revert URL to original one */
5432 zfree(context
->url
);
5433 context
->url
= saved_url
;
5435 #if HAVE_CURL_REAUTHORIZATION_BUG
5436 if (NULL
!= saved_curl
) {
5437 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5438 context
->curl
= saved_curl
;
5444 xmlFreeDoc(response
);
5445 xmlFreeNode(request
);
5448 isds_log(ILF_ISDS
, ILL_DEBUG
,
5449 _("New OTP code has been sent successfully on SendSMSCode "
5455 /* Convert response status code to isds_error code and set long message
5456 * @context is context to save long message to
5457 * @map is mapping from codes to errors and messages. Pass NULL for generic
5459 * @code is status code to translate
5460 * @message is non-localized status message to put into long message in case
5461 * of uknown error. It can be NULL if server did not provide any.
5462 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5463 * invalid invocation. */
5464 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5465 const struct code_map_isds_error
*map
,
5466 const xmlChar
*code
, const xmlChar
*message
) {
5468 isds_log_message(context
,
5469 _("NULL status code passed to statuscode2isds_error()"));
5474 /* Check for known error codes */
5475 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5476 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5477 isds_log_message(context
, _(map
->meanings
[i
]));
5478 return map
->errors
[i
];
5484 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5485 char *message_locale
= _isds_utf82locale((char*)message
);
5486 if (NULL
== message_locale
)
5487 isds_log_message(context
, _("ISDS server returned unknown error"));
5489 isds_log_message(context
, message_locale
);
5490 free(message_locale
);
5499 /* Change user password in ISDS.
5500 * User must supply old password, new password will takes effect after some
5501 * time, current session can continue. Password must fulfill some constraints.
5502 * @context is session context
5503 * @old_password is current password.
5504 * @new_password is requested new password
5505 * @otp auxiliary data required if one-time password authentication is in use,
5506 * defines OTP code (if known) and returns fine grade resolution of OTP
5507 * procedure. Pass NULL, if one-time password authentication is not needed.
5508 * Please note the @otp argument must match OTP method used at log-in time. See
5509 * isds_login() function for more details.
5510 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5511 * NULL, if you don't care.
5512 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5513 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5514 * awaiting OTP code that has been delivered by side channel to the user. */
5515 isds_error
isds_change_password(struct isds_ctx
*context
,
5516 const char *old_password
, const char *new_password
,
5517 struct isds_otp
*otp
, char **refnumber
) {
5518 isds_error err
= IE_SUCCESS
;
5520 char *saved_url
= NULL
; /* No copy */
5521 #if HAVE_CURL_REAUTHORIZATION_BUG
5522 CURL
*saved_curl
= NULL
; /* No copy */
5524 xmlNsPtr isds_ns
= NULL
;
5525 xmlNodePtr request
= NULL
, node
;
5526 xmlDocPtr response
= NULL
;
5527 xmlChar
*code
= NULL
, *message
= NULL
;
5528 const xmlChar
*codes
[] = {
5541 const char *meanings
[] = {
5542 N_("Password length must be between 8 and 32 characters"),
5543 N_("Password cannot be reused"), /* Server does not distinguish 1067
5544 and 1091 on ChangePasswordOTP */
5545 N_("Password contains forbidden character"),
5546 N_("Password must contain at least one upper-case letter, "
5547 "one lower-case, and one digit"),
5548 N_("Password cannot contain sequence of three identical characters"),
5549 N_("Password cannot contain user identifier"),
5550 N_("Password is too simmple"),
5551 N_("Old password is not valid"),
5552 N_("Password cannot be reused"),
5553 N_("Unexpected error"),
5554 N_("LDAP update error")
5558 if (!context
) return IE_INVALID_CONTEXT
;
5559 zfree(context
->long_message
);
5560 if (NULL
!= refnumber
)
5562 if (NULL
== old_password
) {
5563 isds_log_message(context
,
5564 _("Second argument (old password) of isds_change_password() "
5568 if (NULL
== otp
&& NULL
== new_password
) {
5569 isds_log_message(context
,
5570 _("Third argument (new password) of isds_change_password() "
5576 /* Check if connection is established
5577 * TODO: This check should be done downstairs. */
5578 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5580 if (context
->otp
&& NULL
== otp
) {
5581 isds_log_message(context
, _("If one-time password authentication "
5582 "method is in use, changing password requires one-time "
5583 "credentials either"));
5587 /* Build ChangeISDSPassword request */
5588 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5589 BAD_CAST
"ChangePasswordOTP");
5591 isds_log_message(context
, (NULL
== otp
) ?
5592 _("Could not build ChangeISDSPassword request") :
5593 _("Could not build ChangePasswordOTP request"));
5596 isds_ns
= xmlNewNs(request
,
5597 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5600 isds_log_message(context
, _("Could not create ISDS name space"));
5601 xmlFreeNode(request
);
5604 xmlSetNs(request
, isds_ns
);
5606 INSERT_STRING(request
, "dbOldPassword", old_password
);
5607 INSERT_STRING(request
, "dbNewPassword", new_password
);
5610 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5611 switch (otp
->method
) {
5613 isds_log(ILF_SEC
, ILL_INFO
,
5614 _("Selected authentication method: "
5615 "HMAC-based one-time password\n"));
5616 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5619 isds_log(ILF_SEC
, ILL_INFO
,
5620 _("Selected authentication method: "
5621 "Time-based one-time password\n"));
5622 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5623 if (otp
->otp_code
== NULL
) {
5624 isds_log(ILF_SEC
, ILL_INFO
,
5625 _("OTP code has not been provided by "
5626 "application, requesting server for "
5628 err
= _isds_request_totp_code(context
, old_password
, otp
,
5630 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5634 isds_log(ILF_SEC
, ILL_INFO
,
5635 _("OTP code has been provided by "
5636 "application, not requesting server "
5641 isds_log_message(context
,
5642 _("Unknown one-time password authentication "
5643 "method requested by application"));
5648 /* Change URL temporarily for sending this request only */
5650 char *new_url
= NULL
;
5651 if ((err
= _isds_build_url_from_context(context
,
5652 "%1$.*2$sasws/changePassword", &new_url
))) {
5655 saved_url
= context
->url
;
5656 context
->url
= new_url
;
5659 /* Store credentials for sending this request only */
5660 context
->otp_credentials
= otp
;
5661 _isds_discard_credentials(context
, 0);
5662 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5663 old_password
, NULL
))) {
5664 _isds_discard_credentials(context
, 0);
5667 #if HAVE_CURL_REAUTHORIZATION_BUG
5668 saved_curl
= context
->curl
;
5669 context
->curl
= curl_easy_init();
5670 if (NULL
== context
->curl
) {
5674 if (context
->timeout
) {
5675 err
= isds_set_timeout(context
, context
->timeout
);
5676 if (err
) goto leave
;
5681 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5682 _("Sending ChangeISDSPassword request to ISDS\n") :
5683 _("Sending ChangePasswordOTP request to ISDS\n"));
5686 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5687 request
, &response
, NULL
, NULL
);
5690 /* Remove temporal credentials */
5691 _isds_discard_credentials(context
, 0);
5692 /* Detach pointer to OTP credentials from context */
5693 context
->otp_credentials
= NULL
;
5694 /* Keep context->otp true to keep signaling this is OTP session */
5697 /* Destroy request */
5698 xmlFreeNode(request
); request
= NULL
;
5701 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5702 _("Processing ISDS response on ChangeISDSPassword "
5703 "request failed\n") :
5704 _("Processing ISDS response on ChangePasswordOTP "
5705 "request failed\n"));
5709 /* Check for response status */
5710 err
= isds_response_status(context
,
5711 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5712 &code
, &message
, (xmlChar
**)refnumber
);
5714 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5715 _("ISDS response on ChangeISDSPassword request is missing "
5717 _("ISDS response on ChangePasswordOTP request is missing "
5722 /* Check for known error codes */
5723 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5724 if (!xmlStrcmp(code
, codes
[i
])) {
5725 char *code_locale
= _isds_utf82locale((char*)code
);
5726 char *message_locale
= _isds_utf82locale((char*)message
);
5727 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5728 _("Server refused to change password on ChangeISDSPassword "
5729 "request (code=%s, message=%s)\n") :
5730 _("Server refused to change password on ChangePasswordOTP "
5731 "request (code=%s, message=%s)\n"),
5732 code_locale
, message_locale
);
5734 free(message_locale
);
5735 isds_log_message(context
, _(meanings
[i
]));
5742 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5743 char *code_locale
= _isds_utf82locale((char*)code
);
5744 char *message_locale
= _isds_utf82locale((char*)message
);
5745 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5746 _("Server refused to change password on ChangeISDSPassword "
5747 "request (code=%s, message=%s)\n") :
5748 _("Server refused to change password on ChangePasswordOTP "
5749 "request (code=%s, message=%s)\n"),
5750 code_locale
, message_locale
);
5751 isds_log_message(context
, message_locale
);
5753 free(message_locale
);
5758 /* Otherwise password changed successfully */
5761 if (NULL
!= saved_url
) {
5762 /* Revert URL to original one */
5763 zfree(context
->url
);
5764 context
->url
= saved_url
;
5766 #if HAVE_CURL_REAUTHORIZATION_BUG
5767 if (NULL
!= saved_curl
) {
5768 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5769 context
->curl
= saved_curl
;
5775 xmlFreeDoc(response
);
5776 xmlFreeNode(request
);
5779 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5780 _("Password changed successfully on ChangeISDSPassword "
5782 _("Password changed successfully on ChangePasswordOTP "
5784 #else /* not HAVE_LIBCURL */
5793 /* Generic middle part with request sending and response check.
5794 * It sends prepared request and checks for error code.
5795 * @context is ISDS session context.
5796 * @service is ISDS service handler
5797 * @service_name is name in scope of given @service
5798 * @request is XML tree with request. Will be freed to save memory.
5799 * @response is XML document outputting ISDS response.
5800 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5801 * @map is mapping from status code to library error. Pass NULL if no special
5802 * handling is requested.
5803 * NULL, if you don't care. */
5804 static isds_error
send_destroy_request_check_response(
5805 struct isds_ctx
*context
,
5806 const isds_service service
, const xmlChar
*service_name
,
5807 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5808 const struct code_map_isds_error
*map
) {
5809 isds_error err
= IE_SUCCESS
;
5810 char *service_name_locale
= NULL
;
5811 xmlChar
*code
= NULL
, *message
= NULL
;
5814 if (!context
) return IE_INVALID_CONTEXT
;
5815 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5819 /* Check if connection is established
5820 * TODO: This check should be done downstairs. */
5821 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5823 service_name_locale
= _isds_utf82locale((char*) service_name
);
5824 if (!service_name_locale
) {
5829 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5830 service_name_locale
);
5833 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5834 xmlFreeNode(*request
); *request
= NULL
;
5837 isds_log(ILF_ISDS
, ILL_DEBUG
,
5838 _("Processing ISDS response on %s request failed\n"),
5839 service_name_locale
);
5843 /* Check for response status */
5844 err
= isds_response_status(context
, service
, *response
,
5845 &code
, &message
, refnumber
);
5847 isds_log(ILF_ISDS
, ILL_DEBUG
,
5848 _("ISDS response on %s request is missing status\n"),
5849 service_name_locale
);
5853 err
= statuscode2isds_error(context
, map
, code
, message
);
5855 /* Request processed, but server failed */
5856 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5857 char *code_locale
= _isds_utf82locale((char*) code
);
5858 char *message_locale
= _isds_utf82locale((char*) message
);
5859 isds_log(ILF_ISDS
, ILL_DEBUG
,
5860 _("Server refused %s request (code=%s, message=%s)\n"),
5861 service_name_locale
, code_locale
, message_locale
);
5863 free(message_locale
);
5871 if (err
&& *response
) {
5872 xmlFreeDoc(*response
);
5876 xmlFreeNode(*request
);
5879 free(service_name_locale
);
5885 /* Generic bottom half with request sending.
5886 * It sends prepared request, checks for error code, destroys response and
5887 * request and log success or failure.
5888 * @context is ISDS session context.
5889 * @service is ISDS service handler
5890 * @service_name is name in scope of given @service
5891 * @request is XML tree with request. Will be freed to save memory.
5892 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5893 * NULL, if you don't care. */
5894 static isds_error
send_request_check_drop_response(
5895 struct isds_ctx
*context
,
5896 const isds_service service
, const xmlChar
*service_name
,
5897 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5898 isds_error err
= IE_SUCCESS
;
5899 xmlDocPtr response
= NULL
;
5902 if (!context
) return IE_INVALID_CONTEXT
;
5903 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5906 /* Send request and check response*/
5907 err
= send_destroy_request_check_response(context
,
5908 service
, service_name
, request
, &response
, refnumber
, NULL
);
5910 xmlFreeDoc(response
);
5913 xmlFreeNode(*request
);
5918 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5919 isds_log(ILF_ISDS
, ILL_DEBUG
,
5920 _("%s request processed by server successfully.\n"),
5921 service_name_locale
);
5922 free(service_name_locale
);
5929 /* Insert isds_credentials_delivery structure into XML request if not NULL
5930 * @context is session context
5931 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5932 * credentials delivery. The email field is passed.
5933 * @parent is XML element where to insert */
5934 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5935 const struct isds_credentials_delivery
*credentials_delivery
,
5936 xmlNodePtr parent
) {
5937 isds_error err
= IE_SUCCESS
;
5940 if (!context
) return IE_INVALID_CONTEXT
;
5941 if (!parent
) return IE_INVAL
;
5943 if (credentials_delivery
) {
5944 /* Following elements are valid only for services:
5945 * NewAccessData, AddDataBoxUser, CreateDataBox */
5946 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5947 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5955 /* Extract credentials delivery from ISDS response.
5956 * @context is session context
5957 * @credentials_delivery is pointer to valid structure to fill in returned
5958 * user's password (and new log-in name). If NULL, do not extract the data.
5959 * @response is pointer to XML document with ISDS response
5960 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5961 * @return IE_SUCCESS even if new user name has not been found because it's not
5962 * clear whether it's returned always. */
5963 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5964 struct isds_credentials_delivery
*credentials_delivery
,
5965 xmlDocPtr response
, const char *request_name
) {
5966 isds_error err
= IE_SUCCESS
;
5967 xmlXPathContextPtr xpath_ctx
= NULL
;
5968 xmlXPathObjectPtr result
= NULL
;
5969 char *xpath_query
= NULL
;
5971 if (!context
) return IE_INVALID_CONTEXT
;
5972 if (credentials_delivery
) {
5973 zfree(credentials_delivery
->token
);
5974 zfree(credentials_delivery
->new_user_name
);
5976 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5979 /* Extract optional token */
5980 if (credentials_delivery
) {
5981 xpath_ctx
= xmlXPathNewContext(response
);
5986 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5991 /* Verify root element */
5992 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5997 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6002 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6003 char *request_name_locale
= _isds_utf82locale(request_name
);
6004 isds_log(ILF_ISDS
, ILL_WARNING
,
6005 _("Wrong element in ISDS response for %s request "
6006 "while extracting credentials delivery details\n"),
6007 request_name_locale
);
6008 free(request_name_locale
);
6012 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6015 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6017 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6019 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6020 if (!credentials_delivery
->token
) {
6021 char *request_name_locale
= _isds_utf82locale(request_name
);
6022 isds_log(ILF_ISDS
, ILL_ERR
,
6023 _("ISDS did not return token on %s request "
6024 "even if requested\n"), request_name_locale
);
6025 free(request_name_locale
);
6032 xmlXPathFreeObject(result
);
6033 xmlXPathFreeContext(xpath_ctx
);
6039 /* Build XSD:tCreateDBInput request type for box creating.
6040 * @context is session context
6041 * @request outputs built XML tree
6042 * @service_name is request name of SERVICE_DB_MANIPULATION service
6043 * @box is box description to create including single primary user (in case of
6045 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6046 * box, or contact address of PFO box owner)
6047 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6048 * @upper_box_id is optional ID of supper box if currently created box is
6050 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6052 * @credentials_delivery is valid pointer if ISDS should return token that box
6053 * owner can use to obtain his new credentials in on-line way. Then valid email
6054 * member value should be supplied.
6055 * @approval is optional external approval of box manipulation */
6056 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6057 xmlNodePtr
*request
, const xmlChar
*service_name
,
6058 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6059 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6060 const xmlChar
*ceo_label
,
6061 const struct isds_credentials_delivery
*credentials_delivery
,
6062 const struct isds_approval
*approval
) {
6063 isds_error err
= IE_SUCCESS
;
6064 xmlNsPtr isds_ns
= NULL
;
6065 xmlNodePtr node
, dbPrimaryUsers
;
6066 xmlChar
*string
= NULL
;
6067 const struct isds_list
*item
;
6070 if (!context
) return IE_INVALID_CONTEXT
;
6071 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6075 /* Build CreateDataBox-similar request */
6076 *request
= xmlNewNode(NULL
, service_name
);
6078 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6079 isds_printf_message(context
, _("Could build %s request"),
6080 service_name_locale
);
6081 free(service_name_locale
);
6084 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6085 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6087 isds_log_message(context
, _("Could not create ISDS1 name space"));
6088 xmlFreeNode(*request
);
6092 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6094 isds_log_message(context
, _("Could not create ISDS name space"));
6095 xmlFreeNode(*request
);
6099 xmlSetNs(*request
, isds_ns
);
6101 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6102 err
= insert_DbOwnerInfo(context
, box
, node
);
6103 if (err
) goto leave
;
6106 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6107 * verbose documentation allows none dbUserInfo */
6108 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6109 for (item
= users
; item
; item
= item
->next
) {
6111 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6112 err
= insert_DbUserInfo(context
,
6113 (struct isds_DbUserInfo
*) item
->data
, node
);
6114 if (err
) goto leave
;
6118 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6119 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6120 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6122 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6123 if (err
) goto leave
;
6125 err
= insert_GExtApproval(context
, approval
, *request
);
6126 if (err
) goto leave
;
6130 xmlFreeNode(*request
);
6136 #endif /* HAVE_LIBCURL */
6140 * @context is session context
6141 * @box is box description to create including single primary user (in case of
6142 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6143 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6144 * box, or contact address of PFO box owner)
6145 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6146 * @upper_box_id is optional ID of supper box if currently created box is
6148 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6149 * @credentials_delivery is NULL if new password should be delivered off-line
6150 * to box owner. It is valid pointer if owner should obtain new password on-line
6151 * on dedicated web server. Then input @credentials_delivery.email value is
6152 * his e-mail address he must provide to dedicated web server together
6153 * with output reallocated @credentials_delivery.token member. Output
6154 * member @credentials_delivery.new_user_name is unused up on this call.
6155 * @approval is optional external approval of box manipulation
6156 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6157 * NULL, if you don't care.*/
6158 isds_error
isds_add_box(struct isds_ctx
*context
,
6159 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6160 const char *former_names
, const char *upper_box_id
,
6161 const char *ceo_label
,
6162 struct isds_credentials_delivery
*credentials_delivery
,
6163 const struct isds_approval
*approval
, char **refnumber
) {
6164 isds_error err
= IE_SUCCESS
;
6166 xmlNodePtr request
= NULL
;
6167 xmlDocPtr response
= NULL
;
6168 xmlXPathContextPtr xpath_ctx
= NULL
;
6169 xmlXPathObjectPtr result
= NULL
;
6173 if (!context
) return IE_INVALID_CONTEXT
;
6174 zfree(context
->long_message
);
6175 if (credentials_delivery
) {
6176 zfree(credentials_delivery
->token
);
6177 zfree(credentials_delivery
->new_user_name
);
6179 if (!box
) return IE_INVAL
;
6182 /* Scratch box ID */
6185 /* Build CreateDataBox request */
6186 err
= build_CreateDBInput_request(context
,
6187 &request
, BAD_CAST
"CreateDataBox",
6188 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6189 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6190 if (err
) goto leave
;
6192 /* Send it to server and process response */
6193 err
= send_destroy_request_check_response(context
,
6194 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6195 &response
, (xmlChar
**) refnumber
, NULL
);
6197 /* Extract box ID */
6198 xpath_ctx
= xmlXPathNewContext(response
);
6203 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6207 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6209 /* Extract optional token */
6210 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6214 xmlXPathFreeObject(result
);
6215 xmlXPathFreeContext(xpath_ctx
);
6216 xmlFreeDoc(response
);
6217 xmlFreeNode(request
);
6220 isds_log(ILF_ISDS
, ILL_DEBUG
,
6221 _("CreateDataBox request processed by server successfully.\n"));
6223 #else /* not HAVE_LIBCURL */
6231 /* Notify ISDS about new PFO entity.
6232 * This function has no real effect.
6233 * @context is session context
6234 * @box is PFO description including single primary user.
6235 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6236 * @former_names is optional undocumented string. Pass NULL if you don't care.
6237 * @upper_box_id is optional ID of supper box if currently created box is
6239 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6240 * @approval is optional external approval of box manipulation
6241 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6242 * NULL, if you don't care.*/
6243 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6244 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6245 const char *former_names
, const char *upper_box_id
,
6246 const char *ceo_label
, const struct isds_approval
*approval
,
6248 isds_error err
= IE_SUCCESS
;
6250 xmlNodePtr request
= NULL
;
6253 if (!context
) return IE_INVALID_CONTEXT
;
6254 zfree(context
->long_message
);
6255 if (!box
) return IE_INVAL
;
6258 /* Build CreateDataBoxPFOInfo request */
6259 err
= build_CreateDBInput_request(context
,
6260 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6261 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6262 (xmlChar
*) ceo_label
, NULL
, approval
);
6263 if (err
) goto leave
;
6265 /* Send it to server and process response */
6266 err
= send_request_check_drop_response(context
,
6267 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6268 (xmlChar
**) refnumber
);
6269 /* XXX: XML Schema names output dbID element but textual documentation
6270 * states no box identifier is returned. */
6272 xmlFreeNode(request
);
6273 #else /* not HAVE_LIBCURL */
6280 /* Common implementation for removing given box.
6281 * @context is session context
6282 * @service_name is UTF-8 encoded name fo ISDS service
6283 * @box is box description to delete
6284 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6285 * carry sane value. If NULL, do not inject this information into request.
6286 * @approval is optional external approval of box manipulation
6287 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6288 * NULL, if you don't care.*/
6289 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6290 const xmlChar
*service_name
,
6291 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6292 const struct isds_approval
*approval
, char **refnumber
) {
6293 isds_error err
= IE_SUCCESS
;
6295 xmlNsPtr isds_ns
= NULL
;
6296 xmlNodePtr request
= NULL
;
6298 xmlChar
*string
= NULL
;
6302 if (!context
) return IE_INVALID_CONTEXT
;
6303 zfree(context
->long_message
);
6304 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6308 /* Build DeleteDataBox(Promptly) request */
6309 request
= xmlNewNode(NULL
, service_name
);
6311 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6312 isds_printf_message(context
,
6313 _("Could build %s request"), service_name_locale
);
6314 free(service_name_locale
);
6317 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6319 isds_log_message(context
, _("Could not create ISDS name space"));
6320 xmlFreeNode(request
);
6323 xmlSetNs(request
, isds_ns
);
6325 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6326 err
= insert_DbOwnerInfo(context
, box
, node
);
6327 if (err
) goto leave
;
6330 err
= tm2datestring(since
, &string
);
6332 isds_log_message(context
,
6333 _("Could not convert `since' argument to ISO date string"));
6336 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6340 err
= insert_GExtApproval(context
, approval
, request
);
6341 if (err
) goto leave
;
6344 /* Send it to server and process response */
6345 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6346 service_name
, &request
, (xmlChar
**) refnumber
);
6349 xmlFreeNode(request
);
6351 #else /* not HAVE_LIBCURL */
6358 /* Remove given box permanently.
6359 * @context is session context
6360 * @box is box description to delete
6361 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6363 * @approval is optional external approval of box manipulation
6364 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6365 * NULL, if you don't care.*/
6366 isds_error
isds_delete_box(struct isds_ctx
*context
,
6367 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6368 const struct isds_approval
*approval
, char **refnumber
) {
6369 if (!context
) return IE_INVALID_CONTEXT
;
6370 zfree(context
->long_message
);
6371 if (!box
|| !since
) return IE_INVAL
;
6373 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6374 box
, since
, approval
, refnumber
);
6378 /* Undocumented function.
6379 * @context is session context
6380 * @box is box description to delete
6381 * @approval is optional external approval of box manipulation
6382 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6383 * NULL, if you don't care.*/
6384 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6385 const struct isds_DbOwnerInfo
*box
,
6386 const struct isds_approval
*approval
, char **refnumber
) {
6387 if (!context
) return IE_INVALID_CONTEXT
;
6388 zfree(context
->long_message
);
6389 if (!box
) return IE_INVAL
;
6391 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6392 box
, NULL
, approval
, refnumber
);
6396 /* Update data about given box.
6397 * @context is session context
6398 * @old_box current box description
6399 * @new_box are updated data about @old_box
6400 * @approval is optional external approval of box manipulation
6401 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6402 * NULL, if you don't care.*/
6403 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6404 const struct isds_DbOwnerInfo
*old_box
,
6405 const struct isds_DbOwnerInfo
*new_box
,
6406 const struct isds_approval
*approval
, char **refnumber
) {
6407 isds_error err
= IE_SUCCESS
;
6409 xmlNsPtr isds_ns
= NULL
;
6410 xmlNodePtr request
= NULL
;
6415 if (!context
) return IE_INVALID_CONTEXT
;
6416 zfree(context
->long_message
);
6417 if (!old_box
|| !new_box
) return IE_INVAL
;
6421 /* Build UpdateDataBoxDescr request */
6422 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6424 isds_log_message(context
,
6425 _("Could build UpdateDataBoxDescr request"));
6428 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6430 isds_log_message(context
, _("Could not create ISDS name space"));
6431 xmlFreeNode(request
);
6434 xmlSetNs(request
, isds_ns
);
6436 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6437 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6438 if (err
) goto leave
;
6440 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6441 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6442 if (err
) goto leave
;
6444 err
= insert_GExtApproval(context
, approval
, request
);
6445 if (err
) goto leave
;
6448 /* Send it to server and process response */
6449 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6450 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6453 xmlFreeNode(request
);
6454 #else /* not HAVE_LIBCURL */
6463 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6465 * @context is session context
6466 * @service is SOAP service
6467 * @service_name is name of request in @service
6468 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6469 * @box_id is box ID of interest
6470 * @approval is optional external approval of box manipulation
6471 * @response is server SOAP body response as XML document
6472 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6473 * NULL, if you don't care.
6474 * @return error coded from lower layer, context message will be set up
6476 static isds_error
build_send_dbid_request_check_response(
6477 struct isds_ctx
*context
, const isds_service service
,
6478 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6479 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6480 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6482 isds_error err
= IE_SUCCESS
;
6483 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6484 xmlNodePtr request
= NULL
, node
;
6485 xmlNsPtr isds_ns
= NULL
;
6487 if (!context
) return IE_INVALID_CONTEXT
;
6488 if (!service_name
|| !box_id
) return IE_INVAL
;
6489 if (!response
) return IE_INVAL
;
6491 /* Free output argument */
6492 xmlFreeDoc(*response
); *response
= NULL
;
6494 /* Prepare strings */
6495 service_name_locale
= _isds_utf82locale((char*)service_name
);
6496 if (!service_name_locale
) {
6500 box_id_locale
= _isds_utf82locale((char*)box_id
);
6501 if (!box_id_locale
) {
6507 request
= xmlNewNode(NULL
, service_name
);
6509 isds_printf_message(context
,
6510 _("Could not build %s request for %s box"), service_name_locale
,
6515 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6517 isds_log_message(context
, _("Could not create ISDS name space"));
6521 xmlSetNs(request
, isds_ns
);
6523 /* Add XSD:tIdDbInput children */
6524 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6525 INSERT_STRING(request
, box_id_element
, box_id
);
6526 err
= insert_GExtApproval(context
, approval
, request
);
6527 if (err
) goto leave
;
6529 /* Send request and check response*/
6530 err
= send_destroy_request_check_response(context
,
6531 service
, service_name
, &request
, response
, refnumber
, NULL
);
6534 free(service_name_locale
);
6535 free(box_id_locale
);
6536 xmlFreeNode(request
);
6539 #endif /* HAVE_LIBCURL */
6542 /* Get data about all users assigned to given box.
6543 * @context is session context
6545 * @users is automatically reallocated list of struct isds_DbUserInfo */
6546 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6547 struct isds_list
**users
) {
6548 isds_error err
= IE_SUCCESS
;
6550 xmlDocPtr response
= NULL
;
6551 xmlXPathContextPtr xpath_ctx
= NULL
;
6552 xmlXPathObjectPtr result
= NULL
;
6554 struct isds_list
*item
, *prev_item
= NULL
;
6557 if (!context
) return IE_INVALID_CONTEXT
;
6558 zfree(context
->long_message
);
6559 if (!users
|| !box_id
) return IE_INVAL
;
6560 isds_list_free(users
);
6564 /* Do request and check for success */
6565 err
= build_send_dbid_request_check_response(context
,
6566 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6567 BAD_CAST box_id
, NULL
, &response
, NULL
);
6568 if (err
) goto leave
;
6572 /* Prepare structure */
6573 xpath_ctx
= xmlXPathNewContext(response
);
6578 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6583 /* Set context node */
6584 result
= xmlXPathEvalExpression(BAD_CAST
6585 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6591 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6592 /* Iterate over all users */
6593 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6595 /* Prepare structure */
6596 item
= calloc(1, sizeof(*item
));
6601 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6602 if (i
== 0) *users
= item
;
6603 else prev_item
->next
= item
;
6607 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6608 err
= extract_DbUserInfo(context
,
6609 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6610 if (err
) goto leave
;
6616 isds_list_free(users
);
6619 xmlXPathFreeObject(result
);
6620 xmlXPathFreeContext(xpath_ctx
);
6621 xmlFreeDoc(response
);
6624 isds_log(ILF_ISDS
, ILL_DEBUG
,
6625 _("GetDataBoxUsers request processed by server "
6626 "successfully.\n"));
6627 #else /* not HAVE_LIBCURL */
6635 /* Update data about user assigned to given box.
6636 * @context is session context
6637 * @box is box identification
6638 * @old_user identifies user to update
6639 * @new_user are updated data about @old_user
6640 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6641 * NULL, if you don't care.*/
6642 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6643 const struct isds_DbOwnerInfo
*box
,
6644 const struct isds_DbUserInfo
*old_user
,
6645 const struct isds_DbUserInfo
*new_user
,
6647 isds_error err
= IE_SUCCESS
;
6649 xmlNsPtr isds_ns
= NULL
;
6650 xmlNodePtr request
= NULL
;
6655 if (!context
) return IE_INVALID_CONTEXT
;
6656 zfree(context
->long_message
);
6657 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6661 /* Build UpdateDataBoxUser request */
6662 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6664 isds_log_message(context
,
6665 _("Could build UpdateDataBoxUser request"));
6668 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6670 isds_log_message(context
, _("Could not create ISDS name space"));
6671 xmlFreeNode(request
);
6674 xmlSetNs(request
, isds_ns
);
6676 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6677 err
= insert_DbOwnerInfo(context
, box
, node
);
6678 if (err
) goto leave
;
6680 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6681 err
= insert_DbUserInfo(context
, old_user
, node
);
6682 if (err
) goto leave
;
6684 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6685 err
= insert_DbUserInfo(context
, new_user
, node
);
6686 if (err
) goto leave
;
6688 /* Send it to server and process response */
6689 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6690 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6693 xmlFreeNode(request
);
6694 #else /* not HAVE_LIBCURL */
6702 /* Undocumented function.
6703 * @context is session context
6704 * @box_id is UTF-8 encoded box identifier
6705 * @token is UTF-8 encoded temporary password
6706 * @user_id outputs UTF-8 encoded reallocated user identifier
6707 * @password outpus UTF-8 encoded reallocated user password
6708 * Output arguments will be nulled in case of error */
6709 isds_error
isds_activate(struct isds_ctx
*context
,
6710 const char *box_id
, const char *token
,
6711 char **user_id
, char **password
) {
6712 isds_error err
= IE_SUCCESS
;
6714 xmlNsPtr isds_ns
= NULL
;
6715 xmlNodePtr request
= NULL
, node
;
6716 xmlDocPtr response
= NULL
;
6717 xmlXPathContextPtr xpath_ctx
= NULL
;
6718 xmlXPathObjectPtr result
= NULL
;
6722 if (!context
) return IE_INVALID_CONTEXT
;
6723 zfree(context
->long_message
);
6725 if (user_id
) zfree(*user_id
);
6726 if (password
) zfree(*password
);
6728 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6732 /* Build Activate request */
6733 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6735 isds_log_message(context
, _("Could build Activate request"));
6738 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6740 isds_log_message(context
, _("Could not create ISDS name space"));
6741 xmlFreeNode(request
);
6744 xmlSetNs(request
, isds_ns
);
6746 INSERT_STRING(request
, "dbAccessDataId", token
);
6747 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6748 INSERT_STRING(request
, "dbID", box_id
);
6751 /* Send request and check response*/
6752 err
= send_destroy_request_check_response(context
,
6753 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6754 &response
, NULL
, NULL
);
6755 if (err
) goto leave
;
6759 xpath_ctx
= xmlXPathNewContext(response
);
6764 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6768 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6774 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6775 isds_log_message(context
, _("Missing ActivateResponse element"));
6779 if (result
->nodesetval
->nodeNr
> 1) {
6780 isds_log_message(context
, _("Multiple ActivateResponse element"));
6784 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6785 xmlXPathFreeObject(result
); result
= NULL
;
6787 EXTRACT_STRING("isds:userId", *user_id
);
6789 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6790 "but did not return `userId' element.\n"));
6792 EXTRACT_STRING("isds:password", *password
);
6794 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6795 "but did not return `password' element.\n"));
6798 xmlXPathFreeObject(result
);
6799 xmlXPathFreeContext(xpath_ctx
);
6800 xmlFreeDoc(response
);
6801 xmlFreeNode(request
);
6804 isds_log(ILF_ISDS
, ILL_DEBUG
,
6805 _("Activate request processed by server successfully.\n"));
6806 #else /* not HAVE_LIBCURL */
6814 /* Reset credentials of user assigned to given box.
6815 * @context is session context
6816 * @box is box identification
6817 * @user identifies user to reset password
6818 * @fee_paid is true if fee has been paid, false otherwise
6819 * @approval is optional external approval of box manipulation
6820 * @credentials_delivery is NULL if new password should be delivered off-line
6821 * to the user. It is valid pointer if user should obtain new password on-line
6822 * on dedicated web server. Then input @credentials_delivery.email value is
6823 * user's e-mail address user must provide to dedicated web server together
6824 * with @credentials_delivery.token. The output reallocated token user needs
6825 * to use to authorize on the web server to view his new password. Output
6826 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6827 * ISDS changed up on this call. (No reason why server could change the name
6829 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6830 * NULL, if you don't care.*/
6831 isds_error
isds_reset_password(struct isds_ctx
*context
,
6832 const struct isds_DbOwnerInfo
*box
,
6833 const struct isds_DbUserInfo
*user
,
6834 const _Bool fee_paid
, const struct isds_approval
*approval
,
6835 struct isds_credentials_delivery
*credentials_delivery
,
6837 isds_error err
= IE_SUCCESS
;
6839 xmlNsPtr isds_ns
= NULL
;
6840 xmlNodePtr request
= NULL
, node
;
6841 xmlDocPtr response
= NULL
;
6845 if (!context
) return IE_INVALID_CONTEXT
;
6846 zfree(context
->long_message
);
6848 if (credentials_delivery
) {
6849 zfree(credentials_delivery
->token
);
6850 zfree(credentials_delivery
->new_user_name
);
6852 if (!box
|| !user
) return IE_INVAL
;
6856 /* Build NewAccessData request */
6857 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6859 isds_log_message(context
,
6860 _("Could build NewAccessData request"));
6863 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6865 isds_log_message(context
, _("Could not create ISDS name space"));
6866 xmlFreeNode(request
);
6869 xmlSetNs(request
, isds_ns
);
6871 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6872 err
= insert_DbOwnerInfo(context
, box
, node
);
6873 if (err
) goto leave
;
6875 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6876 err
= insert_DbUserInfo(context
, user
, node
);
6877 if (err
) goto leave
;
6879 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6881 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6882 if (err
) goto leave
;
6884 err
= insert_GExtApproval(context
, approval
, request
);
6885 if (err
) goto leave
;
6887 /* Send request and check response*/
6888 err
= send_destroy_request_check_response(context
,
6889 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6890 &response
, (xmlChar
**) refnumber
, NULL
);
6891 if (err
) goto leave
;
6894 /* Extract optional token */
6895 err
= extract_credentials_delivery(context
, credentials_delivery
,
6896 response
, "NewAccessData");
6899 xmlFreeDoc(response
);
6900 xmlFreeNode(request
);
6903 isds_log(ILF_ISDS
, ILL_DEBUG
,
6904 _("NewAccessData request processed by server "
6905 "successfully.\n"));
6906 #else /* not HAVE_LIBCURL */
6914 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6915 * code, destroy response and log success.
6916 * @context is ISDS session context.
6917 * @service_name is name of SERVICE_DB_MANIPULATION service
6918 * @box is box identification
6919 * @user identifies user to remove
6920 * @credentials_delivery is NULL if new user's password should be delivered
6921 * off-line to the user. It is valid pointer if user should obtain new
6922 * password on-line on dedicated web server. Then input
6923 * @credentials_delivery.email value is user's e-mail address user must
6924 * provide to dedicated web server together with @credentials_delivery.token.
6925 * The output reallocated token user needs to use to authorize on the web
6926 * server to view his new password. Output reallocated
6927 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6928 * assingned or changed up on this call.
6929 * @approval is optional external approval of box manipulation
6930 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6931 * NULL, if you don't care. */
6932 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6933 struct isds_ctx
*context
, const xmlChar
*service_name
,
6934 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6935 struct isds_credentials_delivery
*credentials_delivery
,
6936 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6937 isds_error err
= IE_SUCCESS
;
6939 xmlNsPtr isds_ns
= NULL
;
6940 xmlNodePtr request
= NULL
, node
;
6941 xmlDocPtr response
= NULL
;
6945 if (!context
) return IE_INVALID_CONTEXT
;
6946 zfree(context
->long_message
);
6947 if (credentials_delivery
) {
6948 zfree(credentials_delivery
->token
);
6949 zfree(credentials_delivery
->new_user_name
);
6951 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6956 /* Build NewAccessData or similar request */
6957 request
= xmlNewNode(NULL
, service_name
);
6959 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6960 isds_printf_message(context
, _("Could not build %s request"),
6961 service_name_locale
);
6962 free(service_name_locale
);
6965 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6967 isds_log_message(context
, _("Could not create ISDS name space"));
6968 xmlFreeNode(request
);
6971 xmlSetNs(request
, isds_ns
);
6973 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6974 err
= insert_DbOwnerInfo(context
, box
, node
);
6975 if (err
) goto leave
;
6977 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6978 err
= insert_DbUserInfo(context
, user
, node
);
6979 if (err
) goto leave
;
6981 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6982 if (err
) goto leave
;
6984 err
= insert_GExtApproval(context
, approval
, request
);
6985 if (err
) goto leave
;
6988 /* Send request and check response*/
6989 err
= send_destroy_request_check_response(context
,
6990 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6993 xmlFreeNode(request
);
6996 /* Pick up credentials_delivery if requested */
6997 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6998 (char *)service_name
);
7001 xmlFreeDoc(response
);
7002 if (request
) xmlFreeNode(request
);
7005 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7006 isds_log(ILF_ISDS
, ILL_DEBUG
,
7007 _("%s request processed by server successfully.\n"),
7008 service_name_locale
);
7009 free(service_name_locale
);
7011 #else /* not HAVE_LIBCURL */
7019 /* Assign new user to given box.
7020 * @context is session context
7021 * @box is box identification
7022 * @user defines new user to add
7023 * @credentials_delivery is NULL if new user's password should be delivered
7024 * off-line to the user. It is valid pointer if user should obtain new
7025 * password on-line on dedicated web server. Then input
7026 * @credentials_delivery.email value is user's e-mail address user must
7027 * provide to dedicated web server together with @credentials_delivery.token.
7028 * The output reallocated token user needs to use to authorize on the web
7029 * server to view his new password. Output reallocated
7030 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7031 * assingned up on this call.
7032 * @approval is optional external approval of box manipulation
7033 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7034 * NULL, if you don't care.*/
7035 isds_error
isds_add_user(struct isds_ctx
*context
,
7036 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7037 struct isds_credentials_delivery
*credentials_delivery
,
7038 const struct isds_approval
*approval
, char **refnumber
) {
7039 return build_send_manipulationboxuser_request_check_drop_response(context
,
7040 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
7041 approval
, (xmlChar
**) refnumber
);
7045 /* Remove user assigned to given box.
7046 * @context is session context
7047 * @box is box identification
7048 * @user identifies user to remove
7049 * @approval is optional external approval of box manipulation
7050 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7051 * NULL, if you don't care.*/
7052 isds_error
isds_delete_user(struct isds_ctx
*context
,
7053 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7054 const struct isds_approval
*approval
, char **refnumber
) {
7055 return build_send_manipulationboxuser_request_check_drop_response(context
,
7056 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
7057 (xmlChar
**) refnumber
);
7061 /* Get list of boxes in ZIP archive.
7062 * @context is session context
7063 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7064 * System recognizes following values currently: ALL (all boxes), UPG
7065 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7066 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7067 * commercial messages). This argument is a string because specification
7068 * states new values can appear in the future. Not all list types are
7069 * available to all users.
7070 * @buffer is automatically reallocated memory to store the list of boxes. The
7071 * list is zipped CSV file.
7072 * @buffer_length is size of @buffer data in bytes.
7073 * In case of error @buffer will be freed and @buffer_length will be
7075 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7076 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7077 isds_error err
= IE_SUCCESS
;
7079 xmlNsPtr isds_ns
= NULL
;
7080 xmlNodePtr request
= NULL
, node
;
7081 xmlDocPtr response
= NULL
;
7082 xmlXPathContextPtr xpath_ctx
= NULL
;
7083 xmlXPathObjectPtr result
= NULL
;
7084 char *string
= NULL
;
7088 if (!context
) return IE_INVALID_CONTEXT
;
7089 zfree(context
->long_message
);
7090 if (buffer
) zfree(*buffer
);
7091 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7095 /* Check if connection is established
7096 * TODO: This check should be done downstairs. */
7097 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7100 /* Build AuthenticateMessage request */
7101 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7103 isds_log_message(context
,
7104 _("Could not build GetDataBoxList request"));
7107 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7109 isds_log_message(context
, _("Could not create ISDS name space"));
7110 xmlFreeNode(request
);
7113 xmlSetNs(request
, isds_ns
);
7114 INSERT_STRING(request
, "dblType", list_identifier
);
7116 /* Send request to server and process response */
7117 err
= send_destroy_request_check_response(context
,
7118 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7119 &response
, NULL
, NULL
);
7120 if (err
) goto leave
;
7123 /* Extract Base-64 encoded ZIP file */
7124 xpath_ctx
= xmlXPathNewContext(response
);
7129 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7133 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7135 /* Decode non-empty archive */
7136 if (string
&& string
[0] != '\0') {
7137 *buffer_length
= _isds_b64decode(string
, buffer
);
7138 if (*buffer_length
== (size_t) -1) {
7139 isds_printf_message(context
,
7140 _("Error while Base64-decoding box list archive"));
7149 xmlXPathFreeObject(result
);
7150 xmlXPathFreeContext(xpath_ctx
);
7151 xmlFreeDoc(response
);
7152 xmlFreeNode(request
);
7155 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7156 "processed by server successfully.\n"));
7158 #else /* not HAVE_LIBCURL */
7166 /* Find boxes suiting given criteria.
7167 * @criteria is filter. You should fill in at least some members.
7168 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7169 * possibly empty. Input NULL or valid old structure.
7171 * IE_SUCCESS if search succeeded, @boxes contains useful data
7172 * IE_NOEXIST if no such box exists, @boxes will be NULL
7173 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7174 * contains still valid data
7175 * other code if something bad happens. @boxes will be NULL. */
7176 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7177 const struct isds_DbOwnerInfo
*criteria
,
7178 struct isds_list
**boxes
) {
7179 isds_error err
= IE_SUCCESS
;
7181 _Bool truncated
= 0;
7182 xmlNsPtr isds_ns
= NULL
;
7183 xmlNodePtr request
= NULL
;
7184 xmlDocPtr response
= NULL
;
7185 xmlChar
*code
= NULL
, *message
= NULL
;
7186 xmlNodePtr db_owner_info
;
7187 xmlXPathContextPtr xpath_ctx
= NULL
;
7188 xmlXPathObjectPtr result
= NULL
;
7189 xmlChar
*string
= NULL
;
7193 if (!context
) return IE_INVALID_CONTEXT
;
7194 zfree(context
->long_message
);
7195 if (!boxes
) return IE_INVAL
;
7196 isds_list_free(boxes
);
7203 /* Check if connection is established
7204 * TODO: This check should be done downstairs. */
7205 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7208 /* Build FindDataBox request */
7209 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7211 isds_log_message(context
,
7212 _("Could build FindDataBox request"));
7215 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7217 isds_log_message(context
, _("Could not create ISDS name space"));
7218 xmlFreeNode(request
);
7221 xmlSetNs(request
, isds_ns
);
7222 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7223 if (!db_owner_info
) {
7224 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7225 "FindDataBox element"));
7226 xmlFreeNode(request
);
7230 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7231 if (err
) goto leave
;
7234 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7237 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7239 /* Destroy request */
7240 xmlFreeNode(request
); request
= NULL
;
7243 isds_log(ILF_ISDS
, ILL_DEBUG
,
7244 _("Processing ISDS response on FindDataBox "
7245 "request failed\n"));
7249 /* Check for response status */
7250 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7251 &code
, &message
, NULL
);
7253 isds_log(ILF_ISDS
, ILL_DEBUG
,
7254 _("ISDS response on FindDataBox request is missing status\n"));
7258 /* Request processed, but nothing found */
7259 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7260 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7261 char *code_locale
= _isds_utf82locale((char*)code
);
7262 char *message_locale
= _isds_utf82locale((char*)message
);
7263 isds_log(ILF_ISDS
, ILL_DEBUG
,
7264 _("Server did not found any box on FindDataBox request "
7265 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7266 isds_log_message(context
, message_locale
);
7268 free(message_locale
);
7273 /* Warning, not a error */
7274 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7275 char *code_locale
= _isds_utf82locale((char*)code
);
7276 char *message_locale
= _isds_utf82locale((char*)message
);
7277 isds_log(ILF_ISDS
, ILL_DEBUG
,
7278 _("Server truncated response on FindDataBox request "
7279 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7280 isds_log_message(context
, message_locale
);
7282 free(message_locale
);
7287 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7288 char *code_locale
= _isds_utf82locale((char*)code
);
7289 char *message_locale
= _isds_utf82locale((char*)message
);
7290 isds_log(ILF_ISDS
, ILL_DEBUG
,
7291 _("Server refused FindDataBox request "
7292 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7293 isds_log_message(context
, message_locale
);
7295 free(message_locale
);
7300 xpath_ctx
= xmlXPathNewContext(response
);
7305 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7310 /* Extract boxes if they present */
7311 result
= xmlXPathEvalExpression(BAD_CAST
7312 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7318 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7319 struct isds_list
*item
, *prev_item
= NULL
;
7320 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7321 item
= calloc(1, sizeof(*item
));
7327 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7328 if (i
== 0) *boxes
= item
;
7329 else prev_item
->next
= item
;
7332 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7333 err
= extract_DbOwnerInfo(context
,
7334 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7335 if (err
) goto leave
;
7341 isds_list_free(boxes
);
7343 if (truncated
) err
= IE_2BIG
;
7347 xmlFreeNode(request
);
7348 xmlXPathFreeObject(result
);
7349 xmlXPathFreeContext(xpath_ctx
);
7353 xmlFreeDoc(response
);
7356 isds_log(ILF_ISDS
, ILL_DEBUG
,
7357 _("FindDataBox request processed by server successfully.\n"));
7358 #else /* not HAVE_LIBCURL */
7367 /* Convert a string with match markers into a plain string with list of
7368 * pointers to the matches
7369 * @string is an UTF-8 encoded non-constant string with match markers
7370 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7371 * The markers will be removed from the string.
7372 * @starts is a reallocated list of static pointers into the @string pointing
7373 * to places where match start markers occured.
7374 * @ends is a reallocated list of static pointers into the @string pointing
7375 * to places where match end markers occured.
7376 * @return IE_SUCCESS in case of no failure. */
7377 static isds_error
interpret_matches(xmlChar
*string
,
7378 struct isds_list
**starts
, struct isds_list
**ends
) {
7379 isds_error err
= IE_SUCCESS
;
7380 xmlChar
*pointer
, *destination
, *source
;
7381 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7383 isds_list_free(starts
);
7384 isds_list_free(ends
);
7385 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7386 if (NULL
== string
) return IE_SUCCESS
;
7388 for (pointer
= string
; *pointer
!= '\0';) {
7389 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7390 /* Remove the start marker */
7391 for (source
= pointer
+ 14, destination
= pointer
;
7392 *source
!= '\0'; source
++, destination
++) {
7393 *destination
= *source
;
7395 *destination
= '\0';
7396 /* Append the pointer into the list */
7397 item
= calloc(1, sizeof(*item
));
7402 item
->destructor
= (void (*)(void **))NULL
;
7403 item
->data
= pointer
;
7404 if (NULL
== prev_start
) *starts
= item
;
7405 else prev_start
->next
= item
;
7407 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7408 /* Remove the end marker */
7409 for (source
= pointer
+ 12, destination
= pointer
;
7410 *source
!= '\0'; source
++, destination
++) {
7411 *destination
= *source
;
7413 *destination
= '\0';
7414 /* Append the pointer into the list */
7415 item
= calloc(1, sizeof(*item
));
7420 item
->destructor
= (void (*)(void **))NULL
;
7421 item
->data
= pointer
;
7422 if (NULL
== prev_end
) *ends
= item
;
7423 else prev_end
->next
= item
;
7432 isds_list_free(starts
);
7433 isds_list_free(ends
);
7439 /* Convert isds:dbResult XML tree into structure
7440 * @context is ISDS context.
7441 * @fulltext_result is automatically reallocated found box structure.
7442 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7443 * @collect_matches is true to interpret match markers.
7444 * In case of error @result will be freed. */
7445 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7446 struct isds_fulltext_result
**fulltext_result
,
7447 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7448 isds_error err
= IE_SUCCESS
;
7449 xmlXPathObjectPtr result
= NULL
;
7450 char *string
= NULL
;
7452 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7453 if (NULL
== fulltext_result
) return IE_INVAL
;
7454 isds_fulltext_result_free(fulltext_result
);
7455 if (!xpath_ctx
) return IE_INVAL
;
7458 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7459 if (NULL
== *fulltext_result
) {
7465 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7467 EXTRACT_STRING("isds:dbType", string
);
7468 if (NULL
== string
) {
7470 isds_log_message(context
, _("Empty isds:dbType element"));
7473 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7475 if (err
== IE_ENUM
) {
7477 char *string_locale
= _isds_utf82locale(string
);
7478 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7480 free(string_locale
);
7486 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7487 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7489 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7490 if (err
) goto leave
;
7492 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7493 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7494 (*fulltext_result
)->dbEffectiveOVM
);
7496 EXTRACT_STRING("isds:dbSendOptions", string
);
7497 if (NULL
== string
) {
7499 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7502 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7503 (*fulltext_result
)->active
= 1;
7504 (*fulltext_result
)->public_sending
= 1;
7505 (*fulltext_result
)->commercial_sending
= 0;
7506 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7507 (*fulltext_result
)->active
= 1;
7508 (*fulltext_result
)->public_sending
= 1;
7509 (*fulltext_result
)->commercial_sending
= 1;
7510 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7511 (*fulltext_result
)->active
= 1;
7512 (*fulltext_result
)->public_sending
= 0;
7513 (*fulltext_result
)->commercial_sending
= 1;
7514 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7515 (*fulltext_result
)->active
= 1;
7516 (*fulltext_result
)->public_sending
= 0;
7517 (*fulltext_result
)->commercial_sending
= 0;
7518 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7519 (*fulltext_result
)->active
= 0;
7520 (*fulltext_result
)->public_sending
= 0;
7521 (*fulltext_result
)->commercial_sending
= 0;
7524 char *string_locale
= _isds_utf82locale(string
);
7525 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7527 free(string_locale
);
7532 /* Interpret match marks */
7533 if (collect_matches
) {
7534 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7535 &((*fulltext_result
)->name_match_start
),
7536 &((*fulltext_result
)->name_match_end
));
7537 if (err
) goto leave
;
7538 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7539 &((*fulltext_result
)->address_match_start
),
7540 &((*fulltext_result
)->address_match_end
));
7541 if (err
) goto leave
;
7545 if (err
) isds_fulltext_result_free(fulltext_result
);
7547 xmlXPathFreeObject(result
);
7550 #endif /* HAVE_LIBCURL */
7553 /* Find boxes matching a given full-text criteria.
7554 * @context is a session context
7555 * @query is a non-empty string which consists of words to search
7556 * @target selects box attributes to search for @query words. Pass NULL if you
7558 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7559 * to search in all box types. Pass NULL to let server to use default value
7560 * which is DBTYPE_SYSTEM.
7561 * @page_size defines count of boxes to constitute a response page. It counts
7562 * from zero. Pass NULL to let server to use a default value (50 now).
7563 * @page_number defines ordinar number of the response page to return. It
7564 * counts from zero. Pass NULL to let server to use a default value (0 now).
7565 * @track_matches points to true for marking @query words found in the box
7566 * attributes. It points to false for not marking. Pass NULL to let the server
7567 * to use default value (false now).
7568 * @total_matching_boxes outputs reallocated number of all boxes matching the
7569 * query. Will be pointer to NULL if server did not provide the value.
7570 * Pass NULL if you don't care.
7571 * @current_page_beginning outputs reallocated ordinar number of the first box
7572 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7573 * server did not provide the value. Pass NULL if you don't care.
7574 * @current_page_size outputs reallocated count of boxes in the this @boxes
7575 * page. It will be pointer to NULL if the server did not provide the value.
7576 * Pass NULL if you don't care.
7577 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7578 * is the last one, false if more boxes match, NULL if the server did not
7579 * provude the value. Pass NULL if you don't care.
7580 * @boxes outputs reallocated list of isds_fulltext_result structures,
7583 * IE_SUCCESS if search succeeded
7584 * IE_2BIG if @page_size is too large
7585 * other code if something bad happens; output arguments will be NULL. */
7586 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7588 const isds_fulltext_target
*target
,
7589 const isds_DbType
*box_type
,
7590 const unsigned long int *page_size
,
7591 const unsigned long int *page_number
,
7592 const _Bool
*track_matches
,
7593 unsigned long int **total_matching_boxes
,
7594 unsigned long int **current_page_beginning
,
7595 unsigned long int **current_page_size
,
7597 struct isds_list
**boxes
) {
7598 isds_error err
= IE_SUCCESS
;
7600 xmlNsPtr isds_ns
= NULL
;
7601 xmlNodePtr request
= NULL
;
7602 xmlDocPtr response
= NULL
;
7604 xmlXPathContextPtr xpath_ctx
= NULL
;
7605 xmlXPathObjectPtr result
= NULL
;
7606 const xmlChar
*static_string
= NULL
;
7607 xmlChar
*string
= NULL
;
7609 const xmlChar
*codes
[] = {
7619 const char *meanings
[] = {
7620 N_("You are not allowed to perform the search"),
7621 N_("The query string is empty"),
7622 N_("Searched box ID is malformed"),
7623 N_("Searched organization ID is malformed"),
7624 N_("Invalid input"),
7625 N_("Requested page size is too large"),
7626 N_("Search engine internal error")
7628 const isds_error errors
[] = {
7637 struct code_map_isds_error map
= {
7639 .meanings
= meanings
,
7645 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7646 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7647 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7648 if (NULL
!= last_page
) zfree(*last_page
);
7649 isds_list_free(boxes
);
7651 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7652 zfree(context
->long_message
);
7654 if (NULL
== boxes
) return IE_INVAL
;
7656 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7657 isds_log_message(context
, _("Query string must be non-empty"));
7662 /* Check if connection is established
7663 * TODO: This check should be done downstairs. */
7664 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7666 /* Build FindDataBox request */
7667 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7668 if (NULL
== request
) {
7669 isds_log_message(context
,
7670 _("Could not build ISDSSearch2 request"));
7673 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7674 if(NULL
== isds_ns
) {
7675 isds_log_message(context
, _("Could not create ISDS name space"));
7676 xmlFreeNode(request
);
7679 xmlSetNs(request
, isds_ns
);
7681 INSERT_STRING(request
, "searchText", query
);
7683 if (NULL
!= target
) {
7684 static_string
= isds_fulltext_target2string(*(target
));
7685 if (NULL
== static_string
) {
7686 isds_printf_message(context
, _("Invalid target value: %d"),
7692 INSERT_STRING(request
, "searchType", static_string
);
7693 static_string
= NULL
;
7695 if (NULL
!= box_type
) {
7696 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7697 if (DBTYPE_SYSTEM
== *box_type
) {
7698 static_string
= BAD_CAST
"ALL";
7700 static_string
= isds_DbType2string(*(box_type
));
7701 if (NULL
== static_string
) {
7702 isds_printf_message(context
, _("Invalid box type value: %d"),
7709 INSERT_STRING(request
, "searchScope", static_string
);
7710 static_string
= NULL
;
7712 INSERT_ULONGINT(request
, "page", page_number
, string
);
7713 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7714 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7716 /* Send request and check response */
7717 err
= send_destroy_request_check_response(context
,
7718 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7719 &request
, &response
, NULL
, &map
);
7720 if (err
) goto leave
;
7722 /* Parse response */
7723 xpath_ctx
= xmlXPathNewContext(response
);
7724 if (NULL
== xpath_ctx
) {
7728 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7732 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7738 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7739 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7743 if (result
->nodesetval
->nodeNr
> 1) {
7744 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7748 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7749 xmlXPathFreeObject(result
); result
= NULL
;
7752 /* Extract counters */
7753 if (NULL
!= total_matching_boxes
) {
7754 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7756 if (NULL
!= current_page_size
) {
7757 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7759 if (NULL
!= current_page_beginning
) {
7760 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7762 if (NULL
!= last_page
) {
7763 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7765 xmlXPathFreeObject(result
); result
= NULL
;
7767 /* Extract boxes if they present */
7768 result
= xmlXPathEvalExpression(BAD_CAST
7769 "isds:dbResults/isds:dbResult", xpath_ctx
);
7770 if (NULL
== result
) {
7774 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7775 struct isds_list
*item
, *prev_item
= NULL
;
7776 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7777 item
= calloc(1, sizeof(*item
));
7783 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7784 if (i
== 0) *boxes
= item
;
7785 else prev_item
->next
= item
;
7788 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7789 err
= extract_dbResult(context
,
7790 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7791 (NULL
== track_matches
) ? 0 : *track_matches
);
7792 if (err
) goto leave
;
7798 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7799 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7800 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7801 if (NULL
!= last_page
) zfree(*last_page
);
7802 isds_list_free(boxes
);
7806 xmlFreeNode(request
);
7807 xmlXPathFreeObject(result
);
7808 xmlXPathFreeContext(xpath_ctx
);
7809 xmlFreeDoc(response
);
7812 isds_log(ILF_ISDS
, ILL_DEBUG
,
7813 _("ISDSSearch2 request processed by server successfully.\n"));
7814 #else /* not HAVE_LIBCURL */
7822 /* Get status of a box.
7823 * @context is ISDS session context.
7824 * @box_id is UTF-8 encoded box identifier as zero terminated string
7825 * @box_status is return value of box status.
7827 * IE_SUCCESS if box has been found and its status retrieved
7828 * IE_NOEXIST if box is not known to ISDS server
7829 * or other appropriate error.
7830 * You can use isds_DbState to enumerate box status. However out of enum
7831 * range value can be returned too. This is feature because ISDS
7832 * specification leaves the set of values open.
7833 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7834 * the box has been deleted, but ISDS still lists its former existence. */
7835 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7836 long int *box_status
) {
7837 isds_error err
= IE_SUCCESS
;
7839 xmlNsPtr isds_ns
= NULL
;
7840 xmlNodePtr request
= NULL
, db_id
;
7841 xmlDocPtr response
= NULL
;
7842 xmlXPathContextPtr xpath_ctx
= NULL
;
7843 xmlXPathObjectPtr result
= NULL
;
7844 xmlChar
*string
= NULL
;
7846 const xmlChar
*codes
[] = {
7852 const char *meanings
[] = {
7853 "The box does not exist",
7854 "Box ID is malformed",
7857 const isds_error errors
[] = {
7862 struct code_map_isds_error map
= {
7864 .meanings
= meanings
,
7869 if (!context
) return IE_INVALID_CONTEXT
;
7870 zfree(context
->long_message
);
7871 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7874 /* Check if connection is established
7875 * TODO: This check should be done downstairs. */
7876 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7879 /* Build CheckDataBox request */
7880 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7882 isds_log_message(context
,
7883 _("Could build CheckDataBox request"));
7886 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7888 isds_log_message(context
, _("Could not create ISDS name space"));
7889 xmlFreeNode(request
);
7892 xmlSetNs(request
, isds_ns
);
7893 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7895 isds_log_message(context
, _("Could not add dbID child to "
7896 "CheckDataBox element"));
7897 xmlFreeNode(request
);
7902 /* Send request and check response*/
7903 err
= send_destroy_request_check_response(context
,
7904 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7905 &request
, &response
, NULL
, &map
);
7906 if (err
) goto leave
;
7910 xpath_ctx
= xmlXPathNewContext(response
);
7915 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7919 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7925 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7926 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7930 if (result
->nodesetval
->nodeNr
> 1) {
7931 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7935 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7936 xmlXPathFreeObject(result
); result
= NULL
;
7938 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7943 xmlXPathFreeObject(result
);
7944 xmlXPathFreeContext(xpath_ctx
);
7946 xmlFreeDoc(response
);
7949 isds_log(ILF_ISDS
, ILL_DEBUG
,
7950 _("CheckDataBox request processed by server successfully.\n"));
7951 #else /* not HAVE_LIBCURL */
7959 /* Get list of permissions to send commercial messages.
7960 * @context is ISDS session context.
7961 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7962 * @permissions is a reallocated list of permissions (struct
7963 * isds_commercial_permission*) to send commercial messages from @box_id. The
7964 * order of permissions is significant as the server applies the permissions
7965 * and associated pre-paid credits in the order. Empty list means no
7968 * IE_SUCCESS if the list has been obtained correctly,
7969 * or other appropriate error. */
7970 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7971 const char *box_id
, struct isds_list
**permissions
) {
7972 isds_error err
= IE_SUCCESS
;
7974 xmlDocPtr response
= NULL
;
7975 xmlXPathContextPtr xpath_ctx
= NULL
;
7976 xmlXPathObjectPtr result
= NULL
;
7979 if (!context
) return IE_INVALID_CONTEXT
;
7980 zfree(context
->long_message
);
7981 if (NULL
== permissions
) return IE_INVAL
;
7982 isds_list_free(permissions
);
7983 if (NULL
== box_id
) return IE_INVAL
;
7986 /* Check if connection is established */
7987 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7989 /* Do request and check for success */
7990 err
= build_send_dbid_request_check_response(context
,
7991 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7992 BAD_CAST box_id
, NULL
, &response
, NULL
);
7994 isds_log(ILF_ISDS
, ILL_DEBUG
,
7995 _("PDZInfo request processed by server successfully.\n"));
7999 /* Prepare structure */
8000 xpath_ctx
= xmlXPathNewContext(response
);
8005 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8010 /* Set context node */
8011 result
= xmlXPathEvalExpression(BAD_CAST
8012 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8018 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8019 struct isds_list
*prev_item
= NULL
;
8021 /* Iterate over all permission records */
8022 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8023 struct isds_list
*item
;
8025 /* Prepare structure */
8026 item
= calloc(1, sizeof(*item
));
8031 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8032 if (i
== 0) *permissions
= item
;
8033 else prev_item
->next
= item
;
8037 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8038 err
= extract_DbPDZRecord(context
,
8039 (struct isds_commercial_permission
**) (&item
->data
),
8041 if (err
) goto leave
;
8047 isds_list_free(permissions
);
8050 xmlXPathFreeObject(result
);
8051 xmlXPathFreeContext(xpath_ctx
);
8052 xmlFreeDoc(response
);
8054 #else /* not HAVE_LIBCURL */
8062 /* Get details about credit for sending pre-paid commercial messages.
8063 * @context is ISDS session context.
8064 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8065 * @from_date is first day of credit history to return in @history. Only
8066 * tm_year, tm_mon and tm_mday carry sane value.
8067 * @to_date is last day of credit history to return in @history. Only
8068 * tm_year, tm_mon and tm_mday carry sane value.
8069 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8070 * if you don't care. This and all other credit values are integers in
8071 * hundredths of Czech Crowns.
8072 * @email outputs notification e-mail address where notifications about credit
8073 * are sent. This is automatically reallocated string. Pass NULL if you don't
8074 * care. It can return NULL if no address is defined.
8075 * @history outputs auto-reallocated list of pointers to struct
8076 * isds_credit_event. Events in closed interval @from_time to @to_time are
8077 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8078 * are sorted by time.
8080 * IE_SUCCESS if the credit details have been obtained correctly,
8081 * or other appropriate error. Please note that server allows to retrieve
8082 * only limited history of events. */
8083 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8085 const struct tm
*from_date
, const struct tm
*to_date
,
8086 long int *credit
, char **email
, struct isds_list
**history
) {
8087 isds_error err
= IE_SUCCESS
;
8089 char *box_id_locale
= NULL
;
8090 xmlNodePtr request
= NULL
, node
;
8091 xmlNsPtr isds_ns
= NULL
;
8092 xmlChar
*string
= NULL
;
8094 xmlDocPtr response
= NULL
;
8095 xmlXPathContextPtr xpath_ctx
= NULL
;
8096 xmlXPathObjectPtr result
= NULL
;
8098 const xmlChar
*codes
[] = {
8106 const char *meanings
[] = {
8107 "Insufficient priviledges for the box",
8108 "The box does not exist",
8109 "Date is too long (history is not available after 15 months)",
8110 "Interval is too long (limit is 3 months)",
8113 const isds_error errors
[] = {
8120 struct code_map_isds_error map
= {
8122 .meanings
= meanings
,
8127 if (!context
) return IE_INVALID_CONTEXT
;
8128 zfree(context
->long_message
);
8130 /* Free output argument */
8131 if (NULL
!= credit
) *credit
= 0;
8132 if (NULL
!= email
) zfree(*email
);
8133 isds_list_free(history
);
8135 if (NULL
== box_id
) return IE_INVAL
;
8138 /* Check if connection is established */
8139 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8141 box_id_locale
= _isds_utf82locale((char*)box_id
);
8142 if (NULL
== box_id_locale
) {
8148 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8149 if (NULL
== request
) {
8150 isds_printf_message(context
,
8151 _("Could not build DataBoxCreditInfo request for %s box"),
8156 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8158 isds_log_message(context
, _("Could not create ISDS name space"));
8162 xmlSetNs(request
, isds_ns
);
8164 /* Add mandatory XSD:tIdDbInput child */
8165 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8166 /* Add mandatory dates elements with optional values */
8168 err
= tm2datestring(from_date
, &string
);
8170 isds_log_message(context
,
8171 _("Could not convert `from_date' argument to ISO date "
8175 INSERT_STRING(request
, "ciFromDate", string
);
8178 INSERT_STRING(request
, "ciFromDate", NULL
);
8181 err
= tm2datestring(to_date
, &string
);
8183 isds_log_message(context
,
8184 _("Could not convert `to_date' argument to ISO date "
8188 INSERT_STRING(request
, "ciTodate", string
);
8191 INSERT_STRING(request
, "ciTodate", NULL
);
8194 /* Send request and check response*/
8195 err
= send_destroy_request_check_response(context
,
8196 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8197 &request
, &response
, NULL
, &map
);
8198 if (err
) goto leave
;
8202 /* Set context to the root */
8203 xpath_ctx
= xmlXPathNewContext(response
);
8208 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8212 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8218 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8219 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8223 if (result
->nodesetval
->nodeNr
> 1) {
8224 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8228 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8229 xmlXPathFreeObject(result
); result
= NULL
;
8231 /* Extract common data */
8232 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8233 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8235 /* Extract records */
8236 if (NULL
== history
) goto leave
;
8237 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8243 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8244 struct isds_list
*prev_item
= NULL
;
8246 /* Iterate over all records */
8247 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8248 struct isds_list
*item
;
8250 /* Prepare structure */
8251 item
= calloc(1, sizeof(*item
));
8256 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8257 if (i
== 0) *history
= item
;
8258 else prev_item
->next
= item
;
8262 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8263 err
= extract_CiRecord(context
,
8264 (struct isds_credit_event
**) (&item
->data
),
8266 if (err
) goto leave
;
8272 isds_log(ILF_ISDS
, ILL_DEBUG
,
8273 _("DataBoxCreditInfo request processed by server successfully.\n"));
8276 isds_list_free(history
);
8277 if (NULL
!= email
) zfree(*email
)
8280 free(box_id_locale
);
8281 xmlXPathFreeObject(result
);
8282 xmlXPathFreeContext(xpath_ctx
);
8283 xmlFreeDoc(response
);
8285 #else /* not HAVE_LIBCURL */
8293 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8294 * code, destroy response and log success.
8295 * @context is ISDS session context.
8296 * @service_name is name of SERVICE_DB_MANIPULATION service
8297 * @box_id is UTF-8 encoded box identifier as zero terminated string
8298 * @approval is optional external approval of box manipulation
8299 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8300 * NULL, if you don't care. */
8301 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8302 struct isds_ctx
*context
, const xmlChar
*service_name
,
8303 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8304 xmlChar
**refnumber
) {
8305 isds_error err
= IE_SUCCESS
;
8307 xmlDocPtr response
= NULL
;
8310 if (!context
) return IE_INVALID_CONTEXT
;
8311 zfree(context
->long_message
);
8312 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8315 /* Check if connection is established */
8316 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8318 /* Do request and check for success */
8319 err
= build_send_dbid_request_check_response(context
,
8320 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8321 &response
, refnumber
);
8322 xmlFreeDoc(response
);
8325 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8326 isds_log(ILF_ISDS
, ILL_DEBUG
,
8327 _("%s request processed by server successfully.\n"),
8328 service_name_locale
);
8329 free(service_name_locale
);
8331 #else /* not HAVE_LIBCURL */
8339 /* Switch box into state where box can receive commercial messages (off by
8341 * @context is ISDS session context.
8342 * @box_id is UTF-8 encoded box identifier as zero terminated string
8343 * @allow is true for enable, false for disable commercial messages income
8344 * @approval is optional external approval of box manipulation
8345 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8346 * NULL, if you don't care. */
8347 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8348 const char *box_id
, const _Bool allow
,
8349 const struct isds_approval
*approval
, char **refnumber
) {
8350 return build_send_manipulationdbid_request_check_drop_response(context
,
8351 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8352 BAD_CAST
"ClearOpenAddressing",
8353 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8357 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8358 * message acceptance). This is just a box permission. Sender must apply
8359 * such role by sending each message.
8360 * @context is ISDS session context.
8361 * @box_id is UTF-8 encoded box identifier as zero terminated string
8362 * @allow is true for enable, false for disable OVM role permission
8363 * @approval is optional external approval of box manipulation
8364 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8365 * NULL, if you don't care. */
8366 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8367 const char *box_id
, const _Bool allow
,
8368 const struct isds_approval
*approval
, char **refnumber
) {
8369 return build_send_manipulationdbid_request_check_drop_response(context
,
8370 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8371 BAD_CAST
"ClearEffectiveOVM",
8372 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8376 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8377 * code, destroy response and log success.
8378 * @context is ISDS session context.
8379 * @service_name is name of SERVICE_DB_MANIPULATION service
8380 * @owner is structure describing box
8381 * @approval is optional external approval of box manipulation
8382 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8383 * NULL, if you don't care. */
8384 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8385 struct isds_ctx
*context
, const xmlChar
*service_name
,
8386 const struct isds_DbOwnerInfo
*owner
,
8387 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8388 isds_error err
= IE_SUCCESS
;
8390 char *service_name_locale
= NULL
;
8391 xmlNodePtr request
= NULL
, db_owner_info
;
8392 xmlNsPtr isds_ns
= NULL
;
8396 if (!context
) return IE_INVALID_CONTEXT
;
8397 zfree(context
->long_message
);
8398 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8401 service_name_locale
= _isds_utf82locale((char*)service_name
);
8402 if (!service_name_locale
) {
8408 request
= xmlNewNode(NULL
, service_name
);
8410 isds_printf_message(context
,
8411 _("Could not build %s request"), service_name_locale
);
8415 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8417 isds_log_message(context
, _("Could not create ISDS name space"));
8421 xmlSetNs(request
, isds_ns
);
8424 /* Add XSD:tOwnerInfoInput child*/
8425 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8426 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8427 if (err
) goto leave
;
8429 /* Add XSD:gExtApproval*/
8430 err
= insert_GExtApproval(context
, approval
, request
);
8431 if (err
) goto leave
;
8433 /* Send it to server and process response */
8434 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8435 service_name
, &request
, refnumber
);
8438 xmlFreeNode(request
);
8439 free(service_name_locale
);
8440 #else /* not HAVE_LIBCURL */
8448 /* Switch box accessibility state on request of box owner.
8449 * Despite the name, owner must do the request off-line. This function is
8450 * designed for such off-line meeting points (e.g. Czech POINT).
8451 * @context is ISDS session context.
8452 * @box identifies box to switch accessibility state.
8453 * @allow is true for making accessible, false to disallow access.
8454 * @approval is optional external approval of box manipulation
8455 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8456 * NULL, if you don't care. */
8457 isds_error
isds_switch_box_accessibility_on_owner_request(
8458 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8459 const _Bool allow
, const struct isds_approval
*approval
,
8461 return build_send_manipulationdbowner_request_check_drop_response(context
,
8462 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8463 BAD_CAST
"DisableOwnDataBox",
8464 box
, approval
, (xmlChar
**) refnumber
);
8468 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8470 * @context is ISDS session context.
8471 * @box identifies box to switch accessibility state.
8472 * @since is date since accessibility has been denied. This can be past too.
8473 * Only tm_year, tm_mon and tm_mday carry sane value.
8474 * @approval is optional external approval of box manipulation
8475 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8476 * NULL, if you don't care. */
8477 isds_error
isds_disable_box_accessibility_externaly(
8478 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8479 const struct tm
*since
, const struct isds_approval
*approval
,
8481 isds_error err
= IE_SUCCESS
;
8483 char *service_name_locale
= NULL
;
8484 xmlNodePtr request
= NULL
, node
;
8485 xmlNsPtr isds_ns
= NULL
;
8486 xmlChar
*string
= NULL
;
8490 if (!context
) return IE_INVALID_CONTEXT
;
8491 zfree(context
->long_message
);
8492 if (!box
|| !since
) return IE_INVAL
;
8496 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8498 isds_printf_message(context
,
8499 _("Could not build %s request"), "DisableDataBoxExternally");
8503 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8505 isds_log_message(context
, _("Could not create ISDS name space"));
8509 xmlSetNs(request
, isds_ns
);
8512 /* Add @box identification */
8513 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8514 err
= insert_DbOwnerInfo(context
, box
, node
);
8515 if (err
) goto leave
;
8517 /* Add @since date */
8518 err
= tm2datestring(since
, &string
);
8520 isds_log_message(context
,
8521 _("Could not convert `since' argument to ISO date string"));
8524 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8528 err
= insert_GExtApproval(context
, approval
, request
);
8529 if (err
) goto leave
;
8531 /* Send it to server and process response */
8532 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8533 BAD_CAST
"DisableDataBoxExternally", &request
,
8534 (xmlChar
**) refnumber
);
8538 xmlFreeNode(request
);
8539 free(service_name_locale
);
8540 #else /* not HAVE_LIBCURL */
8549 /* Insert struct isds_message data (envelope (recipient data optional) and
8550 * documents into XML tree
8551 * @context is session context
8552 * @outgoing_message is libisds structure with message data
8553 * @create_message is XML CreateMessage or CreateMultipleMessage element
8554 * @process_recipient true for recipient data serialization, false for no
8556 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8557 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8558 const _Bool process_recipient
) {
8560 isds_error err
= IE_SUCCESS
;
8561 xmlNodePtr envelope
, dm_files
, node
;
8562 xmlChar
*string
= NULL
;
8564 if (!context
) return IE_INVALID_CONTEXT
;
8565 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8568 /* Build envelope */
8569 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8571 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8572 "%s element"), create_message
->name
);
8576 if (!outgoing_message
->envelope
) {
8577 isds_log_message(context
, _("Outgoing message is missing envelope"));
8582 /* Insert optional message type */
8583 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8585 if (err
) goto leave
;
8587 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8588 outgoing_message
->envelope
->dmSenderOrgUnit
);
8589 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8590 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8592 if (process_recipient
) {
8593 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8594 isds_log_message(context
,
8595 _("Outgoing message is missing recipient box identifier"));
8599 INSERT_STRING(envelope
, "dbIDRecipient",
8600 outgoing_message
->envelope
->dbIDRecipient
);
8602 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8603 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8604 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8605 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8606 INSERT_STRING(envelope
, "dmToHands",
8607 outgoing_message
->envelope
->dmToHands
);
8610 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8612 INSERT_STRING(envelope
, "dmAnnotation",
8613 outgoing_message
->envelope
->dmAnnotation
);
8615 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8616 0, 50, "dmRecipientRefNumber");
8617 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8618 outgoing_message
->envelope
->dmRecipientRefNumber
);
8620 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8621 0, 50, "dmSenderRefNumber");
8622 INSERT_STRING(envelope
, "dmSenderRefNumber",
8623 outgoing_message
->envelope
->dmSenderRefNumber
);
8625 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8626 0, 50, "dmRecipientIdent");
8627 INSERT_STRING(envelope
, "dmRecipientIdent",
8628 outgoing_message
->envelope
->dmRecipientIdent
);
8630 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8631 0, 50, "dmSenderIdent");
8632 INSERT_STRING(envelope
, "dmSenderIdent",
8633 outgoing_message
->envelope
->dmSenderIdent
);
8635 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8636 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8637 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8638 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8639 INSERT_STRING(envelope
, "dmLegalTitleSect",
8640 outgoing_message
->envelope
->dmLegalTitleSect
);
8641 INSERT_STRING(envelope
, "dmLegalTitlePar",
8642 outgoing_message
->envelope
->dmLegalTitlePar
);
8643 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8644 outgoing_message
->envelope
->dmLegalTitlePoint
);
8646 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8647 outgoing_message
->envelope
->dmPersonalDelivery
);
8648 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8649 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8651 /* ???: Should we require value for dbEffectiveOVM sender?
8652 * ISDS has default as true */
8653 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8654 INSERT_BOOLEAN(envelope
, "dmOVM",
8655 outgoing_message
->envelope
->dmPublishOwnID
);
8658 /* Append dmFiles */
8659 if (!outgoing_message
->documents
) {
8660 isds_log_message(context
,
8661 _("Outgoing message is missing list of documents"));
8665 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8667 isds_printf_message(context
, _("Could not add dmFiles child to "
8668 "%s element"), create_message
->name
);
8673 /* Check for document hierarchy */
8674 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8675 if (err
) goto leave
;
8677 /* Process each document */
8678 for (struct isds_list
*item
=
8679 (struct isds_list
*) outgoing_message
->documents
;
8680 item
; item
= item
->next
) {
8682 isds_log_message(context
,
8683 _("List of documents contains empty item"));
8687 /* FIXME: Check for dmFileMetaType and for document references.
8688 * Only first document can be of MAIN type */
8689 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8692 if (err
) goto leave
;
8699 #endif /* HAVE_LIBCURL */
8702 /* Send a message via ISDS to a recipient
8703 * @context is session context
8704 * @outgoing_message is message to send; Some members are mandatory (like
8705 * dbIDRecipient), some are optional and some are irrelevant (especially data
8706 * about sender). Included pointer to isds_list documents must contain at
8707 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8708 * members will be filled with valid data from ISDS. Exact list of write
8709 * members is subject to change. Currently dmID is changed.
8710 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8711 isds_error
isds_send_message(struct isds_ctx
*context
,
8712 struct isds_message
*outgoing_message
) {
8714 isds_error err
= IE_SUCCESS
;
8716 xmlNsPtr isds_ns
= NULL
;
8717 xmlNodePtr request
= NULL
;
8718 xmlDocPtr response
= NULL
;
8719 xmlChar
*code
= NULL
, *message
= NULL
;
8720 xmlXPathContextPtr xpath_ctx
= NULL
;
8721 xmlXPathObjectPtr result
= NULL
;
8722 /*_Bool message_is_complete = 0;*/
8725 if (!context
) return IE_INVALID_CONTEXT
;
8726 zfree(context
->long_message
);
8727 if (!outgoing_message
) return IE_INVAL
;
8730 /* Check if connection is established
8731 * TODO: This check should be done downstairs. */
8732 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8735 /* Build CreateMessage request */
8736 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8738 isds_log_message(context
,
8739 _("Could not build CreateMessage request"));
8742 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8744 isds_log_message(context
, _("Could not create ISDS name space"));
8745 xmlFreeNode(request
);
8748 xmlSetNs(request
, isds_ns
);
8750 /* Append envelope and files */
8751 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8752 if (err
) goto leave
;
8755 /* Signal we can serialize message since now */
8756 /*message_is_complete = 1;*/
8759 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8762 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8764 /* Don't' destroy request, we want to provide it to application later */
8767 isds_log(ILF_ISDS
, ILL_DEBUG
,
8768 _("Processing ISDS response on CreateMessage "
8769 "request failed\n"));
8773 /* Check for response status */
8774 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8775 &code
, &message
, NULL
);
8777 isds_log(ILF_ISDS
, ILL_DEBUG
,
8778 _("ISDS response on CreateMessage request "
8779 "is missing status\n"));
8783 /* Request processed, but refused by server or server failed */
8784 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8785 char *box_id_locale
=
8786 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8787 char *code_locale
= _isds_utf82locale((char*)code
);
8788 char *message_locale
= _isds_utf82locale((char*)message
);
8789 isds_log(ILF_ISDS
, ILL_DEBUG
,
8790 _("Server did not accept message for %s on CreateMessage "
8791 "request (code=%s, message=%s)\n"),
8792 box_id_locale
, code_locale
, message_locale
);
8793 isds_log_message(context
, message_locale
);
8794 free(box_id_locale
);
8796 free(message_locale
);
8803 xpath_ctx
= xmlXPathNewContext(response
);
8808 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8812 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8818 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8819 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8823 if (result
->nodesetval
->nodeNr
> 1) {
8824 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8828 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8829 xmlXPathFreeObject(result
); result
= NULL
;
8831 if (outgoing_message
->envelope
->dmID
) {
8832 free(outgoing_message
->envelope
->dmID
);
8833 outgoing_message
->envelope
->dmID
= NULL
;
8835 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8836 if (!outgoing_message
->envelope
->dmID
) {
8837 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8838 "but did not return assigned message ID\n"));
8842 /* TODO: Serialize message into structure member raw */
8843 /* XXX: Each web service transport message in different format.
8844 * Therefore it's not possible to save them directly.
8845 * To save them, one must figure out common format.
8846 * We can leave it on application, or we can implement the ESS format. */
8847 /*if (message_is_complete) {
8848 if (outgoing_message->envelope->dmID) {
8850 /* Add assigned message ID as first child*/
8851 /*xmlNodePtr dmid_text = xmlNewText(
8852 (xmlChar *) outgoing_message->envelope->dmID);
8853 if (!dmid_text) goto serialization_failed;
8855 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8857 if (!dmid_element) {
8858 xmlFreeNode(dmid_text);
8859 goto serialization_failed;
8862 xmlNodePtr dmid_element_with_text =
8863 xmlAddChild(dmid_element, dmid_text);
8864 if (!dmid_element_with_text) {
8865 xmlFreeNode(dmid_element);
8866 xmlFreeNode(dmid_text);
8867 goto serialization_failed;
8870 node = xmlAddPrevSibling(envelope->childern,
8871 dmid_element_with_text);
8873 xmlFreeNodeList(dmid_element_with_text);
8874 goto serialization_failed;
8878 /* Serialize message with ID into raw */
8879 /*buffer = serialize_element(envelope)*/
8882 serialization_failed:
8887 xmlXPathFreeObject(result
);
8888 xmlXPathFreeContext(xpath_ctx
);
8892 xmlFreeDoc(response
);
8893 xmlFreeNode(request
);
8896 isds_log(ILF_ISDS
, ILL_DEBUG
,
8897 _("CreateMessage request processed by server "
8898 "successfully.\n"));
8899 #else /* not HAVE_LIBCURL */
8907 /* Send a message via ISDS to a multiple recipients
8908 * @context is session context
8909 * @outgoing_message is message to send; Some members are mandatory,
8910 * some are optional and some are irrelevant (especially data
8911 * about sender). Data about recipient will be substituted by ISDS from
8912 * @copies. Included pointer to isds_list documents must
8913 * contain at least one document of FILEMETATYPE_MAIN.
8914 * @copies is list of isds_message_copy structures addressing all desired
8915 * recipients. This is read-write structure, some members will be filled with
8916 * valid data from ISDS (message IDs, error codes, error descriptions).
8918 * ISDS_SUCCESS if all messages have been sent
8919 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8920 * succeeded messages can be identified by copies->data->error),
8921 * or other error code if something other goes wrong. */
8922 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8923 const struct isds_message
*outgoing_message
,
8924 struct isds_list
*copies
) {
8926 isds_error err
= IE_SUCCESS
;
8928 isds_error append_err
;
8929 xmlNsPtr isds_ns
= NULL
;
8930 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8931 struct isds_list
*item
;
8932 struct isds_message_copy
*copy
;
8933 xmlDocPtr response
= NULL
;
8934 xmlChar
*code
= NULL
, *message
= NULL
;
8935 xmlXPathContextPtr xpath_ctx
= NULL
;
8936 xmlXPathObjectPtr result
= NULL
;
8937 xmlChar
*string
= NULL
;
8941 if (!context
) return IE_INVALID_CONTEXT
;
8942 zfree(context
->long_message
);
8943 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8946 /* Check if connection is established
8947 * TODO: This check should be done downstairs. */
8948 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8951 /* Build CreateMultipleMessage request */
8952 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8954 isds_log_message(context
,
8955 _("Could not build CreateMultipleMessage request"));
8958 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8960 isds_log_message(context
, _("Could not create ISDS name space"));
8961 xmlFreeNode(request
);
8964 xmlSetNs(request
, isds_ns
);
8967 /* Build recipients */
8968 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8970 isds_log_message(context
, _("Could not add dmRecipients child to "
8971 "CreateMultipleMessage element"));
8972 xmlFreeNode(request
);
8976 /* Insert each recipient */
8977 for (item
= copies
; item
; item
= item
->next
) {
8978 copy
= (struct isds_message_copy
*) item
->data
;
8980 isds_log_message(context
,
8981 _("`copies' list item contains empty data"));
8986 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8988 isds_log_message(context
, _("Could not add dmRecipient child to "
8989 "dmRecipients element"));
8994 if (!copy
->dbIDRecipient
) {
8995 isds_log_message(context
,
8996 _("Message copy is missing recipient box identifier"));
9000 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9001 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9002 copy
->dmRecipientOrgUnit
);
9003 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9004 copy
->dmRecipientOrgUnitNum
, string
);
9005 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9008 /* Append envelope and files */
9009 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9010 if (err
) goto leave
;
9013 isds_log(ILF_ISDS
, ILL_DEBUG
,
9014 _("Sending CreateMultipleMessage request to ISDS\n"));
9017 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9019 isds_log(ILF_ISDS
, ILL_DEBUG
,
9020 _("Processing ISDS response on CreateMultipleMessage "
9021 "request failed\n"));
9025 /* Check for response status */
9026 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9027 &code
, &message
, NULL
);
9029 isds_log(ILF_ISDS
, ILL_DEBUG
,
9030 _("ISDS response on CreateMultipleMessage request "
9031 "is missing status\n"));
9035 /* Request processed, but some copies failed */
9036 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9037 char *box_id_locale
=
9038 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9039 char *code_locale
= _isds_utf82locale((char*)code
);
9040 char *message_locale
= _isds_utf82locale((char*)message
);
9041 isds_log(ILF_ISDS
, ILL_DEBUG
,
9042 _("Server did accept message for multiple recipients "
9043 "on CreateMultipleMessage request but delivery to "
9044 "some of them failed (code=%s, message=%s)\n"),
9045 box_id_locale
, code_locale
, message_locale
);
9046 isds_log_message(context
, message_locale
);
9047 free(box_id_locale
);
9049 free(message_locale
);
9050 err
= IE_PARTIAL_SUCCESS
;
9053 /* Request refused by server as whole */
9054 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9055 char *box_id_locale
=
9056 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9057 char *code_locale
= _isds_utf82locale((char*)code
);
9058 char *message_locale
= _isds_utf82locale((char*)message
);
9059 isds_log(ILF_ISDS
, ILL_DEBUG
,
9060 _("Server did not accept message for multiple recipients "
9061 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9062 box_id_locale
, code_locale
, message_locale
);
9063 isds_log_message(context
, message_locale
);
9064 free(box_id_locale
);
9066 free(message_locale
);
9073 xpath_ctx
= xmlXPathNewContext(response
);
9078 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9082 result
= xmlXPathEvalExpression(
9083 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9084 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9090 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9091 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9096 /* Extract message ID and delivery status for each copy */
9097 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9098 item
= item
->next
, i
++) {
9099 copy
= (struct isds_message_copy
*) item
->data
;
9100 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9102 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9108 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9109 isds_printf_message(context
, _("ISDS returned unexpected number of "
9110 "message copy delivery states: %d"),
9111 result
->nodesetval
->nodeNr
);
9120 xmlXPathFreeObject(result
);
9121 xmlXPathFreeContext(xpath_ctx
);
9125 xmlFreeDoc(response
);
9126 xmlFreeNode(request
);
9129 isds_log(ILF_ISDS
, ILL_DEBUG
,
9130 _("CreateMultipleMessageResponse request processed by server "
9131 "successfully.\n"));
9132 #else /* not HAVE_LIBCURL */
9140 /* Get list of messages. This is common core for getting sent or received
9142 * Any criterion argument can be NULL, if you don't care about it.
9143 * @context is session context. Must not be NULL.
9144 * @outgoing_direction is true if you want list of outgoing messages,
9145 * it's false if you want incoming messages.
9146 * @from_time is minimal time and date of message sending inclusive.
9147 * @to_time is maximal time and date of message sending inclusive
9148 * @organization_unit_number is number of sender/recipient respectively.
9149 * @status_filter is bit field of isds_message_status values. Use special
9150 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9151 * all values, you can use bit-wise arithmetic if you want.)
9152 * @offset is index of first message we are interested in. First message is 1.
9153 * Set to 0 (or 1) if you don't care.
9154 * @number is maximal length of list you want to get as input value, outputs
9155 * number of messages matching these criteria. Can be NULL if you don't care
9156 * (applies to output value either).
9157 * @messages is automatically reallocated list of isds_message's. Be ware that
9158 * it returns only brief overview (envelope and some other fields) about each
9159 * message, not the complete message. FIXME: Specify exact fields.
9160 * The list is sorted by delivery time in ascending order.
9161 * Use NULL if you don't care about don't need the data (useful if you want to
9162 * know only the @number). If you provide &NULL, list will be allocated on
9163 * heap, if you provide pointer to non-NULL, list will be freed automatically
9164 * at first. Also in case of error the list will be NULLed.
9165 * @return IE_SUCCESS or appropriate error code. */
9166 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9167 _Bool outgoing_direction
,
9168 const struct timeval
*from_time
, const struct timeval
*to_time
,
9169 const long int *organization_unit_number
,
9170 const unsigned int status_filter
,
9171 const unsigned long int offset
, unsigned long int *number
,
9172 struct isds_list
**messages
) {
9174 isds_error err
= IE_SUCCESS
;
9176 xmlNsPtr isds_ns
= NULL
;
9177 xmlNodePtr request
= NULL
, node
;
9178 xmlDocPtr response
= NULL
;
9179 xmlChar
*code
= NULL
, *message
= NULL
;
9180 xmlXPathContextPtr xpath_ctx
= NULL
;
9181 xmlXPathObjectPtr result
= NULL
;
9182 xmlChar
*string
= NULL
;
9186 if (!context
) return IE_INVALID_CONTEXT
;
9187 zfree(context
->long_message
);
9189 /* Free former message list if any */
9190 if (messages
) isds_list_free(messages
);
9193 /* Check if connection is established
9194 * TODO: This check should be done downstairs. */
9195 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9197 /* Build GetListOf*Messages request */
9198 request
= xmlNewNode(NULL
,
9199 (outgoing_direction
) ?
9200 BAD_CAST
"GetListOfSentMessages" :
9201 BAD_CAST
"GetListOfReceivedMessages"
9204 isds_log_message(context
,
9205 (outgoing_direction
) ?
9206 _("Could not build GetListOfSentMessages request") :
9207 _("Could not build GetListOfReceivedMessages request")
9211 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9213 isds_log_message(context
, _("Could not create ISDS name space"));
9214 xmlFreeNode(request
);
9217 xmlSetNs(request
, isds_ns
);
9221 err
= timeval2timestring(from_time
, &string
);
9222 if (err
) goto leave
;
9224 INSERT_STRING(request
, "dmFromTime", string
);
9225 free(string
); string
= NULL
;
9228 err
= timeval2timestring(to_time
, &string
);
9229 if (err
) goto leave
;
9231 INSERT_STRING(request
, "dmToTime", string
);
9232 free(string
); string
= NULL
;
9234 if (outgoing_direction
) {
9235 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9236 organization_unit_number
, string
);
9238 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9239 organization_unit_number
, string
);
9242 if (status_filter
> MESSAGESTATE_ANY
) {
9243 isds_printf_message(context
,
9244 _("Invalid message state filter value: %ld"), status_filter
);
9248 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9251 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9253 INSERT_STRING(request
, "dmOffset", "1");
9256 /* number 0 means no limit */
9257 if (number
&& *number
== 0) {
9258 INSERT_STRING(request
, "dmLimit", NULL
);
9260 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9264 isds_log(ILF_ISDS
, ILL_DEBUG
,
9265 (outgoing_direction
) ?
9266 _("Sending GetListOfSentMessages request to ISDS\n") :
9267 _("Sending GetListOfReceivedMessages request to ISDS\n")
9271 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9272 xmlFreeNode(request
); request
= NULL
;
9275 isds_log(ILF_ISDS
, ILL_DEBUG
,
9276 (outgoing_direction
) ?
9277 _("Processing ISDS response on GetListOfSentMessages "
9278 "request failed\n") :
9279 _("Processing ISDS response on GetListOfReceivedMessages "
9285 /* Check for response status */
9286 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9287 &code
, &message
, NULL
);
9289 isds_log(ILF_ISDS
, ILL_DEBUG
,
9290 (outgoing_direction
) ?
9291 _("ISDS response on GetListOfSentMessages request "
9292 "is missing status\n") :
9293 _("ISDS response on GetListOfReceivedMessages request "
9294 "is missing status\n")
9299 /* Request processed, but nothing found */
9300 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9301 char *code_locale
= _isds_utf82locale((char*)code
);
9302 char *message_locale
= _isds_utf82locale((char*)message
);
9303 isds_log(ILF_ISDS
, ILL_DEBUG
,
9304 (outgoing_direction
) ?
9305 _("Server refused GetListOfSentMessages request "
9306 "(code=%s, message=%s)\n") :
9307 _("Server refused GetListOfReceivedMessages request "
9308 "(code=%s, message=%s)\n"),
9309 code_locale
, message_locale
);
9310 isds_log_message(context
, message_locale
);
9312 free(message_locale
);
9319 xpath_ctx
= xmlXPathNewContext(response
);
9324 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9328 result
= xmlXPathEvalExpression(
9329 (outgoing_direction
) ?
9330 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9331 "isds:dmRecords/isds:dmRecord" :
9332 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9333 "isds:dmRecords/isds:dmRecord",
9340 /* Fill output arguments in */
9341 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9342 struct isds_envelope
*envelope
;
9343 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9345 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9346 /* Create new message */
9347 item
= calloc(1, sizeof(*item
));
9352 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9353 item
->data
= calloc(1, sizeof(struct isds_message
));
9355 isds_list_free(&item
);
9360 /* Extract envelope data */
9361 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9363 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9365 isds_list_free(&item
);
9369 /* Attach extracted envelope */
9370 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9372 /* Append new message into the list */
9374 *messages
= last_item
= item
;
9376 last_item
->next
= item
;
9381 if (number
) *number
= count
;
9385 isds_list_free(messages
);
9389 xmlXPathFreeObject(result
);
9390 xmlXPathFreeContext(xpath_ctx
);
9394 xmlFreeDoc(response
);
9395 xmlFreeNode(request
);
9398 isds_log(ILF_ISDS
, ILL_DEBUG
,
9399 (outgoing_direction
) ?
9400 _("GetListOfSentMessages request processed by server "
9401 "successfully.\n") :
9402 _("GetListOfReceivedMessages request processed by server "
9405 #else /* not HAVE_LIBCURL */
9412 /* Get list of outgoing (already sent) messages.
9413 * Any criterion argument can be NULL, if you don't care about it.
9414 * @context is session context. Must not be NULL.
9415 * @from_time is minimal time and date of message sending inclusive.
9416 * @to_time is maximal time and date of message sending inclusive
9417 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9418 * @status_filter is bit field of isds_message_status values. Use special
9419 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9420 * all values, you can use bit-wise arithmetic if you want.)
9421 * @offset is index of first message we are interested in. First message is 1.
9422 * Set to 0 (or 1) if you don't care.
9423 * @number is maximal length of list you want to get as input value, outputs
9424 * number of messages matching these criteria. Can be NULL if you don't care
9425 * (applies to output value either).
9426 * @messages is automatically reallocated list of isds_message's. Be ware that
9427 * it returns only brief overview (envelope and some other fields) about each
9428 * message, not the complete message. FIXME: Specify exact fields.
9429 * The list is sorted by delivery time in ascending order.
9430 * Use NULL if you don't care about the meta data (useful if you want to know
9431 * only the @number). If you provide &NULL, list will be allocated on heap,
9432 * if you provide pointer to non-NULL, list will be freed automatically at
9433 * first. Also in case of error the list will be NULLed.
9434 * @return IE_SUCCESS or appropriate error code. */
9435 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9436 const struct timeval
*from_time
, const struct timeval
*to_time
,
9437 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9438 const unsigned long int offset
, unsigned long int *number
,
9439 struct isds_list
**messages
) {
9441 return isds_get_list_of_messages(
9443 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9449 /* Get list of incoming (addressed to you) messages.
9450 * Any criterion argument can be NULL, if you don't care about it.
9451 * @context is session context. Must not be NULL.
9452 * @from_time is minimal time and date of message sending inclusive.
9453 * @to_time is maximal time and date of message sending inclusive
9454 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9455 * @status_filter is bit field of isds_message_status values. Use special
9456 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9457 * all values, you can use bit-wise arithmetic if you want.)
9458 * @offset is index of first message we are interested in. First message is 1.
9459 * Set to 0 (or 1) if you don't care.
9460 * @number is maximal length of list you want to get as input value, outputs
9461 * number of messages matching these criteria. Can be NULL if you don't care
9462 * (applies to output value either).
9463 * @messages is automatically reallocated list of isds_message's. Be ware that
9464 * it returns only brief overview (envelope and some other fields) about each
9465 * message, not the complete message. FIXME: Specify exact fields.
9466 * Use NULL if you don't care about the meta data (useful if you want to know
9467 * only the @number). If you provide &NULL, list will be allocated on heap,
9468 * if you provide pointer to non-NULL, list will be freed automatically at
9469 * first. Also in case of error the list will be NULLed.
9470 * @return IE_SUCCESS or appropriate error code. */
9471 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9472 const struct timeval
*from_time
, const struct timeval
*to_time
,
9473 const long int *dmRecipientOrgUnitNum
,
9474 const unsigned int status_filter
,
9475 const unsigned long int offset
, unsigned long int *number
,
9476 struct isds_list
**messages
) {
9478 return isds_get_list_of_messages(
9480 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9486 /* Get list of sent message state changes.
9487 * Any criterion argument can be NULL, if you don't care about it.
9488 * @context is session context. Must not be NULL.
9489 * @from_time is minimal time and date of status changes inclusive
9490 * @to_time is maximal time and date of status changes inclusive
9491 * @changed_states is automatically reallocated list of
9492 * isds_message_status_change's. If you provide &NULL, list will be allocated
9493 * on heap, if you provide pointer to non-NULL, list will be freed
9494 * automatically at first. Also in case of error the list will be NULLed.
9495 * XXX: The list item ordering is not specified.
9496 * XXX: Server provides only `recent' changes.
9497 * @return IE_SUCCESS or appropriate error code. */
9498 isds_error
isds_get_list_of_sent_message_state_changes(
9499 struct isds_ctx
*context
,
9500 const struct timeval
*from_time
, const struct timeval
*to_time
,
9501 struct isds_list
**changed_states
) {
9503 isds_error err
= IE_SUCCESS
;
9505 xmlNsPtr isds_ns
= NULL
;
9506 xmlNodePtr request
= NULL
, node
;
9507 xmlDocPtr response
= NULL
;
9508 xmlXPathContextPtr xpath_ctx
= NULL
;
9509 xmlXPathObjectPtr result
= NULL
;
9510 xmlChar
*string
= NULL
;
9514 if (!context
) return IE_INVALID_CONTEXT
;
9515 zfree(context
->long_message
);
9517 /* Free former message list if any */
9518 isds_list_free(changed_states
);
9521 /* Check if connection is established
9522 * TODO: This check should be done downstairs. */
9523 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9525 /* Build GetMessageStateChanges request */
9526 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9528 isds_log_message(context
,
9529 _("Could not build GetMessageStateChanges request"));
9532 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9534 isds_log_message(context
, _("Could not create ISDS name space"));
9535 xmlFreeNode(request
);
9538 xmlSetNs(request
, isds_ns
);
9542 err
= timeval2timestring(from_time
, &string
);
9543 if (err
) goto leave
;
9545 INSERT_STRING(request
, "dmFromTime", string
);
9549 err
= timeval2timestring(to_time
, &string
);
9550 if (err
) goto leave
;
9552 INSERT_STRING(request
, "dmToTime", string
);
9557 err
= send_destroy_request_check_response(context
,
9558 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9559 &response
, NULL
, NULL
);
9560 if (err
) goto leave
;
9564 xpath_ctx
= xmlXPathNewContext(response
);
9569 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9573 result
= xmlXPathEvalExpression(
9574 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9575 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9581 /* Fill output arguments in */
9582 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9583 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9585 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9586 /* Create new status change */
9587 item
= calloc(1, sizeof(*item
));
9593 (void(*)(void**)) &isds_message_status_change_free
;
9595 /* Extract message status change */
9596 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9597 err
= extract_StateChangesRecord(context
,
9598 (struct isds_message_status_change
**) &item
->data
,
9601 isds_list_free(&item
);
9605 /* Append new message status change into the list */
9606 if (!*changed_states
) {
9607 *changed_states
= last_item
= item
;
9609 last_item
->next
= item
;
9617 isds_list_free(changed_states
);
9621 xmlXPathFreeObject(result
);
9622 xmlXPathFreeContext(xpath_ctx
);
9623 xmlFreeDoc(response
);
9624 xmlFreeNode(request
);
9627 isds_log(ILF_ISDS
, ILL_DEBUG
,
9628 _("GetMessageStateChanges request processed by server "
9629 "successfully.\n"));
9630 #else /* not HAVE_LIBCURL */
9638 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9640 * @context is session context
9641 * @service is ISDS WS service handler
9642 * @service_name is name of SERVICE_DM_OPERATIONS
9643 * @message_id is message ID to send as service argument to ISDS
9644 * @response is reallocated server SOAP body response as XML document
9645 * @raw_response is reallocated bit stream with response body. Use
9646 * NULL if you don't care
9647 * @raw_response_length is size of @raw_response in bytes
9648 * @code is reallocated ISDS status code
9649 * @status_message is reallocated ISDS status message
9650 * @return error coded from lower layer, context message will be set up
9652 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9653 const isds_service service
, const xmlChar
*service_name
,
9654 const char *message_id
,
9655 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9656 xmlChar
**code
, xmlChar
**status_message
) {
9658 isds_error err
= IE_SUCCESS
;
9659 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9660 xmlNodePtr request
= NULL
, node
;
9661 xmlNsPtr isds_ns
= NULL
;
9663 if (!context
) return IE_INVALID_CONTEXT
;
9664 if (!service_name
|| !message_id
) return IE_INVAL
;
9665 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9666 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9668 /* Free output argument */
9669 xmlFreeDoc(*response
); *response
= NULL
;
9670 if (raw_response
) zfree(*raw_response
);
9672 zfree(*status_message
);
9675 /* Check if connection is established
9676 * TODO: This check should be done downstairs. */
9677 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9679 service_name_locale
= _isds_utf82locale((char*)service_name
);
9680 message_id_locale
= _isds_utf82locale(message_id
);
9681 if (!service_name_locale
|| !message_id_locale
) {
9687 request
= xmlNewNode(NULL
, service_name
);
9689 isds_printf_message(context
,
9690 _("Could not build %s request for %s message ID"),
9691 service_name_locale
, message_id_locale
);
9695 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9697 isds_log_message(context
, _("Could not create ISDS name space"));
9701 xmlSetNs(request
, isds_ns
);
9704 /* Add requested ID */
9705 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9706 if (err
) goto leave
;
9707 INSERT_STRING(request
, "dmID", message_id
);
9710 isds_log(ILF_ISDS
, ILL_DEBUG
,
9711 _("Sending %s request for %s message ID to ISDS\n"),
9712 service_name_locale
, message_id_locale
);
9715 err
= _isds(context
, service
, request
, response
,
9716 raw_response
, raw_response_length
);
9717 xmlFreeNode(request
); request
= NULL
;
9720 isds_log(ILF_ISDS
, ILL_DEBUG
,
9721 _("Processing ISDS response on %s request failed\n"),
9722 service_name_locale
);
9726 /* Check for response status */
9727 err
= isds_response_status(context
, service
, *response
,
9728 code
, status_message
, NULL
);
9730 isds_log(ILF_ISDS
, ILL_DEBUG
,
9731 _("ISDS response on %s request is missing status\n"),
9732 service_name_locale
);
9736 /* Request processed, but nothing found */
9737 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9738 char *code_locale
= _isds_utf82locale((char*) *code
);
9739 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9740 isds_log(ILF_ISDS
, ILL_DEBUG
,
9741 _("Server refused %s request for %s message ID "
9742 "(code=%s, message=%s)\n"),
9743 service_name_locale
, message_id_locale
,
9744 code_locale
, status_message_locale
);
9745 isds_log_message(context
, status_message_locale
);
9747 free(status_message_locale
);
9753 free(message_id_locale
);
9754 free(service_name_locale
);
9755 xmlFreeNode(request
);
9760 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9761 * signed data and free ISDS response.
9762 * @context is session context
9763 * @message_id is UTF-8 encoded message ID for logging purpose
9764 * @response is parsed XML document. It will be freed and NULLed in the middle
9765 * of function run to save memory. This is not guaranteed in case of error.
9766 * @request_name is name of ISDS request used to construct response root
9767 * element name and for logging purpose.
9768 * @raw is reallocated output buffer with DER encoded CMS data
9769 * @raw_length is size of @raw buffer in bytes
9770 * @returns standard error codes, in case of error, @raw will be freed and
9771 * NULLed, @response sometimes. */
9772 static isds_error
find_extract_signed_data_free_response(
9773 struct isds_ctx
*context
, const xmlChar
*message_id
,
9774 xmlDocPtr
*response
, const xmlChar
*request_name
,
9775 void **raw
, size_t *raw_length
) {
9777 isds_error err
= IE_SUCCESS
;
9778 char *xpath_expression
= NULL
;
9779 xmlXPathContextPtr xpath_ctx
= NULL
;
9780 xmlXPathObjectPtr result
= NULL
;
9781 char *encoded_structure
= NULL
;
9783 if (!context
) return IE_INVALID_CONTEXT
;
9784 if (!raw
) return IE_INVAL
;
9786 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9789 /* Build XPath expression */
9790 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9791 "Response/isds:dmSignature");
9792 if (!xpath_expression
) return IE_NOMEM
;
9795 xpath_ctx
= xmlXPathNewContext(*response
);
9800 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9804 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9809 /* Empty response */
9810 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9811 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9812 isds_printf_message(context
,
9813 _("Server did not return any signed data for message ID `%s' "
9815 message_id_locale
, request_name
);
9816 free(message_id_locale
);
9820 /* More responses */
9821 if (result
->nodesetval
->nodeNr
> 1) {
9822 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9823 isds_printf_message(context
,
9824 _("Server did return more signed data for message ID `%s' "
9826 message_id_locale
, request_name
);
9827 free(message_id_locale
);
9832 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9834 /* Extract PKCS#7 structure */
9835 EXTRACT_STRING(".", encoded_structure
);
9836 if (!encoded_structure
) {
9837 isds_log_message(context
, _("dmSignature element is empty"));
9840 /* Here we have delivery info as standalone CMS in encoded_structure.
9841 * We don't need any other data, free them: */
9842 xmlXPathFreeObject(result
); result
= NULL
;
9843 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9844 xmlFreeDoc(*response
); *response
= NULL
;
9847 /* Decode PKCS#7 to DER format */
9848 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9849 if (*raw_length
== (size_t) -1) {
9850 isds_log_message(context
,
9851 _("Error while Base64-decoding PKCS#7 structure"));
9862 free(encoded_structure
);
9863 xmlXPathFreeObject(result
);
9864 xmlXPathFreeContext(xpath_ctx
);
9865 free(xpath_expression
);
9869 #endif /* HAVE_LIBCURL */
9872 /* Download incoming message envelope identified by ID.
9873 * @context is session context
9874 * @message_id is message identifier (you can get them from
9875 * isds_get_list_of_received_messages())
9876 * @message is automatically reallocated message retrieved from ISDS.
9877 * It will miss documents per se. Use isds_get_received_message(), if you are
9878 * interested in documents (content) too.
9879 * Returned hash and timestamp require documents to be verifiable. */
9880 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9881 const char *message_id
, struct isds_message
**message
) {
9883 isds_error err
= IE_SUCCESS
;
9885 xmlDocPtr response
= NULL
;
9886 xmlChar
*code
= NULL
, *status_message
= NULL
;
9887 xmlXPathContextPtr xpath_ctx
= NULL
;
9888 xmlXPathObjectPtr result
= NULL
;
9891 if (!context
) return IE_INVALID_CONTEXT
;
9892 zfree(context
->long_message
);
9894 /* Free former message if any */
9895 if (!message
) return IE_INVAL
;
9896 isds_message_free(message
);
9899 /* Do request and check for success */
9900 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9901 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9902 &response
, NULL
, NULL
, &code
, &status_message
);
9903 if (err
) goto leave
;
9906 xpath_ctx
= xmlXPathNewContext(response
);
9911 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9915 result
= xmlXPathEvalExpression(
9916 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9917 "isds:dmReturnedMessageEnvelope",
9923 /* Empty response */
9924 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9925 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9926 isds_printf_message(context
,
9927 _("Server did not return any envelope for ID `%s' "
9928 "on MessageEnvelopeDownload request"), message_id_locale
);
9929 free(message_id_locale
);
9934 if (result
->nodesetval
->nodeNr
> 1) {
9935 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9936 isds_printf_message(context
,
9937 _("Server did return more envelopes for ID `%s' "
9938 "on MessageEnvelopeDownload request"), message_id_locale
);
9939 free(message_id_locale
);
9944 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9946 /* Extract the envelope (= message without documents, hence 0) */
9947 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9948 if (err
) goto leave
;
9951 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9952 &(*message
)->raw_length
);
9956 isds_message_free(message
);
9959 xmlXPathFreeObject(result
);
9960 xmlXPathFreeContext(xpath_ctx
);
9963 free(status_message
);
9964 if (!*message
|| !(*message
)->xml
) {
9965 xmlFreeDoc(response
);
9969 isds_log(ILF_ISDS
, ILL_DEBUG
,
9970 _("MessageEnvelopeDownload request processed by server "
9973 #else /* not HAVE_LIBCURL */
9980 /* Load delivery info of any format from buffer.
9981 * @context is session context
9982 * @raw_type advertises format of @buffer content. Only delivery info types
9984 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9985 * retrieve such data from message->raw after calling
9986 * isds_get_signed_delivery_info().
9987 * @length is length of buffer in bytes.
9988 * @message is automatically reallocated message parsed from @buffer.
9989 * @strategy selects how buffer will be attached into raw isds_message member.
9991 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9992 const isds_raw_type raw_type
,
9993 const void *buffer
, const size_t length
,
9994 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9996 isds_error err
= IE_SUCCESS
;
9997 message_ns_type message_ns
;
9998 xmlDocPtr message_doc
= NULL
;
9999 xmlXPathContextPtr xpath_ctx
= NULL
;
10000 xmlXPathObjectPtr result
= NULL
;
10001 void *xml_stream
= NULL
;
10002 size_t xml_stream_length
= 0;
10004 if (!context
) return IE_INVALID_CONTEXT
;
10005 zfree(context
->long_message
);
10006 if (!message
) return IE_INVAL
;
10007 isds_message_free(message
);
10008 if (!buffer
) return IE_INVAL
;
10011 /* Select buffer format and extract XML from CMS*/
10012 switch (raw_type
) {
10013 case RAWTYPE_DELIVERYINFO
:
10014 message_ns
= MESSAGE_NS_UNSIGNED
;
10015 xml_stream
= (void *) buffer
;
10016 xml_stream_length
= length
;
10019 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10020 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10021 xml_stream
= (void *) buffer
;
10022 xml_stream_length
= length
;
10025 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10026 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10027 err
= _isds_extract_cms_data(context
, buffer
, length
,
10028 &xml_stream
, &xml_stream_length
);
10029 if (err
) goto leave
;
10033 isds_log_message(context
, _("Bad raw delivery representation type"));
10038 isds_log(ILF_ISDS
, ILL_DEBUG
,
10039 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10040 xml_stream_length
, xml_stream
);
10042 /* Convert delivery info XML stream into XPath context */
10043 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10044 if (!message_doc
) {
10048 xpath_ctx
= xmlXPathNewContext(message_doc
);
10053 /* XXX: Name spaces mangled for signed delivery info:
10054 * http://isds.czechpoint.cz/v20/delivery:
10056 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10058 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10059 * <p:dmID>170272</p:dmID>
10062 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10064 * </q:dmEvents>...</q:dmEvents>
10066 * </q:GetDeliveryInfoResponse>
10068 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10072 result
= xmlXPathEvalExpression(
10073 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10079 /* Empty delivery info */
10080 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10081 isds_printf_message(context
,
10082 _("XML document is not sisds:dmDelivery document"));
10086 /* More delivery info's */
10087 if (result
->nodesetval
->nodeNr
> 1) {
10088 isds_printf_message(context
,
10089 _("XML document has more sisds:dmDelivery elements"));
10093 /* One delivery info */
10094 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10096 /* Extract the envelope (= message without documents, hence 0).
10097 * XXX: extract_TReturnedMessage() can obtain attachments size,
10098 * but delivery info carries none. It's coded as option elements,
10099 * so it should work. */
10100 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10101 if (err
) goto leave
;
10103 /* Extract events */
10104 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10105 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10106 if (err
) { err
= IE_ERROR
; goto leave
; }
10107 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10108 if (err
) goto leave
;
10110 /* Append raw CMS structure into message */
10111 (*message
)->raw_type
= raw_type
;
10112 switch (strategy
) {
10113 case BUFFER_DONT_STORE
:
10116 (*message
)->raw
= malloc(length
);
10117 if (!(*message
)->raw
) {
10121 memcpy((*message
)->raw
, buffer
, length
);
10122 (*message
)->raw_length
= length
;
10125 (*message
)->raw
= (void *) buffer
;
10126 (*message
)->raw_length
= length
;
10135 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10136 isds_message_free(message
);
10139 xmlXPathFreeObject(result
);
10140 xmlXPathFreeContext(xpath_ctx
);
10141 if (!*message
|| !(*message
)->xml
) {
10142 xmlFreeDoc(message_doc
);
10144 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10147 isds_log(ILF_ISDS
, ILL_DEBUG
,
10148 _("Delivery info loaded successfully.\n"));
10153 /* Download signed delivery info-sheet of given message identified by ID.
10154 * @context is session context
10155 * @message_id is message identifier (you can get them from
10156 * isds_get_list_of_{sent,received}_messages())
10157 * @message is automatically reallocated message retrieved from ISDS.
10158 * It will miss documents per se. Use isds_get_signed_received_message(),
10159 * if you are interested in documents (content). OTOH, only this function
10160 * can get list events message has gone through. */
10161 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10162 const char *message_id
, struct isds_message
**message
) {
10164 isds_error err
= IE_SUCCESS
;
10166 xmlDocPtr response
= NULL
;
10167 xmlChar
*code
= NULL
, *status_message
= NULL
;
10169 size_t raw_length
= 0;
10172 if (!context
) return IE_INVALID_CONTEXT
;
10173 zfree(context
->long_message
);
10175 /* Free former message if any */
10176 if (!message
) return IE_INVAL
;
10177 isds_message_free(message
);
10180 /* Do request and check for success */
10181 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10182 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10183 &response
, NULL
, NULL
, &code
, &status_message
);
10184 if (err
) goto leave
;
10186 /* Find signed delivery info, extract it into raw and maybe free
10188 err
= find_extract_signed_data_free_response(context
,
10189 (xmlChar
*)message_id
, &response
,
10190 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10191 if (err
) goto leave
;
10193 /* Parse delivery info */
10194 err
= isds_load_delivery_info(context
,
10195 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10196 message
, BUFFER_MOVE
);
10197 if (err
) goto leave
;
10203 isds_message_free(message
);
10208 free(status_message
);
10209 xmlFreeDoc(response
);
10212 isds_log(ILF_ISDS
, ILL_DEBUG
,
10213 _("GetSignedDeliveryInfo request processed by server "
10216 #else /* not HAVE_LIBCURL */
10223 /* Download delivery info-sheet of given message identified by ID.
10224 * @context is session context
10225 * @message_id is message identifier (you can get them from
10226 * isds_get_list_of_{sent,received}_messages())
10227 * @message is automatically reallocated message retrieved from ISDS.
10228 * It will miss documents per se. Use isds_get_received_message(), if you are
10229 * interested in documents (content). OTOH, only this function can get list
10230 * of events message has gone through. */
10231 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10232 const char *message_id
, struct isds_message
**message
) {
10234 isds_error err
= IE_SUCCESS
;
10236 xmlDocPtr response
= NULL
;
10237 xmlChar
*code
= NULL
, *status_message
= NULL
;
10238 xmlNodePtr delivery_node
= NULL
;
10240 size_t raw_length
= 0;
10243 if (!context
) return IE_INVALID_CONTEXT
;
10244 zfree(context
->long_message
);
10246 /* Free former message if any */
10247 if (!message
) return IE_INVAL
;
10248 isds_message_free(message
);
10251 /* Do request and check for success */
10252 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10253 BAD_CAST
"GetDeliveryInfo", message_id
,
10254 &response
, NULL
, NULL
, &code
, &status_message
);
10255 if (err
) goto leave
;
10258 /* Serialize delivery info */
10259 delivery_node
= xmlDocGetRootElement(response
);
10260 if (!delivery_node
) {
10261 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10262 isds_printf_message(context
,
10263 _("Server did not return any delivery info for ID `%s' "
10264 "on GetDeliveryInfo request"), message_id_locale
);
10265 free(message_id_locale
);
10269 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10270 if (err
) goto leave
;
10272 /* Parse delivery info */
10273 /* TODO: Here we parse the response second time. We could single delivery
10274 * parser from isds_load_delivery_info() to make things faster. */
10275 err
= isds_load_delivery_info(context
,
10276 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10277 message
, BUFFER_MOVE
);
10278 if (err
) goto leave
;
10285 isds_message_free(message
);
10290 free(status_message
);
10291 xmlFreeDoc(response
);
10294 isds_log(ILF_ISDS
, ILL_DEBUG
,
10295 _("GetDeliveryInfo request processed by server "
10298 #else /* not HAVE_LIBCURL */
10305 /* Download incoming message identified by ID.
10306 * @context is session context
10307 * @message_id is message identifier (you can get them from
10308 * isds_get_list_of_received_messages())
10309 * @message is automatically reallocated message retrieved from ISDS */
10310 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10311 const char *message_id
, struct isds_message
**message
) {
10313 isds_error err
= IE_SUCCESS
;
10315 xmlDocPtr response
= NULL
;
10316 void *xml_stream
= NULL
;
10317 size_t xml_stream_length
;
10318 xmlChar
*code
= NULL
, *status_message
= NULL
;
10319 xmlXPathContextPtr xpath_ctx
= NULL
;
10320 xmlXPathObjectPtr result
= NULL
;
10321 char *phys_path
= NULL
;
10322 size_t phys_start
, phys_end
;
10325 if (!context
) return IE_INVALID_CONTEXT
;
10326 zfree(context
->long_message
);
10328 /* Free former message if any */
10329 if (NULL
== message
) return IE_INVAL
;
10330 if (message
) isds_message_free(message
);
10333 /* Do request and check for success */
10334 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10335 BAD_CAST
"MessageDownload", message_id
,
10336 &response
, &xml_stream
, &xml_stream_length
,
10337 &code
, &status_message
);
10338 if (err
) goto leave
;
10341 xpath_ctx
= xmlXPathNewContext(response
);
10346 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10350 result
= xmlXPathEvalExpression(
10351 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10357 /* Empty response */
10358 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10359 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10360 isds_printf_message(context
,
10361 _("Server did not return any message for ID `%s' "
10362 "on MessageDownload request"), message_id_locale
);
10363 free(message_id_locale
);
10367 /* More messages */
10368 if (result
->nodesetval
->nodeNr
> 1) {
10369 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10370 isds_printf_message(context
,
10371 _("Server did return more messages for ID `%s' "
10372 "on MessageDownload request"), message_id_locale
);
10373 free(message_id_locale
);
10378 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10380 /* Extract the message */
10381 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10382 if (err
) goto leave
;
10384 /* Locate raw XML blob */
10385 phys_path
= strdup(
10386 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10387 PHYSXML_ELEMENT_SEPARATOR
10388 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10389 PHYSXML_ELEMENT_SEPARATOR
10390 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10396 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10397 phys_path
, &phys_start
, &phys_end
);
10400 isds_log_message(context
,
10401 _("Substring with isds:MessageDownloadResponse element "
10402 "could not be located in raw SOAP message"));
10405 /* Save XML blob */
10406 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10407 &(*message)->raw_length);*/
10408 /* TODO: Store name space declarations from ancestors */
10409 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10410 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10411 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10412 (*message
)->raw
= malloc((*message
)->raw_length
);
10413 if (!(*message
)->raw
) {
10417 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10422 isds_message_free(message
);
10427 xmlXPathFreeObject(result
);
10428 xmlXPathFreeContext(xpath_ctx
);
10431 free(status_message
);
10433 if (!*message
|| !(*message
)->xml
) {
10434 xmlFreeDoc(response
);
10438 isds_log(ILF_ISDS
, ILL_DEBUG
,
10439 _("MessageDownload request processed by server "
10442 #else /* not HAVE_LIBCURL */
10449 /* Load message of any type from buffer.
10450 * @context is session context
10451 * @raw_type defines content type of @buffer. Only message types are allowed.
10452 * @buffer is message raw representation. Format (CMS, plain signed,
10453 * message direction) is defined in @raw_type. You can retrieve such data
10454 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10455 * @length is length of buffer in bytes.
10456 * @message is automatically reallocated message parsed from @buffer.
10457 * @strategy selects how buffer will be attached into raw isds_message member.
10459 isds_error
isds_load_message(struct isds_ctx
*context
,
10460 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10461 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10463 isds_error err
= IE_SUCCESS
;
10464 void *xml_stream
= NULL
;
10465 size_t xml_stream_length
= 0;
10466 message_ns_type message_ns
;
10467 xmlDocPtr message_doc
= NULL
;
10468 xmlXPathContextPtr xpath_ctx
= NULL
;
10469 xmlXPathObjectPtr result
= NULL
;
10471 if (!context
) return IE_INVALID_CONTEXT
;
10472 zfree(context
->long_message
);
10473 if (!message
) return IE_INVAL
;
10474 isds_message_free(message
);
10475 if (!buffer
) return IE_INVAL
;
10478 /* Select buffer format and extract XML from CMS*/
10479 switch (raw_type
) {
10480 case RAWTYPE_INCOMING_MESSAGE
:
10481 message_ns
= MESSAGE_NS_UNSIGNED
;
10482 xml_stream
= (void *) buffer
;
10483 xml_stream_length
= length
;
10486 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10487 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10488 xml_stream
= (void *) buffer
;
10489 xml_stream_length
= length
;
10492 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10493 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10494 err
= _isds_extract_cms_data(context
, buffer
, length
,
10495 &xml_stream
, &xml_stream_length
);
10496 if (err
) goto leave
;
10499 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10500 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10501 xml_stream
= (void *) buffer
;
10502 xml_stream_length
= length
;
10505 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10506 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10507 err
= _isds_extract_cms_data(context
, buffer
, length
,
10508 &xml_stream
, &xml_stream_length
);
10509 if (err
) goto leave
;
10513 isds_log_message(context
, _("Bad raw message representation type"));
10518 isds_log(ILF_ISDS
, ILL_DEBUG
,
10519 _("Loading message:\n%.*s\nEnd of message\n"),
10520 xml_stream_length
, xml_stream
);
10522 /* Convert messages XML stream into XPath context */
10523 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10524 if (!message_doc
) {
10528 xpath_ctx
= xmlXPathNewContext(message_doc
);
10533 /* XXX: Standard name space for unsigned incoming direction:
10534 * http://isds.czechpoint.cz/v20/
10536 * XXX: Name spaces mangled for signed outgoing direction:
10537 * http://isds.czechpoint.cz/v20/SentMessage:
10539 * <q:MessageDownloadResponse
10540 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10541 * <q:dmReturnedMessage>
10542 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10543 * <p:dmID>151916</p:dmID>
10546 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10548 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10549 * </q:dmReturnedMessage>
10550 * </q:MessageDownloadResponse>
10552 * XXX: Name spaces mangled for signed incoming direction:
10553 * http://isds.czechpoint.cz/v20/message:
10555 * <q:MessageDownloadResponse
10556 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10557 * <q:dmReturnedMessage>
10558 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10559 * <p:dmID>151916</p:dmID>
10562 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10564 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10565 * </q:dmReturnedMessage>
10566 * </q:MessageDownloadResponse>
10568 * Stupidity of ISDS developers is unlimited */
10569 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10573 result
= xmlXPathEvalExpression(
10574 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10580 /* Empty message */
10581 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10582 isds_printf_message(context
,
10583 _("XML document does not contain "
10584 "sisds:dmReturnedMessage element"));
10588 /* More messages */
10589 if (result
->nodesetval
->nodeNr
> 1) {
10590 isds_printf_message(context
,
10591 _("XML document has more sisds:dmReturnedMessage elements"));
10596 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10598 /* Extract the message */
10599 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10600 if (err
) goto leave
;
10602 /* Append raw buffer into message */
10603 (*message
)->raw_type
= raw_type
;
10604 switch (strategy
) {
10605 case BUFFER_DONT_STORE
:
10608 (*message
)->raw
= malloc(length
);
10609 if (!(*message
)->raw
) {
10613 memcpy((*message
)->raw
, buffer
, length
);
10614 (*message
)->raw_length
= length
;
10617 (*message
)->raw
= (void *) buffer
;
10618 (*message
)->raw_length
= length
;
10628 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10629 isds_message_free(message
);
10632 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10633 xmlXPathFreeObject(result
);
10634 xmlXPathFreeContext(xpath_ctx
);
10635 if (!*message
|| !(*message
)->xml
) {
10636 xmlFreeDoc(message_doc
);
10640 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10645 /* Determine type of raw message or delivery info according some heuristics.
10646 * It does not validate the raw blob.
10647 * @context is session context
10648 * @raw_type returns content type of @buffer. Valid only if exit code of this
10649 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10650 * reallocated memory.
10651 * @buffer is message raw representation.
10652 * @length is length of buffer in bytes. */
10653 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10654 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10656 void *xml_stream
= NULL
;
10657 size_t xml_stream_length
= 0;
10658 xmlDocPtr document
= NULL
;
10659 xmlNodePtr root
= NULL
;
10661 if (!context
) return IE_INVALID_CONTEXT
;
10662 zfree(context
->long_message
);
10663 if (length
== 0 || !buffer
) return IE_INVAL
;
10664 if (!raw_type
) return IE_INVAL
;
10667 err
= _isds_extract_cms_data(context
, buffer
, length
,
10668 &xml_stream
, &xml_stream_length
);
10670 xml_stream
= (void *) buffer
;
10671 xml_stream_length
= (size_t) length
;
10676 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10678 isds_printf_message(context
,
10679 _("Could not parse data as XML document"));
10684 /* Get root element */
10685 root
= xmlDocGetRootElement(document
);
10687 isds_printf_message(context
,
10688 _("XML document is missing root element"));
10693 if (!root
->ns
|| !root
->ns
->href
) {
10694 isds_printf_message(context
,
10695 _("Root element does not belong to any name space"));
10700 /* Test name space */
10701 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10702 if (xml_stream
== buffer
)
10703 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10705 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10706 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10707 if (xml_stream
== buffer
)
10708 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10710 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10711 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10712 if (xml_stream
== buffer
)
10713 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10715 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10716 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10717 if (xml_stream
!= buffer
) {
10718 isds_printf_message(context
,
10719 _("Document in ISDS name space is encapsulated into CMS" ));
10721 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10722 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10723 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10724 *raw_type
= RAWTYPE_DELIVERYINFO
;
10726 isds_printf_message(context
,
10727 _("Unknown root element in ISDS name space"));
10731 isds_printf_message(context
,
10732 _("Unknown name space"));
10737 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10738 xmlFreeDoc(document
);
10743 /* Download signed incoming/outgoing message identified by ID.
10744 * @context is session context
10745 * @output is true for outgoing message, false for incoming message
10746 * @message_id is message identifier (you can get them from
10747 * isds_get_list_of_{sent,received}_messages())
10748 * @message is automatically reallocated message retrieved from ISDS. The raw
10749 * member will be filled with PKCS#7 structure in DER format. */
10750 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10751 const _Bool outgoing
, const char *message_id
,
10752 struct isds_message
**message
) {
10754 isds_error err
= IE_SUCCESS
;
10756 xmlDocPtr response
= NULL
;
10757 xmlChar
*code
= NULL
, *status_message
= NULL
;
10758 xmlXPathContextPtr xpath_ctx
= NULL
;
10759 xmlXPathObjectPtr result
= NULL
;
10760 char *encoded_structure
= NULL
;
10762 size_t raw_length
= 0;
10765 if (!context
) return IE_INVALID_CONTEXT
;
10766 zfree(context
->long_message
);
10767 if (!message
) return IE_INVAL
;
10768 isds_message_free(message
);
10771 /* Do request and check for success */
10772 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10773 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10774 BAD_CAST
"SignedMessageDownload",
10775 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10776 if (err
) goto leave
;
10778 /* Find signed message, extract it into raw and maybe free
10780 err
= find_extract_signed_data_free_response(context
,
10781 (xmlChar
*)message_id
, &response
,
10782 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10783 BAD_CAST
"SignedMessageDownload",
10784 &raw
, &raw_length
);
10785 if (err
) goto leave
;
10787 /* Parse message */
10788 err
= isds_load_message(context
,
10789 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10790 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10791 raw
, raw_length
, message
, BUFFER_MOVE
);
10792 if (err
) goto leave
;
10798 isds_message_free(message
);
10801 free(encoded_structure
);
10802 xmlXPathFreeObject(result
);
10803 xmlXPathFreeContext(xpath_ctx
);
10807 free(status_message
);
10808 xmlFreeDoc(response
);
10811 isds_log(ILF_ISDS
, ILL_DEBUG
,
10813 _("SignedSentMessageDownload request processed by server "
10814 "successfully.\n") :
10815 _("SignedMessageDownload request processed by server "
10818 #else /* not HAVE_LIBCURL */
10825 /* Download signed incoming message identified by ID.
10826 * @context is session context
10827 * @message_id is message identifier (you can get them from
10828 * isds_get_list_of_received_messages())
10829 * @message is automatically reallocated message retrieved from ISDS. The raw
10830 * member will be filled with PKCS#7 structure in DER format. */
10831 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10832 const char *message_id
, struct isds_message
**message
) {
10833 return isds_get_signed_message(context
, 0, message_id
, message
);
10837 /* Download signed outgoing message identified by ID.
10838 * @context is session context
10839 * @message_id is message identifier (you can get them from
10840 * isds_get_list_of_sent_messages())
10841 * @message is automatically reallocated message retrieved from ISDS. The raw
10842 * member will be filled with PKCS#7 structure in DER format. */
10843 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10844 const char *message_id
, struct isds_message
**message
) {
10845 return isds_get_signed_message(context
, 1, message_id
, message
);
10849 /* Get type and name of user who sent a message identified by ID.
10850 * @context is session context
10851 * @message_id is message identifier
10852 * @sender_type is pointer to automatically allocated type of sender detected
10853 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10854 * library or to the server, NULL will be returned. Pass NULL if you don't
10856 * @raw_sender_type is automatically reallocated UTF-8 string describing
10857 * sender type or NULL if not known to server. Pass NULL if you don't care.
10858 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10859 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10860 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10861 const char *message_id
, isds_sender_type
**sender_type
,
10862 char **raw_sender_type
, char **sender_name
) {
10863 isds_error err
= IE_SUCCESS
;
10865 xmlDocPtr response
= NULL
;
10866 xmlChar
*code
= NULL
, *status_message
= NULL
;
10867 xmlXPathContextPtr xpath_ctx
= NULL
;
10868 xmlXPathObjectPtr result
= NULL
;
10869 char *type_string
= NULL
;
10872 if (!context
) return IE_INVALID_CONTEXT
;
10873 zfree(context
->long_message
);
10874 if (sender_type
) zfree(*sender_type
);
10875 if (raw_sender_type
) zfree(*raw_sender_type
);
10876 if (sender_name
) zfree(*sender_name
);
10877 if (!message_id
) return IE_INVAL
;
10880 /* Do request and check for success */
10881 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10882 BAD_CAST
"GetMessageAuthor",
10883 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10884 if (err
) goto leave
;
10887 xpath_ctx
= xmlXPathNewContext(response
);
10892 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10896 result
= xmlXPathEvalExpression(
10897 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10902 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10903 isds_log_message(context
,
10904 _("Missing GetMessageAuthorResponse element"));
10908 if (result
->nodesetval
->nodeNr
> 1) {
10909 isds_log_message(context
,
10910 _("Multiple GetMessageAuthorResponse element"));
10914 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10915 xmlXPathFreeObject(result
); result
= NULL
;
10917 /* Fill output arguments in */
10918 EXTRACT_STRING("isds:userType", type_string
);
10919 if (NULL
!= type_string
) {
10920 if (NULL
!= sender_type
) {
10921 *sender_type
= calloc(1, sizeof(**sender_type
));
10922 if (NULL
== *sender_type
) {
10927 err
= string2isds_sender_type((xmlChar
*)type_string
,
10930 zfree(*sender_type
);
10931 if (err
== IE_ENUM
) {
10933 char *type_string_locale
= _isds_utf82locale(type_string
);
10934 isds_log(ILF_ISDS
, ILL_WARNING
,
10935 _("Unknown isds:userType value: %s"),
10936 type_string_locale
);
10937 free(type_string_locale
);
10942 if (NULL
!= sender_name
)
10943 EXTRACT_STRING("isds:authorName", *sender_name
);
10947 if (NULL
!= sender_type
) zfree(*sender_type
);
10948 zfree(type_string
);
10949 if (NULL
!= sender_name
) zfree(*sender_name
);
10951 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10953 xmlXPathFreeObject(result
);
10954 xmlXPathFreeContext(xpath_ctx
);
10957 free(status_message
);
10958 xmlFreeDoc(response
);
10961 isds_log(ILF_ISDS
, ILL_DEBUG
,
10962 _("GetMessageAuthor request processed by server "
10963 "successfully.\n"));
10964 #else /* not HAVE_LIBCURL */
10971 /* Retrieve hash of message identified by ID stored in ISDS.
10972 * @context is session context
10973 * @message_id is message identifier
10974 * @hash is automatically reallocated message hash downloaded from ISDS.
10975 * Message must exist in system and must not be deleted. */
10976 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10977 const char *message_id
, struct isds_hash
**hash
) {
10979 isds_error err
= IE_SUCCESS
;
10981 xmlDocPtr response
= NULL
;
10982 xmlChar
*code
= NULL
, *status_message
= NULL
;
10983 xmlXPathContextPtr xpath_ctx
= NULL
;
10984 xmlXPathObjectPtr result
= NULL
;
10987 if (!context
) return IE_INVALID_CONTEXT
;
10988 zfree(context
->long_message
);
10990 isds_hash_free(hash
);
10993 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10994 BAD_CAST
"VerifyMessage", message_id
,
10995 &response
, NULL
, NULL
, &code
, &status_message
);
10996 if (err
) goto leave
;
11000 xpath_ctx
= xmlXPathNewContext(response
);
11005 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11009 result
= xmlXPathEvalExpression(
11010 BAD_CAST
"/isds:VerifyMessageResponse",
11016 /* Empty response */
11017 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11018 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11019 isds_printf_message(context
,
11020 _("Server did not return any response for ID `%s' "
11021 "on VerifyMessage request"), message_id_locale
);
11022 free(message_id_locale
);
11026 /* More responses */
11027 if (result
->nodesetval
->nodeNr
> 1) {
11028 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11029 isds_printf_message(context
,
11030 _("Server did return more responses for ID `%s' "
11031 "on VerifyMessage request"), message_id_locale
);
11032 free(message_id_locale
);
11037 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11039 /* Extract the hash */
11040 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11044 isds_hash_free(hash
);
11047 xmlXPathFreeObject(result
);
11048 xmlXPathFreeContext(xpath_ctx
);
11051 free(status_message
);
11052 xmlFreeDoc(response
);
11055 isds_log(ILF_ISDS
, ILL_DEBUG
,
11056 _("VerifyMessage request processed by server "
11059 #else /* not HAVE_LIBCURL */
11066 /* Erase message specified by @message_id from long term storage. Other
11067 * message cannot be erased on user request.
11068 * @context is session context
11069 * @message_id is message identifier.
11070 * @incoming is true for incoming message, false for outgoing message.
11072 * IE_SUCCESS if message has ben removed
11073 * IE_INVAL if message does not exist in long term storage or message
11074 * belongs to different box
11075 * TODO: IE_NOEPRM if user has no permission to erase a message */
11076 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11077 const char *message_id
, _Bool incoming
) {
11078 isds_error err
= IE_SUCCESS
;
11080 xmlNodePtr request
= NULL
, node
;
11081 xmlNsPtr isds_ns
= NULL
;
11082 xmlDocPtr response
= NULL
;
11083 xmlChar
*code
= NULL
, *status_message
= NULL
;
11086 if (!context
) return IE_INVALID_CONTEXT
;
11087 zfree(context
->long_message
);
11088 if (NULL
== message_id
) return IE_INVAL
;
11090 /* Check if connection is established
11091 * TODO: This check should be done downstairs. */
11092 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11095 /* Build request */
11096 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11098 isds_log_message(context
,
11099 _("Could build EraseMessage request"));
11102 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11104 isds_log_message(context
, _("Could not create ISDS name space"));
11105 xmlFreeNode(request
);
11108 xmlSetNs(request
, isds_ns
);
11110 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11111 if (err
) goto leave
;
11112 INSERT_STRING(request
, "dmID", message_id
);
11114 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11118 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11119 "message ID %s to ISDS\n"), message_id
);
11120 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11121 xmlFreeNode(request
); request
= NULL
;
11124 isds_log(ILF_ISDS
, ILL_DEBUG
,
11125 _("Processing ISDS response on EraseMessage request "
11130 /* Check for response status */
11131 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11132 &code
, &status_message
, NULL
);
11134 isds_log(ILF_ISDS
, ILL_DEBUG
,
11135 _("ISDS response on EraseMessage request is missing "
11140 /* Check server status code */
11141 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11142 isds_log_message(context
, _("Message to erase belongs to other box"));
11144 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11145 isds_log_message(context
, _("Message to erase is not saved in "
11146 "long term storage or the direction does not match"));
11148 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11149 char *code_locale
= _isds_utf82locale((char*) code
);
11150 char *message_locale
= _isds_utf82locale((char*) status_message
);
11151 isds_log(ILF_ISDS
, ILL_DEBUG
,
11152 _("Server refused EraseMessage request "
11153 "(code=%s, message=%s)\n"),
11154 code_locale
, message_locale
);
11155 isds_log_message(context
, message_locale
);
11157 free(message_locale
);
11164 free(status_message
);
11165 xmlFreeDoc(response
);
11166 xmlFreeNode(request
);
11169 isds_log(ILF_ISDS
, ILL_DEBUG
,
11170 _("EraseMessage request processed by server "
11173 #else /* not HAVE_LIBCURL */
11180 /* Mark message as read. This is a transactional commit function to acknowledge
11181 * to ISDS the message has been downloaded and processed by client properly.
11182 * @context is session context
11183 * @message_id is message identifier. */
11184 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11185 const char *message_id
) {
11187 isds_error err
= IE_SUCCESS
;
11189 xmlDocPtr response
= NULL
;
11190 xmlChar
*code
= NULL
, *status_message
= NULL
;
11193 if (!context
) return IE_INVALID_CONTEXT
;
11194 zfree(context
->long_message
);
11197 /* Do request and check for success */
11198 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11199 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11200 &response
, NULL
, NULL
, &code
, &status_message
);
11203 free(status_message
);
11204 xmlFreeDoc(response
);
11207 isds_log(ILF_ISDS
, ILL_DEBUG
,
11208 _("MarkMessageAsDownloaded request processed by server "
11211 #else /* not HAVE_LIBCURL */
11218 /* Mark message as received by recipient. This is applicable only to
11219 * commercial message. Use envelope->dmType message member to distinguish
11220 * commercial message from government message. Government message is
11221 * received automatically (by law), commercial message on recipient request.
11222 * @context is session context
11223 * @message_id is message identifier. */
11224 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11225 const char *message_id
) {
11227 isds_error err
= IE_SUCCESS
;
11229 xmlDocPtr response
= NULL
;
11230 xmlChar
*code
= NULL
, *status_message
= NULL
;
11233 if (!context
) return IE_INVALID_CONTEXT
;
11234 zfree(context
->long_message
);
11237 /* Do request and check for success */
11238 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11239 BAD_CAST
"ConfirmDelivery", message_id
,
11240 &response
, NULL
, NULL
, &code
, &status_message
);
11243 free(status_message
);
11244 xmlFreeDoc(response
);
11247 isds_log(ILF_ISDS
, ILL_DEBUG
,
11248 _("ConfirmDelivery request processed by server "
11251 #else /* not HAVE_LIBCURL */
11258 /* Send document for authorized conversion into Czech POINT system.
11259 * This is public anonymous service, no log-in necessary. Special context is
11260 * used to reuse keep-a-live HTTPS connection.
11261 * @context is Czech POINT session context. DO NOT use context connected to
11262 * ISDS server. Use new context or context used by this function previously.
11263 * @document is document to convert. Only data, data_length, dmFileDescr and
11264 * is_xml members are significant. Be ware that not all document formats can be
11265 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11266 * @id is reallocated identifier assigned by Czech POINT system to
11267 * your document on submit. Use is to tell it to Czech POINT officer.
11268 * @date is reallocated document submit date (submitted documents
11269 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11271 isds_error
czp_convert_document(struct isds_ctx
*context
,
11272 const struct isds_document
*document
,
11273 char **id
, struct tm
**date
) {
11274 isds_error err
= IE_SUCCESS
;
11276 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11277 xmlNodePtr request
= NULL
, node
;
11278 xmlDocPtr response
= NULL
;
11280 xmlXPathContextPtr xpath_ctx
= NULL
;
11281 xmlXPathObjectPtr result
= NULL
;
11282 long int status
= -1;
11283 long int *status_ptr
= &status
;
11284 char *string
= NULL
;
11288 if (!context
) return IE_INVALID_CONTEXT
;
11289 zfree(context
->long_message
);
11290 if (!document
|| !id
|| !date
) return IE_INVAL
;
11292 if (document
->is_xml
) {
11293 isds_log_message(context
,
11294 _("XML documents cannot be submitted to conversion"));
11298 /* Free output arguments */
11303 /* Store configuration */
11304 context
->type
= CTX_TYPE_CZP
;
11305 free(context
->url
);
11306 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11307 if (!(context
->url
))
11310 /* Prepare CURL handle if not yet connected */
11311 if (!context
->curl
) {
11312 context
->curl
= curl_easy_init();
11313 if (!(context
->curl
))
11317 /* Build conversion request */
11318 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11320 isds_log_message(context
,
11321 _("Could not build Czech POINT conversion request"));
11324 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11326 isds_log_message(context
,
11327 _("Could not create Czech POINT deposit name space"));
11328 xmlFreeNode(request
);
11331 xmlSetNs(request
, deposit_ns
);
11333 /* Insert children. They are in empty namespace! */
11334 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11336 isds_log_message(context
, _("Could not create empty name space"));
11340 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11341 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11342 document
->dmFileDescr
);
11344 /* Document encoded in Base64 */
11345 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11346 document
->data
, document
->data_length
);
11347 if (err
) goto leave
;
11349 isds_log(ILF_ISDS
, ILL_DEBUG
,
11350 _("Submitting document for conversion into Czech POINT deposit"));
11352 /* Send conversion request */
11353 err
= _czp_czpdeposit(context
, request
, &response
);
11354 xmlFreeNode(request
); request
= NULL
;
11357 czp_do_close_connection(context
);
11362 /* Extract response */
11363 xpath_ctx
= xmlXPathNewContext(response
);
11368 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11372 result
= xmlXPathEvalExpression(
11373 BAD_CAST
"/deposit:saveDocumentResponse/return",
11379 /* Empty response */
11380 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11381 isds_printf_message(context
,
11382 _("Missing `return' element in Czech POINT deposit response"));
11386 /* More responses */
11387 if (result
->nodesetval
->nodeNr
> 1) {
11388 isds_printf_message(context
,
11389 _("Multiple `return' element in Czech POINT deposit response"));
11394 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11397 EXTRACT_LONGINT("status", status_ptr
, 1);
11399 EXTRACT_STRING("statusMsg", string
);
11400 char *string_locale
= _isds_utf82locale(string
);
11401 isds_printf_message(context
,
11402 _("Czech POINT deposit refused document for conversion "
11403 "(code=%ld, message=%s)"),
11404 status
, string_locale
);
11405 free(string_locale
);
11410 /* Get document ID */
11411 EXTRACT_STRING("documentID", *id
);
11413 /* Get submit date */
11414 EXTRACT_STRING("dateInserted", string
);
11416 *date
= calloc(1, sizeof(**date
));
11421 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11423 if (err
== IE_NOTSUP
) {
11425 char *string_locale
= _isds_utf82locale(string
);
11426 isds_printf_message(context
,
11427 _("Invalid dateInserted value: %s"), string_locale
);
11428 free(string_locale
);
11436 xmlXPathFreeObject(result
);
11437 xmlXPathFreeContext(xpath_ctx
);
11439 xmlFreeDoc(response
);
11440 xmlFreeNode(request
);
11443 char *id_locale
= _isds_utf82locale((char *) *id
);
11444 isds_log(ILF_ISDS
, ILL_DEBUG
,
11445 _("Document %s has been submitted for conversion "
11446 "to server successfully\n"), id_locale
);
11449 #else /* not HAVE_LIBCURL */
11456 /* Close possibly opened connection to Czech POINT document deposit.
11457 * @context is Czech POINT session context. */
11458 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11459 if (!context
) return IE_INVALID_CONTEXT
;
11460 zfree(context
->long_message
);
11462 return czp_do_close_connection(context
);
11469 /* Send request for new box creation in testing ISDS instance.
11470 * It's not possible to request for a production box currently, as it
11471 * communicates via e-mail.
11472 * XXX: This function does not work either. Server complains about invalid
11474 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11476 * @context is special session context for box creation request. DO NOT use
11477 * standard context as it could reveal your password. Use fresh new context or
11478 * context previously used by this function.
11479 * @box is box description to create including single primary user (in case of
11480 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11481 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11482 * box, or contact address of PFO box owner). The email member is mandatory as
11483 * it will be used to deliver credentials.
11484 * @former_names is former name of box owner. Pass NULL if you don't care.
11485 * @approval is optional external approval of box manipulation
11486 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11487 * NULL, if you don't care.*/
11488 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11489 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11490 const char *former_names
, const struct isds_approval
*approval
,
11491 char **refnumber
) {
11492 isds_error err
= IE_SUCCESS
;
11494 xmlNodePtr request
= NULL
;
11495 xmlDocPtr response
= NULL
;
11496 xmlXPathContextPtr xpath_ctx
= NULL
;
11497 xmlXPathObjectPtr result
= NULL
;
11501 if (!context
) return IE_INVALID_CONTEXT
;
11502 zfree(context
->long_message
);
11503 if (!box
) return IE_INVAL
;
11506 if (!box
->email
|| box
->email
[0] == '\0') {
11507 isds_log_message(context
, _("E-mail field is mandatory"));
11511 /* Scratch box ID */
11514 /* Store configuration */
11515 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11516 free(context
->url
);
11517 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11518 if (!(context
->url
))
11521 /* Prepare CURL handle if not yet connected */
11522 if (!context
->curl
) {
11523 context
->curl
= curl_easy_init();
11524 if (!(context
->curl
))
11528 /* Build CreateDataBox request */
11529 err
= build_CreateDBInput_request(context
,
11530 &request
, BAD_CAST
"CreateDataBox",
11531 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11532 if (err
) goto leave
;
11534 /* Send it to server and process response */
11535 err
= send_destroy_request_check_response(context
,
11536 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11537 &response
, (xmlChar
**) refnumber
, NULL
);
11538 if (err
) goto leave
;
11540 /* Extract box ID */
11541 xpath_ctx
= xmlXPathNewContext(response
);
11546 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11550 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11553 xmlXPathFreeObject(result
);
11554 xmlXPathFreeContext(xpath_ctx
);
11555 xmlFreeDoc(response
);
11556 xmlFreeNode(request
);
11559 isds_log(ILF_ISDS
, ILL_DEBUG
,
11560 _("CreateDataBox request processed by server successfully.\n"));
11562 #else /* not HAVE_LIBCURL */
11570 /* Submit CMS signed message to ISDS to verify its originality. This is
11571 * stronger form of isds_verify_message_hash() because ISDS does more checks
11572 * than simple one (potentialy old weak) hash comparison.
11573 * @context is session context
11574 * @message is memory with raw CMS signed message bit stream
11575 * @length is @message size in bytes
11577 * IE_SUCCESS if message originates in ISDS
11578 * IE_NOTEQUAL if message is unknown to ISDS
11579 * other code for other errors */
11580 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11581 const void *message
, size_t length
) {
11582 isds_error err
= IE_SUCCESS
;
11584 xmlNsPtr isds_ns
= NULL
;
11585 xmlNodePtr request
= NULL
;
11586 xmlDocPtr response
= NULL
;
11587 xmlXPathContextPtr xpath_ctx
= NULL
;
11588 xmlXPathObjectPtr result
= NULL
;
11589 _Bool
*authentic
= NULL
;
11592 if (!context
) return IE_INVALID_CONTEXT
;
11593 zfree(context
->long_message
);
11594 if (!message
|| length
== 0) return IE_INVAL
;
11597 /* Check if connection is established
11598 * TODO: This check should be done downstairs. */
11599 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11602 /* Build AuthenticateMessage request */
11603 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11605 isds_log_message(context
,
11606 _("Could not build AuthenticateMessage request"));
11609 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11611 isds_log_message(context
, _("Could not create ISDS name space"));
11612 xmlFreeNode(request
);
11615 xmlSetNs(request
, isds_ns
);
11617 /* Insert Base64 encoded message */
11618 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11620 if (err
) goto leave
;
11622 /* Send request to server and process response */
11623 err
= send_destroy_request_check_response(context
,
11624 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11625 &response
, NULL
, NULL
);
11626 if (err
) goto leave
;
11629 /* ISDS has decided */
11630 xpath_ctx
= xmlXPathNewContext(response
);
11635 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11640 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11643 isds_log_message(context
,
11644 _("Server did not return any response on "
11645 "AuthenticateMessage request"));
11650 isds_log(ILF_ISDS
, ILL_DEBUG
,
11651 _("ISDS authenticated the message successfully\n"));
11653 isds_log_message(context
, _("ISDS does not know the message"));
11660 xmlXPathFreeObject(result
);
11661 xmlXPathFreeContext(xpath_ctx
);
11663 xmlFreeDoc(response
);
11664 xmlFreeNode(request
);
11665 #else /* not HAVE_LIBCURL */
11673 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11674 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11676 * @context is session context
11677 * @input_data is memory with raw CMS signed message or delivery info bit
11678 * stream to re-sign
11679 * @input_length is @input_data size in bytes
11680 * @output_data is pointer to auto-allocated memory where to store re-signed
11681 * input data blob. Caller must free it.
11682 * @output_data is pointer where to store @output_data size in bytes
11683 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11684 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11686 * IE_SUCCESS if CMS blob has been re-signed successfully
11687 * other code for other errors */
11688 isds_error
isds_resign_message(struct isds_ctx
*context
,
11689 const void *input_data
, size_t input_length
,
11690 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11691 isds_error err
= IE_SUCCESS
;
11693 xmlNsPtr isds_ns
= NULL
;
11694 xmlNodePtr request
= NULL
;
11695 xmlDocPtr response
= NULL
;
11696 xmlXPathContextPtr xpath_ctx
= NULL
;
11697 xmlXPathObjectPtr result
= NULL
;
11698 char *string
= NULL
;
11699 const xmlChar
*codes
[] = {
11706 const char *meanings
[] = {
11708 "Message is not original",
11709 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11710 "Time stamp could not been generated in time"
11712 const isds_error errors
[] = {
11718 struct code_map_isds_error map
= {
11720 .meanings
= meanings
,
11725 if (NULL
!= output_data
) *output_data
= NULL
;
11726 if (NULL
!= output_length
) *output_length
= 0;
11727 if (NULL
!= valid_to
) *valid_to
= NULL
;
11729 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11730 zfree(context
->long_message
);
11731 if (NULL
== input_data
|| 0 == input_length
) {
11732 isds_log_message(context
, _("Empty CMS blob on input"));
11735 if (NULL
== output_data
|| NULL
== output_length
) {
11736 isds_log_message(context
,
11737 _("NULL pointer provided for output CMS blob"));
11742 /* Check if connection is established
11743 * TODO: This check should be done downstairs. */
11744 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11747 /* Build Re-signISDSDocument request */
11748 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11750 isds_log_message(context
,
11751 _("Could not build Re-signISDSDocument request"));
11754 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11756 isds_log_message(context
, _("Could not create ISDS name space"));
11757 xmlFreeNode(request
);
11760 xmlSetNs(request
, isds_ns
);
11762 /* Insert Base64 encoded CMS blob */
11763 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11764 input_data
, input_length
);
11765 if (err
) goto leave
;
11767 /* Send request to server and process response */
11768 err
= send_destroy_request_check_response(context
,
11769 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11770 &response
, NULL
, &map
);
11771 if (err
) goto leave
;
11774 /* Extract re-signed data */
11775 xpath_ctx
= xmlXPathNewContext(response
);
11780 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11784 result
= xmlXPathEvalExpression(
11785 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11790 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11791 isds_log_message(context
,
11792 _("Missing Re-signISDSDocumentResponse element"));
11796 if (result
->nodesetval
->nodeNr
> 1) {
11797 isds_log_message(context
,
11798 _("Multiple Re-signISDSDocumentResponse element"));
11802 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11803 xmlXPathFreeObject(result
); result
= NULL
;
11805 EXTRACT_STRING("isds:dmResultDoc", string
);
11806 /* Decode non-empty data */
11807 if (NULL
!= string
&& string
[0] != '\0') {
11808 *output_length
= _isds_b64decode(string
, output_data
);
11809 if (*output_length
== (size_t) -1) {
11810 isds_log_message(context
,
11811 _("Error while Base64-decoding re-signed data"));
11816 isds_log_message(context
, _("Server did not send re-signed data"));
11822 if (NULL
!= valid_to
) {
11823 /* Get time stamp expiration date */
11824 EXTRACT_STRING("isds:dmValidTo", string
);
11825 if (NULL
!= string
) {
11826 *valid_to
= calloc(1, sizeof(**valid_to
));
11831 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11833 if (err
== IE_NOTSUP
) {
11835 char *string_locale
= _isds_utf82locale(string
);
11836 isds_printf_message(context
,
11837 _("Invalid dmValidTo value: %s"), string_locale
);
11838 free(string_locale
);
11848 xmlXPathFreeObject(result
);
11849 xmlXPathFreeContext(xpath_ctx
);
11851 xmlFreeDoc(response
);
11852 xmlFreeNode(request
);
11853 #else /* not HAVE_LIBCURL */
11860 #undef INSERT_ELEMENT
11861 #undef CHECK_FOR_STRING_LENGTH
11862 #undef INSERT_STRING_ATTRIBUTE
11863 #undef INSERT_ULONGINTNOPTR
11864 #undef INSERT_ULONGINT
11865 #undef INSERT_LONGINT
11866 #undef INSERT_BOOLEAN
11867 #undef INSERT_SCALAR_BOOLEAN
11868 #undef INSERT_STRING
11869 #undef INSERT_STRING_WITH_NS
11870 #undef EXTRACT_STRING_ATTRIBUTE
11871 #undef EXTRACT_ULONGINT
11872 #undef EXTRACT_LONGINT
11873 #undef EXTRACT_BOOLEAN
11874 #undef EXTRACT_STRING
11877 /* Compute hash of message from raw representation and store it into envelope.
11878 * Original hash structure will be destroyed in envelope.
11879 * @context is session context
11880 * @message is message carrying raw XML message blob
11881 * @algorithm is desired hash algorithm to use */
11882 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11883 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11884 isds_error err
= IE_SUCCESS
;
11886 void *xml_stream
= NULL
;
11887 size_t xml_stream_length
;
11888 size_t phys_start
, phys_end
;
11889 char *phys_path
= NULL
;
11890 struct isds_hash
*new_hash
= NULL
;
11893 if (!context
) return IE_INVALID_CONTEXT
;
11894 zfree(context
->long_message
);
11895 if (!message
) return IE_INVAL
;
11897 if (!message
->raw
) {
11898 isds_log_message(context
,
11899 _("Message does not carry raw representation"));
11903 switch (message
->raw_type
) {
11904 case RAWTYPE_INCOMING_MESSAGE
:
11906 xml_stream
= message
->raw
;
11907 xml_stream_length
= message
->raw_length
;
11910 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11911 nsuri
= SISDS_INCOMING_NS
;
11912 xml_stream
= message
->raw
;
11913 xml_stream_length
= message
->raw_length
;
11916 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11917 nsuri
= SISDS_INCOMING_NS
;
11918 err
= _isds_extract_cms_data(context
,
11919 message
->raw
, message
->raw_length
,
11920 &xml_stream
, &xml_stream_length
);
11921 if (err
) goto leave
;
11924 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11925 nsuri
= SISDS_OUTGOING_NS
;
11926 xml_stream
= message
->raw
;
11927 xml_stream_length
= message
->raw_length
;
11930 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11931 nsuri
= SISDS_OUTGOING_NS
;
11932 err
= _isds_extract_cms_data(context
,
11933 message
->raw
, message
->raw_length
,
11934 &xml_stream
, &xml_stream_length
);
11935 if (err
) goto leave
;
11939 isds_log_message(context
, _("Bad raw representation type"));
11945 /* XXX: Hash is computed from original string representing isds:dmDm
11946 * subtree. That means no encoding, white space, xmlns attributes changes.
11947 * In other words, input for hash can be invalid XML stream. */
11948 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11949 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11950 PHYSXML_ELEMENT_SEPARATOR
,
11951 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11952 PHYSXML_ELEMENT_SEPARATOR
11953 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11957 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11958 phys_path
, &phys_start
, &phys_end
);
11961 isds_log_message(context
,
11962 _("Substring with isds:dmDM element could not be located "
11963 "in raw message"));
11969 new_hash
= calloc(1, sizeof(*new_hash
));
11974 new_hash
->algorithm
= algorithm
;
11975 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11978 isds_log_message(context
, _("Could not compute message hash"));
11982 /* Save computed hash */
11983 if (!message
->envelope
) {
11984 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11985 if (!message
->envelope
) {
11990 isds_hash_free(&message
->envelope
->hash
);
11991 message
->envelope
->hash
= new_hash
;
11995 isds_hash_free(&new_hash
);
11999 if (xml_stream
!= message
->raw
) free(xml_stream
);
12004 /* Compare two hashes.
12005 * @h1 is first hash
12006 * @h2 is another hash
12008 * IE_SUCCESS if hashes equal
12009 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12010 * IE_ENUM if not comparable, but both structures defined
12011 * IE_INVAL if some of the structures are undefined (NULL)
12012 * IE_ERROR if internal error occurs */
12013 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12014 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12015 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12016 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12017 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12018 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12020 for (size_t i
= 0; i
< h1
->length
; i
++) {
12021 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12022 return IE_NOTEQUAL
;
12028 /* Check message has gone through ISDS by comparing message hash stored in
12029 * ISDS and locally computed hash. You must provide message with valid raw
12030 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12031 * This is convenient wrapper for isds_download_message_hash(),
12032 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12033 * @context is session context
12034 * @message is message with valid raw and envelope member; envelope->hash
12035 * member will be changed during function run. Use envelope on heap only.
12037 * IE_SUCCESS if message originates in ISDS
12038 * IE_NOTEQUAL if message is unknown to ISDS
12039 * other code for other errors */
12040 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12041 struct isds_message
*message
) {
12042 isds_error err
= IE_SUCCESS
;
12043 struct isds_hash
*downloaded_hash
= NULL
;
12045 if (!context
) return IE_INVALID_CONTEXT
;
12046 zfree(context
->long_message
);
12047 if (!message
) return IE_INVAL
;
12049 if (!message
->envelope
) {
12050 isds_log_message(context
,
12051 _("Given message structure is missing envelope"));
12054 if (!message
->raw
) {
12055 isds_log_message(context
,
12056 _("Given message structure is missing raw representation"));
12060 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12062 if (err
) goto leave
;
12064 err
= isds_compute_message_hash(context
, message
,
12065 downloaded_hash
->algorithm
);
12066 if (err
) goto leave
;
12068 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12071 isds_hash_free(&downloaded_hash
);
12076 /* Search for document by document ID in list of documents. IDs are compared
12078 * @documents is list of isds_documents
12079 * @id is document identifier
12080 * @return first matching document or NULL. */
12081 const struct isds_document
*isds_find_document_by_id(
12082 const struct isds_list
*documents
, const char *id
) {
12083 const struct isds_list
*item
;
12084 const struct isds_document
*document
;
12086 for (item
= documents
; item
; item
= item
->next
) {
12087 document
= (struct isds_document
*) item
->data
;
12088 if (!document
) continue;
12090 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12098 /* Normalize @mime_type to be proper MIME type.
12099 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12100 * guess regular MIME type (e.g. "application/pdf").
12101 * @mime_type is UTF-8 encoded MIME type to fix
12102 * @return original @mime_type if no better interpretation exists, or
12103 * constant static UTF-8 encoded string with proper MIME type. */
12104 const char *isds_normalize_mime_type(const char *mime_type
) {
12105 if (!mime_type
) return NULL
;
12107 for (size_t offset
= 0;
12108 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12110 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12111 extension_map_mime
[offset
]))
12112 return (const char *) extension_map_mime
[offset
+ 1];
12119 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12120 struct isds_message **message);
12121 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12122 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12123 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12124 struct isds_address **address);
12126 int isds_message_free(struct isds_message **message);
12127 int isds_address_free(struct isds_address **address);
12131 /* Makes known all relevant namespaces to given XPath context
12132 * @xpath_ctx is XPath context
12133 * @message_ns selects proper message name space. Unsigned and signed
12134 * messages and delivery info's differ in prefix and URI. */
12135 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12136 const message_ns_type message_ns
) {
12137 const xmlChar
*message_namespace
= NULL
;
12139 if (!xpath_ctx
) return IE_ERROR
;
12141 switch(message_ns
) {
12143 message_namespace
= BAD_CAST ISDS1_NS
; break;
12144 case MESSAGE_NS_UNSIGNED
:
12145 message_namespace
= BAD_CAST ISDS_NS
; break;
12146 case MESSAGE_NS_SIGNED_INCOMING
:
12147 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12148 case MESSAGE_NS_SIGNED_OUTGOING
:
12149 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12150 case MESSAGE_NS_SIGNED_DELIVERY
:
12151 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12156 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12158 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12160 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12162 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12164 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12166 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))