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
,
2227 time_t seconds_as_time_t
;
2229 if (!time
|| !string
) return IE_INVAL
;
2231 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2232 * 32-bit long in Microsoft API. Convert value to the type expected by
2234 seconds_as_time_t
= time
->tv_sec
;
2235 if (!gmtime_r(&seconds_as_time_t
, &broken
)) return IE_DATE
;
2236 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2238 /* TODO: small negative year should be formatted as "-0012". This is not
2239 * true for glibc "%04d". We should implement it.
2240 * time->tv_usec type is su_seconds_t which is required to be signed
2241 * integer to accomodate values from range [-1, 1000000].
2242 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2243 if (-1 == isds_asprintf((char **) string
,
2244 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX
,
2245 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2246 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2247 (intmax_t)time
->tv_usec
))
2252 #endif /* HAVE_LIBCURL */
2255 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2256 * It respects microseconds too. Microseconds are rounded half up.
2257 * In case of error, @time will be freed. */
2258 static isds_error
timestring2timeval(const xmlChar
*string
,
2259 struct timeval
**time
) {
2261 char *offset
, *delim
, *endptr
;
2262 const int subsecond_resolution
= 6;
2263 char subseconds
[subsecond_resolution
+ 1];
2265 int offset_hours
, offset_minutes
;
2267 long int long_number
;
2272 if (!time
) return IE_INVAL
;
2278 memset(&broken
, 0, sizeof(broken
));
2281 *time
= calloc(1, sizeof(**time
));
2282 if (!*time
) return IE_NOMEM
;
2284 memset(*time
, 0, sizeof(**time
));
2288 /* xsd:date is ISO 8601 string, thus ASCII */
2289 /*TODO: negative year */
2293 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2294 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2295 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2301 broken
.tm_year
-= 1900;
2303 broken
.tm_isdst
= -1;
2304 offset
= (char*)string
+ i
;
2306 /* Parse date and time without subseconds and offset */
2307 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2314 /* Get subseconds */
2315 if (*offset
== '.' ) {
2318 /* Copy first 6 digits, pad it with zeros.
2319 * Current server implementation uses only millisecond resolution. */
2320 /* TODO: isdigit() is locale sensitive */
2322 i
< subsecond_resolution
&& isdigit(*offset
);
2324 subseconds
[i
] = *offset
;
2326 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2327 /* Check 7th digit for rounding */
2328 if (*offset
>= '5') round_up
= 1;
2331 for (; i
< subsecond_resolution
; i
++) {
2332 subseconds
[i
] = '0';
2334 subseconds
[subsecond_resolution
] = '\0';
2336 /* Convert it into integer */
2337 long_number
= strtol(subseconds
, &endptr
, 10);
2338 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2339 long_number
== LONG_MAX
) {
2343 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2344 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2345 * microseconds" and "the type shall be a signed integer capable of
2346 * storing values at least in the range [-1, 1000000]. */
2347 if (long_number
< -1 || long_number
>= 1000000) {
2351 (*time
)->tv_usec
= long_number
;
2353 /* Round the subseconds */
2355 if (999999 == (*time
)->tv_usec
) {
2356 (*time
)->tv_usec
= 0;
2363 /* move to the zone offset delimiter or signal NULL*/
2364 delim
= strchr(offset
, '-');
2366 delim
= strchr(offset
, '+');
2368 delim
= strchr(offset
, 'Z');
2372 /* Get zone offset */
2373 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2374 * "" equals to "Z" and it means UTC zone. */
2375 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2376 * colon separator */
2377 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2378 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2382 if (*offset
== '+') {
2383 broken
.tm_hour
-= offset_hours
;
2384 broken
.tm_min
-= offset_minutes
;
2386 broken
.tm_hour
+= offset_hours
;
2387 broken
.tm_min
+= offset_minutes
;
2391 /* Convert to time_t */
2392 (*time
)->tv_sec
= _isds_timegm(&broken
);
2393 if ((*time
)->tv_sec
== (time_t) -1) {
2402 /* Convert unsigned int into isds_message_status.
2403 * @context is session context
2404 * @number is pointer to number value. NULL will be treated as invalid value.
2405 * @status is automatically reallocated status
2406 * @return IE_SUCCESS, or error code and free status */
2407 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2408 const unsigned long int *number
, isds_message_status
**status
) {
2409 if (!context
) return IE_INVALID_CONTEXT
;
2410 if (!status
) return IE_INVAL
;
2412 free(*status
); *status
= NULL
;
2413 if (!number
) return IE_INVAL
;
2415 if (*number
< 1 || *number
> 10) {
2416 isds_printf_message(context
, _("Invalid message status value: %lu"),
2421 *status
= malloc(sizeof(**status
));
2422 if (!*status
) return IE_NOMEM
;
2424 **status
= 1 << *number
;
2429 /* Convert event description string into isds_event members type and
2431 * @string is raw event description starting with event prefix
2432 * @event is structure where to store type and stripped description to
2433 * @return standard error code, unknown prefix is not classified as an error.
2435 static isds_error
eventstring2event(const xmlChar
*string
,
2436 struct isds_event
* event
) {
2437 const xmlChar
*known_prefixes
[] = {
2448 const isds_event_type types
[] = {
2449 EVENT_ENTERED_SYSTEM
,
2450 EVENT_ACCEPTED_BY_RECIPIENT
,
2451 EVENT_ACCEPTED_BY_FICTION
,
2452 EVENT_UNDELIVERABLE
,
2453 EVENT_COMMERCIAL_ACCEPTED
,
2455 EVENT_PRIMARY_LOGIN
,
2456 EVENT_ENTRUSTED_LOGIN
,
2462 if (!string
|| !event
) return IE_INVAL
;
2465 event
->type
= malloc(sizeof(*event
->type
));
2466 if (!(event
->type
)) return IE_NOMEM
;
2468 zfree(event
->description
);
2470 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2472 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2474 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2475 /* Prefix is known */
2476 *event
->type
= types
[index
];
2478 /* Strip prefix from description and spaces */
2479 /* TODO: Recognize all white spaces from UCS blank class and
2480 * operate on UTF-8 chars. */
2481 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2482 event
->description
= strdup((char *) (string
+ length
));
2483 if (!(event
->description
)) return IE_NOMEM
;
2489 /* Unknown event prefix.
2490 * XSD allows any string */
2491 char *string_locale
= _isds_utf82locale((char *) string
);
2492 isds_log(ILF_ISDS
, ILL_WARNING
,
2493 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2494 free(string_locale
);
2496 *event
->type
= EVENT_UKNOWN
;
2497 event
->description
= strdup((char *) string
);
2498 if (!(event
->description
)) return IE_NOMEM
;
2504 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2505 * and leave label */
2506 #define EXTRACT_STRING(element, string) { \
2507 xmlXPathFreeObject(result); \
2508 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2509 if (NULL == (result)) { \
2513 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2514 if (result->nodesetval->nodeNr > 1) { \
2515 isds_printf_message(context, _("Multiple %s element"), element); \
2519 (string) = (char *) \
2520 xmlXPathCastNodeSetToString(result->nodesetval); \
2521 if (NULL == (string)) { \
2528 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2530 char *string = NULL; \
2531 EXTRACT_STRING(element, string); \
2534 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2535 if (!(booleanPtr)) { \
2541 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2542 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2543 *(booleanPtr) = 1; \
2544 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2545 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2546 *(booleanPtr) = 0; \
2548 char *string_locale = _isds_utf82locale((char*)string); \
2549 isds_printf_message(context, \
2550 _("%s value is not valid boolean: %s"), \
2551 element, string_locale); \
2552 free(string_locale); \
2562 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2564 char *string = NULL; \
2565 EXTRACT_STRING(element, string); \
2567 if (NULL == string) { \
2568 isds_printf_message(context, _("%s element is empty"), element); \
2572 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2573 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2575 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2576 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2579 char *string_locale = _isds_utf82locale((char*)string); \
2580 isds_printf_message(context, \
2581 _("%s value is not valid boolean: %s"), \
2582 element, string_locale); \
2583 free(string_locale); \
2592 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2594 char *string = NULL; \
2595 EXTRACT_STRING(element, string); \
2600 number = strtol((char*)string, &endptr, 10); \
2602 if (*endptr != '\0') { \
2603 char *string_locale = _isds_utf82locale((char *)string); \
2604 isds_printf_message(context, \
2605 _("%s is not valid integer: %s"), \
2606 element, string_locale); \
2607 free(string_locale); \
2613 if (number == LONG_MIN || number == LONG_MAX) { \
2614 char *string_locale = _isds_utf82locale((char *)string); \
2615 isds_printf_message(context, \
2616 _("%s value out of range of long int: %s"), \
2617 element, string_locale); \
2618 free(string_locale); \
2624 free(string); string = NULL; \
2626 if (!(preallocated)) { \
2627 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2628 if (!(longintPtr)) { \
2633 *(longintPtr) = number; \
2637 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2639 char *string = NULL; \
2640 EXTRACT_STRING(element, string); \
2645 number = strtol((char*)string, &endptr, 10); \
2647 if (*endptr != '\0') { \
2648 char *string_locale = _isds_utf82locale((char *)string); \
2649 isds_printf_message(context, \
2650 _("%s is not valid integer: %s"), \
2651 element, string_locale); \
2652 free(string_locale); \
2658 if (number == LONG_MIN || number == LONG_MAX) { \
2659 char *string_locale = _isds_utf82locale((char *)string); \
2660 isds_printf_message(context, \
2661 _("%s value out of range of long int: %s"), \
2662 element, string_locale); \
2663 free(string_locale); \
2669 free(string); string = NULL; \
2671 isds_printf_message(context, \
2672 _("%s value is negative: %ld"), element, number); \
2677 if (!(preallocated)) { \
2678 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2679 if (!(ulongintPtr)) { \
2684 *(ulongintPtr) = number; \
2688 #define EXTRACT_DATE(element, tmPtr) { \
2689 char *string = NULL; \
2690 EXTRACT_STRING(element, string); \
2691 if (NULL != string) { \
2692 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2693 if (NULL == (tmPtr)) { \
2698 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2700 if (err == IE_NOTSUP) { \
2702 char *string_locale = _isds_utf82locale(string); \
2703 char *element_locale = _isds_utf82locale(element); \
2704 isds_printf_message(context, _("Invalid %s value: %s"), \
2705 element_locale, string_locale); \
2706 free(string_locale); \
2707 free(element_locale); \
2716 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2717 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2719 if ((required) && (!string)) { \
2720 char *attribute_locale = _isds_utf82locale(attribute); \
2721 char *element_locale = \
2722 _isds_utf82locale((char *)xpath_ctx->node->name); \
2723 isds_printf_message(context, \
2724 _("Could not extract required %s attribute value from " \
2725 "%s element"), attribute_locale, element_locale); \
2726 free(element_locale); \
2727 free(attribute_locale); \
2734 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2736 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2737 (xmlChar *) (string)); \
2739 isds_printf_message(context, \
2740 _("Could not add %s child to %s element"), \
2741 element, (parent)->name); \
2747 #define INSERT_STRING(parent, element, string) \
2748 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2750 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2752 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2753 else { INSERT_STRING(parent, element, "false"); } \
2756 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2759 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2761 INSERT_STRING(parent, element, NULL); \
2765 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2766 if ((longintPtr)) { \
2767 /* FIXME: locale sensitive */ \
2768 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2772 INSERT_STRING(parent, element, buffer) \
2773 free(buffer); (buffer) = NULL; \
2774 } else { INSERT_STRING(parent, element, NULL) } \
2777 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2778 if ((ulongintPtr)) { \
2779 /* FIXME: locale sensitive */ \
2780 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2784 INSERT_STRING(parent, element, buffer) \
2785 free(buffer); (buffer) = NULL; \
2786 } else { INSERT_STRING(parent, element, NULL) } \
2789 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2791 /* FIXME: locale sensitive */ \
2792 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2796 INSERT_STRING(parent, element, buffer) \
2797 free(buffer); (buffer) = NULL; \
2800 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2802 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2804 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2805 (xmlChar *) (string)); \
2806 if (!attribute_node) { \
2807 isds_printf_message(context, _("Could not add %s " \
2808 "attribute to %s element"), \
2809 (attribute), (parent)->name); \
2815 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2817 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2818 if (length > (maximum)) { \
2819 isds_printf_message(context, \
2820 ngettext("%s has more than %d characters", \
2821 "%s has more than %d characters", (maximum)), \
2822 (name), (maximum)); \
2826 if (length < (minimum)) { \
2827 isds_printf_message(context, \
2828 ngettext("%s has less than %d characters", \
2829 "%s has less than %d characters", (minimum)), \
2830 (name), (minimum)); \
2837 #define INSERT_ELEMENT(child, parent, element) \
2839 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2841 isds_printf_message(context, \
2842 _("Could not add %s child to %s element"), \
2843 (element), (parent)->name); \
2850 /* Find child element by name in given XPath context and switch context onto
2851 * it. The child must be uniq and must exist. Otherwise fails.
2852 * @context is ISDS context
2853 * @child is child element name
2854 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2855 * into it child. In error case, the @xpath_ctx keeps original value. */
2856 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2857 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2858 isds_error err
= IE_SUCCESS
;
2859 xmlXPathObjectPtr result
= NULL
;
2861 if (!context
) return IE_INVALID_CONTEXT
;
2862 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2865 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2872 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2873 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2874 char *child_locale
= _isds_utf82locale((char*) child
);
2875 isds_printf_message(context
,
2876 _("%s element does not contain %s child"),
2877 parent_locale
, child_locale
);
2879 free(parent_locale
);
2885 if (result
->nodesetval
->nodeNr
> 1) {
2886 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2887 char *child_locale
= _isds_utf82locale((char*) child
);
2888 isds_printf_message(context
,
2889 _("%s element contains multiple %s children"),
2890 parent_locale
, child_locale
);
2892 free(parent_locale
);
2897 /* Switch context */
2898 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2901 xmlXPathFreeObject(result
);
2908 /* Find and convert XSD:gPersonName group in current node into structure
2909 * @context is ISDS context
2910 * @personName is automatically reallocated person name structure. If no member
2911 * value is found, will be freed.
2912 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2914 * In case of error @personName will be freed. */
2915 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2916 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2917 isds_error err
= IE_SUCCESS
;
2918 xmlXPathObjectPtr result
= NULL
;
2920 if (!context
) return IE_INVALID_CONTEXT
;
2921 if (!personName
) return IE_INVAL
;
2922 isds_PersonName_free(personName
);
2923 if (!xpath_ctx
) return IE_INVAL
;
2926 *personName
= calloc(1, sizeof(**personName
));
2932 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2933 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2934 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2935 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2937 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2938 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2939 isds_PersonName_free(personName
);
2942 if (err
) isds_PersonName_free(personName
);
2943 xmlXPathFreeObject(result
);
2948 /* Find and convert XSD:gAddress group in current node into structure
2949 * @context is ISDS context
2950 * @address is automatically reallocated address structure. If no member
2951 * value is found, will be freed.
2952 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2954 * In case of error @address will be freed. */
2955 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2956 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2957 isds_error err
= IE_SUCCESS
;
2958 xmlXPathObjectPtr result
= NULL
;
2960 if (!context
) return IE_INVALID_CONTEXT
;
2961 if (!address
) return IE_INVAL
;
2962 isds_Address_free(address
);
2963 if (!xpath_ctx
) return IE_INVAL
;
2966 *address
= calloc(1, sizeof(**address
));
2972 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2973 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2974 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2975 EXTRACT_STRING("isds:adNumberInMunicipality",
2976 (*address
)->adNumberInMunicipality
);
2977 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2978 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2980 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2981 !(*address
)->adNumberInStreet
&&
2982 !(*address
)->adNumberInMunicipality
&&
2983 !(*address
)->adZipCode
&& !(*address
)->adState
)
2984 isds_Address_free(address
);
2987 if (err
) isds_Address_free(address
);
2988 xmlXPathFreeObject(result
);
2993 /* Find and convert isds:biDate element in current node into structure
2994 * @context is ISDS context
2995 * @biDate is automatically reallocated birth date structure. If no member
2996 * value is found, will be freed.
2997 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2999 * In case of error @biDate will be freed. */
3000 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3001 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3002 isds_error err
= IE_SUCCESS
;
3003 xmlXPathObjectPtr result
= NULL
;
3004 char *string
= NULL
;
3006 if (!context
) return IE_INVALID_CONTEXT
;
3007 if (!biDate
) return IE_INVAL
;
3009 if (!xpath_ctx
) return IE_INVAL
;
3011 EXTRACT_STRING("isds:biDate", string
);
3013 *biDate
= calloc(1, sizeof(**biDate
));
3018 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3020 if (err
== IE_NOTSUP
) {
3022 char *string_locale
= _isds_utf82locale(string
);
3023 isds_printf_message(context
,
3024 _("Invalid isds:biDate value: %s"), string_locale
);
3025 free(string_locale
);
3032 if (err
) zfree(*biDate
);
3034 xmlXPathFreeObject(result
);
3039 /* Convert isds:dBOwnerInfo XML tree into structure
3040 * @context is ISDS context
3041 * @db_owner_info is automatically reallocated box owner info structure
3042 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3043 * In case of error @db_owner_info will be freed. */
3044 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3045 struct isds_DbOwnerInfo
**db_owner_info
,
3046 xmlXPathContextPtr xpath_ctx
) {
3047 isds_error err
= IE_SUCCESS
;
3048 xmlXPathObjectPtr result
= NULL
;
3049 char *string
= NULL
;
3051 if (!context
) return IE_INVALID_CONTEXT
;
3052 if (!db_owner_info
) return IE_INVAL
;
3053 isds_DbOwnerInfo_free(db_owner_info
);
3054 if (!xpath_ctx
) return IE_INVAL
;
3057 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3058 if (!*db_owner_info
) {
3063 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3065 EXTRACT_STRING("isds:dbType", string
);
3067 (*db_owner_info
)->dbType
=
3068 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3069 if (!(*db_owner_info
)->dbType
) {
3073 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3075 zfree((*db_owner_info
)->dbType
);
3076 if (err
== IE_ENUM
) {
3078 char *string_locale
= _isds_utf82locale(string
);
3079 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3081 free(string_locale
);
3088 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3090 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3092 if (err
) goto leave
;
3094 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3096 (*db_owner_info
)->birthInfo
=
3097 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3098 if (!(*db_owner_info
)->birthInfo
) {
3102 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3104 if (err
) goto leave
;
3105 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3106 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3107 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3108 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3109 !(*db_owner_info
)->birthInfo
->biCity
&&
3110 !(*db_owner_info
)->birthInfo
->biCounty
&&
3111 !(*db_owner_info
)->birthInfo
->biState
)
3112 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3114 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3115 if (err
) goto leave
;
3117 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3118 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3119 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3120 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3121 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3123 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3125 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3126 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3127 (*db_owner_info
)->dbOpenAddressing
);
3130 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3132 xmlXPathFreeObject(result
);
3137 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3138 * @context is session context
3139 * @owner is libisds structure with box description
3140 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3141 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3142 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3144 isds_error err
= IE_SUCCESS
;
3146 xmlChar
*string
= NULL
;
3148 if (!context
) return IE_INVALID_CONTEXT
;
3149 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3152 /* Build XSD:tDbOwnerInfo */
3153 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3154 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3157 if (owner
->dbType
) {
3158 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3160 isds_printf_message(context
, _("Invalid dbType value: %d"),
3165 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3167 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3168 if (owner
->personName
) {
3169 INSERT_STRING(db_owner_info
, "pnFirstName",
3170 owner
->personName
->pnFirstName
);
3171 INSERT_STRING(db_owner_info
, "pnMiddleName",
3172 owner
->personName
->pnMiddleName
);
3173 INSERT_STRING(db_owner_info
, "pnLastName",
3174 owner
->personName
->pnLastName
);
3175 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3176 owner
->personName
->pnLastNameAtBirth
);
3178 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3179 if (owner
->birthInfo
) {
3180 if (owner
->birthInfo
->biDate
) {
3181 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3182 INSERT_STRING(db_owner_info
, "biDate", string
);
3183 free(string
); string
= NULL
;
3185 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3186 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3187 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3189 if (owner
->address
) {
3190 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3191 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3192 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3193 owner
->address
->adNumberInStreet
);
3194 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3195 owner
->address
->adNumberInMunicipality
);
3196 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3197 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3199 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3200 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3201 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3203 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3204 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3206 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3207 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3209 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3211 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3212 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3213 owner
->dbOpenAddressing
);
3221 /* Convert XSD:tDbUserInfo XML tree into structure
3222 * @context is ISDS context
3223 * @db_user_info is automatically reallocated user info structure
3224 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3225 * In case of error @db_user_info will be freed. */
3226 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3227 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3228 isds_error err
= IE_SUCCESS
;
3229 xmlXPathObjectPtr result
= NULL
;
3230 char *string
= NULL
;
3232 if (!context
) return IE_INVALID_CONTEXT
;
3233 if (!db_user_info
) return IE_INVAL
;
3234 isds_DbUserInfo_free(db_user_info
);
3235 if (!xpath_ctx
) return IE_INVAL
;
3238 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3239 if (!*db_user_info
) {
3244 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3246 EXTRACT_STRING("isds:userType", string
);
3248 (*db_user_info
)->userType
=
3249 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3250 if (!(*db_user_info
)->userType
) {
3254 err
= string2isds_UserType((xmlChar
*)string
,
3255 (*db_user_info
)->userType
);
3257 zfree((*db_user_info
)->userType
);
3258 if (err
== IE_ENUM
) {
3260 char *string_locale
= _isds_utf82locale(string
);
3261 isds_printf_message(context
,
3262 _("Unknown isds:userType value: %s"), string_locale
);
3263 free(string_locale
);
3270 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3272 (*db_user_info
)->personName
=
3273 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3274 if (!(*db_user_info
)->personName
) {
3279 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3281 if (err
) goto leave
;
3283 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3284 if (err
) goto leave
;
3286 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3287 if (err
) goto leave
;
3289 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3290 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3292 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3293 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3294 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3296 /* ???: Default value is "CZ" according specification. Should we provide
3298 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3301 if (err
) isds_DbUserInfo_free(db_user_info
);
3303 xmlXPathFreeObject(result
);
3308 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3309 * @context is session context
3310 * @user is libisds structure with user description
3311 * @db_user_info is XML element of XSD:tDbUserInfo */
3312 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3313 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3315 isds_error err
= IE_SUCCESS
;
3317 xmlChar
*string
= NULL
;
3319 if (!context
) return IE_INVALID_CONTEXT
;
3320 if (!user
|| !db_user_info
) return IE_INVAL
;
3322 /* Build XSD:tDbUserInfo */
3323 if (user
->personName
) {
3324 INSERT_STRING(db_user_info
, "pnFirstName",
3325 user
->personName
->pnFirstName
);
3326 INSERT_STRING(db_user_info
, "pnMiddleName",
3327 user
->personName
->pnMiddleName
);
3328 INSERT_STRING(db_user_info
, "pnLastName",
3329 user
->personName
->pnLastName
);
3330 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3331 user
->personName
->pnLastNameAtBirth
);
3333 if (user
->address
) {
3334 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3335 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3336 INSERT_STRING(db_user_info
, "adNumberInStreet",
3337 user
->address
->adNumberInStreet
);
3338 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3339 user
->address
->adNumberInMunicipality
);
3340 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3341 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3344 if (!tm2datestring(user
->biDate
, &string
))
3345 INSERT_STRING(db_user_info
, "biDate", string
);
3348 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3349 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3352 if (user
->userType
) {
3353 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3355 isds_printf_message(context
, _("Invalid userType value: %d"),
3360 INSERT_STRING(db_user_info
, "userType", type_string
);
3363 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3364 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3365 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3366 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3367 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3368 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3369 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3370 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3371 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3379 /* Convert XSD:tPDZRec XML tree into structure
3380 * @context is ISDS context
3381 * @permission is automatically reallocated commercial permission structure
3382 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3383 * In case of error @permission will be freed. */
3384 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3385 struct isds_commercial_permission
**permission
,
3386 xmlXPathContextPtr xpath_ctx
) {
3387 isds_error err
= IE_SUCCESS
;
3388 xmlXPathObjectPtr result
= NULL
;
3389 char *string
= NULL
;
3391 if (!context
) return IE_INVALID_CONTEXT
;
3392 if (!permission
) return IE_INVAL
;
3393 isds_commercial_permission_free(permission
);
3394 if (!xpath_ctx
) return IE_INVAL
;
3397 *permission
= calloc(1, sizeof(**permission
));
3403 EXTRACT_STRING("isds:PDZType", string
);
3405 err
= string2isds_payment_type((xmlChar
*)string
,
3406 &(*permission
)->type
);
3408 if (err
== IE_ENUM
) {
3410 char *string_locale
= _isds_utf82locale(string
);
3411 isds_printf_message(context
,
3412 _("Unknown isds:PDZType value: %s"), string_locale
);
3413 free(string_locale
);
3420 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3421 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3423 EXTRACT_STRING("isds:PDZExpire", string
);
3425 err
= timestring2timeval((xmlChar
*) string
,
3426 &((*permission
)->expiration
));
3428 char *string_locale
= _isds_utf82locale(string
);
3429 if (err
== IE_DATE
) err
= IE_ISDS
;
3430 isds_printf_message(context
,
3431 _("Could not convert PDZExpire as ISO time: %s"),
3433 free(string_locale
);
3439 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3440 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3443 if (err
) isds_commercial_permission_free(permission
);
3445 xmlXPathFreeObject(result
);
3450 /* Convert XSD:tCiRecord XML tree into structure
3451 * @context is ISDS context
3452 * @event is automatically reallocated commercial credit event structure
3453 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3454 * In case of error @event will be freed. */
3455 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3456 struct isds_credit_event
**event
,
3457 xmlXPathContextPtr xpath_ctx
) {
3458 isds_error err
= IE_SUCCESS
;
3459 xmlXPathObjectPtr result
= NULL
;
3460 char *string
= NULL
;
3461 long int *number_ptr
;
3463 if (!context
) return IE_INVALID_CONTEXT
;
3464 if (!event
) return IE_INVAL
;
3465 isds_credit_event_free(event
);
3466 if (!xpath_ctx
) return IE_INVAL
;
3469 *event
= calloc(1, sizeof(**event
));
3475 EXTRACT_STRING("isds:ciEventTime", string
);
3477 err
= timestring2timeval((xmlChar
*) string
,
3480 char *string_locale
= _isds_utf82locale(string
);
3481 if (err
== IE_DATE
) err
= IE_ISDS
;
3482 isds_printf_message(context
,
3483 _("Could not convert ciEventTime as ISO time: %s"),
3485 free(string_locale
);
3491 EXTRACT_STRING("isds:ciEventType", string
);
3493 err
= string2isds_credit_event_type((xmlChar
*)string
,
3496 if (err
== IE_ENUM
) {
3498 char *string_locale
= _isds_utf82locale(string
);
3499 isds_printf_message(context
,
3500 _("Unknown isds:ciEventType value: %s"), string_locale
);
3501 free(string_locale
);
3508 number_ptr
= &((*event
)->credit_change
);
3509 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3510 number_ptr
= &(*event
)->new_credit
;
3511 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3513 switch((*event
)->type
) {
3514 case ISDS_CREDIT_CHARGED
:
3515 EXTRACT_STRING("isds:ciTransID",
3516 (*event
)->details
.charged
.transaction
);
3518 case ISDS_CREDIT_DISCHARGED
:
3519 EXTRACT_STRING("isds:ciTransID",
3520 (*event
)->details
.discharged
.transaction
);
3522 case ISDS_CREDIT_MESSAGE_SENT
:
3523 EXTRACT_STRING("isds:ciRecipientID",
3524 (*event
)->details
.message_sent
.recipient
);
3525 EXTRACT_STRING("isds:ciPDZID",
3526 (*event
)->details
.message_sent
.message_id
);
3528 case ISDS_CREDIT_STORAGE_SET
:
3529 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3530 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3531 EXTRACT_DATE("isds:ciNewFrom",
3532 (*event
)->details
.storage_set
.new_valid_from
);
3533 EXTRACT_DATE("isds:ciNewTo",
3534 (*event
)->details
.storage_set
.new_valid_to
);
3535 EXTRACT_LONGINT("isds:ciOldCapacity",
3536 (*event
)->details
.storage_set
.old_capacity
, 0);
3537 EXTRACT_DATE("isds:ciOldFrom",
3538 (*event
)->details
.storage_set
.old_valid_from
);
3539 EXTRACT_DATE("isds:ciOldTo",
3540 (*event
)->details
.storage_set
.old_valid_to
);
3541 EXTRACT_STRING("isds:ciDoneBy",
3542 (*event
)->details
.storage_set
.initiator
);
3544 case ISDS_CREDIT_EXPIRED
:
3549 if (err
) isds_credit_event_free(event
);
3551 xmlXPathFreeObject(result
);
3556 #endif /* HAVE_LIBCURL */
3559 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3560 * isds_envelope structure. The envelope is automatically allocated but not
3561 * reallocated. The date are just appended into envelope structure.
3562 * @context is ISDS context
3563 * @envelope is automatically allocated message envelope structure
3564 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3565 * In case of error @envelope will be freed. */
3566 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3567 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3568 isds_error err
= IE_SUCCESS
;
3569 xmlXPathObjectPtr result
= NULL
;
3571 if (!context
) return IE_INVALID_CONTEXT
;
3572 if (!envelope
) return IE_INVAL
;
3573 if (!xpath_ctx
) return IE_INVAL
;
3577 /* Allocate envelope */
3578 *envelope
= calloc(1, sizeof(**envelope
));
3584 /* Else free former data */
3585 zfree((*envelope
)->dmSenderOrgUnit
);
3586 zfree((*envelope
)->dmSenderOrgUnitNum
);
3587 zfree((*envelope
)->dbIDRecipient
);
3588 zfree((*envelope
)->dmRecipientOrgUnit
);
3589 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3590 zfree((*envelope
)->dmToHands
);
3591 zfree((*envelope
)->dmAnnotation
);
3592 zfree((*envelope
)->dmRecipientRefNumber
);
3593 zfree((*envelope
)->dmSenderRefNumber
);
3594 zfree((*envelope
)->dmRecipientIdent
);
3595 zfree((*envelope
)->dmSenderIdent
);
3596 zfree((*envelope
)->dmLegalTitleLaw
);
3597 zfree((*envelope
)->dmLegalTitleYear
);
3598 zfree((*envelope
)->dmLegalTitleSect
);
3599 zfree((*envelope
)->dmLegalTitlePar
);
3600 zfree((*envelope
)->dmLegalTitlePoint
);
3601 zfree((*envelope
)->dmPersonalDelivery
);
3602 zfree((*envelope
)->dmAllowSubstDelivery
);
3605 /* Extract envelope elements added by sender or ISDS
3606 * (XSD: gMessageEnvelopeSub type) */
3607 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3608 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3609 (*envelope
)->dmSenderOrgUnitNum
, 0);
3610 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3611 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3612 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3613 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3614 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3615 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3616 EXTRACT_STRING("isds:dmRecipientRefNumber",
3617 (*envelope
)->dmRecipientRefNumber
);
3618 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3619 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3620 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3622 /* Extract envelope elements regarding law reference */
3623 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3624 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3625 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3626 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3627 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3629 /* Extract envelope other elements */
3630 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3631 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3632 (*envelope
)->dmAllowSubstDelivery
);
3635 if (err
) isds_envelope_free(envelope
);
3636 xmlXPathFreeObject(result
);
3642 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3643 * isds_envelope structure. The envelope is automatically allocated but not
3644 * reallocated. The date are just appended into envelope structure.
3645 * @context is ISDS context
3646 * @envelope is automatically allocated message envelope structure
3647 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3648 * In case of error @envelope will be freed. */
3649 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3650 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3651 isds_error err
= IE_SUCCESS
;
3652 xmlXPathObjectPtr result
= NULL
;
3654 if (!context
) return IE_INVALID_CONTEXT
;
3655 if (!envelope
) return IE_INVAL
;
3656 if (!xpath_ctx
) return IE_INVAL
;
3660 /* Allocate envelope */
3661 *envelope
= calloc(1, sizeof(**envelope
));
3667 /* Else free former data */
3668 zfree((*envelope
)->dmID
);
3669 zfree((*envelope
)->dbIDSender
);
3670 zfree((*envelope
)->dmSender
);
3671 zfree((*envelope
)->dmSenderAddress
);
3672 zfree((*envelope
)->dmSenderType
);
3673 zfree((*envelope
)->dmRecipient
);
3674 zfree((*envelope
)->dmRecipientAddress
);
3675 zfree((*envelope
)->dmAmbiguousRecipient
);
3678 /* Extract envelope elements added by ISDS
3679 * (XSD: gMessageEnvelope type) */
3680 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3681 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3682 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3683 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3684 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3685 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3686 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3687 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3688 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3689 (*envelope
)->dmAmbiguousRecipient
);
3691 /* Extract envelope elements added by sender and ISDS
3692 * (XSD: gMessageEnvelope type) */
3693 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3694 if (err
) goto leave
;
3697 if (err
) isds_envelope_free(envelope
);
3698 xmlXPathFreeObject(result
);
3703 /* Convert other envelope elements from XML tree into isds_envelope structure:
3704 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3705 * The envelope is automatically allocated but not reallocated.
3706 * The data are just appended into envelope structure.
3707 * @context is ISDS context
3708 * @envelope is automatically allocated message envelope structure
3709 * @xpath_ctx is XPath context with current node as parent desired elements
3710 * In case of error @envelope will be freed. */
3711 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3712 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3713 isds_error err
= IE_SUCCESS
;
3714 xmlXPathObjectPtr result
= NULL
;
3715 char *string
= NULL
;
3716 unsigned long int *unumber
= NULL
;
3718 if (!context
) return IE_INVALID_CONTEXT
;
3719 if (!envelope
) return IE_INVAL
;
3720 if (!xpath_ctx
) return IE_INVAL
;
3725 *envelope
= calloc(1, sizeof(**envelope
));
3732 zfree((*envelope
)->dmMessageStatus
);
3733 zfree((*envelope
)->dmAttachmentSize
);
3734 zfree((*envelope
)->dmDeliveryTime
);
3735 zfree((*envelope
)->dmAcceptanceTime
);
3739 /* dmMessageStatus element is mandatory */
3740 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3742 isds_log_message(context
,
3743 _("Missing mandatory sisds:dmMessageStatus integer"));
3747 err
= uint2isds_message_status(context
, unumber
,
3748 &((*envelope
)->dmMessageStatus
));
3750 if (err
== IE_ENUM
) err
= IE_ISDS
;
3753 free(unumber
); unumber
= NULL
;
3755 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3758 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3760 err
= timestring2timeval((xmlChar
*) string
,
3761 &((*envelope
)->dmDeliveryTime
));
3763 char *string_locale
= _isds_utf82locale(string
);
3764 if (err
== IE_DATE
) err
= IE_ISDS
;
3765 isds_printf_message(context
,
3766 _("Could not convert dmDeliveryTime as ISO time: %s"),
3768 free(string_locale
);
3774 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3776 err
= timestring2timeval((xmlChar
*) string
,
3777 &((*envelope
)->dmAcceptanceTime
));
3779 char *string_locale
= _isds_utf82locale(string
);
3780 if (err
== IE_DATE
) err
= IE_ISDS
;
3781 isds_printf_message(context
,
3782 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3784 free(string_locale
);
3791 if (err
) isds_envelope_free(envelope
);
3794 xmlXPathFreeObject(result
);
3799 /* Convert message type attribute of current element into isds_envelope
3801 * TODO: This function can be incorporated into append_status_size_times() as
3802 * they are called always together.
3803 * The envelope is automatically allocated but not reallocated.
3804 * The data are just appended into envelope structure.
3805 * @context is ISDS context
3806 * @envelope is automatically allocated message envelope structure
3807 * @xpath_ctx is XPath context with current node as parent of attribute
3808 * carrying message type
3809 * In case of error @envelope will be freed. */
3810 static isds_error
append_message_type(struct isds_ctx
*context
,
3811 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3812 isds_error err
= IE_SUCCESS
;
3814 if (!context
) return IE_INVALID_CONTEXT
;
3815 if (!envelope
) return IE_INVAL
;
3816 if (!xpath_ctx
) return IE_INVAL
;
3821 *envelope
= calloc(1, sizeof(**envelope
));
3828 zfree((*envelope
)->dmType
);
3832 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3834 if (!(*envelope
)->dmType
) {
3835 /* Use default value */
3836 (*envelope
)->dmType
= strdup("V");
3837 if (!(*envelope
)->dmType
) {
3841 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3842 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3843 isds_printf_message(context
,
3844 _("Message type in dmType attribute is not 1 character long: "
3853 if (err
) isds_envelope_free(envelope
);
3859 /* Convert dmType isds_envelope member into XML attribute and append it to
3861 * @context is ISDS context
3862 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3863 * @dm_envelope is XML element the resulting attribute will be appended to.
3864 * @return error code, in case of error context' message is filled. */
3865 static isds_error
insert_message_type(struct isds_ctx
*context
,
3866 const char *type
, xmlNodePtr dm_envelope
) {
3867 isds_error err
= IE_SUCCESS
;
3868 xmlAttrPtr attribute_node
;
3870 if (!context
) return IE_INVALID_CONTEXT
;
3871 if (!dm_envelope
) return IE_INVAL
;
3873 /* Insert optional message type */
3875 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3876 char *type_locale
= _isds_utf82locale(type
);
3877 isds_printf_message(context
,
3878 _("Message type in envelope is not 1 character long: %s"),
3884 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3890 #endif /* HAVE_LIBCURL */
3893 /* Extract message document into reallocated document structure
3894 * @context is ISDS context
3895 * @document is automatically reallocated message documents structure
3896 * @xpath_ctx is XPath context with current node as isds:dmFile
3897 * In case of error @document will be freed. */
3898 static isds_error
extract_document(struct isds_ctx
*context
,
3899 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3900 isds_error err
= IE_SUCCESS
;
3901 xmlXPathObjectPtr result
= NULL
;
3902 xmlNodePtr file_node
;
3903 char *string
= NULL
;
3905 if (!context
) return IE_INVALID_CONTEXT
;
3906 if (!document
) return IE_INVAL
;
3907 isds_document_free(document
);
3908 if (!xpath_ctx
) return IE_INVAL
;
3909 file_node
= xpath_ctx
->node
;
3911 *document
= calloc(1, sizeof(**document
));
3917 /* Extract document meta data */
3918 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3919 if (context
->normalize_mime_type
) {
3920 const char *normalized_type
=
3921 isds_normalize_mime_type((*document
)->dmMimeType
);
3922 if (NULL
!= normalized_type
&&
3923 normalized_type
!= (*document
)->dmMimeType
) {
3924 char *new_type
= strdup(normalized_type
);
3925 if (NULL
== new_type
) {
3926 isds_printf_message(context
,
3927 _("Not enough memory to normalize document MIME type"));
3931 free((*document
)->dmMimeType
);
3932 (*document
)->dmMimeType
= new_type
;
3936 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3937 err
= string2isds_FileMetaType((xmlChar
*)string
,
3938 &((*document
)->dmFileMetaType
));
3940 char *meta_type_locale
= _isds_utf82locale(string
);
3941 isds_printf_message(context
,
3942 _("Document has invalid dmFileMetaType attribute value: %s"),
3944 free(meta_type_locale
);
3950 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3951 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3952 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3953 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3956 /* Extract document data.
3957 * Base64 encoded blob or XML subtree must be presented. */
3959 /* Check for dmEncodedContent */
3960 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3967 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3968 /* Here we have Base64 blob */
3969 (*document
)->is_xml
= 0;
3971 if (result
->nodesetval
->nodeNr
> 1) {
3972 isds_printf_message(context
,
3973 _("Document has more dmEncodedContent elements"));
3978 xmlXPathFreeObject(result
); result
= NULL
;
3979 EXTRACT_STRING("isds:dmEncodedContent", string
);
3981 /* Decode non-empty document */
3982 if (string
&& string
[0] != '\0') {
3983 (*document
)->data_length
=
3984 _isds_b64decode(string
, &((*document
)->data
));
3985 if ((*document
)->data_length
== (size_t) -1) {
3986 isds_printf_message(context
,
3987 _("Error while Base64-decoding document content"));
3993 /* No Base64 blob, try XML document */
3994 xmlXPathFreeObject(result
); result
= NULL
;
3995 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4002 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4003 /* Here we have XML document */
4004 (*document
)->is_xml
= 1;
4006 if (result
->nodesetval
->nodeNr
> 1) {
4007 isds_printf_message(context
,
4008 _("Document has more dmXMLContent elements"));
4013 /* XXX: We cannot serialize the content simply because:
4014 * - XML document may point out of its scope (e.g. to message
4016 * - isds:dmXMLContent can contain more elements, no element,
4018 * - it's not the XML way
4019 * Thus we provide the only right solution: XML DOM. Let's
4020 * application to cope with this hot potato :) */
4021 (*document
)->xml_node_list
=
4022 result
->nodesetval
->nodeTab
[0]->children
;
4024 /* No base64 blob, nor XML document */
4025 isds_printf_message(context
,
4026 _("Document has no dmEncodedContent, nor dmXMLContent "
4035 if (err
) isds_document_free(document
);
4037 xmlXPathFreeObject(result
);
4038 xpath_ctx
->node
= file_node
;
4044 /* Extract message documents into reallocated list of documents
4045 * @context is ISDS context
4046 * @documents is automatically reallocated message documents list structure
4047 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4048 * In case of error @documents will be freed. */
4049 static isds_error
extract_documents(struct isds_ctx
*context
,
4050 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4051 isds_error err
= IE_SUCCESS
;
4052 xmlXPathObjectPtr result
= NULL
;
4053 xmlNodePtr files_node
;
4054 struct isds_list
*document
, *prev_document
= NULL
;
4056 if (!context
) return IE_INVALID_CONTEXT
;
4057 if (!documents
) return IE_INVAL
;
4058 isds_list_free(documents
);
4059 if (!xpath_ctx
) return IE_INVAL
;
4060 files_node
= xpath_ctx
->node
;
4062 /* Find documents */
4063 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4070 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4071 isds_printf_message(context
,
4072 _("Message does not contain any document"));
4078 /* Iterate over documents */
4079 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4081 /* Allocate and append list item */
4082 document
= calloc(1, sizeof(*document
));
4087 document
->destructor
= (void (*)(void **))isds_document_free
;
4088 if (i
== 0) *documents
= document
;
4089 else prev_document
->next
= document
;
4090 prev_document
= document
;
4092 /* Extract document */
4093 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4094 err
= extract_document(context
,
4095 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4096 if (err
) goto leave
;
4101 if (err
) isds_list_free(documents
);
4102 xmlXPathFreeObject(result
);
4103 xpath_ctx
->node
= files_node
;
4109 /* Convert isds:dmRecord XML tree into structure
4110 * @context is ISDS context
4111 * @envelope is automatically reallocated message envelope structure
4112 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4113 * In case of error @envelope will be freed. */
4114 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4115 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4116 isds_error err
= IE_SUCCESS
;
4117 xmlXPathObjectPtr result
= NULL
;
4119 if (!context
) return IE_INVALID_CONTEXT
;
4120 if (!envelope
) return IE_INVAL
;
4121 isds_envelope_free(envelope
);
4122 if (!xpath_ctx
) return IE_INVAL
;
4125 *envelope
= calloc(1, sizeof(**envelope
));
4132 /* Extract tRecord data */
4133 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4135 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4136 * dmAcceptanceTime. */
4137 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4138 if (err
) goto leave
;
4140 /* Extract envelope elements added by sender and ISDS
4141 * (XSD: gMessageEnvelope type) */
4142 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4143 if (err
) goto leave
;
4145 /* Get message type */
4146 err
= append_message_type(context
, envelope
, xpath_ctx
);
4147 if (err
) goto leave
;
4151 if (err
) isds_envelope_free(envelope
);
4152 xmlXPathFreeObject(result
);
4157 /* Convert XSD:tStateChangesRecord type XML tree into structure
4158 * @context is ISDS context
4159 * @changed_status is automatically reallocated message state change structure
4160 * @xpath_ctx is XPath context with current node as element of
4161 * XSD:tStateChangesRecord type
4162 * In case of error @changed_status will be freed. */
4163 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4164 struct isds_message_status_change
**changed_status
,
4165 xmlXPathContextPtr xpath_ctx
) {
4166 isds_error err
= IE_SUCCESS
;
4167 xmlXPathObjectPtr result
= NULL
;
4168 unsigned long int *unumber
= NULL
;
4169 char *string
= NULL
;
4171 if (!context
) return IE_INVALID_CONTEXT
;
4172 if (!changed_status
) return IE_INVAL
;
4173 isds_message_status_change_free(changed_status
);
4174 if (!xpath_ctx
) return IE_INVAL
;
4177 *changed_status
= calloc(1, sizeof(**changed_status
));
4178 if (!*changed_status
) {
4184 /* Extract tGetStateChangesInput data */
4185 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4187 /* dmEventTime is mandatory */
4188 EXTRACT_STRING("isds:dmEventTime", string
);
4190 err
= timestring2timeval((xmlChar
*) string
,
4191 &((*changed_status
)->time
));
4193 char *string_locale
= _isds_utf82locale(string
);
4194 if (err
== IE_DATE
) err
= IE_ISDS
;
4195 isds_printf_message(context
,
4196 _("Could not convert dmEventTime as ISO time: %s"),
4198 free(string_locale
);
4204 /* dmMessageStatus element is mandatory */
4205 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4207 isds_log_message(context
,
4208 _("Missing mandatory isds:dmMessageStatus integer"));
4212 err
= uint2isds_message_status(context
, unumber
,
4213 &((*changed_status
)->dmMessageStatus
));
4215 if (err
== IE_ENUM
) err
= IE_ISDS
;
4224 if (err
) isds_message_status_change_free(changed_status
);
4225 xmlXPathFreeObject(result
);
4228 #endif /* HAVE_LIBCURL */
4231 /* Find and convert isds:dmHash XML tree into structure
4232 * @context is ISDS context
4233 * @envelope is automatically reallocated message hash structure
4234 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4235 * In case of error @hash will be freed. */
4236 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4237 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4238 isds_error err
= IE_SUCCESS
;
4239 xmlNodePtr old_ctx_node
;
4240 xmlXPathObjectPtr result
= NULL
;
4241 char *string
= NULL
;
4243 if (!context
) return IE_INVALID_CONTEXT
;
4244 if (!hash
) return IE_INVAL
;
4245 isds_hash_free(hash
);
4246 if (!xpath_ctx
) return IE_INVAL
;
4248 old_ctx_node
= xpath_ctx
->node
;
4250 *hash
= calloc(1, sizeof(**hash
));
4257 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4258 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4267 /* Get hash algorithm */
4268 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4269 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4271 if (err
== IE_ENUM
) {
4272 char *string_locale
= _isds_utf82locale(string
);
4273 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4275 free(string_locale
);
4281 /* Get hash value */
4282 EXTRACT_STRING(".", string
);
4284 isds_printf_message(context
,
4285 _("sisds:dmHash element is missing hash value"));
4289 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4290 if ((*hash
)->length
== (size_t) -1) {
4291 isds_printf_message(context
,
4292 _("Error while Base64-decoding hash value"));
4298 if (err
) isds_hash_free(hash
);
4300 xmlXPathFreeObject(result
);
4301 xpath_ctx
->node
= old_ctx_node
;
4306 /* Find and append isds:dmQTimestamp XML tree into envelope.
4307 * Because one service is allowed to miss time-stamp content, and we think
4308 * other could too (flaw in specification), this function is deliberated and
4309 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4310 * @context is ISDS context
4311 * @envelope is automatically allocated envelope structure
4312 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4314 * In case of error @envelope will be freed. */
4315 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4316 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4317 isds_error err
= IE_SUCCESS
;
4318 xmlXPathObjectPtr result
= NULL
;
4319 char *string
= NULL
;
4321 if (!context
) return IE_INVALID_CONTEXT
;
4322 if (!envelope
) return IE_INVAL
;
4324 isds_envelope_free(envelope
);
4329 *envelope
= calloc(1, sizeof(**envelope
));
4335 zfree((*envelope
)->timestamp
);
4336 (*envelope
)->timestamp_length
= 0;
4339 /* Get dmQTimestamp */
4340 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4342 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4345 (*envelope
)->timestamp_length
=
4346 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4347 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4348 isds_printf_message(context
,
4349 _("Error while Base64-decoding time stamp value"));
4355 if (err
) isds_envelope_free(envelope
);
4357 xmlXPathFreeObject(result
);
4362 /* Convert XSD tReturnedMessage XML tree into message structure.
4363 * It does not store serialized XML tree into message->raw.
4364 * It does store (pointer to) parsed XML tree into message->xml if needed.
4365 * @context is ISDS context
4366 * @include_documents Use true if documents must be extracted
4367 * (tReturnedMessage XSD type), use false if documents shall be omitted
4368 * (tReturnedMessageEnvelope).
4369 * @message is automatically reallocated message structure
4370 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4372 * In case of error @message will be freed. */
4373 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4374 const _Bool include_documents
, struct isds_message
**message
,
4375 xmlXPathContextPtr xpath_ctx
) {
4376 isds_error err
= IE_SUCCESS
;
4377 xmlNodePtr message_node
;
4379 if (!context
) return IE_INVALID_CONTEXT
;
4380 if (!message
) return IE_INVAL
;
4381 isds_message_free(message
);
4382 if (!xpath_ctx
) return IE_INVAL
;
4385 *message
= calloc(1, sizeof(**message
));
4391 /* Save message XPATH context node */
4392 message_node
= xpath_ctx
->node
;
4396 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4397 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4398 if (err
) { err
= IE_ERROR
; goto leave
; }
4399 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4400 if (err
) goto leave
;
4402 if (include_documents
) {
4403 struct isds_list
*item
;
4405 /* Extract dmFiles */
4406 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4408 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4409 err
= IE_ISDS
; goto leave
;
4411 if (err
) { err
= IE_ERROR
; goto leave
; }
4412 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4413 if (err
) goto leave
;
4415 /* Store xmlDoc of this message if needed */
4416 /* Only if we got a XML document in all the documents. */
4417 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4418 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4419 (*message
)->xml
= xpath_ctx
->doc
;
4426 /* Restore context to message */
4427 xpath_ctx
->node
= message_node
;
4429 /* Extract dmHash */
4430 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4432 if (err
) goto leave
;
4434 /* Extract dmQTimestamp, */
4435 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4437 if (err
) goto leave
;
4439 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4440 * dmAcceptanceTime. */
4441 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4442 if (err
) goto leave
;
4444 /* Get message type */
4445 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4446 if (err
) goto leave
;
4449 if (err
) isds_message_free(message
);
4454 /* Extract message event into reallocated isds_event structure
4455 * @context is ISDS context
4456 * @event is automatically reallocated message event structure
4457 * @xpath_ctx is XPath context with current node as isds:dmEvent
4458 * In case of error @event will be freed. */
4459 static isds_error
extract_event(struct isds_ctx
*context
,
4460 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4461 isds_error err
= IE_SUCCESS
;
4462 xmlXPathObjectPtr result
= NULL
;
4463 xmlNodePtr event_node
;
4464 char *string
= NULL
;
4466 if (!context
) return IE_INVALID_CONTEXT
;
4467 if (!event
) return IE_INVAL
;
4468 isds_event_free(event
);
4469 if (!xpath_ctx
) return IE_INVAL
;
4470 event_node
= xpath_ctx
->node
;
4472 *event
= calloc(1, sizeof(**event
));
4478 /* Extract event data.
4479 * All elements are optional according XSD. That's funny. */
4480 EXTRACT_STRING("sisds:dmEventTime", string
);
4482 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4484 char *string_locale
= _isds_utf82locale(string
);
4485 if (err
== IE_DATE
) err
= IE_ISDS
;
4486 isds_printf_message(context
,
4487 _("Could not convert dmEventTime as ISO time: %s"),
4489 free(string_locale
);
4495 /* dmEventDescr element has prefix and the rest */
4496 EXTRACT_STRING("sisds:dmEventDescr", string
);
4498 err
= eventstring2event((xmlChar
*) string
, *event
);
4499 if (err
) goto leave
;
4504 if (err
) isds_event_free(event
);
4506 xmlXPathFreeObject(result
);
4507 xpath_ctx
->node
= event_node
;
4512 /* Convert element of XSD tEventsArray type from XML tree into
4513 * isds_list of isds_event's structure. The list is automatically reallocated.
4514 * @context is ISDS context
4515 * @events is automatically reallocated list of event structures
4516 * @xpath_ctx is XPath context with current node as tEventsArray
4517 * In case of error @events will be freed. */
4518 static isds_error
extract_events(struct isds_ctx
*context
,
4519 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4520 isds_error err
= IE_SUCCESS
;
4521 xmlXPathObjectPtr result
= NULL
;
4522 xmlNodePtr events_node
;
4523 struct isds_list
*event
, *prev_event
= NULL
;
4525 if (!context
) return IE_INVALID_CONTEXT
;
4526 if (!events
) return IE_INVAL
;
4527 if (!xpath_ctx
) return IE_INVAL
;
4528 events_node
= xpath_ctx
->node
;
4531 isds_list_free(events
);
4534 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4541 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4542 isds_printf_message(context
,
4543 _("Delivery info does not contain any event"));
4549 /* Iterate over events */
4550 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4552 /* Allocate and append list item */
4553 event
= calloc(1, sizeof(*event
));
4558 event
->destructor
= (void (*)(void **))isds_event_free
;
4559 if (i
== 0) *events
= event
;
4560 else prev_event
->next
= event
;
4564 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4565 err
= extract_event(context
,
4566 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4567 if (err
) goto leave
;
4572 if (err
) isds_list_free(events
);
4573 xmlXPathFreeObject(result
);
4574 xpath_ctx
->node
= events_node
;
4580 /* Insert Base64 encoded data as element with text child.
4581 * @context is session context
4582 * @parent is XML node to append @element with @data as child
4583 * @ns is XML namespace of @element, use NULL to inherit from @parent
4584 * @element is UTF-8 encoded name of new element
4585 * @data is bit stream to encode into @element
4586 * @length is size of @data in bytes
4587 * @return standard error code and fill long error message if needed */
4588 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4589 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4590 const void *data
, size_t length
) {
4591 isds_error err
= IE_SUCCESS
;
4594 if (!context
) return IE_INVALID_CONTEXT
;
4595 if (!data
&& length
> 0) return IE_INVAL
;
4596 if (!parent
|| !element
) return IE_INVAL
;
4598 xmlChar
*base64data
= NULL
;
4599 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4601 isds_printf_message(context
,
4602 ngettext("Not enough memory to encode %zd byte into Base64",
4603 "Not enough memory to encode %zd bytes into Base64",
4609 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4617 /* Convert isds_document structure into XML tree and append to dmFiles node.
4618 * @context is session context
4619 * @document is ISDS document
4620 * @dm_files is XML element the resulting tree will be appended to as a child.
4621 * @return error code, in case of error context' message is filled. */
4622 static isds_error
insert_document(struct isds_ctx
*context
,
4623 struct isds_document
*document
, xmlNodePtr dm_files
) {
4624 isds_error err
= IE_SUCCESS
;
4625 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4626 xmlAttrPtr attribute_node
;
4628 if (!context
) return IE_INVALID_CONTEXT
;
4629 if (!document
|| !dm_files
) return IE_INVAL
;
4631 /* Allocate new dmFile */
4632 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4634 isds_printf_message(context
, _("Could not allocate main dmFile"));
4638 /* Append the new dmFile.
4639 * XXX: Main document must go first */
4640 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4641 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4643 file
= xmlAddChild(dm_files
, new_file
);
4646 xmlFreeNode(new_file
); new_file
= NULL
;
4647 isds_printf_message(context
, _("Could not add dmFile child to "
4648 "%s element"), dm_files
->name
);
4653 /* @dmMimeType is required */
4654 if (!document
->dmMimeType
) {
4655 isds_log_message(context
,
4656 _("Document is missing mandatory MIME type definition"));
4660 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4662 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4664 isds_printf_message(context
,
4665 _("Document has unknown dmFileMetaType: %ld"),
4666 document
->dmFileMetaType
);
4670 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4672 if (document
->dmFileGuid
) {
4673 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4675 if (document
->dmUpFileGuid
) {
4676 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4679 /* @dmFileDescr is required */
4680 if (!document
->dmFileDescr
) {
4681 isds_log_message(context
,
4682 _("Document is missing mandatory description (title)"));
4686 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4688 if (document
->dmFormat
) {
4689 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4693 /* Insert content (body) of the document. */
4694 if (document
->is_xml
) {
4695 /* XML document requested */
4697 /* Allocate new dmXMLContent */
4698 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4700 isds_printf_message(context
,
4701 _("Could not allocate dmXMLContent element"));
4706 node
= xmlAddChild(file
, xmlcontent
);
4708 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4709 isds_printf_message(context
,
4710 _("Could not add dmXMLContent child to %s element"),
4716 /* Copy non-empty node list */
4717 if (document
->xml_node_list
) {
4718 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4719 document
->xml_node_list
);
4721 isds_printf_message(context
,
4722 _("Not enough memory to copy XML document"));
4727 if (!xmlAddChildList(node
, content
)) {
4728 xmlFreeNodeList(content
);
4729 isds_printf_message(context
,
4730 _("Error while adding XML document into dmXMLContent"));
4734 /* XXX: We cannot free the content here because it's part of node's
4735 * document since now. It will be freed with it automatically. */
4738 /* Binary document requested */
4739 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4740 document
->data
, document
->data_length
);
4741 if (err
) goto leave
;
4749 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4750 * The copy must be preallocated, the date are just appended into structure.
4751 * @context is ISDS context
4752 * @copy is message copy structure
4753 * @xpath_ctx is XPath context with current node as tMStatus */
4754 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4755 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4756 isds_error err
= IE_SUCCESS
;
4757 xmlXPathObjectPtr result
= NULL
;
4758 char *code
= NULL
, *message
= NULL
;
4760 if (!context
) return IE_INVALID_CONTEXT
;
4761 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4763 /* Free old values */
4764 zfree(copy
->dmStatus
);
4767 /* Get error specific to this copy */
4768 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4770 isds_log_message(context
,
4771 _("Missing isds:dmStatusCode under "
4772 "XSD:tMStatus type element"));
4777 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4778 /* This copy failed */
4779 copy
->error
= IE_ISDS
;
4780 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4782 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4783 if (!copy
->dmStatus
) {
4784 copy
->dmStatus
= code
;
4788 copy
->dmStatus
= code
;
4792 /* This copy succeeded. In this case only, message ID is valid */
4793 copy
->error
= IE_SUCCESS
;
4795 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4797 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4798 "but did not returned assigned message ID\n"));
4806 xmlXPathFreeObject(result
);
4811 /* Insert struct isds_approval data (box approval) into XML tree
4812 * @context is session context
4813 * @approval is libisds structure with approval description. NULL is
4815 * @parent is XML element to append @approval to */
4816 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4817 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4819 isds_error err
= IE_SUCCESS
;
4822 if (!context
) return IE_INVALID_CONTEXT
;
4823 if (!parent
) return IE_INVAL
;
4825 if (!approval
) return IE_SUCCESS
;
4827 /* Build XSD:gExtApproval */
4828 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4829 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4836 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4838 * @context is session context
4839 * @service_name is name of SERVICE_DB_ACCESS
4840 * @response is reallocated server SOAP body response as XML document
4841 * @raw_response is reallocated bit stream with response body. Use
4842 * NULL if you don't care
4843 * @raw_response_length is size of @raw_response in bytes
4844 * @code is reallocated ISDS status code
4845 * @status_message is reallocated ISDS status message
4846 * @return error coded from lower layer, context message will be set up
4848 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4849 const xmlChar
*service_name
,
4850 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4851 xmlChar
**code
, xmlChar
**status_message
) {
4853 isds_error err
= IE_SUCCESS
;
4854 char *service_name_locale
= NULL
;
4855 xmlNodePtr request
= NULL
, node
;
4856 xmlNsPtr isds_ns
= NULL
;
4858 if (!context
) return IE_INVALID_CONTEXT
;
4859 if (!service_name
) return IE_INVAL
;
4860 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4861 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4863 /* Free output argument */
4864 xmlFreeDoc(*response
); *response
= NULL
;
4865 if (raw_response
) zfree(*raw_response
);
4867 zfree(*status_message
);
4870 /* Check if connection is established
4871 * TODO: This check should be done downstairs. */
4872 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4874 service_name_locale
= _isds_utf82locale((char*)service_name
);
4875 if (!service_name_locale
) {
4881 request
= xmlNewNode(NULL
, service_name
);
4883 isds_printf_message(context
,
4884 _("Could not build %s request"), service_name_locale
);
4888 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4890 isds_log_message(context
, _("Could not create ISDS name space"));
4894 xmlSetNs(request
, isds_ns
);
4897 /* Add XSD:tDummyInput child */
4898 INSERT_STRING(request
, "dbDummy", NULL
);
4901 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4902 service_name_locale
);
4905 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4906 raw_response
, raw_response_length
);
4907 xmlFreeNode(request
); request
= NULL
;
4910 isds_log(ILF_ISDS
, ILL_DEBUG
,
4911 _("Processing ISDS response on %s request failed\n"),
4912 service_name_locale
);
4916 /* Check for response status */
4917 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4918 code
, status_message
, NULL
);
4920 isds_log(ILF_ISDS
, ILL_DEBUG
,
4921 _("ISDS response on %s request is missing status\n"),
4922 service_name_locale
);
4926 /* Request processed, but nothing found */
4927 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4928 char *code_locale
= _isds_utf82locale((char*) *code
);
4929 char *status_message_locale
=
4930 _isds_utf82locale((char*) *status_message
);
4931 isds_log(ILF_ISDS
, ILL_DEBUG
,
4932 _("Server refused %s request (code=%s, message=%s)\n"),
4933 service_name_locale
, code_locale
, status_message_locale
);
4934 isds_log_message(context
, status_message_locale
);
4936 free(status_message_locale
);
4942 free(service_name_locale
);
4943 xmlFreeNode(request
);
4949 /* Get data about logged in user and his box. */
4950 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4951 struct isds_DbOwnerInfo
**db_owner_info
) {
4952 isds_error err
= IE_SUCCESS
;
4954 xmlDocPtr response
= NULL
;
4955 xmlChar
*code
= NULL
, *message
= NULL
;
4956 xmlXPathContextPtr xpath_ctx
= NULL
;
4957 xmlXPathObjectPtr result
= NULL
;
4958 char *string
= NULL
;
4961 if (!context
) return IE_INVALID_CONTEXT
;
4962 zfree(context
->long_message
);
4963 if (!db_owner_info
) return IE_INVAL
;
4964 isds_DbOwnerInfo_free(db_owner_info
);
4967 /* Check if connection is established */
4968 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4971 /* Do request and check for success */
4972 err
= build_send_check_dbdummy_request(context
,
4973 BAD_CAST
"GetOwnerInfoFromLogin",
4974 &response
, NULL
, NULL
, &code
, &message
);
4975 if (err
) goto leave
;
4979 /* Prepare structure */
4980 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4981 if (!*db_owner_info
) {
4985 xpath_ctx
= xmlXPathNewContext(response
);
4990 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4995 /* Set context node */
4996 result
= xmlXPathEvalExpression(BAD_CAST
4997 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5002 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5003 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5007 if (result
->nodesetval
->nodeNr
> 1) {
5008 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5012 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5013 xmlXPathFreeObject(result
); result
= NULL
;
5016 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5021 isds_DbOwnerInfo_free(db_owner_info
);
5025 xmlXPathFreeObject(result
);
5026 xmlXPathFreeContext(xpath_ctx
);
5030 xmlFreeDoc(response
);
5033 isds_log(ILF_ISDS
, ILL_DEBUG
,
5034 _("GetOwnerInfoFromLogin request processed by server "
5035 "successfully.\n"));
5036 #else /* not HAVE_LIBCURL */
5044 /* Get data about logged in user. */
5045 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5046 struct isds_DbUserInfo
**db_user_info
) {
5047 isds_error err
= IE_SUCCESS
;
5049 xmlDocPtr response
= NULL
;
5050 xmlChar
*code
= NULL
, *message
= NULL
;
5051 xmlXPathContextPtr xpath_ctx
= NULL
;
5052 xmlXPathObjectPtr result
= NULL
;
5055 if (!context
) return IE_INVALID_CONTEXT
;
5056 zfree(context
->long_message
);
5057 if (!db_user_info
) return IE_INVAL
;
5058 isds_DbUserInfo_free(db_user_info
);
5061 /* Check if connection is established */
5062 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5065 /* Do request and check for success */
5066 err
= build_send_check_dbdummy_request(context
,
5067 BAD_CAST
"GetUserInfoFromLogin",
5068 &response
, NULL
, NULL
, &code
, &message
);
5069 if (err
) goto leave
;
5073 /* Prepare structure */
5074 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5075 if (!*db_user_info
) {
5079 xpath_ctx
= xmlXPathNewContext(response
);
5084 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5089 /* Set context node */
5090 result
= xmlXPathEvalExpression(BAD_CAST
5091 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5096 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5097 isds_log_message(context
, _("Missing dbUserInfo element"));
5101 if (result
->nodesetval
->nodeNr
> 1) {
5102 isds_log_message(context
, _("Multiple dbUserInfo element"));
5106 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5107 xmlXPathFreeObject(result
); result
= NULL
;
5110 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5114 isds_DbUserInfo_free(db_user_info
);
5117 xmlXPathFreeObject(result
);
5118 xmlXPathFreeContext(xpath_ctx
);
5122 xmlFreeDoc(response
);
5125 isds_log(ILF_ISDS
, ILL_DEBUG
,
5126 _("GetUserInfoFromLogin request processed by server "
5127 "successfully.\n"));
5128 #else /* not HAVE_LIBCURL */
5136 /* Get expiration time of current password
5137 * @context is session context
5138 * @expiration is automatically reallocated time when password expires. If
5139 * password expiration is disabled, NULL will be returned. In case of error
5140 * it will be nulled too. */
5141 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5142 struct timeval
**expiration
) {
5143 isds_error err
= IE_SUCCESS
;
5145 xmlDocPtr response
= NULL
;
5146 xmlChar
*code
= NULL
, *message
= NULL
;
5147 xmlXPathContextPtr xpath_ctx
= NULL
;
5148 xmlXPathObjectPtr result
= NULL
;
5149 char *string
= NULL
;
5152 if (!context
) return IE_INVALID_CONTEXT
;
5153 zfree(context
->long_message
);
5154 if (!expiration
) return IE_INVAL
;
5158 /* Check if connection is established */
5159 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5162 /* Do request and check for success */
5163 err
= build_send_check_dbdummy_request(context
,
5164 BAD_CAST
"GetPasswordInfo",
5165 &response
, NULL
, NULL
, &code
, &message
);
5166 if (err
) goto leave
;
5170 xpath_ctx
= xmlXPathNewContext(response
);
5175 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5180 /* Set context node */
5181 result
= xmlXPathEvalExpression(BAD_CAST
5182 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5187 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5188 isds_log_message(context
,
5189 _("Missing GetPasswordInfoResponse element"));
5193 if (result
->nodesetval
->nodeNr
> 1) {
5194 isds_log_message(context
,
5195 _("Multiple GetPasswordInfoResponse element"));
5199 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5200 xmlXPathFreeObject(result
); result
= NULL
;
5202 /* Extract expiration date */
5203 EXTRACT_STRING("isds:pswExpDate", string
);
5205 /* And convert it if any returned. Otherwise expiration is disabled. */
5206 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5208 char *string_locale
= _isds_utf82locale(string
);
5209 if (err
== IE_DATE
) err
= IE_ISDS
;
5210 isds_printf_message(context
,
5211 _("Could not convert pswExpDate as ISO time: %s"),
5213 free(string_locale
);
5226 xmlXPathFreeObject(result
);
5227 xmlXPathFreeContext(xpath_ctx
);
5231 xmlFreeDoc(response
);
5234 isds_log(ILF_ISDS
, ILL_DEBUG
,
5235 _("GetPasswordInfo request processed by server "
5236 "successfully.\n"));
5237 #else /* not HAVE_LIBCURL */
5246 /* Request delivering new TOTP code from ISDS through side channel before
5247 * changing password.
5248 * @context is session context
5249 * @password is current password.
5250 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5251 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5252 * function for more details.
5253 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5254 * NULL, if you don't care.
5255 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5257 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5258 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5259 isds_error err
= IE_SUCCESS
;
5260 char *saved_url
= NULL
; /* No copy */
5261 #if HAVE_CURL_REAUTHORIZATION_BUG
5262 CURL
*saved_curl
= NULL
; /* No copy */
5264 xmlNsPtr isds_ns
= NULL
;
5265 xmlNodePtr request
= NULL
;
5266 xmlDocPtr response
= NULL
;
5267 xmlChar
*code
= NULL
, *message
= NULL
;
5268 const xmlChar
*codes
[] = {
5273 const char *meanings
[] = {
5274 N_("Unexpected error"),
5275 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5276 N_("One-time code could not been sent. Try later again.")
5278 const isds_otp_resolution resolutions
[] = {
5279 OTP_RESOLUTION_UNKNOWN
,
5280 OTP_RESOLUTION_TO_FAST
,
5281 OTP_RESOLUTION_TOTP_NOT_SENT
5284 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5285 zfree(context
->long_message
);
5286 if (NULL
== password
) {
5287 isds_log_message(context
,
5288 _("Second argument (password) of isds_change_password() "
5293 /* Check if connection is established
5294 * TODO: This check should be done downstairs. */
5295 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5297 if (!context
->otp
) {
5298 isds_log_message(context
, _("This function requires OTP-authenticated "
5300 return IE_INVALID_CONTEXT
;
5303 isds_log_message(context
, _("If one-time password authentication "
5304 "method is in use, requesting new OTP code requires "
5305 "one-time credentials argument either"));
5308 if (otp
->method
!= OTP_TIME
) {
5309 isds_log_message(context
, _("Requesting new time-based OTP code from "
5310 "server requires one-time password authentication "
5314 if (otp
->otp_code
!= NULL
) {
5315 isds_log_message(context
, _("Requesting new time-based OTP code from "
5316 "server requires undefined OTP code member in "
5317 "one-time credentials argument"));
5323 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5325 isds_log_message(context
, _("Could not build SendSMSCode request"));
5328 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5330 isds_log_message(context
, _("Could not create ISDS name space"));
5331 xmlFreeNode(request
);
5334 xmlSetNs(request
, isds_ns
);
5336 /* Change URL temporarily for sending this request only */
5338 char *new_url
= NULL
;
5339 if ((err
= _isds_build_url_from_context(context
,
5340 "%1$.*2$sasws/changePassword", &new_url
))) {
5343 saved_url
= context
->url
;
5344 context
->url
= new_url
;
5347 /* Store credentials for sending this request only */
5348 context
->otp_credentials
= otp
;
5349 _isds_discard_credentials(context
, 0);
5350 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5352 _isds_discard_credentials(context
, 0);
5355 #if HAVE_CURL_REAUTHORIZATION_BUG
5356 saved_curl
= context
->curl
;
5357 context
->curl
= curl_easy_init();
5358 if (NULL
== context
->curl
) {
5362 if (context
->timeout
) {
5363 err
= isds_set_timeout(context
, context
->timeout
);
5364 if (err
) goto leave
;
5368 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5371 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5373 /* Remove temporal credentials */
5374 _isds_discard_credentials(context
, 0);
5375 /* Detach pointer to OTP credentials from context */
5376 context
->otp_credentials
= NULL
;
5377 /* Keep context->otp true to keep signaling this is OTP session */
5379 /* Destroy request */
5380 xmlFreeNode(request
); request
= NULL
;
5383 isds_log(ILF_ISDS
, ILL_DEBUG
,
5384 _("Processing ISDS response on SendSMSCode request failed\n"));
5388 /* Check for response status */
5389 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5390 &code
, &message
, (xmlChar
**)refnumber
);
5392 isds_log(ILF_ISDS
, ILL_DEBUG
,
5393 _("ISDS response on SendSMSCode request is missing "
5398 /* Check for error */
5399 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5400 char *code_locale
= _isds_utf82locale((char*)code
);
5401 char *message_locale
= _isds_utf82locale((char*)message
);
5403 isds_log(ILF_ISDS
, ILL_DEBUG
,
5404 _("Server refused to send new code on SendSMSCode "
5405 "request (code=%s, message=%s)\n"),
5406 code_locale
, message_locale
);
5408 /* Check for known error codes */
5409 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5410 if (!xmlStrcmp(code
, codes
[i
])) break;
5412 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5413 isds_log_message(context
, _(meanings
[i
]));
5414 /* Mimic otp->resolution according to the code, specification does
5415 * prescribe OTP header to be available. */
5416 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5417 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5418 otp
->resolution
= resolutions
[i
];
5420 isds_log_message(context
, message_locale
);
5423 free(message_locale
);
5429 /* Otherwise new code sent successfully */
5430 /* Mimic otp->resolution according to the code, specification does
5431 * prescribe OTP header to be available. */
5432 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5433 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5436 if (NULL
!= saved_url
) {
5437 /* Revert URL to original one */
5438 zfree(context
->url
);
5439 context
->url
= saved_url
;
5441 #if HAVE_CURL_REAUTHORIZATION_BUG
5442 if (NULL
!= saved_curl
) {
5443 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5444 context
->curl
= saved_curl
;
5450 xmlFreeDoc(response
);
5451 xmlFreeNode(request
);
5454 isds_log(ILF_ISDS
, ILL_DEBUG
,
5455 _("New OTP code has been sent successfully on SendSMSCode "
5461 /* Convert response status code to isds_error code and set long message
5462 * @context is context to save long message to
5463 * @map is mapping from codes to errors and messages. Pass NULL for generic
5465 * @code is status code to translate
5466 * @message is non-localized status message to put into long message in case
5467 * of uknown error. It can be NULL if server did not provide any.
5468 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5469 * invalid invocation. */
5470 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5471 const struct code_map_isds_error
*map
,
5472 const xmlChar
*code
, const xmlChar
*message
) {
5474 isds_log_message(context
,
5475 _("NULL status code passed to statuscode2isds_error()"));
5480 /* Check for known error codes */
5481 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5482 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5483 isds_log_message(context
, _(map
->meanings
[i
]));
5484 return map
->errors
[i
];
5490 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5491 char *message_locale
= _isds_utf82locale((char*)message
);
5492 if (NULL
== message_locale
)
5493 isds_log_message(context
, _("ISDS server returned unknown error"));
5495 isds_log_message(context
, message_locale
);
5496 free(message_locale
);
5505 /* Change user password in ISDS.
5506 * User must supply old password, new password will takes effect after some
5507 * time, current session can continue. Password must fulfill some constraints.
5508 * @context is session context
5509 * @old_password is current password.
5510 * @new_password is requested new password
5511 * @otp auxiliary data required if one-time password authentication is in use,
5512 * defines OTP code (if known) and returns fine grade resolution of OTP
5513 * procedure. Pass NULL, if one-time password authentication is not needed.
5514 * Please note the @otp argument must match OTP method used at log-in time. See
5515 * isds_login() function for more details.
5516 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5517 * NULL, if you don't care.
5518 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5519 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5520 * awaiting OTP code that has been delivered by side channel to the user. */
5521 isds_error
isds_change_password(struct isds_ctx
*context
,
5522 const char *old_password
, const char *new_password
,
5523 struct isds_otp
*otp
, char **refnumber
) {
5524 isds_error err
= IE_SUCCESS
;
5526 char *saved_url
= NULL
; /* No copy */
5527 #if HAVE_CURL_REAUTHORIZATION_BUG
5528 CURL
*saved_curl
= NULL
; /* No copy */
5530 xmlNsPtr isds_ns
= NULL
;
5531 xmlNodePtr request
= NULL
, node
;
5532 xmlDocPtr response
= NULL
;
5533 xmlChar
*code
= NULL
, *message
= NULL
;
5534 const xmlChar
*codes
[] = {
5547 const char *meanings
[] = {
5548 N_("Password length must be between 8 and 32 characters"),
5549 N_("Password cannot be reused"), /* Server does not distinguish 1067
5550 and 1091 on ChangePasswordOTP */
5551 N_("Password contains forbidden character"),
5552 N_("Password must contain at least one upper-case letter, "
5553 "one lower-case, and one digit"),
5554 N_("Password cannot contain sequence of three identical characters"),
5555 N_("Password cannot contain user identifier"),
5556 N_("Password is too simmple"),
5557 N_("Old password is not valid"),
5558 N_("Password cannot be reused"),
5559 N_("Unexpected error"),
5560 N_("LDAP update error")
5564 if (!context
) return IE_INVALID_CONTEXT
;
5565 zfree(context
->long_message
);
5566 if (NULL
!= refnumber
)
5568 if (NULL
== old_password
) {
5569 isds_log_message(context
,
5570 _("Second argument (old password) of isds_change_password() "
5574 if (NULL
== otp
&& NULL
== new_password
) {
5575 isds_log_message(context
,
5576 _("Third argument (new password) of isds_change_password() "
5582 /* Check if connection is established
5583 * TODO: This check should be done downstairs. */
5584 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5586 if (context
->otp
&& NULL
== otp
) {
5587 isds_log_message(context
, _("If one-time password authentication "
5588 "method is in use, changing password requires one-time "
5589 "credentials either"));
5593 /* Build ChangeISDSPassword request */
5594 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5595 BAD_CAST
"ChangePasswordOTP");
5597 isds_log_message(context
, (NULL
== otp
) ?
5598 _("Could not build ChangeISDSPassword request") :
5599 _("Could not build ChangePasswordOTP request"));
5602 isds_ns
= xmlNewNs(request
,
5603 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5606 isds_log_message(context
, _("Could not create ISDS name space"));
5607 xmlFreeNode(request
);
5610 xmlSetNs(request
, isds_ns
);
5612 INSERT_STRING(request
, "dbOldPassword", old_password
);
5613 INSERT_STRING(request
, "dbNewPassword", new_password
);
5616 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5617 switch (otp
->method
) {
5619 isds_log(ILF_SEC
, ILL_INFO
,
5620 _("Selected authentication method: "
5621 "HMAC-based one-time password\n"));
5622 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5625 isds_log(ILF_SEC
, ILL_INFO
,
5626 _("Selected authentication method: "
5627 "Time-based one-time password\n"));
5628 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5629 if (otp
->otp_code
== NULL
) {
5630 isds_log(ILF_SEC
, ILL_INFO
,
5631 _("OTP code has not been provided by "
5632 "application, requesting server for "
5634 err
= _isds_request_totp_code(context
, old_password
, otp
,
5636 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5640 isds_log(ILF_SEC
, ILL_INFO
,
5641 _("OTP code has been provided by "
5642 "application, not requesting server "
5647 isds_log_message(context
,
5648 _("Unknown one-time password authentication "
5649 "method requested by application"));
5654 /* Change URL temporarily for sending this request only */
5656 char *new_url
= NULL
;
5657 if ((err
= _isds_build_url_from_context(context
,
5658 "%1$.*2$sasws/changePassword", &new_url
))) {
5661 saved_url
= context
->url
;
5662 context
->url
= new_url
;
5665 /* Store credentials for sending this request only */
5666 context
->otp_credentials
= otp
;
5667 _isds_discard_credentials(context
, 0);
5668 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5669 old_password
, NULL
))) {
5670 _isds_discard_credentials(context
, 0);
5673 #if HAVE_CURL_REAUTHORIZATION_BUG
5674 saved_curl
= context
->curl
;
5675 context
->curl
= curl_easy_init();
5676 if (NULL
== context
->curl
) {
5680 if (context
->timeout
) {
5681 err
= isds_set_timeout(context
, context
->timeout
);
5682 if (err
) goto leave
;
5687 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5688 _("Sending ChangeISDSPassword request to ISDS\n") :
5689 _("Sending ChangePasswordOTP request to ISDS\n"));
5692 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5693 request
, &response
, NULL
, NULL
);
5696 /* Remove temporal credentials */
5697 _isds_discard_credentials(context
, 0);
5698 /* Detach pointer to OTP credentials from context */
5699 context
->otp_credentials
= NULL
;
5700 /* Keep context->otp true to keep signaling this is OTP session */
5703 /* Destroy request */
5704 xmlFreeNode(request
); request
= NULL
;
5707 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5708 _("Processing ISDS response on ChangeISDSPassword "
5709 "request failed\n") :
5710 _("Processing ISDS response on ChangePasswordOTP "
5711 "request failed\n"));
5715 /* Check for response status */
5716 err
= isds_response_status(context
,
5717 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5718 &code
, &message
, (xmlChar
**)refnumber
);
5720 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5721 _("ISDS response on ChangeISDSPassword request is missing "
5723 _("ISDS response on ChangePasswordOTP request is missing "
5728 /* Check for known error codes */
5729 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5730 if (!xmlStrcmp(code
, codes
[i
])) {
5731 char *code_locale
= _isds_utf82locale((char*)code
);
5732 char *message_locale
= _isds_utf82locale((char*)message
);
5733 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5734 _("Server refused to change password on ChangeISDSPassword "
5735 "request (code=%s, message=%s)\n") :
5736 _("Server refused to change password on ChangePasswordOTP "
5737 "request (code=%s, message=%s)\n"),
5738 code_locale
, message_locale
);
5740 free(message_locale
);
5741 isds_log_message(context
, _(meanings
[i
]));
5748 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5749 char *code_locale
= _isds_utf82locale((char*)code
);
5750 char *message_locale
= _isds_utf82locale((char*)message
);
5751 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5752 _("Server refused to change password on ChangeISDSPassword "
5753 "request (code=%s, message=%s)\n") :
5754 _("Server refused to change password on ChangePasswordOTP "
5755 "request (code=%s, message=%s)\n"),
5756 code_locale
, message_locale
);
5757 isds_log_message(context
, message_locale
);
5759 free(message_locale
);
5764 /* Otherwise password changed successfully */
5767 if (NULL
!= saved_url
) {
5768 /* Revert URL to original one */
5769 zfree(context
->url
);
5770 context
->url
= saved_url
;
5772 #if HAVE_CURL_REAUTHORIZATION_BUG
5773 if (NULL
!= saved_curl
) {
5774 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5775 context
->curl
= saved_curl
;
5781 xmlFreeDoc(response
);
5782 xmlFreeNode(request
);
5785 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5786 _("Password changed successfully on ChangeISDSPassword "
5788 _("Password changed successfully on ChangePasswordOTP "
5790 #else /* not HAVE_LIBCURL */
5799 /* Generic middle part with request sending and response check.
5800 * It sends prepared request and checks for error code.
5801 * @context is ISDS session context.
5802 * @service is ISDS service handler
5803 * @service_name is name in scope of given @service
5804 * @request is XML tree with request. Will be freed to save memory.
5805 * @response is XML document outputting ISDS response.
5806 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5807 * @map is mapping from status code to library error. Pass NULL if no special
5808 * handling is requested.
5809 * NULL, if you don't care. */
5810 static isds_error
send_destroy_request_check_response(
5811 struct isds_ctx
*context
,
5812 const isds_service service
, const xmlChar
*service_name
,
5813 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5814 const struct code_map_isds_error
*map
) {
5815 isds_error err
= IE_SUCCESS
;
5816 char *service_name_locale
= NULL
;
5817 xmlChar
*code
= NULL
, *message
= NULL
;
5820 if (!context
) return IE_INVALID_CONTEXT
;
5821 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5825 /* Check if connection is established
5826 * TODO: This check should be done downstairs. */
5827 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5829 service_name_locale
= _isds_utf82locale((char*) service_name
);
5830 if (!service_name_locale
) {
5835 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5836 service_name_locale
);
5839 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5840 xmlFreeNode(*request
); *request
= NULL
;
5843 isds_log(ILF_ISDS
, ILL_DEBUG
,
5844 _("Processing ISDS response on %s request failed\n"),
5845 service_name_locale
);
5849 /* Check for response status */
5850 err
= isds_response_status(context
, service
, *response
,
5851 &code
, &message
, refnumber
);
5853 isds_log(ILF_ISDS
, ILL_DEBUG
,
5854 _("ISDS response on %s request is missing status\n"),
5855 service_name_locale
);
5859 err
= statuscode2isds_error(context
, map
, code
, message
);
5861 /* Request processed, but server failed */
5862 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5863 char *code_locale
= _isds_utf82locale((char*) code
);
5864 char *message_locale
= _isds_utf82locale((char*) message
);
5865 isds_log(ILF_ISDS
, ILL_DEBUG
,
5866 _("Server refused %s request (code=%s, message=%s)\n"),
5867 service_name_locale
, code_locale
, message_locale
);
5869 free(message_locale
);
5877 if (err
&& *response
) {
5878 xmlFreeDoc(*response
);
5882 xmlFreeNode(*request
);
5885 free(service_name_locale
);
5891 /* Generic bottom half with request sending.
5892 * It sends prepared request, checks for error code, destroys response and
5893 * request and log success or failure.
5894 * @context is ISDS session context.
5895 * @service is ISDS service handler
5896 * @service_name is name in scope of given @service
5897 * @request is XML tree with request. Will be freed to save memory.
5898 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5899 * NULL, if you don't care. */
5900 static isds_error
send_request_check_drop_response(
5901 struct isds_ctx
*context
,
5902 const isds_service service
, const xmlChar
*service_name
,
5903 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5904 isds_error err
= IE_SUCCESS
;
5905 xmlDocPtr response
= NULL
;
5908 if (!context
) return IE_INVALID_CONTEXT
;
5909 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5912 /* Send request and check response*/
5913 err
= send_destroy_request_check_response(context
,
5914 service
, service_name
, request
, &response
, refnumber
, NULL
);
5916 xmlFreeDoc(response
);
5919 xmlFreeNode(*request
);
5924 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5925 isds_log(ILF_ISDS
, ILL_DEBUG
,
5926 _("%s request processed by server successfully.\n"),
5927 service_name_locale
);
5928 free(service_name_locale
);
5935 /* Insert isds_credentials_delivery structure into XML request if not NULL
5936 * @context is session context
5937 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5938 * credentials delivery. The email field is passed.
5939 * @parent is XML element where to insert */
5940 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5941 const struct isds_credentials_delivery
*credentials_delivery
,
5942 xmlNodePtr parent
) {
5943 isds_error err
= IE_SUCCESS
;
5946 if (!context
) return IE_INVALID_CONTEXT
;
5947 if (!parent
) return IE_INVAL
;
5949 if (credentials_delivery
) {
5950 /* Following elements are valid only for services:
5951 * NewAccessData, AddDataBoxUser, CreateDataBox */
5952 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5953 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5961 /* Extract credentials delivery from ISDS response.
5962 * @context is session context
5963 * @credentials_delivery is pointer to valid structure to fill in returned
5964 * user's password (and new log-in name). If NULL, do not extract the data.
5965 * @response is pointer to XML document with ISDS response
5966 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5967 * @return IE_SUCCESS even if new user name has not been found because it's not
5968 * clear whether it's returned always. */
5969 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5970 struct isds_credentials_delivery
*credentials_delivery
,
5971 xmlDocPtr response
, const char *request_name
) {
5972 isds_error err
= IE_SUCCESS
;
5973 xmlXPathContextPtr xpath_ctx
= NULL
;
5974 xmlXPathObjectPtr result
= NULL
;
5975 char *xpath_query
= NULL
;
5977 if (!context
) return IE_INVALID_CONTEXT
;
5978 if (credentials_delivery
) {
5979 zfree(credentials_delivery
->token
);
5980 zfree(credentials_delivery
->new_user_name
);
5982 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5985 /* Extract optional token */
5986 if (credentials_delivery
) {
5987 xpath_ctx
= xmlXPathNewContext(response
);
5992 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5997 /* Verify root element */
5998 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6003 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6008 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6009 char *request_name_locale
= _isds_utf82locale(request_name
);
6010 isds_log(ILF_ISDS
, ILL_WARNING
,
6011 _("Wrong element in ISDS response for %s request "
6012 "while extracting credentials delivery details\n"),
6013 request_name_locale
);
6014 free(request_name_locale
);
6018 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6021 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6023 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6025 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6026 if (!credentials_delivery
->token
) {
6027 char *request_name_locale
= _isds_utf82locale(request_name
);
6028 isds_log(ILF_ISDS
, ILL_ERR
,
6029 _("ISDS did not return token on %s request "
6030 "even if requested\n"), request_name_locale
);
6031 free(request_name_locale
);
6038 xmlXPathFreeObject(result
);
6039 xmlXPathFreeContext(xpath_ctx
);
6045 /* Build XSD:tCreateDBInput request type for box creating.
6046 * @context is session context
6047 * @request outputs built XML tree
6048 * @service_name is request name of SERVICE_DB_MANIPULATION service
6049 * @box is box description to create including single primary user (in case of
6051 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6052 * box, or contact address of PFO box owner)
6053 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6054 * @upper_box_id is optional ID of supper box if currently created box is
6056 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6058 * @credentials_delivery is valid pointer if ISDS should return token that box
6059 * owner can use to obtain his new credentials in on-line way. Then valid email
6060 * member value should be supplied.
6061 * @approval is optional external approval of box manipulation */
6062 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6063 xmlNodePtr
*request
, const xmlChar
*service_name
,
6064 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6065 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6066 const xmlChar
*ceo_label
,
6067 const struct isds_credentials_delivery
*credentials_delivery
,
6068 const struct isds_approval
*approval
) {
6069 isds_error err
= IE_SUCCESS
;
6070 xmlNsPtr isds_ns
= NULL
;
6071 xmlNodePtr node
, dbPrimaryUsers
;
6072 xmlChar
*string
= NULL
;
6073 const struct isds_list
*item
;
6076 if (!context
) return IE_INVALID_CONTEXT
;
6077 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6081 /* Build CreateDataBox-similar request */
6082 *request
= xmlNewNode(NULL
, service_name
);
6084 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6085 isds_printf_message(context
, _("Could build %s request"),
6086 service_name_locale
);
6087 free(service_name_locale
);
6090 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6091 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6093 isds_log_message(context
, _("Could not create ISDS1 name space"));
6094 xmlFreeNode(*request
);
6098 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6100 isds_log_message(context
, _("Could not create ISDS name space"));
6101 xmlFreeNode(*request
);
6105 xmlSetNs(*request
, isds_ns
);
6107 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6108 err
= insert_DbOwnerInfo(context
, box
, node
);
6109 if (err
) goto leave
;
6112 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6113 * verbose documentation allows none dbUserInfo */
6114 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6115 for (item
= users
; item
; item
= item
->next
) {
6117 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6118 err
= insert_DbUserInfo(context
,
6119 (struct isds_DbUserInfo
*) item
->data
, node
);
6120 if (err
) goto leave
;
6124 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6125 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6126 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6128 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6129 if (err
) goto leave
;
6131 err
= insert_GExtApproval(context
, approval
, *request
);
6132 if (err
) goto leave
;
6136 xmlFreeNode(*request
);
6142 #endif /* HAVE_LIBCURL */
6146 * @context is session context
6147 * @box is box description to create including single primary user (in case of
6148 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6149 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6150 * box, or contact address of PFO box owner)
6151 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6152 * @upper_box_id is optional ID of supper box if currently created box is
6154 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6155 * @credentials_delivery is NULL if new password should be delivered off-line
6156 * to box owner. It is valid pointer if owner should obtain new password on-line
6157 * on dedicated web server. Then input @credentials_delivery.email value is
6158 * his e-mail address he must provide to dedicated web server together
6159 * with output reallocated @credentials_delivery.token member. Output
6160 * member @credentials_delivery.new_user_name is unused up on this call.
6161 * @approval is optional external approval of box manipulation
6162 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6163 * NULL, if you don't care.*/
6164 isds_error
isds_add_box(struct isds_ctx
*context
,
6165 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6166 const char *former_names
, const char *upper_box_id
,
6167 const char *ceo_label
,
6168 struct isds_credentials_delivery
*credentials_delivery
,
6169 const struct isds_approval
*approval
, char **refnumber
) {
6170 isds_error err
= IE_SUCCESS
;
6172 xmlNodePtr request
= NULL
;
6173 xmlDocPtr response
= NULL
;
6174 xmlXPathContextPtr xpath_ctx
= NULL
;
6175 xmlXPathObjectPtr result
= NULL
;
6179 if (!context
) return IE_INVALID_CONTEXT
;
6180 zfree(context
->long_message
);
6181 if (credentials_delivery
) {
6182 zfree(credentials_delivery
->token
);
6183 zfree(credentials_delivery
->new_user_name
);
6185 if (!box
) return IE_INVAL
;
6188 /* Scratch box ID */
6191 /* Build CreateDataBox request */
6192 err
= build_CreateDBInput_request(context
,
6193 &request
, BAD_CAST
"CreateDataBox",
6194 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6195 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6196 if (err
) goto leave
;
6198 /* Send it to server and process response */
6199 err
= send_destroy_request_check_response(context
,
6200 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6201 &response
, (xmlChar
**) refnumber
, NULL
);
6203 /* Extract box ID */
6204 xpath_ctx
= xmlXPathNewContext(response
);
6209 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6213 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6215 /* Extract optional token */
6216 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6220 xmlXPathFreeObject(result
);
6221 xmlXPathFreeContext(xpath_ctx
);
6222 xmlFreeDoc(response
);
6223 xmlFreeNode(request
);
6226 isds_log(ILF_ISDS
, ILL_DEBUG
,
6227 _("CreateDataBox request processed by server successfully.\n"));
6229 #else /* not HAVE_LIBCURL */
6237 /* Notify ISDS about new PFO entity.
6238 * This function has no real effect.
6239 * @context is session context
6240 * @box is PFO description including single primary user.
6241 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6242 * @former_names is optional undocumented string. Pass NULL if you don't care.
6243 * @upper_box_id is optional ID of supper box if currently created box is
6245 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6246 * @approval is optional external approval of box manipulation
6247 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6248 * NULL, if you don't care.*/
6249 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6250 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6251 const char *former_names
, const char *upper_box_id
,
6252 const char *ceo_label
, const struct isds_approval
*approval
,
6254 isds_error err
= IE_SUCCESS
;
6256 xmlNodePtr request
= NULL
;
6259 if (!context
) return IE_INVALID_CONTEXT
;
6260 zfree(context
->long_message
);
6261 if (!box
) return IE_INVAL
;
6264 /* Build CreateDataBoxPFOInfo request */
6265 err
= build_CreateDBInput_request(context
,
6266 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6267 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6268 (xmlChar
*) ceo_label
, NULL
, approval
);
6269 if (err
) goto leave
;
6271 /* Send it to server and process response */
6272 err
= send_request_check_drop_response(context
,
6273 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6274 (xmlChar
**) refnumber
);
6275 /* XXX: XML Schema names output dbID element but textual documentation
6276 * states no box identifier is returned. */
6278 xmlFreeNode(request
);
6279 #else /* not HAVE_LIBCURL */
6286 /* Common implementation for removing given box.
6287 * @context is session context
6288 * @service_name is UTF-8 encoded name fo ISDS service
6289 * @box is box description to delete
6290 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6291 * carry sane value. If NULL, do not inject this information into request.
6292 * @approval is optional external approval of box manipulation
6293 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6294 * NULL, if you don't care.*/
6295 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6296 const xmlChar
*service_name
,
6297 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6298 const struct isds_approval
*approval
, char **refnumber
) {
6299 isds_error err
= IE_SUCCESS
;
6301 xmlNsPtr isds_ns
= NULL
;
6302 xmlNodePtr request
= NULL
;
6304 xmlChar
*string
= NULL
;
6308 if (!context
) return IE_INVALID_CONTEXT
;
6309 zfree(context
->long_message
);
6310 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6314 /* Build DeleteDataBox(Promptly) request */
6315 request
= xmlNewNode(NULL
, service_name
);
6317 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6318 isds_printf_message(context
,
6319 _("Could build %s request"), service_name_locale
);
6320 free(service_name_locale
);
6323 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6325 isds_log_message(context
, _("Could not create ISDS name space"));
6326 xmlFreeNode(request
);
6329 xmlSetNs(request
, isds_ns
);
6331 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6332 err
= insert_DbOwnerInfo(context
, box
, node
);
6333 if (err
) goto leave
;
6336 err
= tm2datestring(since
, &string
);
6338 isds_log_message(context
,
6339 _("Could not convert `since' argument to ISO date string"));
6342 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6346 err
= insert_GExtApproval(context
, approval
, request
);
6347 if (err
) goto leave
;
6350 /* Send it to server and process response */
6351 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6352 service_name
, &request
, (xmlChar
**) refnumber
);
6355 xmlFreeNode(request
);
6357 #else /* not HAVE_LIBCURL */
6364 /* Remove given box permanently.
6365 * @context is session context
6366 * @box is box description to delete
6367 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6369 * @approval is optional external approval of box manipulation
6370 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6371 * NULL, if you don't care.*/
6372 isds_error
isds_delete_box(struct isds_ctx
*context
,
6373 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6374 const struct isds_approval
*approval
, char **refnumber
) {
6375 if (!context
) return IE_INVALID_CONTEXT
;
6376 zfree(context
->long_message
);
6377 if (!box
|| !since
) return IE_INVAL
;
6379 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6380 box
, since
, approval
, refnumber
);
6384 /* Undocumented function.
6385 * @context is session context
6386 * @box is box description to delete
6387 * @approval is optional external approval of box manipulation
6388 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6389 * NULL, if you don't care.*/
6390 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6391 const struct isds_DbOwnerInfo
*box
,
6392 const struct isds_approval
*approval
, char **refnumber
) {
6393 if (!context
) return IE_INVALID_CONTEXT
;
6394 zfree(context
->long_message
);
6395 if (!box
) return IE_INVAL
;
6397 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6398 box
, NULL
, approval
, refnumber
);
6402 /* Update data about given box.
6403 * @context is session context
6404 * @old_box current box description
6405 * @new_box are updated data about @old_box
6406 * @approval is optional external approval of box manipulation
6407 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6408 * NULL, if you don't care.*/
6409 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6410 const struct isds_DbOwnerInfo
*old_box
,
6411 const struct isds_DbOwnerInfo
*new_box
,
6412 const struct isds_approval
*approval
, char **refnumber
) {
6413 isds_error err
= IE_SUCCESS
;
6415 xmlNsPtr isds_ns
= NULL
;
6416 xmlNodePtr request
= NULL
;
6421 if (!context
) return IE_INVALID_CONTEXT
;
6422 zfree(context
->long_message
);
6423 if (!old_box
|| !new_box
) return IE_INVAL
;
6427 /* Build UpdateDataBoxDescr request */
6428 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6430 isds_log_message(context
,
6431 _("Could build UpdateDataBoxDescr request"));
6434 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6436 isds_log_message(context
, _("Could not create ISDS name space"));
6437 xmlFreeNode(request
);
6440 xmlSetNs(request
, isds_ns
);
6442 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6443 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6444 if (err
) goto leave
;
6446 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6447 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6448 if (err
) goto leave
;
6450 err
= insert_GExtApproval(context
, approval
, request
);
6451 if (err
) goto leave
;
6454 /* Send it to server and process response */
6455 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6456 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6459 xmlFreeNode(request
);
6460 #else /* not HAVE_LIBCURL */
6469 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6471 * @context is session context
6472 * @service is SOAP service
6473 * @service_name is name of request in @service
6474 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6475 * @box_id is box ID of interest
6476 * @approval is optional external approval of box manipulation
6477 * @response is server SOAP body response as XML document
6478 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6479 * NULL, if you don't care.
6480 * @return error coded from lower layer, context message will be set up
6482 static isds_error
build_send_dbid_request_check_response(
6483 struct isds_ctx
*context
, const isds_service service
,
6484 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6485 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6486 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6488 isds_error err
= IE_SUCCESS
;
6489 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6490 xmlNodePtr request
= NULL
, node
;
6491 xmlNsPtr isds_ns
= NULL
;
6493 if (!context
) return IE_INVALID_CONTEXT
;
6494 if (!service_name
|| !box_id
) return IE_INVAL
;
6495 if (!response
) return IE_INVAL
;
6497 /* Free output argument */
6498 xmlFreeDoc(*response
); *response
= NULL
;
6500 /* Prepare strings */
6501 service_name_locale
= _isds_utf82locale((char*)service_name
);
6502 if (!service_name_locale
) {
6506 box_id_locale
= _isds_utf82locale((char*)box_id
);
6507 if (!box_id_locale
) {
6513 request
= xmlNewNode(NULL
, service_name
);
6515 isds_printf_message(context
,
6516 _("Could not build %s request for %s box"), service_name_locale
,
6521 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6523 isds_log_message(context
, _("Could not create ISDS name space"));
6527 xmlSetNs(request
, isds_ns
);
6529 /* Add XSD:tIdDbInput children */
6530 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6531 INSERT_STRING(request
, box_id_element
, box_id
);
6532 err
= insert_GExtApproval(context
, approval
, request
);
6533 if (err
) goto leave
;
6535 /* Send request and check response*/
6536 err
= send_destroy_request_check_response(context
,
6537 service
, service_name
, &request
, response
, refnumber
, NULL
);
6540 free(service_name_locale
);
6541 free(box_id_locale
);
6542 xmlFreeNode(request
);
6545 #endif /* HAVE_LIBCURL */
6548 /* Get data about all users assigned to given box.
6549 * @context is session context
6551 * @users is automatically reallocated list of struct isds_DbUserInfo */
6552 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6553 struct isds_list
**users
) {
6554 isds_error err
= IE_SUCCESS
;
6556 xmlDocPtr response
= NULL
;
6557 xmlXPathContextPtr xpath_ctx
= NULL
;
6558 xmlXPathObjectPtr result
= NULL
;
6560 struct isds_list
*item
, *prev_item
= NULL
;
6563 if (!context
) return IE_INVALID_CONTEXT
;
6564 zfree(context
->long_message
);
6565 if (!users
|| !box_id
) return IE_INVAL
;
6566 isds_list_free(users
);
6570 /* Do request and check for success */
6571 err
= build_send_dbid_request_check_response(context
,
6572 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6573 BAD_CAST box_id
, NULL
, &response
, NULL
);
6574 if (err
) goto leave
;
6578 /* Prepare structure */
6579 xpath_ctx
= xmlXPathNewContext(response
);
6584 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6589 /* Set context node */
6590 result
= xmlXPathEvalExpression(BAD_CAST
6591 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6597 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6598 /* Iterate over all users */
6599 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6601 /* Prepare structure */
6602 item
= calloc(1, sizeof(*item
));
6607 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6608 if (i
== 0) *users
= item
;
6609 else prev_item
->next
= item
;
6613 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6614 err
= extract_DbUserInfo(context
,
6615 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6616 if (err
) goto leave
;
6622 isds_list_free(users
);
6625 xmlXPathFreeObject(result
);
6626 xmlXPathFreeContext(xpath_ctx
);
6627 xmlFreeDoc(response
);
6630 isds_log(ILF_ISDS
, ILL_DEBUG
,
6631 _("GetDataBoxUsers request processed by server "
6632 "successfully.\n"));
6633 #else /* not HAVE_LIBCURL */
6641 /* Update data about user assigned to given box.
6642 * @context is session context
6643 * @box is box identification
6644 * @old_user identifies user to update
6645 * @new_user are updated data about @old_user
6646 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6647 * NULL, if you don't care.*/
6648 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6649 const struct isds_DbOwnerInfo
*box
,
6650 const struct isds_DbUserInfo
*old_user
,
6651 const struct isds_DbUserInfo
*new_user
,
6653 isds_error err
= IE_SUCCESS
;
6655 xmlNsPtr isds_ns
= NULL
;
6656 xmlNodePtr request
= NULL
;
6661 if (!context
) return IE_INVALID_CONTEXT
;
6662 zfree(context
->long_message
);
6663 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6667 /* Build UpdateDataBoxUser request */
6668 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6670 isds_log_message(context
,
6671 _("Could build UpdateDataBoxUser request"));
6674 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6676 isds_log_message(context
, _("Could not create ISDS name space"));
6677 xmlFreeNode(request
);
6680 xmlSetNs(request
, isds_ns
);
6682 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6683 err
= insert_DbOwnerInfo(context
, box
, node
);
6684 if (err
) goto leave
;
6686 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6687 err
= insert_DbUserInfo(context
, old_user
, node
);
6688 if (err
) goto leave
;
6690 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6691 err
= insert_DbUserInfo(context
, new_user
, node
);
6692 if (err
) goto leave
;
6694 /* Send it to server and process response */
6695 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6696 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6699 xmlFreeNode(request
);
6700 #else /* not HAVE_LIBCURL */
6708 /* Undocumented function.
6709 * @context is session context
6710 * @box_id is UTF-8 encoded box identifier
6711 * @token is UTF-8 encoded temporary password
6712 * @user_id outputs UTF-8 encoded reallocated user identifier
6713 * @password outpus UTF-8 encoded reallocated user password
6714 * Output arguments will be nulled in case of error */
6715 isds_error
isds_activate(struct isds_ctx
*context
,
6716 const char *box_id
, const char *token
,
6717 char **user_id
, char **password
) {
6718 isds_error err
= IE_SUCCESS
;
6720 xmlNsPtr isds_ns
= NULL
;
6721 xmlNodePtr request
= NULL
, node
;
6722 xmlDocPtr response
= NULL
;
6723 xmlXPathContextPtr xpath_ctx
= NULL
;
6724 xmlXPathObjectPtr result
= NULL
;
6728 if (!context
) return IE_INVALID_CONTEXT
;
6729 zfree(context
->long_message
);
6731 if (user_id
) zfree(*user_id
);
6732 if (password
) zfree(*password
);
6734 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6738 /* Build Activate request */
6739 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6741 isds_log_message(context
, _("Could build Activate request"));
6744 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6746 isds_log_message(context
, _("Could not create ISDS name space"));
6747 xmlFreeNode(request
);
6750 xmlSetNs(request
, isds_ns
);
6752 INSERT_STRING(request
, "dbAccessDataId", token
);
6753 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6754 INSERT_STRING(request
, "dbID", box_id
);
6757 /* Send request and check response*/
6758 err
= send_destroy_request_check_response(context
,
6759 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6760 &response
, NULL
, NULL
);
6761 if (err
) goto leave
;
6765 xpath_ctx
= xmlXPathNewContext(response
);
6770 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6774 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6780 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6781 isds_log_message(context
, _("Missing ActivateResponse element"));
6785 if (result
->nodesetval
->nodeNr
> 1) {
6786 isds_log_message(context
, _("Multiple ActivateResponse element"));
6790 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6791 xmlXPathFreeObject(result
); result
= NULL
;
6793 EXTRACT_STRING("isds:userId", *user_id
);
6795 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6796 "but did not return `userId' element.\n"));
6798 EXTRACT_STRING("isds:password", *password
);
6800 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6801 "but did not return `password' element.\n"));
6804 xmlXPathFreeObject(result
);
6805 xmlXPathFreeContext(xpath_ctx
);
6806 xmlFreeDoc(response
);
6807 xmlFreeNode(request
);
6810 isds_log(ILF_ISDS
, ILL_DEBUG
,
6811 _("Activate request processed by server successfully.\n"));
6812 #else /* not HAVE_LIBCURL */
6820 /* Reset credentials of user assigned to given box.
6821 * @context is session context
6822 * @box is box identification
6823 * @user identifies user to reset password
6824 * @fee_paid is true if fee has been paid, false otherwise
6825 * @approval is optional external approval of box manipulation
6826 * @credentials_delivery is NULL if new password should be delivered off-line
6827 * to the user. It is valid pointer if user should obtain new password on-line
6828 * on dedicated web server. Then input @credentials_delivery.email value is
6829 * user's e-mail address user must provide to dedicated web server together
6830 * with @credentials_delivery.token. The output reallocated token user needs
6831 * to use to authorize on the web server to view his new password. Output
6832 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6833 * ISDS changed up on this call. (No reason why server could change the name
6835 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6836 * NULL, if you don't care.*/
6837 isds_error
isds_reset_password(struct isds_ctx
*context
,
6838 const struct isds_DbOwnerInfo
*box
,
6839 const struct isds_DbUserInfo
*user
,
6840 const _Bool fee_paid
, const struct isds_approval
*approval
,
6841 struct isds_credentials_delivery
*credentials_delivery
,
6843 isds_error err
= IE_SUCCESS
;
6845 xmlNsPtr isds_ns
= NULL
;
6846 xmlNodePtr request
= NULL
, node
;
6847 xmlDocPtr response
= NULL
;
6851 if (!context
) return IE_INVALID_CONTEXT
;
6852 zfree(context
->long_message
);
6854 if (credentials_delivery
) {
6855 zfree(credentials_delivery
->token
);
6856 zfree(credentials_delivery
->new_user_name
);
6858 if (!box
|| !user
) return IE_INVAL
;
6862 /* Build NewAccessData request */
6863 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6865 isds_log_message(context
,
6866 _("Could build NewAccessData request"));
6869 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6871 isds_log_message(context
, _("Could not create ISDS name space"));
6872 xmlFreeNode(request
);
6875 xmlSetNs(request
, isds_ns
);
6877 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6878 err
= insert_DbOwnerInfo(context
, box
, node
);
6879 if (err
) goto leave
;
6881 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6882 err
= insert_DbUserInfo(context
, user
, node
);
6883 if (err
) goto leave
;
6885 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6887 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6888 if (err
) goto leave
;
6890 err
= insert_GExtApproval(context
, approval
, request
);
6891 if (err
) goto leave
;
6893 /* Send request and check response*/
6894 err
= send_destroy_request_check_response(context
,
6895 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6896 &response
, (xmlChar
**) refnumber
, NULL
);
6897 if (err
) goto leave
;
6900 /* Extract optional token */
6901 err
= extract_credentials_delivery(context
, credentials_delivery
,
6902 response
, "NewAccessData");
6905 xmlFreeDoc(response
);
6906 xmlFreeNode(request
);
6909 isds_log(ILF_ISDS
, ILL_DEBUG
,
6910 _("NewAccessData request processed by server "
6911 "successfully.\n"));
6912 #else /* not HAVE_LIBCURL */
6920 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6921 * code, destroy response and log success.
6922 * @context is ISDS session context.
6923 * @service_name is name of SERVICE_DB_MANIPULATION service
6924 * @box is box identification
6925 * @user identifies user to remove
6926 * @credentials_delivery is NULL if new user's password should be delivered
6927 * off-line to the user. It is valid pointer if user should obtain new
6928 * password on-line on dedicated web server. Then input
6929 * @credentials_delivery.email value is user's e-mail address user must
6930 * provide to dedicated web server together with @credentials_delivery.token.
6931 * The output reallocated token user needs to use to authorize on the web
6932 * server to view his new password. Output reallocated
6933 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6934 * assingned or changed up on this call.
6935 * @approval is optional external approval of box manipulation
6936 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6937 * NULL, if you don't care. */
6938 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6939 struct isds_ctx
*context
, const xmlChar
*service_name
,
6940 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6941 struct isds_credentials_delivery
*credentials_delivery
,
6942 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6943 isds_error err
= IE_SUCCESS
;
6945 xmlNsPtr isds_ns
= NULL
;
6946 xmlNodePtr request
= NULL
, node
;
6947 xmlDocPtr response
= NULL
;
6951 if (!context
) return IE_INVALID_CONTEXT
;
6952 zfree(context
->long_message
);
6953 if (credentials_delivery
) {
6954 zfree(credentials_delivery
->token
);
6955 zfree(credentials_delivery
->new_user_name
);
6957 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6962 /* Build NewAccessData or similar request */
6963 request
= xmlNewNode(NULL
, service_name
);
6965 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6966 isds_printf_message(context
, _("Could not build %s request"),
6967 service_name_locale
);
6968 free(service_name_locale
);
6971 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6973 isds_log_message(context
, _("Could not create ISDS name space"));
6974 xmlFreeNode(request
);
6977 xmlSetNs(request
, isds_ns
);
6979 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6980 err
= insert_DbOwnerInfo(context
, box
, node
);
6981 if (err
) goto leave
;
6983 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6984 err
= insert_DbUserInfo(context
, user
, node
);
6985 if (err
) goto leave
;
6987 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6988 if (err
) goto leave
;
6990 err
= insert_GExtApproval(context
, approval
, request
);
6991 if (err
) goto leave
;
6994 /* Send request and check response*/
6995 err
= send_destroy_request_check_response(context
,
6996 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6999 xmlFreeNode(request
);
7002 /* Pick up credentials_delivery if requested */
7003 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7004 (char *)service_name
);
7007 xmlFreeDoc(response
);
7008 if (request
) xmlFreeNode(request
);
7011 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7012 isds_log(ILF_ISDS
, ILL_DEBUG
,
7013 _("%s request processed by server successfully.\n"),
7014 service_name_locale
);
7015 free(service_name_locale
);
7017 #else /* not HAVE_LIBCURL */
7025 /* Assign new user to given box.
7026 * @context is session context
7027 * @box is box identification
7028 * @user defines new user to add
7029 * @credentials_delivery is NULL if new user's password should be delivered
7030 * off-line to the user. It is valid pointer if user should obtain new
7031 * password on-line on dedicated web server. Then input
7032 * @credentials_delivery.email value is user's e-mail address user must
7033 * provide to dedicated web server together with @credentials_delivery.token.
7034 * The output reallocated token user needs to use to authorize on the web
7035 * server to view his new password. Output reallocated
7036 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7037 * assingned up on this call.
7038 * @approval is optional external approval of box manipulation
7039 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7040 * NULL, if you don't care.*/
7041 isds_error
isds_add_user(struct isds_ctx
*context
,
7042 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7043 struct isds_credentials_delivery
*credentials_delivery
,
7044 const struct isds_approval
*approval
, char **refnumber
) {
7045 return build_send_manipulationboxuser_request_check_drop_response(context
,
7046 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
7047 approval
, (xmlChar
**) refnumber
);
7051 /* Remove user assigned to given box.
7052 * @context is session context
7053 * @box is box identification
7054 * @user identifies user to remove
7055 * @approval is optional external approval of box manipulation
7056 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7057 * NULL, if you don't care.*/
7058 isds_error
isds_delete_user(struct isds_ctx
*context
,
7059 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7060 const struct isds_approval
*approval
, char **refnumber
) {
7061 return build_send_manipulationboxuser_request_check_drop_response(context
,
7062 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
7063 (xmlChar
**) refnumber
);
7067 /* Get list of boxes in ZIP archive.
7068 * @context is session context
7069 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7070 * System recognizes following values currently: ALL (all boxes), UPG
7071 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7072 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7073 * commercial messages). This argument is a string because specification
7074 * states new values can appear in the future. Not all list types are
7075 * available to all users.
7076 * @buffer is automatically reallocated memory to store the list of boxes. The
7077 * list is zipped CSV file.
7078 * @buffer_length is size of @buffer data in bytes.
7079 * In case of error @buffer will be freed and @buffer_length will be
7081 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7082 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7083 isds_error err
= IE_SUCCESS
;
7085 xmlNsPtr isds_ns
= NULL
;
7086 xmlNodePtr request
= NULL
, node
;
7087 xmlDocPtr response
= NULL
;
7088 xmlXPathContextPtr xpath_ctx
= NULL
;
7089 xmlXPathObjectPtr result
= NULL
;
7090 char *string
= NULL
;
7094 if (!context
) return IE_INVALID_CONTEXT
;
7095 zfree(context
->long_message
);
7096 if (buffer
) zfree(*buffer
);
7097 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7101 /* Check if connection is established
7102 * TODO: This check should be done downstairs. */
7103 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7106 /* Build AuthenticateMessage request */
7107 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7109 isds_log_message(context
,
7110 _("Could not build GetDataBoxList request"));
7113 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7115 isds_log_message(context
, _("Could not create ISDS name space"));
7116 xmlFreeNode(request
);
7119 xmlSetNs(request
, isds_ns
);
7120 INSERT_STRING(request
, "dblType", list_identifier
);
7122 /* Send request to server and process response */
7123 err
= send_destroy_request_check_response(context
,
7124 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7125 &response
, NULL
, NULL
);
7126 if (err
) goto leave
;
7129 /* Extract Base-64 encoded ZIP file */
7130 xpath_ctx
= xmlXPathNewContext(response
);
7135 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7139 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7141 /* Decode non-empty archive */
7142 if (string
&& string
[0] != '\0') {
7143 *buffer_length
= _isds_b64decode(string
, buffer
);
7144 if (*buffer_length
== (size_t) -1) {
7145 isds_printf_message(context
,
7146 _("Error while Base64-decoding box list archive"));
7155 xmlXPathFreeObject(result
);
7156 xmlXPathFreeContext(xpath_ctx
);
7157 xmlFreeDoc(response
);
7158 xmlFreeNode(request
);
7161 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7162 "processed by server successfully.\n"));
7164 #else /* not HAVE_LIBCURL */
7172 /* Find boxes suiting given criteria.
7173 * @criteria is filter. You should fill in at least some members.
7174 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7175 * possibly empty. Input NULL or valid old structure.
7177 * IE_SUCCESS if search succeeded, @boxes contains useful data
7178 * IE_NOEXIST if no such box exists, @boxes will be NULL
7179 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7180 * contains still valid data
7181 * other code if something bad happens. @boxes will be NULL. */
7182 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7183 const struct isds_DbOwnerInfo
*criteria
,
7184 struct isds_list
**boxes
) {
7185 isds_error err
= IE_SUCCESS
;
7187 _Bool truncated
= 0;
7188 xmlNsPtr isds_ns
= NULL
;
7189 xmlNodePtr request
= NULL
;
7190 xmlDocPtr response
= NULL
;
7191 xmlChar
*code
= NULL
, *message
= NULL
;
7192 xmlNodePtr db_owner_info
;
7193 xmlXPathContextPtr xpath_ctx
= NULL
;
7194 xmlXPathObjectPtr result
= NULL
;
7195 xmlChar
*string
= NULL
;
7199 if (!context
) return IE_INVALID_CONTEXT
;
7200 zfree(context
->long_message
);
7201 if (!boxes
) return IE_INVAL
;
7202 isds_list_free(boxes
);
7209 /* Check if connection is established
7210 * TODO: This check should be done downstairs. */
7211 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7214 /* Build FindDataBox request */
7215 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7217 isds_log_message(context
,
7218 _("Could build FindDataBox request"));
7221 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7223 isds_log_message(context
, _("Could not create ISDS name space"));
7224 xmlFreeNode(request
);
7227 xmlSetNs(request
, isds_ns
);
7228 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7229 if (!db_owner_info
) {
7230 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7231 "FindDataBox element"));
7232 xmlFreeNode(request
);
7236 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7237 if (err
) goto leave
;
7240 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7243 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7245 /* Destroy request */
7246 xmlFreeNode(request
); request
= NULL
;
7249 isds_log(ILF_ISDS
, ILL_DEBUG
,
7250 _("Processing ISDS response on FindDataBox "
7251 "request failed\n"));
7255 /* Check for response status */
7256 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7257 &code
, &message
, NULL
);
7259 isds_log(ILF_ISDS
, ILL_DEBUG
,
7260 _("ISDS response on FindDataBox request is missing status\n"));
7264 /* Request processed, but nothing found */
7265 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7266 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7267 char *code_locale
= _isds_utf82locale((char*)code
);
7268 char *message_locale
= _isds_utf82locale((char*)message
);
7269 isds_log(ILF_ISDS
, ILL_DEBUG
,
7270 _("Server did not found any box on FindDataBox request "
7271 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7272 isds_log_message(context
, message_locale
);
7274 free(message_locale
);
7279 /* Warning, not a error */
7280 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7281 char *code_locale
= _isds_utf82locale((char*)code
);
7282 char *message_locale
= _isds_utf82locale((char*)message
);
7283 isds_log(ILF_ISDS
, ILL_DEBUG
,
7284 _("Server truncated response on FindDataBox request "
7285 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7286 isds_log_message(context
, message_locale
);
7288 free(message_locale
);
7293 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7294 char *code_locale
= _isds_utf82locale((char*)code
);
7295 char *message_locale
= _isds_utf82locale((char*)message
);
7296 isds_log(ILF_ISDS
, ILL_DEBUG
,
7297 _("Server refused FindDataBox request "
7298 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7299 isds_log_message(context
, message_locale
);
7301 free(message_locale
);
7306 xpath_ctx
= xmlXPathNewContext(response
);
7311 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7316 /* Extract boxes if they present */
7317 result
= xmlXPathEvalExpression(BAD_CAST
7318 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7324 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7325 struct isds_list
*item
, *prev_item
= NULL
;
7326 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7327 item
= calloc(1, sizeof(*item
));
7333 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7334 if (i
== 0) *boxes
= item
;
7335 else prev_item
->next
= item
;
7338 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7339 err
= extract_DbOwnerInfo(context
,
7340 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7341 if (err
) goto leave
;
7347 isds_list_free(boxes
);
7349 if (truncated
) err
= IE_2BIG
;
7353 xmlFreeNode(request
);
7354 xmlXPathFreeObject(result
);
7355 xmlXPathFreeContext(xpath_ctx
);
7359 xmlFreeDoc(response
);
7362 isds_log(ILF_ISDS
, ILL_DEBUG
,
7363 _("FindDataBox request processed by server successfully.\n"));
7364 #else /* not HAVE_LIBCURL */
7373 /* Convert a string with match markers into a plain string with list of
7374 * pointers to the matches
7375 * @string is an UTF-8 encoded non-constant string with match markers
7376 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7377 * The markers will be removed from the string.
7378 * @starts is a reallocated list of static pointers into the @string pointing
7379 * to places where match start markers occured.
7380 * @ends is a reallocated list of static pointers into the @string pointing
7381 * to places where match end markers occured.
7382 * @return IE_SUCCESS in case of no failure. */
7383 static isds_error
interpret_matches(xmlChar
*string
,
7384 struct isds_list
**starts
, struct isds_list
**ends
) {
7385 isds_error err
= IE_SUCCESS
;
7386 xmlChar
*pointer
, *destination
, *source
;
7387 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7389 isds_list_free(starts
);
7390 isds_list_free(ends
);
7391 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7392 if (NULL
== string
) return IE_SUCCESS
;
7394 for (pointer
= string
; *pointer
!= '\0';) {
7395 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7396 /* Remove the start marker */
7397 for (source
= pointer
+ 14, destination
= pointer
;
7398 *source
!= '\0'; source
++, destination
++) {
7399 *destination
= *source
;
7401 *destination
= '\0';
7402 /* Append the pointer into the list */
7403 item
= calloc(1, sizeof(*item
));
7408 item
->destructor
= (void (*)(void **))NULL
;
7409 item
->data
= pointer
;
7410 if (NULL
== prev_start
) *starts
= item
;
7411 else prev_start
->next
= item
;
7413 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7414 /* Remove the end marker */
7415 for (source
= pointer
+ 12, destination
= pointer
;
7416 *source
!= '\0'; source
++, destination
++) {
7417 *destination
= *source
;
7419 *destination
= '\0';
7420 /* Append the pointer into the list */
7421 item
= calloc(1, sizeof(*item
));
7426 item
->destructor
= (void (*)(void **))NULL
;
7427 item
->data
= pointer
;
7428 if (NULL
== prev_end
) *ends
= item
;
7429 else prev_end
->next
= item
;
7438 isds_list_free(starts
);
7439 isds_list_free(ends
);
7445 /* Convert isds:dbResult XML tree into structure
7446 * @context is ISDS context.
7447 * @fulltext_result is automatically reallocated found box structure.
7448 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7449 * @collect_matches is true to interpret match markers.
7450 * In case of error @result will be freed. */
7451 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7452 struct isds_fulltext_result
**fulltext_result
,
7453 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7454 isds_error err
= IE_SUCCESS
;
7455 xmlXPathObjectPtr result
= NULL
;
7456 char *string
= NULL
;
7458 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7459 if (NULL
== fulltext_result
) return IE_INVAL
;
7460 isds_fulltext_result_free(fulltext_result
);
7461 if (!xpath_ctx
) return IE_INVAL
;
7464 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7465 if (NULL
== *fulltext_result
) {
7471 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7473 EXTRACT_STRING("isds:dbType", string
);
7474 if (NULL
== string
) {
7476 isds_log_message(context
, _("Empty isds:dbType element"));
7479 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7481 if (err
== IE_ENUM
) {
7483 char *string_locale
= _isds_utf82locale(string
);
7484 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7486 free(string_locale
);
7492 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7493 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7495 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7496 if (err
) goto leave
;
7498 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7499 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7500 (*fulltext_result
)->dbEffectiveOVM
);
7502 EXTRACT_STRING("isds:dbSendOptions", string
);
7503 if (NULL
== string
) {
7505 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7508 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7509 (*fulltext_result
)->active
= 1;
7510 (*fulltext_result
)->public_sending
= 1;
7511 (*fulltext_result
)->commercial_sending
= 0;
7512 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7513 (*fulltext_result
)->active
= 1;
7514 (*fulltext_result
)->public_sending
= 1;
7515 (*fulltext_result
)->commercial_sending
= 1;
7516 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7517 (*fulltext_result
)->active
= 1;
7518 (*fulltext_result
)->public_sending
= 0;
7519 (*fulltext_result
)->commercial_sending
= 1;
7520 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7521 (*fulltext_result
)->active
= 1;
7522 (*fulltext_result
)->public_sending
= 0;
7523 (*fulltext_result
)->commercial_sending
= 0;
7524 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7525 (*fulltext_result
)->active
= 0;
7526 (*fulltext_result
)->public_sending
= 0;
7527 (*fulltext_result
)->commercial_sending
= 0;
7530 char *string_locale
= _isds_utf82locale(string
);
7531 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7533 free(string_locale
);
7538 /* Interpret match marks */
7539 if (collect_matches
) {
7540 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7541 &((*fulltext_result
)->name_match_start
),
7542 &((*fulltext_result
)->name_match_end
));
7543 if (err
) goto leave
;
7544 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7545 &((*fulltext_result
)->address_match_start
),
7546 &((*fulltext_result
)->address_match_end
));
7547 if (err
) goto leave
;
7551 if (err
) isds_fulltext_result_free(fulltext_result
);
7553 xmlXPathFreeObject(result
);
7556 #endif /* HAVE_LIBCURL */
7559 /* Find boxes matching a given full-text criteria.
7560 * @context is a session context
7561 * @query is a non-empty string which consists of words to search
7562 * @target selects box attributes to search for @query words. Pass NULL if you
7564 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7565 * to search in all box types. Pass NULL to let server to use default value
7566 * which is DBTYPE_SYSTEM.
7567 * @page_size defines count of boxes to constitute a response page. It counts
7568 * from zero. Pass NULL to let server to use a default value (50 now).
7569 * @page_number defines ordinar number of the response page to return. It
7570 * counts from zero. Pass NULL to let server to use a default value (0 now).
7571 * @track_matches points to true for marking @query words found in the box
7572 * attributes. It points to false for not marking. Pass NULL to let the server
7573 * to use default value (false now).
7574 * @total_matching_boxes outputs reallocated number of all boxes matching the
7575 * query. Will be pointer to NULL if server did not provide the value.
7576 * Pass NULL if you don't care.
7577 * @current_page_beginning outputs reallocated ordinar number of the first box
7578 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7579 * server did not provide the value. Pass NULL if you don't care.
7580 * @current_page_size outputs reallocated count of boxes in the this @boxes
7581 * page. It will be pointer to NULL if the server did not provide the value.
7582 * Pass NULL if you don't care.
7583 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7584 * is the last one, false if more boxes match, NULL if the server did not
7585 * provude the value. Pass NULL if you don't care.
7586 * @boxes outputs reallocated list of isds_fulltext_result structures,
7589 * IE_SUCCESS if search succeeded
7590 * IE_2BIG if @page_size is too large
7591 * other code if something bad happens; output arguments will be NULL. */
7592 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7594 const isds_fulltext_target
*target
,
7595 const isds_DbType
*box_type
,
7596 const unsigned long int *page_size
,
7597 const unsigned long int *page_number
,
7598 const _Bool
*track_matches
,
7599 unsigned long int **total_matching_boxes
,
7600 unsigned long int **current_page_beginning
,
7601 unsigned long int **current_page_size
,
7603 struct isds_list
**boxes
) {
7604 isds_error err
= IE_SUCCESS
;
7606 xmlNsPtr isds_ns
= NULL
;
7607 xmlNodePtr request
= NULL
;
7608 xmlDocPtr response
= NULL
;
7610 xmlXPathContextPtr xpath_ctx
= NULL
;
7611 xmlXPathObjectPtr result
= NULL
;
7612 const xmlChar
*static_string
= NULL
;
7613 xmlChar
*string
= NULL
;
7615 const xmlChar
*codes
[] = {
7625 const char *meanings
[] = {
7626 N_("You are not allowed to perform the search"),
7627 N_("The query string is empty"),
7628 N_("Searched box ID is malformed"),
7629 N_("Searched organization ID is malformed"),
7630 N_("Invalid input"),
7631 N_("Requested page size is too large"),
7632 N_("Search engine internal error")
7634 const isds_error errors
[] = {
7643 struct code_map_isds_error map
= {
7645 .meanings
= meanings
,
7651 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7652 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7653 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7654 if (NULL
!= last_page
) zfree(*last_page
);
7655 isds_list_free(boxes
);
7657 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7658 zfree(context
->long_message
);
7660 if (NULL
== boxes
) return IE_INVAL
;
7662 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7663 isds_log_message(context
, _("Query string must be non-empty"));
7668 /* Check if connection is established
7669 * TODO: This check should be done downstairs. */
7670 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7672 /* Build FindDataBox request */
7673 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7674 if (NULL
== request
) {
7675 isds_log_message(context
,
7676 _("Could not build ISDSSearch2 request"));
7679 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7680 if(NULL
== isds_ns
) {
7681 isds_log_message(context
, _("Could not create ISDS name space"));
7682 xmlFreeNode(request
);
7685 xmlSetNs(request
, isds_ns
);
7687 INSERT_STRING(request
, "searchText", query
);
7689 if (NULL
!= target
) {
7690 static_string
= isds_fulltext_target2string(*(target
));
7691 if (NULL
== static_string
) {
7692 isds_printf_message(context
, _("Invalid target value: %d"),
7698 INSERT_STRING(request
, "searchType", static_string
);
7699 static_string
= NULL
;
7701 if (NULL
!= box_type
) {
7702 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7703 if (DBTYPE_SYSTEM
== *box_type
) {
7704 static_string
= BAD_CAST
"ALL";
7706 static_string
= isds_DbType2string(*(box_type
));
7707 if (NULL
== static_string
) {
7708 isds_printf_message(context
, _("Invalid box type value: %d"),
7715 INSERT_STRING(request
, "searchScope", static_string
);
7716 static_string
= NULL
;
7718 INSERT_ULONGINT(request
, "page", page_number
, string
);
7719 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7720 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7722 /* Send request and check response */
7723 err
= send_destroy_request_check_response(context
,
7724 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7725 &request
, &response
, NULL
, &map
);
7726 if (err
) goto leave
;
7728 /* Parse response */
7729 xpath_ctx
= xmlXPathNewContext(response
);
7730 if (NULL
== xpath_ctx
) {
7734 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7738 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7744 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7745 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7749 if (result
->nodesetval
->nodeNr
> 1) {
7750 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7754 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7755 xmlXPathFreeObject(result
); result
= NULL
;
7758 /* Extract counters */
7759 if (NULL
!= total_matching_boxes
) {
7760 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7762 if (NULL
!= current_page_size
) {
7763 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7765 if (NULL
!= current_page_beginning
) {
7766 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7768 if (NULL
!= last_page
) {
7769 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7771 xmlXPathFreeObject(result
); result
= NULL
;
7773 /* Extract boxes if they present */
7774 result
= xmlXPathEvalExpression(BAD_CAST
7775 "isds:dbResults/isds:dbResult", xpath_ctx
);
7776 if (NULL
== result
) {
7780 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7781 struct isds_list
*item
, *prev_item
= NULL
;
7782 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7783 item
= calloc(1, sizeof(*item
));
7789 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7790 if (i
== 0) *boxes
= item
;
7791 else prev_item
->next
= item
;
7794 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7795 err
= extract_dbResult(context
,
7796 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7797 (NULL
== track_matches
) ? 0 : *track_matches
);
7798 if (err
) goto leave
;
7804 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7805 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7806 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7807 if (NULL
!= last_page
) zfree(*last_page
);
7808 isds_list_free(boxes
);
7812 xmlFreeNode(request
);
7813 xmlXPathFreeObject(result
);
7814 xmlXPathFreeContext(xpath_ctx
);
7815 xmlFreeDoc(response
);
7818 isds_log(ILF_ISDS
, ILL_DEBUG
,
7819 _("ISDSSearch2 request processed by server successfully.\n"));
7820 #else /* not HAVE_LIBCURL */
7828 /* Get status of a box.
7829 * @context is ISDS session context.
7830 * @box_id is UTF-8 encoded box identifier as zero terminated string
7831 * @box_status is return value of box status.
7833 * IE_SUCCESS if box has been found and its status retrieved
7834 * IE_NOEXIST if box is not known to ISDS server
7835 * or other appropriate error.
7836 * You can use isds_DbState to enumerate box status. However out of enum
7837 * range value can be returned too. This is feature because ISDS
7838 * specification leaves the set of values open.
7839 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7840 * the box has been deleted, but ISDS still lists its former existence. */
7841 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7842 long int *box_status
) {
7843 isds_error err
= IE_SUCCESS
;
7845 xmlNsPtr isds_ns
= NULL
;
7846 xmlNodePtr request
= NULL
, db_id
;
7847 xmlDocPtr response
= NULL
;
7848 xmlXPathContextPtr xpath_ctx
= NULL
;
7849 xmlXPathObjectPtr result
= NULL
;
7850 xmlChar
*string
= NULL
;
7852 const xmlChar
*codes
[] = {
7858 const char *meanings
[] = {
7859 "The box does not exist",
7860 "Box ID is malformed",
7863 const isds_error errors
[] = {
7868 struct code_map_isds_error map
= {
7870 .meanings
= meanings
,
7875 if (!context
) return IE_INVALID_CONTEXT
;
7876 zfree(context
->long_message
);
7877 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7880 /* Check if connection is established
7881 * TODO: This check should be done downstairs. */
7882 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7885 /* Build CheckDataBox request */
7886 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7888 isds_log_message(context
,
7889 _("Could build CheckDataBox request"));
7892 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7894 isds_log_message(context
, _("Could not create ISDS name space"));
7895 xmlFreeNode(request
);
7898 xmlSetNs(request
, isds_ns
);
7899 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7901 isds_log_message(context
, _("Could not add dbID child to "
7902 "CheckDataBox element"));
7903 xmlFreeNode(request
);
7908 /* Send request and check response*/
7909 err
= send_destroy_request_check_response(context
,
7910 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7911 &request
, &response
, NULL
, &map
);
7912 if (err
) goto leave
;
7916 xpath_ctx
= xmlXPathNewContext(response
);
7921 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7925 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7931 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7932 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7936 if (result
->nodesetval
->nodeNr
> 1) {
7937 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7941 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7942 xmlXPathFreeObject(result
); result
= NULL
;
7944 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7949 xmlXPathFreeObject(result
);
7950 xmlXPathFreeContext(xpath_ctx
);
7952 xmlFreeDoc(response
);
7955 isds_log(ILF_ISDS
, ILL_DEBUG
,
7956 _("CheckDataBox request processed by server successfully.\n"));
7957 #else /* not HAVE_LIBCURL */
7965 /* Get list of permissions to send commercial messages.
7966 * @context is ISDS session context.
7967 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7968 * @permissions is a reallocated list of permissions (struct
7969 * isds_commercial_permission*) to send commercial messages from @box_id. The
7970 * order of permissions is significant as the server applies the permissions
7971 * and associated pre-paid credits in the order. Empty list means no
7974 * IE_SUCCESS if the list has been obtained correctly,
7975 * or other appropriate error. */
7976 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7977 const char *box_id
, struct isds_list
**permissions
) {
7978 isds_error err
= IE_SUCCESS
;
7980 xmlDocPtr response
= NULL
;
7981 xmlXPathContextPtr xpath_ctx
= NULL
;
7982 xmlXPathObjectPtr result
= NULL
;
7985 if (!context
) return IE_INVALID_CONTEXT
;
7986 zfree(context
->long_message
);
7987 if (NULL
== permissions
) return IE_INVAL
;
7988 isds_list_free(permissions
);
7989 if (NULL
== box_id
) return IE_INVAL
;
7992 /* Check if connection is established */
7993 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7995 /* Do request and check for success */
7996 err
= build_send_dbid_request_check_response(context
,
7997 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7998 BAD_CAST box_id
, NULL
, &response
, NULL
);
8000 isds_log(ILF_ISDS
, ILL_DEBUG
,
8001 _("PDZInfo request processed by server successfully.\n"));
8005 /* Prepare structure */
8006 xpath_ctx
= xmlXPathNewContext(response
);
8011 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8016 /* Set context node */
8017 result
= xmlXPathEvalExpression(BAD_CAST
8018 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8024 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8025 struct isds_list
*prev_item
= NULL
;
8027 /* Iterate over all permission records */
8028 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8029 struct isds_list
*item
;
8031 /* Prepare structure */
8032 item
= calloc(1, sizeof(*item
));
8037 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8038 if (i
== 0) *permissions
= item
;
8039 else prev_item
->next
= item
;
8043 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8044 err
= extract_DbPDZRecord(context
,
8045 (struct isds_commercial_permission
**) (&item
->data
),
8047 if (err
) goto leave
;
8053 isds_list_free(permissions
);
8056 xmlXPathFreeObject(result
);
8057 xmlXPathFreeContext(xpath_ctx
);
8058 xmlFreeDoc(response
);
8060 #else /* not HAVE_LIBCURL */
8068 /* Get details about credit for sending pre-paid commercial messages.
8069 * @context is ISDS session context.
8070 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8071 * @from_date is first day of credit history to return in @history. Only
8072 * tm_year, tm_mon and tm_mday carry sane value.
8073 * @to_date is last day of credit history to return in @history. Only
8074 * tm_year, tm_mon and tm_mday carry sane value.
8075 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8076 * if you don't care. This and all other credit values are integers in
8077 * hundredths of Czech Crowns.
8078 * @email outputs notification e-mail address where notifications about credit
8079 * are sent. This is automatically reallocated string. Pass NULL if you don't
8080 * care. It can return NULL if no address is defined.
8081 * @history outputs auto-reallocated list of pointers to struct
8082 * isds_credit_event. Events in closed interval @from_time to @to_time are
8083 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8084 * are sorted by time.
8086 * IE_SUCCESS if the credit details have been obtained correctly,
8087 * or other appropriate error. Please note that server allows to retrieve
8088 * only limited history of events. */
8089 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8091 const struct tm
*from_date
, const struct tm
*to_date
,
8092 long int *credit
, char **email
, struct isds_list
**history
) {
8093 isds_error err
= IE_SUCCESS
;
8095 char *box_id_locale
= NULL
;
8096 xmlNodePtr request
= NULL
, node
;
8097 xmlNsPtr isds_ns
= NULL
;
8098 xmlChar
*string
= NULL
;
8100 xmlDocPtr response
= NULL
;
8101 xmlXPathContextPtr xpath_ctx
= NULL
;
8102 xmlXPathObjectPtr result
= NULL
;
8104 const xmlChar
*codes
[] = {
8112 const char *meanings
[] = {
8113 "Insufficient priviledges for the box",
8114 "The box does not exist",
8115 "Date is too long (history is not available after 15 months)",
8116 "Interval is too long (limit is 3 months)",
8119 const isds_error errors
[] = {
8126 struct code_map_isds_error map
= {
8128 .meanings
= meanings
,
8133 if (!context
) return IE_INVALID_CONTEXT
;
8134 zfree(context
->long_message
);
8136 /* Free output argument */
8137 if (NULL
!= credit
) *credit
= 0;
8138 if (NULL
!= email
) zfree(*email
);
8139 isds_list_free(history
);
8141 if (NULL
== box_id
) return IE_INVAL
;
8144 /* Check if connection is established */
8145 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8147 box_id_locale
= _isds_utf82locale((char*)box_id
);
8148 if (NULL
== box_id_locale
) {
8154 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8155 if (NULL
== request
) {
8156 isds_printf_message(context
,
8157 _("Could not build DataBoxCreditInfo request for %s box"),
8162 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8164 isds_log_message(context
, _("Could not create ISDS name space"));
8168 xmlSetNs(request
, isds_ns
);
8170 /* Add mandatory XSD:tIdDbInput child */
8171 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8172 /* Add mandatory dates elements with optional values */
8174 err
= tm2datestring(from_date
, &string
);
8176 isds_log_message(context
,
8177 _("Could not convert `from_date' argument to ISO date "
8181 INSERT_STRING(request
, "ciFromDate", string
);
8184 INSERT_STRING(request
, "ciFromDate", NULL
);
8187 err
= tm2datestring(to_date
, &string
);
8189 isds_log_message(context
,
8190 _("Could not convert `to_date' argument to ISO date "
8194 INSERT_STRING(request
, "ciTodate", string
);
8197 INSERT_STRING(request
, "ciTodate", NULL
);
8200 /* Send request and check response*/
8201 err
= send_destroy_request_check_response(context
,
8202 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8203 &request
, &response
, NULL
, &map
);
8204 if (err
) goto leave
;
8208 /* Set context to the root */
8209 xpath_ctx
= xmlXPathNewContext(response
);
8214 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8218 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8224 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8225 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8229 if (result
->nodesetval
->nodeNr
> 1) {
8230 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8234 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8235 xmlXPathFreeObject(result
); result
= NULL
;
8237 /* Extract common data */
8238 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8239 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8241 /* Extract records */
8242 if (NULL
== history
) goto leave
;
8243 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8249 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8250 struct isds_list
*prev_item
= NULL
;
8252 /* Iterate over all records */
8253 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8254 struct isds_list
*item
;
8256 /* Prepare structure */
8257 item
= calloc(1, sizeof(*item
));
8262 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8263 if (i
== 0) *history
= item
;
8264 else prev_item
->next
= item
;
8268 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8269 err
= extract_CiRecord(context
,
8270 (struct isds_credit_event
**) (&item
->data
),
8272 if (err
) goto leave
;
8278 isds_log(ILF_ISDS
, ILL_DEBUG
,
8279 _("DataBoxCreditInfo request processed by server successfully.\n"));
8282 isds_list_free(history
);
8283 if (NULL
!= email
) zfree(*email
)
8286 free(box_id_locale
);
8287 xmlXPathFreeObject(result
);
8288 xmlXPathFreeContext(xpath_ctx
);
8289 xmlFreeDoc(response
);
8291 #else /* not HAVE_LIBCURL */
8299 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8300 * code, destroy response and log success.
8301 * @context is ISDS session context.
8302 * @service_name is name of SERVICE_DB_MANIPULATION service
8303 * @box_id is UTF-8 encoded box identifier as zero terminated string
8304 * @approval is optional external approval of box manipulation
8305 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8306 * NULL, if you don't care. */
8307 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8308 struct isds_ctx
*context
, const xmlChar
*service_name
,
8309 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8310 xmlChar
**refnumber
) {
8311 isds_error err
= IE_SUCCESS
;
8313 xmlDocPtr response
= NULL
;
8316 if (!context
) return IE_INVALID_CONTEXT
;
8317 zfree(context
->long_message
);
8318 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8321 /* Check if connection is established */
8322 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8324 /* Do request and check for success */
8325 err
= build_send_dbid_request_check_response(context
,
8326 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8327 &response
, refnumber
);
8328 xmlFreeDoc(response
);
8331 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8332 isds_log(ILF_ISDS
, ILL_DEBUG
,
8333 _("%s request processed by server successfully.\n"),
8334 service_name_locale
);
8335 free(service_name_locale
);
8337 #else /* not HAVE_LIBCURL */
8345 /* Switch box into state where box can receive commercial messages (off by
8347 * @context is ISDS session context.
8348 * @box_id is UTF-8 encoded box identifier as zero terminated string
8349 * @allow is true for enable, false for disable commercial messages income
8350 * @approval is optional external approval of box manipulation
8351 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8352 * NULL, if you don't care. */
8353 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8354 const char *box_id
, const _Bool allow
,
8355 const struct isds_approval
*approval
, char **refnumber
) {
8356 return build_send_manipulationdbid_request_check_drop_response(context
,
8357 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8358 BAD_CAST
"ClearOpenAddressing",
8359 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8363 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8364 * message acceptance). This is just a box permission. Sender must apply
8365 * such role by sending each message.
8366 * @context is ISDS session context.
8367 * @box_id is UTF-8 encoded box identifier as zero terminated string
8368 * @allow is true for enable, false for disable OVM role permission
8369 * @approval is optional external approval of box manipulation
8370 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8371 * NULL, if you don't care. */
8372 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8373 const char *box_id
, const _Bool allow
,
8374 const struct isds_approval
*approval
, char **refnumber
) {
8375 return build_send_manipulationdbid_request_check_drop_response(context
,
8376 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8377 BAD_CAST
"ClearEffectiveOVM",
8378 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8382 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8383 * code, destroy response and log success.
8384 * @context is ISDS session context.
8385 * @service_name is name of SERVICE_DB_MANIPULATION service
8386 * @owner is structure describing box
8387 * @approval is optional external approval of box manipulation
8388 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8389 * NULL, if you don't care. */
8390 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8391 struct isds_ctx
*context
, const xmlChar
*service_name
,
8392 const struct isds_DbOwnerInfo
*owner
,
8393 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8394 isds_error err
= IE_SUCCESS
;
8396 char *service_name_locale
= NULL
;
8397 xmlNodePtr request
= NULL
, db_owner_info
;
8398 xmlNsPtr isds_ns
= NULL
;
8402 if (!context
) return IE_INVALID_CONTEXT
;
8403 zfree(context
->long_message
);
8404 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8407 service_name_locale
= _isds_utf82locale((char*)service_name
);
8408 if (!service_name_locale
) {
8414 request
= xmlNewNode(NULL
, service_name
);
8416 isds_printf_message(context
,
8417 _("Could not build %s request"), service_name_locale
);
8421 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8423 isds_log_message(context
, _("Could not create ISDS name space"));
8427 xmlSetNs(request
, isds_ns
);
8430 /* Add XSD:tOwnerInfoInput child*/
8431 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8432 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8433 if (err
) goto leave
;
8435 /* Add XSD:gExtApproval*/
8436 err
= insert_GExtApproval(context
, approval
, request
);
8437 if (err
) goto leave
;
8439 /* Send it to server and process response */
8440 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8441 service_name
, &request
, refnumber
);
8444 xmlFreeNode(request
);
8445 free(service_name_locale
);
8446 #else /* not HAVE_LIBCURL */
8454 /* Switch box accessibility state on request of box owner.
8455 * Despite the name, owner must do the request off-line. This function is
8456 * designed for such off-line meeting points (e.g. Czech POINT).
8457 * @context is ISDS session context.
8458 * @box identifies box to switch accessibility state.
8459 * @allow is true for making accessible, false to disallow access.
8460 * @approval is optional external approval of box manipulation
8461 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8462 * NULL, if you don't care. */
8463 isds_error
isds_switch_box_accessibility_on_owner_request(
8464 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8465 const _Bool allow
, const struct isds_approval
*approval
,
8467 return build_send_manipulationdbowner_request_check_drop_response(context
,
8468 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8469 BAD_CAST
"DisableOwnDataBox",
8470 box
, approval
, (xmlChar
**) refnumber
);
8474 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8476 * @context is ISDS session context.
8477 * @box identifies box to switch accessibility state.
8478 * @since is date since accessibility has been denied. This can be past too.
8479 * Only tm_year, tm_mon and tm_mday carry sane value.
8480 * @approval is optional external approval of box manipulation
8481 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8482 * NULL, if you don't care. */
8483 isds_error
isds_disable_box_accessibility_externaly(
8484 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8485 const struct tm
*since
, const struct isds_approval
*approval
,
8487 isds_error err
= IE_SUCCESS
;
8489 char *service_name_locale
= NULL
;
8490 xmlNodePtr request
= NULL
, node
;
8491 xmlNsPtr isds_ns
= NULL
;
8492 xmlChar
*string
= NULL
;
8496 if (!context
) return IE_INVALID_CONTEXT
;
8497 zfree(context
->long_message
);
8498 if (!box
|| !since
) return IE_INVAL
;
8502 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8504 isds_printf_message(context
,
8505 _("Could not build %s request"), "DisableDataBoxExternally");
8509 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8511 isds_log_message(context
, _("Could not create ISDS name space"));
8515 xmlSetNs(request
, isds_ns
);
8518 /* Add @box identification */
8519 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8520 err
= insert_DbOwnerInfo(context
, box
, node
);
8521 if (err
) goto leave
;
8523 /* Add @since date */
8524 err
= tm2datestring(since
, &string
);
8526 isds_log_message(context
,
8527 _("Could not convert `since' argument to ISO date string"));
8530 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8534 err
= insert_GExtApproval(context
, approval
, request
);
8535 if (err
) goto leave
;
8537 /* Send it to server and process response */
8538 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8539 BAD_CAST
"DisableDataBoxExternally", &request
,
8540 (xmlChar
**) refnumber
);
8544 xmlFreeNode(request
);
8545 free(service_name_locale
);
8546 #else /* not HAVE_LIBCURL */
8555 /* Insert struct isds_message data (envelope (recipient data optional) and
8556 * documents into XML tree
8557 * @context is session context
8558 * @outgoing_message is libisds structure with message data
8559 * @create_message is XML CreateMessage or CreateMultipleMessage element
8560 * @process_recipient true for recipient data serialization, false for no
8562 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8563 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8564 const _Bool process_recipient
) {
8566 isds_error err
= IE_SUCCESS
;
8567 xmlNodePtr envelope
, dm_files
, node
;
8568 xmlChar
*string
= NULL
;
8570 if (!context
) return IE_INVALID_CONTEXT
;
8571 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8574 /* Build envelope */
8575 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8577 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8578 "%s element"), create_message
->name
);
8582 if (!outgoing_message
->envelope
) {
8583 isds_log_message(context
, _("Outgoing message is missing envelope"));
8588 /* Insert optional message type */
8589 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8591 if (err
) goto leave
;
8593 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8594 outgoing_message
->envelope
->dmSenderOrgUnit
);
8595 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8596 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8598 if (process_recipient
) {
8599 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8600 isds_log_message(context
,
8601 _("Outgoing message is missing recipient box identifier"));
8605 INSERT_STRING(envelope
, "dbIDRecipient",
8606 outgoing_message
->envelope
->dbIDRecipient
);
8608 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8609 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8610 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8611 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8612 INSERT_STRING(envelope
, "dmToHands",
8613 outgoing_message
->envelope
->dmToHands
);
8616 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8618 INSERT_STRING(envelope
, "dmAnnotation",
8619 outgoing_message
->envelope
->dmAnnotation
);
8621 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8622 0, 50, "dmRecipientRefNumber");
8623 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8624 outgoing_message
->envelope
->dmRecipientRefNumber
);
8626 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8627 0, 50, "dmSenderRefNumber");
8628 INSERT_STRING(envelope
, "dmSenderRefNumber",
8629 outgoing_message
->envelope
->dmSenderRefNumber
);
8631 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8632 0, 50, "dmRecipientIdent");
8633 INSERT_STRING(envelope
, "dmRecipientIdent",
8634 outgoing_message
->envelope
->dmRecipientIdent
);
8636 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8637 0, 50, "dmSenderIdent");
8638 INSERT_STRING(envelope
, "dmSenderIdent",
8639 outgoing_message
->envelope
->dmSenderIdent
);
8641 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8642 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8643 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8644 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8645 INSERT_STRING(envelope
, "dmLegalTitleSect",
8646 outgoing_message
->envelope
->dmLegalTitleSect
);
8647 INSERT_STRING(envelope
, "dmLegalTitlePar",
8648 outgoing_message
->envelope
->dmLegalTitlePar
);
8649 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8650 outgoing_message
->envelope
->dmLegalTitlePoint
);
8652 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8653 outgoing_message
->envelope
->dmPersonalDelivery
);
8654 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8655 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8657 /* ???: Should we require value for dbEffectiveOVM sender?
8658 * ISDS has default as true */
8659 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8660 INSERT_BOOLEAN(envelope
, "dmOVM",
8661 outgoing_message
->envelope
->dmPublishOwnID
);
8664 /* Append dmFiles */
8665 if (!outgoing_message
->documents
) {
8666 isds_log_message(context
,
8667 _("Outgoing message is missing list of documents"));
8671 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8673 isds_printf_message(context
, _("Could not add dmFiles child to "
8674 "%s element"), create_message
->name
);
8679 /* Check for document hierarchy */
8680 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8681 if (err
) goto leave
;
8683 /* Process each document */
8684 for (struct isds_list
*item
=
8685 (struct isds_list
*) outgoing_message
->documents
;
8686 item
; item
= item
->next
) {
8688 isds_log_message(context
,
8689 _("List of documents contains empty item"));
8693 /* FIXME: Check for dmFileMetaType and for document references.
8694 * Only first document can be of MAIN type */
8695 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8698 if (err
) goto leave
;
8705 #endif /* HAVE_LIBCURL */
8708 /* Send a message via ISDS to a recipient
8709 * @context is session context
8710 * @outgoing_message is message to send; Some members are mandatory (like
8711 * dbIDRecipient), some are optional and some are irrelevant (especially data
8712 * about sender). Included pointer to isds_list documents must contain at
8713 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8714 * members will be filled with valid data from ISDS. Exact list of write
8715 * members is subject to change. Currently dmID is changed.
8716 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8717 isds_error
isds_send_message(struct isds_ctx
*context
,
8718 struct isds_message
*outgoing_message
) {
8720 isds_error err
= IE_SUCCESS
;
8722 xmlNsPtr isds_ns
= NULL
;
8723 xmlNodePtr request
= NULL
;
8724 xmlDocPtr response
= NULL
;
8725 xmlChar
*code
= NULL
, *message
= NULL
;
8726 xmlXPathContextPtr xpath_ctx
= NULL
;
8727 xmlXPathObjectPtr result
= NULL
;
8728 /*_Bool message_is_complete = 0;*/
8731 if (!context
) return IE_INVALID_CONTEXT
;
8732 zfree(context
->long_message
);
8733 if (!outgoing_message
) return IE_INVAL
;
8736 /* Check if connection is established
8737 * TODO: This check should be done downstairs. */
8738 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8741 /* Build CreateMessage request */
8742 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8744 isds_log_message(context
,
8745 _("Could not build CreateMessage request"));
8748 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8750 isds_log_message(context
, _("Could not create ISDS name space"));
8751 xmlFreeNode(request
);
8754 xmlSetNs(request
, isds_ns
);
8756 /* Append envelope and files */
8757 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8758 if (err
) goto leave
;
8761 /* Signal we can serialize message since now */
8762 /*message_is_complete = 1;*/
8765 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8768 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8770 /* Don't' destroy request, we want to provide it to application later */
8773 isds_log(ILF_ISDS
, ILL_DEBUG
,
8774 _("Processing ISDS response on CreateMessage "
8775 "request failed\n"));
8779 /* Check for response status */
8780 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8781 &code
, &message
, NULL
);
8783 isds_log(ILF_ISDS
, ILL_DEBUG
,
8784 _("ISDS response on CreateMessage request "
8785 "is missing status\n"));
8789 /* Request processed, but refused by server or server failed */
8790 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8791 char *box_id_locale
=
8792 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8793 char *code_locale
= _isds_utf82locale((char*)code
);
8794 char *message_locale
= _isds_utf82locale((char*)message
);
8795 isds_log(ILF_ISDS
, ILL_DEBUG
,
8796 _("Server did not accept message for %s on CreateMessage "
8797 "request (code=%s, message=%s)\n"),
8798 box_id_locale
, code_locale
, message_locale
);
8799 isds_log_message(context
, message_locale
);
8800 free(box_id_locale
);
8802 free(message_locale
);
8809 xpath_ctx
= xmlXPathNewContext(response
);
8814 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8818 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8824 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8825 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8829 if (result
->nodesetval
->nodeNr
> 1) {
8830 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8834 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8835 xmlXPathFreeObject(result
); result
= NULL
;
8837 if (outgoing_message
->envelope
->dmID
) {
8838 free(outgoing_message
->envelope
->dmID
);
8839 outgoing_message
->envelope
->dmID
= NULL
;
8841 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8842 if (!outgoing_message
->envelope
->dmID
) {
8843 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8844 "but did not return assigned message ID\n"));
8848 /* TODO: Serialize message into structure member raw */
8849 /* XXX: Each web service transport message in different format.
8850 * Therefore it's not possible to save them directly.
8851 * To save them, one must figure out common format.
8852 * We can leave it on application, or we can implement the ESS format. */
8853 /*if (message_is_complete) {
8854 if (outgoing_message->envelope->dmID) {
8856 /* Add assigned message ID as first child*/
8857 /*xmlNodePtr dmid_text = xmlNewText(
8858 (xmlChar *) outgoing_message->envelope->dmID);
8859 if (!dmid_text) goto serialization_failed;
8861 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8863 if (!dmid_element) {
8864 xmlFreeNode(dmid_text);
8865 goto serialization_failed;
8868 xmlNodePtr dmid_element_with_text =
8869 xmlAddChild(dmid_element, dmid_text);
8870 if (!dmid_element_with_text) {
8871 xmlFreeNode(dmid_element);
8872 xmlFreeNode(dmid_text);
8873 goto serialization_failed;
8876 node = xmlAddPrevSibling(envelope->childern,
8877 dmid_element_with_text);
8879 xmlFreeNodeList(dmid_element_with_text);
8880 goto serialization_failed;
8884 /* Serialize message with ID into raw */
8885 /*buffer = serialize_element(envelope)*/
8888 serialization_failed:
8893 xmlXPathFreeObject(result
);
8894 xmlXPathFreeContext(xpath_ctx
);
8898 xmlFreeDoc(response
);
8899 xmlFreeNode(request
);
8902 isds_log(ILF_ISDS
, ILL_DEBUG
,
8903 _("CreateMessage request processed by server "
8904 "successfully.\n"));
8905 #else /* not HAVE_LIBCURL */
8913 /* Send a message via ISDS to a multiple recipients
8914 * @context is session context
8915 * @outgoing_message is message to send; Some members are mandatory,
8916 * some are optional and some are irrelevant (especially data
8917 * about sender). Data about recipient will be substituted by ISDS from
8918 * @copies. Included pointer to isds_list documents must
8919 * contain at least one document of FILEMETATYPE_MAIN.
8920 * @copies is list of isds_message_copy structures addressing all desired
8921 * recipients. This is read-write structure, some members will be filled with
8922 * valid data from ISDS (message IDs, error codes, error descriptions).
8924 * ISDS_SUCCESS if all messages have been sent
8925 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8926 * succeeded messages can be identified by copies->data->error),
8927 * or other error code if something other goes wrong. */
8928 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8929 const struct isds_message
*outgoing_message
,
8930 struct isds_list
*copies
) {
8932 isds_error err
= IE_SUCCESS
;
8934 isds_error append_err
;
8935 xmlNsPtr isds_ns
= NULL
;
8936 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8937 struct isds_list
*item
;
8938 struct isds_message_copy
*copy
;
8939 xmlDocPtr response
= NULL
;
8940 xmlChar
*code
= NULL
, *message
= NULL
;
8941 xmlXPathContextPtr xpath_ctx
= NULL
;
8942 xmlXPathObjectPtr result
= NULL
;
8943 xmlChar
*string
= NULL
;
8947 if (!context
) return IE_INVALID_CONTEXT
;
8948 zfree(context
->long_message
);
8949 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8952 /* Check if connection is established
8953 * TODO: This check should be done downstairs. */
8954 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8957 /* Build CreateMultipleMessage request */
8958 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8960 isds_log_message(context
,
8961 _("Could not build CreateMultipleMessage request"));
8964 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8966 isds_log_message(context
, _("Could not create ISDS name space"));
8967 xmlFreeNode(request
);
8970 xmlSetNs(request
, isds_ns
);
8973 /* Build recipients */
8974 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8976 isds_log_message(context
, _("Could not add dmRecipients child to "
8977 "CreateMultipleMessage element"));
8978 xmlFreeNode(request
);
8982 /* Insert each recipient */
8983 for (item
= copies
; item
; item
= item
->next
) {
8984 copy
= (struct isds_message_copy
*) item
->data
;
8986 isds_log_message(context
,
8987 _("`copies' list item contains empty data"));
8992 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8994 isds_log_message(context
, _("Could not add dmRecipient child to "
8995 "dmRecipients element"));
9000 if (!copy
->dbIDRecipient
) {
9001 isds_log_message(context
,
9002 _("Message copy is missing recipient box identifier"));
9006 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9007 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9008 copy
->dmRecipientOrgUnit
);
9009 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9010 copy
->dmRecipientOrgUnitNum
, string
);
9011 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9014 /* Append envelope and files */
9015 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9016 if (err
) goto leave
;
9019 isds_log(ILF_ISDS
, ILL_DEBUG
,
9020 _("Sending CreateMultipleMessage request to ISDS\n"));
9023 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9025 isds_log(ILF_ISDS
, ILL_DEBUG
,
9026 _("Processing ISDS response on CreateMultipleMessage "
9027 "request failed\n"));
9031 /* Check for response status */
9032 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9033 &code
, &message
, NULL
);
9035 isds_log(ILF_ISDS
, ILL_DEBUG
,
9036 _("ISDS response on CreateMultipleMessage request "
9037 "is missing status\n"));
9041 /* Request processed, but some copies failed */
9042 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9043 char *box_id_locale
=
9044 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9045 char *code_locale
= _isds_utf82locale((char*)code
);
9046 char *message_locale
= _isds_utf82locale((char*)message
);
9047 isds_log(ILF_ISDS
, ILL_DEBUG
,
9048 _("Server did accept message for multiple recipients "
9049 "on CreateMultipleMessage request but delivery to "
9050 "some of them failed (code=%s, message=%s)\n"),
9051 box_id_locale
, code_locale
, message_locale
);
9052 isds_log_message(context
, message_locale
);
9053 free(box_id_locale
);
9055 free(message_locale
);
9056 err
= IE_PARTIAL_SUCCESS
;
9059 /* Request refused by server as whole */
9060 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9061 char *box_id_locale
=
9062 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9063 char *code_locale
= _isds_utf82locale((char*)code
);
9064 char *message_locale
= _isds_utf82locale((char*)message
);
9065 isds_log(ILF_ISDS
, ILL_DEBUG
,
9066 _("Server did not accept message for multiple recipients "
9067 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9068 box_id_locale
, code_locale
, message_locale
);
9069 isds_log_message(context
, message_locale
);
9070 free(box_id_locale
);
9072 free(message_locale
);
9079 xpath_ctx
= xmlXPathNewContext(response
);
9084 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9088 result
= xmlXPathEvalExpression(
9089 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9090 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9096 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9097 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9102 /* Extract message ID and delivery status for each copy */
9103 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9104 item
= item
->next
, i
++) {
9105 copy
= (struct isds_message_copy
*) item
->data
;
9106 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9108 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9114 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9115 isds_printf_message(context
, _("ISDS returned unexpected number of "
9116 "message copy delivery states: %d"),
9117 result
->nodesetval
->nodeNr
);
9126 xmlXPathFreeObject(result
);
9127 xmlXPathFreeContext(xpath_ctx
);
9131 xmlFreeDoc(response
);
9132 xmlFreeNode(request
);
9135 isds_log(ILF_ISDS
, ILL_DEBUG
,
9136 _("CreateMultipleMessageResponse request processed by server "
9137 "successfully.\n"));
9138 #else /* not HAVE_LIBCURL */
9146 /* Get list of messages. This is common core for getting sent or received
9148 * Any criterion argument can be NULL, if you don't care about it.
9149 * @context is session context. Must not be NULL.
9150 * @outgoing_direction is true if you want list of outgoing messages,
9151 * it's false if you want incoming messages.
9152 * @from_time is minimal time and date of message sending inclusive.
9153 * @to_time is maximal time and date of message sending inclusive
9154 * @organization_unit_number is number of sender/recipient respectively.
9155 * @status_filter is bit field of isds_message_status values. Use special
9156 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9157 * all values, you can use bit-wise arithmetic if you want.)
9158 * @offset is index of first message we are interested in. First message is 1.
9159 * Set to 0 (or 1) if you don't care.
9160 * @number is maximal length of list you want to get as input value, outputs
9161 * number of messages matching these criteria. Can be NULL if you don't care
9162 * (applies to output value either).
9163 * @messages is automatically reallocated list of isds_message's. Be ware that
9164 * it returns only brief overview (envelope and some other fields) about each
9165 * message, not the complete message. FIXME: Specify exact fields.
9166 * The list is sorted by delivery time in ascending order.
9167 * Use NULL if you don't care about don't need the data (useful if you want to
9168 * know only the @number). If you provide &NULL, list will be allocated on
9169 * heap, if you provide pointer to non-NULL, list will be freed automatically
9170 * at first. Also in case of error the list will be NULLed.
9171 * @return IE_SUCCESS or appropriate error code. */
9172 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9173 _Bool outgoing_direction
,
9174 const struct timeval
*from_time
, const struct timeval
*to_time
,
9175 const long int *organization_unit_number
,
9176 const unsigned int status_filter
,
9177 const unsigned long int offset
, unsigned long int *number
,
9178 struct isds_list
**messages
) {
9180 isds_error err
= IE_SUCCESS
;
9182 xmlNsPtr isds_ns
= NULL
;
9183 xmlNodePtr request
= NULL
, node
;
9184 xmlDocPtr response
= NULL
;
9185 xmlChar
*code
= NULL
, *message
= NULL
;
9186 xmlXPathContextPtr xpath_ctx
= NULL
;
9187 xmlXPathObjectPtr result
= NULL
;
9188 xmlChar
*string
= NULL
;
9192 if (!context
) return IE_INVALID_CONTEXT
;
9193 zfree(context
->long_message
);
9195 /* Free former message list if any */
9196 if (messages
) isds_list_free(messages
);
9199 /* Check if connection is established
9200 * TODO: This check should be done downstairs. */
9201 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9203 /* Build GetListOf*Messages request */
9204 request
= xmlNewNode(NULL
,
9205 (outgoing_direction
) ?
9206 BAD_CAST
"GetListOfSentMessages" :
9207 BAD_CAST
"GetListOfReceivedMessages"
9210 isds_log_message(context
,
9211 (outgoing_direction
) ?
9212 _("Could not build GetListOfSentMessages request") :
9213 _("Could not build GetListOfReceivedMessages request")
9217 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9219 isds_log_message(context
, _("Could not create ISDS name space"));
9220 xmlFreeNode(request
);
9223 xmlSetNs(request
, isds_ns
);
9227 err
= timeval2timestring(from_time
, &string
);
9228 if (err
) goto leave
;
9230 INSERT_STRING(request
, "dmFromTime", string
);
9231 free(string
); string
= NULL
;
9234 err
= timeval2timestring(to_time
, &string
);
9235 if (err
) goto leave
;
9237 INSERT_STRING(request
, "dmToTime", string
);
9238 free(string
); string
= NULL
;
9240 if (outgoing_direction
) {
9241 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9242 organization_unit_number
, string
);
9244 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9245 organization_unit_number
, string
);
9248 if (status_filter
> MESSAGESTATE_ANY
) {
9249 isds_printf_message(context
,
9250 _("Invalid message state filter value: %ld"), status_filter
);
9254 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9257 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9259 INSERT_STRING(request
, "dmOffset", "1");
9262 /* number 0 means no limit */
9263 if (number
&& *number
== 0) {
9264 INSERT_STRING(request
, "dmLimit", NULL
);
9266 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9270 isds_log(ILF_ISDS
, ILL_DEBUG
,
9271 (outgoing_direction
) ?
9272 _("Sending GetListOfSentMessages request to ISDS\n") :
9273 _("Sending GetListOfReceivedMessages request to ISDS\n")
9277 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9278 xmlFreeNode(request
); request
= NULL
;
9281 isds_log(ILF_ISDS
, ILL_DEBUG
,
9282 (outgoing_direction
) ?
9283 _("Processing ISDS response on GetListOfSentMessages "
9284 "request failed\n") :
9285 _("Processing ISDS response on GetListOfReceivedMessages "
9291 /* Check for response status */
9292 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9293 &code
, &message
, NULL
);
9295 isds_log(ILF_ISDS
, ILL_DEBUG
,
9296 (outgoing_direction
) ?
9297 _("ISDS response on GetListOfSentMessages request "
9298 "is missing status\n") :
9299 _("ISDS response on GetListOfReceivedMessages request "
9300 "is missing status\n")
9305 /* Request processed, but nothing found */
9306 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9307 char *code_locale
= _isds_utf82locale((char*)code
);
9308 char *message_locale
= _isds_utf82locale((char*)message
);
9309 isds_log(ILF_ISDS
, ILL_DEBUG
,
9310 (outgoing_direction
) ?
9311 _("Server refused GetListOfSentMessages request "
9312 "(code=%s, message=%s)\n") :
9313 _("Server refused GetListOfReceivedMessages request "
9314 "(code=%s, message=%s)\n"),
9315 code_locale
, message_locale
);
9316 isds_log_message(context
, message_locale
);
9318 free(message_locale
);
9325 xpath_ctx
= xmlXPathNewContext(response
);
9330 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9334 result
= xmlXPathEvalExpression(
9335 (outgoing_direction
) ?
9336 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9337 "isds:dmRecords/isds:dmRecord" :
9338 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9339 "isds:dmRecords/isds:dmRecord",
9346 /* Fill output arguments in */
9347 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9348 struct isds_envelope
*envelope
;
9349 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9351 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9352 /* Create new message */
9353 item
= calloc(1, sizeof(*item
));
9358 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9359 item
->data
= calloc(1, sizeof(struct isds_message
));
9361 isds_list_free(&item
);
9366 /* Extract envelope data */
9367 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9369 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9371 isds_list_free(&item
);
9375 /* Attach extracted envelope */
9376 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9378 /* Append new message into the list */
9380 *messages
= last_item
= item
;
9382 last_item
->next
= item
;
9387 if (number
) *number
= count
;
9391 isds_list_free(messages
);
9395 xmlXPathFreeObject(result
);
9396 xmlXPathFreeContext(xpath_ctx
);
9400 xmlFreeDoc(response
);
9401 xmlFreeNode(request
);
9404 isds_log(ILF_ISDS
, ILL_DEBUG
,
9405 (outgoing_direction
) ?
9406 _("GetListOfSentMessages request processed by server "
9407 "successfully.\n") :
9408 _("GetListOfReceivedMessages request processed by server "
9411 #else /* not HAVE_LIBCURL */
9418 /* Get list of outgoing (already sent) messages.
9419 * Any criterion argument can be NULL, if you don't care about it.
9420 * @context is session context. Must not be NULL.
9421 * @from_time is minimal time and date of message sending inclusive.
9422 * @to_time is maximal time and date of message sending inclusive
9423 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9424 * @status_filter is bit field of isds_message_status values. Use special
9425 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9426 * all values, you can use bit-wise arithmetic if you want.)
9427 * @offset is index of first message we are interested in. First message is 1.
9428 * Set to 0 (or 1) if you don't care.
9429 * @number is maximal length of list you want to get as input value, outputs
9430 * number of messages matching these criteria. Can be NULL if you don't care
9431 * (applies to output value either).
9432 * @messages is automatically reallocated list of isds_message's. Be ware that
9433 * it returns only brief overview (envelope and some other fields) about each
9434 * message, not the complete message. FIXME: Specify exact fields.
9435 * The list is sorted by delivery time in ascending order.
9436 * Use NULL if you don't care about the meta data (useful if you want to know
9437 * only the @number). If you provide &NULL, list will be allocated on heap,
9438 * if you provide pointer to non-NULL, list will be freed automatically at
9439 * first. Also in case of error the list will be NULLed.
9440 * @return IE_SUCCESS or appropriate error code. */
9441 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9442 const struct timeval
*from_time
, const struct timeval
*to_time
,
9443 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9444 const unsigned long int offset
, unsigned long int *number
,
9445 struct isds_list
**messages
) {
9447 return isds_get_list_of_messages(
9449 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9455 /* Get list of incoming (addressed to you) messages.
9456 * Any criterion argument can be NULL, if you don't care about it.
9457 * @context is session context. Must not be NULL.
9458 * @from_time is minimal time and date of message sending inclusive.
9459 * @to_time is maximal time and date of message sending inclusive
9460 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9461 * @status_filter is bit field of isds_message_status values. Use special
9462 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9463 * all values, you can use bit-wise arithmetic if you want.)
9464 * @offset is index of first message we are interested in. First message is 1.
9465 * Set to 0 (or 1) if you don't care.
9466 * @number is maximal length of list you want to get as input value, outputs
9467 * number of messages matching these criteria. Can be NULL if you don't care
9468 * (applies to output value either).
9469 * @messages is automatically reallocated list of isds_message's. Be ware that
9470 * it returns only brief overview (envelope and some other fields) about each
9471 * message, not the complete message. FIXME: Specify exact fields.
9472 * Use NULL if you don't care about the meta data (useful if you want to know
9473 * only the @number). If you provide &NULL, list will be allocated on heap,
9474 * if you provide pointer to non-NULL, list will be freed automatically at
9475 * first. Also in case of error the list will be NULLed.
9476 * @return IE_SUCCESS or appropriate error code. */
9477 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9478 const struct timeval
*from_time
, const struct timeval
*to_time
,
9479 const long int *dmRecipientOrgUnitNum
,
9480 const unsigned int status_filter
,
9481 const unsigned long int offset
, unsigned long int *number
,
9482 struct isds_list
**messages
) {
9484 return isds_get_list_of_messages(
9486 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9492 /* Get list of sent message state changes.
9493 * Any criterion argument can be NULL, if you don't care about it.
9494 * @context is session context. Must not be NULL.
9495 * @from_time is minimal time and date of status changes inclusive
9496 * @to_time is maximal time and date of status changes inclusive
9497 * @changed_states is automatically reallocated list of
9498 * isds_message_status_change's. If you provide &NULL, list will be allocated
9499 * on heap, if you provide pointer to non-NULL, list will be freed
9500 * automatically at first. Also in case of error the list will be NULLed.
9501 * XXX: The list item ordering is not specified.
9502 * XXX: Server provides only `recent' changes.
9503 * @return IE_SUCCESS or appropriate error code. */
9504 isds_error
isds_get_list_of_sent_message_state_changes(
9505 struct isds_ctx
*context
,
9506 const struct timeval
*from_time
, const struct timeval
*to_time
,
9507 struct isds_list
**changed_states
) {
9509 isds_error err
= IE_SUCCESS
;
9511 xmlNsPtr isds_ns
= NULL
;
9512 xmlNodePtr request
= NULL
, node
;
9513 xmlDocPtr response
= NULL
;
9514 xmlXPathContextPtr xpath_ctx
= NULL
;
9515 xmlXPathObjectPtr result
= NULL
;
9516 xmlChar
*string
= NULL
;
9520 if (!context
) return IE_INVALID_CONTEXT
;
9521 zfree(context
->long_message
);
9523 /* Free former message list if any */
9524 isds_list_free(changed_states
);
9527 /* Check if connection is established
9528 * TODO: This check should be done downstairs. */
9529 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9531 /* Build GetMessageStateChanges request */
9532 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9534 isds_log_message(context
,
9535 _("Could not build GetMessageStateChanges request"));
9538 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9540 isds_log_message(context
, _("Could not create ISDS name space"));
9541 xmlFreeNode(request
);
9544 xmlSetNs(request
, isds_ns
);
9548 err
= timeval2timestring(from_time
, &string
);
9549 if (err
) goto leave
;
9551 INSERT_STRING(request
, "dmFromTime", string
);
9555 err
= timeval2timestring(to_time
, &string
);
9556 if (err
) goto leave
;
9558 INSERT_STRING(request
, "dmToTime", string
);
9563 err
= send_destroy_request_check_response(context
,
9564 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9565 &response
, NULL
, NULL
);
9566 if (err
) goto leave
;
9570 xpath_ctx
= xmlXPathNewContext(response
);
9575 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9579 result
= xmlXPathEvalExpression(
9580 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9581 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9587 /* Fill output arguments in */
9588 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9589 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9591 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9592 /* Create new status change */
9593 item
= calloc(1, sizeof(*item
));
9599 (void(*)(void**)) &isds_message_status_change_free
;
9601 /* Extract message status change */
9602 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9603 err
= extract_StateChangesRecord(context
,
9604 (struct isds_message_status_change
**) &item
->data
,
9607 isds_list_free(&item
);
9611 /* Append new message status change into the list */
9612 if (!*changed_states
) {
9613 *changed_states
= last_item
= item
;
9615 last_item
->next
= item
;
9623 isds_list_free(changed_states
);
9627 xmlXPathFreeObject(result
);
9628 xmlXPathFreeContext(xpath_ctx
);
9629 xmlFreeDoc(response
);
9630 xmlFreeNode(request
);
9633 isds_log(ILF_ISDS
, ILL_DEBUG
,
9634 _("GetMessageStateChanges request processed by server "
9635 "successfully.\n"));
9636 #else /* not HAVE_LIBCURL */
9644 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9646 * @context is session context
9647 * @service is ISDS WS service handler
9648 * @service_name is name of SERVICE_DM_OPERATIONS
9649 * @message_id is message ID to send as service argument to ISDS
9650 * @response is reallocated server SOAP body response as XML document
9651 * @raw_response is reallocated bit stream with response body. Use
9652 * NULL if you don't care
9653 * @raw_response_length is size of @raw_response in bytes
9654 * @code is reallocated ISDS status code
9655 * @status_message is reallocated ISDS status message
9656 * @return error coded from lower layer, context message will be set up
9658 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9659 const isds_service service
, const xmlChar
*service_name
,
9660 const char *message_id
,
9661 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9662 xmlChar
**code
, xmlChar
**status_message
) {
9664 isds_error err
= IE_SUCCESS
;
9665 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9666 xmlNodePtr request
= NULL
, node
;
9667 xmlNsPtr isds_ns
= NULL
;
9669 if (!context
) return IE_INVALID_CONTEXT
;
9670 if (!service_name
|| !message_id
) return IE_INVAL
;
9671 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9672 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9674 /* Free output argument */
9675 xmlFreeDoc(*response
); *response
= NULL
;
9676 if (raw_response
) zfree(*raw_response
);
9678 zfree(*status_message
);
9681 /* Check if connection is established
9682 * TODO: This check should be done downstairs. */
9683 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9685 service_name_locale
= _isds_utf82locale((char*)service_name
);
9686 message_id_locale
= _isds_utf82locale(message_id
);
9687 if (!service_name_locale
|| !message_id_locale
) {
9693 request
= xmlNewNode(NULL
, service_name
);
9695 isds_printf_message(context
,
9696 _("Could not build %s request for %s message ID"),
9697 service_name_locale
, message_id_locale
);
9701 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9703 isds_log_message(context
, _("Could not create ISDS name space"));
9707 xmlSetNs(request
, isds_ns
);
9710 /* Add requested ID */
9711 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9712 if (err
) goto leave
;
9713 INSERT_STRING(request
, "dmID", message_id
);
9716 isds_log(ILF_ISDS
, ILL_DEBUG
,
9717 _("Sending %s request for %s message ID to ISDS\n"),
9718 service_name_locale
, message_id_locale
);
9721 err
= _isds(context
, service
, request
, response
,
9722 raw_response
, raw_response_length
);
9723 xmlFreeNode(request
); request
= NULL
;
9726 isds_log(ILF_ISDS
, ILL_DEBUG
,
9727 _("Processing ISDS response on %s request failed\n"),
9728 service_name_locale
);
9732 /* Check for response status */
9733 err
= isds_response_status(context
, service
, *response
,
9734 code
, status_message
, NULL
);
9736 isds_log(ILF_ISDS
, ILL_DEBUG
,
9737 _("ISDS response on %s request is missing status\n"),
9738 service_name_locale
);
9742 /* Request processed, but nothing found */
9743 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9744 char *code_locale
= _isds_utf82locale((char*) *code
);
9745 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9746 isds_log(ILF_ISDS
, ILL_DEBUG
,
9747 _("Server refused %s request for %s message ID "
9748 "(code=%s, message=%s)\n"),
9749 service_name_locale
, message_id_locale
,
9750 code_locale
, status_message_locale
);
9751 isds_log_message(context
, status_message_locale
);
9753 free(status_message_locale
);
9759 free(message_id_locale
);
9760 free(service_name_locale
);
9761 xmlFreeNode(request
);
9766 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9767 * signed data and free ISDS response.
9768 * @context is session context
9769 * @message_id is UTF-8 encoded message ID for logging purpose
9770 * @response is parsed XML document. It will be freed and NULLed in the middle
9771 * of function run to save memory. This is not guaranteed in case of error.
9772 * @request_name is name of ISDS request used to construct response root
9773 * element name and for logging purpose.
9774 * @raw is reallocated output buffer with DER encoded CMS data
9775 * @raw_length is size of @raw buffer in bytes
9776 * @returns standard error codes, in case of error, @raw will be freed and
9777 * NULLed, @response sometimes. */
9778 static isds_error
find_extract_signed_data_free_response(
9779 struct isds_ctx
*context
, const xmlChar
*message_id
,
9780 xmlDocPtr
*response
, const xmlChar
*request_name
,
9781 void **raw
, size_t *raw_length
) {
9783 isds_error err
= IE_SUCCESS
;
9784 char *xpath_expression
= NULL
;
9785 xmlXPathContextPtr xpath_ctx
= NULL
;
9786 xmlXPathObjectPtr result
= NULL
;
9787 char *encoded_structure
= NULL
;
9789 if (!context
) return IE_INVALID_CONTEXT
;
9790 if (!raw
) return IE_INVAL
;
9792 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9795 /* Build XPath expression */
9796 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9797 "Response/isds:dmSignature");
9798 if (!xpath_expression
) return IE_NOMEM
;
9801 xpath_ctx
= xmlXPathNewContext(*response
);
9806 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9810 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9815 /* Empty response */
9816 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9817 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9818 isds_printf_message(context
,
9819 _("Server did not return any signed data for message ID `%s' "
9821 message_id_locale
, request_name
);
9822 free(message_id_locale
);
9826 /* More responses */
9827 if (result
->nodesetval
->nodeNr
> 1) {
9828 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9829 isds_printf_message(context
,
9830 _("Server did return more signed data for message ID `%s' "
9832 message_id_locale
, request_name
);
9833 free(message_id_locale
);
9838 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9840 /* Extract PKCS#7 structure */
9841 EXTRACT_STRING(".", encoded_structure
);
9842 if (!encoded_structure
) {
9843 isds_log_message(context
, _("dmSignature element is empty"));
9846 /* Here we have delivery info as standalone CMS in encoded_structure.
9847 * We don't need any other data, free them: */
9848 xmlXPathFreeObject(result
); result
= NULL
;
9849 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9850 xmlFreeDoc(*response
); *response
= NULL
;
9853 /* Decode PKCS#7 to DER format */
9854 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9855 if (*raw_length
== (size_t) -1) {
9856 isds_log_message(context
,
9857 _("Error while Base64-decoding PKCS#7 structure"));
9868 free(encoded_structure
);
9869 xmlXPathFreeObject(result
);
9870 xmlXPathFreeContext(xpath_ctx
);
9871 free(xpath_expression
);
9875 #endif /* HAVE_LIBCURL */
9878 /* Download incoming message envelope identified by ID.
9879 * @context is session context
9880 * @message_id is message identifier (you can get them from
9881 * isds_get_list_of_received_messages())
9882 * @message is automatically reallocated message retrieved from ISDS.
9883 * It will miss documents per se. Use isds_get_received_message(), if you are
9884 * interested in documents (content) too.
9885 * Returned hash and timestamp require documents to be verifiable. */
9886 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9887 const char *message_id
, struct isds_message
**message
) {
9889 isds_error err
= IE_SUCCESS
;
9891 xmlDocPtr response
= NULL
;
9892 xmlChar
*code
= NULL
, *status_message
= NULL
;
9893 xmlXPathContextPtr xpath_ctx
= NULL
;
9894 xmlXPathObjectPtr result
= NULL
;
9897 if (!context
) return IE_INVALID_CONTEXT
;
9898 zfree(context
->long_message
);
9900 /* Free former message if any */
9901 if (!message
) return IE_INVAL
;
9902 isds_message_free(message
);
9905 /* Do request and check for success */
9906 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9907 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9908 &response
, NULL
, NULL
, &code
, &status_message
);
9909 if (err
) goto leave
;
9912 xpath_ctx
= xmlXPathNewContext(response
);
9917 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9921 result
= xmlXPathEvalExpression(
9922 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9923 "isds:dmReturnedMessageEnvelope",
9929 /* Empty response */
9930 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9931 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9932 isds_printf_message(context
,
9933 _("Server did not return any envelope for ID `%s' "
9934 "on MessageEnvelopeDownload request"), message_id_locale
);
9935 free(message_id_locale
);
9940 if (result
->nodesetval
->nodeNr
> 1) {
9941 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9942 isds_printf_message(context
,
9943 _("Server did return more envelopes for ID `%s' "
9944 "on MessageEnvelopeDownload request"), message_id_locale
);
9945 free(message_id_locale
);
9950 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9952 /* Extract the envelope (= message without documents, hence 0) */
9953 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9954 if (err
) goto leave
;
9957 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9958 &(*message
)->raw_length
);
9962 isds_message_free(message
);
9965 xmlXPathFreeObject(result
);
9966 xmlXPathFreeContext(xpath_ctx
);
9969 free(status_message
);
9970 if (!*message
|| !(*message
)->xml
) {
9971 xmlFreeDoc(response
);
9975 isds_log(ILF_ISDS
, ILL_DEBUG
,
9976 _("MessageEnvelopeDownload request processed by server "
9979 #else /* not HAVE_LIBCURL */
9986 /* Load delivery info of any format from buffer.
9987 * @context is session context
9988 * @raw_type advertises format of @buffer content. Only delivery info types
9990 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9991 * retrieve such data from message->raw after calling
9992 * isds_get_signed_delivery_info().
9993 * @length is length of buffer in bytes.
9994 * @message is automatically reallocated message parsed from @buffer.
9995 * @strategy selects how buffer will be attached into raw isds_message member.
9997 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9998 const isds_raw_type raw_type
,
9999 const void *buffer
, const size_t length
,
10000 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10002 isds_error err
= IE_SUCCESS
;
10003 message_ns_type message_ns
;
10004 xmlDocPtr message_doc
= NULL
;
10005 xmlXPathContextPtr xpath_ctx
= NULL
;
10006 xmlXPathObjectPtr result
= NULL
;
10007 void *xml_stream
= NULL
;
10008 size_t xml_stream_length
= 0;
10010 if (!context
) return IE_INVALID_CONTEXT
;
10011 zfree(context
->long_message
);
10012 if (!message
) return IE_INVAL
;
10013 isds_message_free(message
);
10014 if (!buffer
) return IE_INVAL
;
10017 /* Select buffer format and extract XML from CMS*/
10018 switch (raw_type
) {
10019 case RAWTYPE_DELIVERYINFO
:
10020 message_ns
= MESSAGE_NS_UNSIGNED
;
10021 xml_stream
= (void *) buffer
;
10022 xml_stream_length
= length
;
10025 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10026 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10027 xml_stream
= (void *) buffer
;
10028 xml_stream_length
= length
;
10031 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10032 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10033 err
= _isds_extract_cms_data(context
, buffer
, length
,
10034 &xml_stream
, &xml_stream_length
);
10035 if (err
) goto leave
;
10039 isds_log_message(context
, _("Bad raw delivery representation type"));
10044 isds_log(ILF_ISDS
, ILL_DEBUG
,
10045 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10046 xml_stream_length
, xml_stream
);
10048 /* Convert delivery info XML stream into XPath context */
10049 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10050 if (!message_doc
) {
10054 xpath_ctx
= xmlXPathNewContext(message_doc
);
10059 /* XXX: Name spaces mangled for signed delivery info:
10060 * http://isds.czechpoint.cz/v20/delivery:
10062 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10064 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10065 * <p:dmID>170272</p:dmID>
10068 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10070 * </q:dmEvents>...</q:dmEvents>
10072 * </q:GetDeliveryInfoResponse>
10074 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10078 result
= xmlXPathEvalExpression(
10079 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10085 /* Empty delivery info */
10086 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10087 isds_printf_message(context
,
10088 _("XML document is not sisds:dmDelivery document"));
10092 /* More delivery info's */
10093 if (result
->nodesetval
->nodeNr
> 1) {
10094 isds_printf_message(context
,
10095 _("XML document has more sisds:dmDelivery elements"));
10099 /* One delivery info */
10100 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10102 /* Extract the envelope (= message without documents, hence 0).
10103 * XXX: extract_TReturnedMessage() can obtain attachments size,
10104 * but delivery info carries none. It's coded as option elements,
10105 * so it should work. */
10106 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10107 if (err
) goto leave
;
10109 /* Extract events */
10110 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10111 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10112 if (err
) { err
= IE_ERROR
; goto leave
; }
10113 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10114 if (err
) goto leave
;
10116 /* Append raw CMS structure into message */
10117 (*message
)->raw_type
= raw_type
;
10118 switch (strategy
) {
10119 case BUFFER_DONT_STORE
:
10122 (*message
)->raw
= malloc(length
);
10123 if (!(*message
)->raw
) {
10127 memcpy((*message
)->raw
, buffer
, length
);
10128 (*message
)->raw_length
= length
;
10131 (*message
)->raw
= (void *) buffer
;
10132 (*message
)->raw_length
= length
;
10141 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10142 isds_message_free(message
);
10145 xmlXPathFreeObject(result
);
10146 xmlXPathFreeContext(xpath_ctx
);
10147 if (!*message
|| !(*message
)->xml
) {
10148 xmlFreeDoc(message_doc
);
10150 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10153 isds_log(ILF_ISDS
, ILL_DEBUG
,
10154 _("Delivery info loaded successfully.\n"));
10159 /* Download signed delivery info-sheet of given message identified by ID.
10160 * @context is session context
10161 * @message_id is message identifier (you can get them from
10162 * isds_get_list_of_{sent,received}_messages())
10163 * @message is automatically reallocated message retrieved from ISDS.
10164 * It will miss documents per se. Use isds_get_signed_received_message(),
10165 * if you are interested in documents (content). OTOH, only this function
10166 * can get list events message has gone through. */
10167 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10168 const char *message_id
, struct isds_message
**message
) {
10170 isds_error err
= IE_SUCCESS
;
10172 xmlDocPtr response
= NULL
;
10173 xmlChar
*code
= NULL
, *status_message
= NULL
;
10175 size_t raw_length
= 0;
10178 if (!context
) return IE_INVALID_CONTEXT
;
10179 zfree(context
->long_message
);
10181 /* Free former message if any */
10182 if (!message
) return IE_INVAL
;
10183 isds_message_free(message
);
10186 /* Do request and check for success */
10187 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10188 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10189 &response
, NULL
, NULL
, &code
, &status_message
);
10190 if (err
) goto leave
;
10192 /* Find signed delivery info, extract it into raw and maybe free
10194 err
= find_extract_signed_data_free_response(context
,
10195 (xmlChar
*)message_id
, &response
,
10196 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10197 if (err
) goto leave
;
10199 /* Parse delivery info */
10200 err
= isds_load_delivery_info(context
,
10201 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10202 message
, BUFFER_MOVE
);
10203 if (err
) goto leave
;
10209 isds_message_free(message
);
10214 free(status_message
);
10215 xmlFreeDoc(response
);
10218 isds_log(ILF_ISDS
, ILL_DEBUG
,
10219 _("GetSignedDeliveryInfo request processed by server "
10222 #else /* not HAVE_LIBCURL */
10229 /* Download delivery info-sheet of given message identified by ID.
10230 * @context is session context
10231 * @message_id is message identifier (you can get them from
10232 * isds_get_list_of_{sent,received}_messages())
10233 * @message is automatically reallocated message retrieved from ISDS.
10234 * It will miss documents per se. Use isds_get_received_message(), if you are
10235 * interested in documents (content). OTOH, only this function can get list
10236 * of events message has gone through. */
10237 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10238 const char *message_id
, struct isds_message
**message
) {
10240 isds_error err
= IE_SUCCESS
;
10242 xmlDocPtr response
= NULL
;
10243 xmlChar
*code
= NULL
, *status_message
= NULL
;
10244 xmlNodePtr delivery_node
= NULL
;
10246 size_t raw_length
= 0;
10249 if (!context
) return IE_INVALID_CONTEXT
;
10250 zfree(context
->long_message
);
10252 /* Free former message if any */
10253 if (!message
) return IE_INVAL
;
10254 isds_message_free(message
);
10257 /* Do request and check for success */
10258 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10259 BAD_CAST
"GetDeliveryInfo", message_id
,
10260 &response
, NULL
, NULL
, &code
, &status_message
);
10261 if (err
) goto leave
;
10264 /* Serialize delivery info */
10265 delivery_node
= xmlDocGetRootElement(response
);
10266 if (!delivery_node
) {
10267 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10268 isds_printf_message(context
,
10269 _("Server did not return any delivery info for ID `%s' "
10270 "on GetDeliveryInfo request"), message_id_locale
);
10271 free(message_id_locale
);
10275 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10276 if (err
) goto leave
;
10278 /* Parse delivery info */
10279 /* TODO: Here we parse the response second time. We could single delivery
10280 * parser from isds_load_delivery_info() to make things faster. */
10281 err
= isds_load_delivery_info(context
,
10282 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10283 message
, BUFFER_MOVE
);
10284 if (err
) goto leave
;
10291 isds_message_free(message
);
10296 free(status_message
);
10297 xmlFreeDoc(response
);
10300 isds_log(ILF_ISDS
, ILL_DEBUG
,
10301 _("GetDeliveryInfo request processed by server "
10304 #else /* not HAVE_LIBCURL */
10311 /* Download incoming message identified by ID.
10312 * @context is session context
10313 * @message_id is message identifier (you can get them from
10314 * isds_get_list_of_received_messages())
10315 * @message is automatically reallocated message retrieved from ISDS */
10316 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10317 const char *message_id
, struct isds_message
**message
) {
10319 isds_error err
= IE_SUCCESS
;
10321 xmlDocPtr response
= NULL
;
10322 void *xml_stream
= NULL
;
10323 size_t xml_stream_length
;
10324 xmlChar
*code
= NULL
, *status_message
= NULL
;
10325 xmlXPathContextPtr xpath_ctx
= NULL
;
10326 xmlXPathObjectPtr result
= NULL
;
10327 char *phys_path
= NULL
;
10328 size_t phys_start
, phys_end
;
10331 if (!context
) return IE_INVALID_CONTEXT
;
10332 zfree(context
->long_message
);
10334 /* Free former message if any */
10335 if (NULL
== message
) return IE_INVAL
;
10336 if (message
) isds_message_free(message
);
10339 /* Do request and check for success */
10340 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10341 BAD_CAST
"MessageDownload", message_id
,
10342 &response
, &xml_stream
, &xml_stream_length
,
10343 &code
, &status_message
);
10344 if (err
) goto leave
;
10347 xpath_ctx
= xmlXPathNewContext(response
);
10352 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10356 result
= xmlXPathEvalExpression(
10357 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10363 /* Empty response */
10364 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10365 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10366 isds_printf_message(context
,
10367 _("Server did not return any message for ID `%s' "
10368 "on MessageDownload request"), message_id_locale
);
10369 free(message_id_locale
);
10373 /* More messages */
10374 if (result
->nodesetval
->nodeNr
> 1) {
10375 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10376 isds_printf_message(context
,
10377 _("Server did return more messages for ID `%s' "
10378 "on MessageDownload request"), message_id_locale
);
10379 free(message_id_locale
);
10384 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10386 /* Extract the message */
10387 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10388 if (err
) goto leave
;
10390 /* Locate raw XML blob */
10391 phys_path
= strdup(
10392 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10393 PHYSXML_ELEMENT_SEPARATOR
10394 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10395 PHYSXML_ELEMENT_SEPARATOR
10396 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10402 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10403 phys_path
, &phys_start
, &phys_end
);
10406 isds_log_message(context
,
10407 _("Substring with isds:MessageDownloadResponse element "
10408 "could not be located in raw SOAP message"));
10411 /* Save XML blob */
10412 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10413 &(*message)->raw_length);*/
10414 /* TODO: Store name space declarations from ancestors */
10415 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10416 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10417 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10418 (*message
)->raw
= malloc((*message
)->raw_length
);
10419 if (!(*message
)->raw
) {
10423 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10428 isds_message_free(message
);
10433 xmlXPathFreeObject(result
);
10434 xmlXPathFreeContext(xpath_ctx
);
10437 free(status_message
);
10439 if (!*message
|| !(*message
)->xml
) {
10440 xmlFreeDoc(response
);
10444 isds_log(ILF_ISDS
, ILL_DEBUG
,
10445 _("MessageDownload request processed by server "
10448 #else /* not HAVE_LIBCURL */
10455 /* Load message of any type from buffer.
10456 * @context is session context
10457 * @raw_type defines content type of @buffer. Only message types are allowed.
10458 * @buffer is message raw representation. Format (CMS, plain signed,
10459 * message direction) is defined in @raw_type. You can retrieve such data
10460 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10461 * @length is length of buffer in bytes.
10462 * @message is automatically reallocated message parsed from @buffer.
10463 * @strategy selects how buffer will be attached into raw isds_message member.
10465 isds_error
isds_load_message(struct isds_ctx
*context
,
10466 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10467 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10469 isds_error err
= IE_SUCCESS
;
10470 void *xml_stream
= NULL
;
10471 size_t xml_stream_length
= 0;
10472 message_ns_type message_ns
;
10473 xmlDocPtr message_doc
= NULL
;
10474 xmlXPathContextPtr xpath_ctx
= NULL
;
10475 xmlXPathObjectPtr result
= NULL
;
10477 if (!context
) return IE_INVALID_CONTEXT
;
10478 zfree(context
->long_message
);
10479 if (!message
) return IE_INVAL
;
10480 isds_message_free(message
);
10481 if (!buffer
) return IE_INVAL
;
10484 /* Select buffer format and extract XML from CMS*/
10485 switch (raw_type
) {
10486 case RAWTYPE_INCOMING_MESSAGE
:
10487 message_ns
= MESSAGE_NS_UNSIGNED
;
10488 xml_stream
= (void *) buffer
;
10489 xml_stream_length
= length
;
10492 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10493 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10494 xml_stream
= (void *) buffer
;
10495 xml_stream_length
= length
;
10498 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10499 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10500 err
= _isds_extract_cms_data(context
, buffer
, length
,
10501 &xml_stream
, &xml_stream_length
);
10502 if (err
) goto leave
;
10505 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10506 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10507 xml_stream
= (void *) buffer
;
10508 xml_stream_length
= length
;
10511 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10512 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10513 err
= _isds_extract_cms_data(context
, buffer
, length
,
10514 &xml_stream
, &xml_stream_length
);
10515 if (err
) goto leave
;
10519 isds_log_message(context
, _("Bad raw message representation type"));
10524 isds_log(ILF_ISDS
, ILL_DEBUG
,
10525 _("Loading message:\n%.*s\nEnd of message\n"),
10526 xml_stream_length
, xml_stream
);
10528 /* Convert messages XML stream into XPath context */
10529 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10530 if (!message_doc
) {
10534 xpath_ctx
= xmlXPathNewContext(message_doc
);
10539 /* XXX: Standard name space for unsigned incoming direction:
10540 * http://isds.czechpoint.cz/v20/
10542 * XXX: Name spaces mangled for signed outgoing direction:
10543 * http://isds.czechpoint.cz/v20/SentMessage:
10545 * <q:MessageDownloadResponse
10546 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10547 * <q:dmReturnedMessage>
10548 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10549 * <p:dmID>151916</p:dmID>
10552 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10554 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10555 * </q:dmReturnedMessage>
10556 * </q:MessageDownloadResponse>
10558 * XXX: Name spaces mangled for signed incoming direction:
10559 * http://isds.czechpoint.cz/v20/message:
10561 * <q:MessageDownloadResponse
10562 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10563 * <q:dmReturnedMessage>
10564 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10565 * <p:dmID>151916</p:dmID>
10568 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10570 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10571 * </q:dmReturnedMessage>
10572 * </q:MessageDownloadResponse>
10574 * Stupidity of ISDS developers is unlimited */
10575 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10579 result
= xmlXPathEvalExpression(
10580 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10586 /* Empty message */
10587 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10588 isds_printf_message(context
,
10589 _("XML document does not contain "
10590 "sisds:dmReturnedMessage element"));
10594 /* More messages */
10595 if (result
->nodesetval
->nodeNr
> 1) {
10596 isds_printf_message(context
,
10597 _("XML document has more sisds:dmReturnedMessage elements"));
10602 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10604 /* Extract the message */
10605 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10606 if (err
) goto leave
;
10608 /* Append raw buffer into message */
10609 (*message
)->raw_type
= raw_type
;
10610 switch (strategy
) {
10611 case BUFFER_DONT_STORE
:
10614 (*message
)->raw
= malloc(length
);
10615 if (!(*message
)->raw
) {
10619 memcpy((*message
)->raw
, buffer
, length
);
10620 (*message
)->raw_length
= length
;
10623 (*message
)->raw
= (void *) buffer
;
10624 (*message
)->raw_length
= length
;
10634 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10635 isds_message_free(message
);
10638 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10639 xmlXPathFreeObject(result
);
10640 xmlXPathFreeContext(xpath_ctx
);
10641 if (!*message
|| !(*message
)->xml
) {
10642 xmlFreeDoc(message_doc
);
10646 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10651 /* Determine type of raw message or delivery info according some heuristics.
10652 * It does not validate the raw blob.
10653 * @context is session context
10654 * @raw_type returns content type of @buffer. Valid only if exit code of this
10655 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10656 * reallocated memory.
10657 * @buffer is message raw representation.
10658 * @length is length of buffer in bytes. */
10659 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10660 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10662 void *xml_stream
= NULL
;
10663 size_t xml_stream_length
= 0;
10664 xmlDocPtr document
= NULL
;
10665 xmlNodePtr root
= NULL
;
10667 if (!context
) return IE_INVALID_CONTEXT
;
10668 zfree(context
->long_message
);
10669 if (length
== 0 || !buffer
) return IE_INVAL
;
10670 if (!raw_type
) return IE_INVAL
;
10673 err
= _isds_extract_cms_data(context
, buffer
, length
,
10674 &xml_stream
, &xml_stream_length
);
10676 xml_stream
= (void *) buffer
;
10677 xml_stream_length
= (size_t) length
;
10682 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10684 isds_printf_message(context
,
10685 _("Could not parse data as XML document"));
10690 /* Get root element */
10691 root
= xmlDocGetRootElement(document
);
10693 isds_printf_message(context
,
10694 _("XML document is missing root element"));
10699 if (!root
->ns
|| !root
->ns
->href
) {
10700 isds_printf_message(context
,
10701 _("Root element does not belong to any name space"));
10706 /* Test name space */
10707 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10708 if (xml_stream
== buffer
)
10709 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10711 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10712 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10713 if (xml_stream
== buffer
)
10714 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10716 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10717 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10718 if (xml_stream
== buffer
)
10719 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10721 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10722 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10723 if (xml_stream
!= buffer
) {
10724 isds_printf_message(context
,
10725 _("Document in ISDS name space is encapsulated into CMS" ));
10727 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10728 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10729 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10730 *raw_type
= RAWTYPE_DELIVERYINFO
;
10732 isds_printf_message(context
,
10733 _("Unknown root element in ISDS name space"));
10737 isds_printf_message(context
,
10738 _("Unknown name space"));
10743 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10744 xmlFreeDoc(document
);
10749 /* Download signed incoming/outgoing message identified by ID.
10750 * @context is session context
10751 * @output is true for outgoing message, false for incoming message
10752 * @message_id is message identifier (you can get them from
10753 * isds_get_list_of_{sent,received}_messages())
10754 * @message is automatically reallocated message retrieved from ISDS. The raw
10755 * member will be filled with PKCS#7 structure in DER format. */
10756 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10757 const _Bool outgoing
, const char *message_id
,
10758 struct isds_message
**message
) {
10760 isds_error err
= IE_SUCCESS
;
10762 xmlDocPtr response
= NULL
;
10763 xmlChar
*code
= NULL
, *status_message
= NULL
;
10764 xmlXPathContextPtr xpath_ctx
= NULL
;
10765 xmlXPathObjectPtr result
= NULL
;
10766 char *encoded_structure
= NULL
;
10768 size_t raw_length
= 0;
10771 if (!context
) return IE_INVALID_CONTEXT
;
10772 zfree(context
->long_message
);
10773 if (!message
) return IE_INVAL
;
10774 isds_message_free(message
);
10777 /* Do request and check for success */
10778 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10779 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10780 BAD_CAST
"SignedMessageDownload",
10781 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10782 if (err
) goto leave
;
10784 /* Find signed message, extract it into raw and maybe free
10786 err
= find_extract_signed_data_free_response(context
,
10787 (xmlChar
*)message_id
, &response
,
10788 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10789 BAD_CAST
"SignedMessageDownload",
10790 &raw
, &raw_length
);
10791 if (err
) goto leave
;
10793 /* Parse message */
10794 err
= isds_load_message(context
,
10795 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10796 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10797 raw
, raw_length
, message
, BUFFER_MOVE
);
10798 if (err
) goto leave
;
10804 isds_message_free(message
);
10807 free(encoded_structure
);
10808 xmlXPathFreeObject(result
);
10809 xmlXPathFreeContext(xpath_ctx
);
10813 free(status_message
);
10814 xmlFreeDoc(response
);
10817 isds_log(ILF_ISDS
, ILL_DEBUG
,
10819 _("SignedSentMessageDownload request processed by server "
10820 "successfully.\n") :
10821 _("SignedMessageDownload request processed by server "
10824 #else /* not HAVE_LIBCURL */
10831 /* Download signed incoming message identified by ID.
10832 * @context is session context
10833 * @message_id is message identifier (you can get them from
10834 * isds_get_list_of_received_messages())
10835 * @message is automatically reallocated message retrieved from ISDS. The raw
10836 * member will be filled with PKCS#7 structure in DER format. */
10837 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10838 const char *message_id
, struct isds_message
**message
) {
10839 return isds_get_signed_message(context
, 0, message_id
, message
);
10843 /* Download signed outgoing message identified by ID.
10844 * @context is session context
10845 * @message_id is message identifier (you can get them from
10846 * isds_get_list_of_sent_messages())
10847 * @message is automatically reallocated message retrieved from ISDS. The raw
10848 * member will be filled with PKCS#7 structure in DER format. */
10849 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10850 const char *message_id
, struct isds_message
**message
) {
10851 return isds_get_signed_message(context
, 1, message_id
, message
);
10855 /* Get type and name of user who sent a message identified by ID.
10856 * @context is session context
10857 * @message_id is message identifier
10858 * @sender_type is pointer to automatically allocated type of sender detected
10859 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10860 * library or to the server, NULL will be returned. Pass NULL if you don't
10862 * @raw_sender_type is automatically reallocated UTF-8 string describing
10863 * sender type or NULL if not known to server. Pass NULL if you don't care.
10864 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10865 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10866 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10867 const char *message_id
, isds_sender_type
**sender_type
,
10868 char **raw_sender_type
, char **sender_name
) {
10869 isds_error err
= IE_SUCCESS
;
10871 xmlDocPtr response
= NULL
;
10872 xmlChar
*code
= NULL
, *status_message
= NULL
;
10873 xmlXPathContextPtr xpath_ctx
= NULL
;
10874 xmlXPathObjectPtr result
= NULL
;
10875 char *type_string
= NULL
;
10878 if (!context
) return IE_INVALID_CONTEXT
;
10879 zfree(context
->long_message
);
10880 if (sender_type
) zfree(*sender_type
);
10881 if (raw_sender_type
) zfree(*raw_sender_type
);
10882 if (sender_name
) zfree(*sender_name
);
10883 if (!message_id
) return IE_INVAL
;
10886 /* Do request and check for success */
10887 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10888 BAD_CAST
"GetMessageAuthor",
10889 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10890 if (err
) goto leave
;
10893 xpath_ctx
= xmlXPathNewContext(response
);
10898 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10902 result
= xmlXPathEvalExpression(
10903 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10908 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10909 isds_log_message(context
,
10910 _("Missing GetMessageAuthorResponse element"));
10914 if (result
->nodesetval
->nodeNr
> 1) {
10915 isds_log_message(context
,
10916 _("Multiple GetMessageAuthorResponse element"));
10920 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10921 xmlXPathFreeObject(result
); result
= NULL
;
10923 /* Fill output arguments in */
10924 EXTRACT_STRING("isds:userType", type_string
);
10925 if (NULL
!= type_string
) {
10926 if (NULL
!= sender_type
) {
10927 *sender_type
= calloc(1, sizeof(**sender_type
));
10928 if (NULL
== *sender_type
) {
10933 err
= string2isds_sender_type((xmlChar
*)type_string
,
10936 zfree(*sender_type
);
10937 if (err
== IE_ENUM
) {
10939 char *type_string_locale
= _isds_utf82locale(type_string
);
10940 isds_log(ILF_ISDS
, ILL_WARNING
,
10941 _("Unknown isds:userType value: %s"),
10942 type_string_locale
);
10943 free(type_string_locale
);
10948 if (NULL
!= sender_name
)
10949 EXTRACT_STRING("isds:authorName", *sender_name
);
10953 if (NULL
!= sender_type
) zfree(*sender_type
);
10954 zfree(type_string
);
10955 if (NULL
!= sender_name
) zfree(*sender_name
);
10957 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10959 xmlXPathFreeObject(result
);
10960 xmlXPathFreeContext(xpath_ctx
);
10963 free(status_message
);
10964 xmlFreeDoc(response
);
10967 isds_log(ILF_ISDS
, ILL_DEBUG
,
10968 _("GetMessageAuthor request processed by server "
10969 "successfully.\n"));
10970 #else /* not HAVE_LIBCURL */
10977 /* Retrieve hash of message identified by ID stored in ISDS.
10978 * @context is session context
10979 * @message_id is message identifier
10980 * @hash is automatically reallocated message hash downloaded from ISDS.
10981 * Message must exist in system and must not be deleted. */
10982 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10983 const char *message_id
, struct isds_hash
**hash
) {
10985 isds_error err
= IE_SUCCESS
;
10987 xmlDocPtr response
= NULL
;
10988 xmlChar
*code
= NULL
, *status_message
= NULL
;
10989 xmlXPathContextPtr xpath_ctx
= NULL
;
10990 xmlXPathObjectPtr result
= NULL
;
10993 if (!context
) return IE_INVALID_CONTEXT
;
10994 zfree(context
->long_message
);
10996 isds_hash_free(hash
);
10999 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11000 BAD_CAST
"VerifyMessage", message_id
,
11001 &response
, NULL
, NULL
, &code
, &status_message
);
11002 if (err
) goto leave
;
11006 xpath_ctx
= xmlXPathNewContext(response
);
11011 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11015 result
= xmlXPathEvalExpression(
11016 BAD_CAST
"/isds:VerifyMessageResponse",
11022 /* Empty response */
11023 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11024 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11025 isds_printf_message(context
,
11026 _("Server did not return any response for ID `%s' "
11027 "on VerifyMessage request"), message_id_locale
);
11028 free(message_id_locale
);
11032 /* More responses */
11033 if (result
->nodesetval
->nodeNr
> 1) {
11034 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11035 isds_printf_message(context
,
11036 _("Server did return more responses for ID `%s' "
11037 "on VerifyMessage request"), message_id_locale
);
11038 free(message_id_locale
);
11043 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11045 /* Extract the hash */
11046 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11050 isds_hash_free(hash
);
11053 xmlXPathFreeObject(result
);
11054 xmlXPathFreeContext(xpath_ctx
);
11057 free(status_message
);
11058 xmlFreeDoc(response
);
11061 isds_log(ILF_ISDS
, ILL_DEBUG
,
11062 _("VerifyMessage request processed by server "
11065 #else /* not HAVE_LIBCURL */
11072 /* Erase message specified by @message_id from long term storage. Other
11073 * message cannot be erased on user request.
11074 * @context is session context
11075 * @message_id is message identifier.
11076 * @incoming is true for incoming message, false for outgoing message.
11078 * IE_SUCCESS if message has ben removed
11079 * IE_INVAL if message does not exist in long term storage or message
11080 * belongs to different box
11081 * TODO: IE_NOEPRM if user has no permission to erase a message */
11082 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11083 const char *message_id
, _Bool incoming
) {
11084 isds_error err
= IE_SUCCESS
;
11086 xmlNodePtr request
= NULL
, node
;
11087 xmlNsPtr isds_ns
= NULL
;
11088 xmlDocPtr response
= NULL
;
11089 xmlChar
*code
= NULL
, *status_message
= NULL
;
11092 if (!context
) return IE_INVALID_CONTEXT
;
11093 zfree(context
->long_message
);
11094 if (NULL
== message_id
) return IE_INVAL
;
11096 /* Check if connection is established
11097 * TODO: This check should be done downstairs. */
11098 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11101 /* Build request */
11102 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11104 isds_log_message(context
,
11105 _("Could build EraseMessage request"));
11108 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11110 isds_log_message(context
, _("Could not create ISDS name space"));
11111 xmlFreeNode(request
);
11114 xmlSetNs(request
, isds_ns
);
11116 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11117 if (err
) goto leave
;
11118 INSERT_STRING(request
, "dmID", message_id
);
11120 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11124 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11125 "message ID %s to ISDS\n"), message_id
);
11126 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11127 xmlFreeNode(request
); request
= NULL
;
11130 isds_log(ILF_ISDS
, ILL_DEBUG
,
11131 _("Processing ISDS response on EraseMessage request "
11136 /* Check for response status */
11137 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11138 &code
, &status_message
, NULL
);
11140 isds_log(ILF_ISDS
, ILL_DEBUG
,
11141 _("ISDS response on EraseMessage request is missing "
11146 /* Check server status code */
11147 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11148 isds_log_message(context
, _("Message to erase belongs to other box"));
11150 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11151 isds_log_message(context
, _("Message to erase is not saved in "
11152 "long term storage or the direction does not match"));
11154 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11155 char *code_locale
= _isds_utf82locale((char*) code
);
11156 char *message_locale
= _isds_utf82locale((char*) status_message
);
11157 isds_log(ILF_ISDS
, ILL_DEBUG
,
11158 _("Server refused EraseMessage request "
11159 "(code=%s, message=%s)\n"),
11160 code_locale
, message_locale
);
11161 isds_log_message(context
, message_locale
);
11163 free(message_locale
);
11170 free(status_message
);
11171 xmlFreeDoc(response
);
11172 xmlFreeNode(request
);
11175 isds_log(ILF_ISDS
, ILL_DEBUG
,
11176 _("EraseMessage request processed by server "
11179 #else /* not HAVE_LIBCURL */
11186 /* Mark message as read. This is a transactional commit function to acknowledge
11187 * to ISDS the message has been downloaded and processed by client properly.
11188 * @context is session context
11189 * @message_id is message identifier. */
11190 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11191 const char *message_id
) {
11193 isds_error err
= IE_SUCCESS
;
11195 xmlDocPtr response
= NULL
;
11196 xmlChar
*code
= NULL
, *status_message
= NULL
;
11199 if (!context
) return IE_INVALID_CONTEXT
;
11200 zfree(context
->long_message
);
11203 /* Do request and check for success */
11204 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11205 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11206 &response
, NULL
, NULL
, &code
, &status_message
);
11209 free(status_message
);
11210 xmlFreeDoc(response
);
11213 isds_log(ILF_ISDS
, ILL_DEBUG
,
11214 _("MarkMessageAsDownloaded request processed by server "
11217 #else /* not HAVE_LIBCURL */
11224 /* Mark message as received by recipient. This is applicable only to
11225 * commercial message. Use envelope->dmType message member to distinguish
11226 * commercial message from government message. Government message is
11227 * received automatically (by law), commercial message on recipient request.
11228 * @context is session context
11229 * @message_id is message identifier. */
11230 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11231 const char *message_id
) {
11233 isds_error err
= IE_SUCCESS
;
11235 xmlDocPtr response
= NULL
;
11236 xmlChar
*code
= NULL
, *status_message
= NULL
;
11239 if (!context
) return IE_INVALID_CONTEXT
;
11240 zfree(context
->long_message
);
11243 /* Do request and check for success */
11244 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11245 BAD_CAST
"ConfirmDelivery", message_id
,
11246 &response
, NULL
, NULL
, &code
, &status_message
);
11249 free(status_message
);
11250 xmlFreeDoc(response
);
11253 isds_log(ILF_ISDS
, ILL_DEBUG
,
11254 _("ConfirmDelivery request processed by server "
11257 #else /* not HAVE_LIBCURL */
11264 /* Send document for authorized conversion into Czech POINT system.
11265 * This is public anonymous service, no log-in necessary. Special context is
11266 * used to reuse keep-a-live HTTPS connection.
11267 * @context is Czech POINT session context. DO NOT use context connected to
11268 * ISDS server. Use new context or context used by this function previously.
11269 * @document is document to convert. Only data, data_length, dmFileDescr and
11270 * is_xml members are significant. Be ware that not all document formats can be
11271 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11272 * @id is reallocated identifier assigned by Czech POINT system to
11273 * your document on submit. Use is to tell it to Czech POINT officer.
11274 * @date is reallocated document submit date (submitted documents
11275 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11277 isds_error
czp_convert_document(struct isds_ctx
*context
,
11278 const struct isds_document
*document
,
11279 char **id
, struct tm
**date
) {
11280 isds_error err
= IE_SUCCESS
;
11282 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11283 xmlNodePtr request
= NULL
, node
;
11284 xmlDocPtr response
= NULL
;
11286 xmlXPathContextPtr xpath_ctx
= NULL
;
11287 xmlXPathObjectPtr result
= NULL
;
11288 long int status
= -1;
11289 long int *status_ptr
= &status
;
11290 char *string
= NULL
;
11294 if (!context
) return IE_INVALID_CONTEXT
;
11295 zfree(context
->long_message
);
11296 if (!document
|| !id
|| !date
) return IE_INVAL
;
11298 if (document
->is_xml
) {
11299 isds_log_message(context
,
11300 _("XML documents cannot be submitted to conversion"));
11304 /* Free output arguments */
11309 /* Store configuration */
11310 context
->type
= CTX_TYPE_CZP
;
11311 free(context
->url
);
11312 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11313 if (!(context
->url
))
11316 /* Prepare CURL handle if not yet connected */
11317 if (!context
->curl
) {
11318 context
->curl
= curl_easy_init();
11319 if (!(context
->curl
))
11323 /* Build conversion request */
11324 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11326 isds_log_message(context
,
11327 _("Could not build Czech POINT conversion request"));
11330 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11332 isds_log_message(context
,
11333 _("Could not create Czech POINT deposit name space"));
11334 xmlFreeNode(request
);
11337 xmlSetNs(request
, deposit_ns
);
11339 /* Insert children. They are in empty namespace! */
11340 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11342 isds_log_message(context
, _("Could not create empty name space"));
11346 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11347 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11348 document
->dmFileDescr
);
11350 /* Document encoded in Base64 */
11351 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11352 document
->data
, document
->data_length
);
11353 if (err
) goto leave
;
11355 isds_log(ILF_ISDS
, ILL_DEBUG
,
11356 _("Submitting document for conversion into Czech POINT deposit"));
11358 /* Send conversion request */
11359 err
= _czp_czpdeposit(context
, request
, &response
);
11360 xmlFreeNode(request
); request
= NULL
;
11363 czp_do_close_connection(context
);
11368 /* Extract response */
11369 xpath_ctx
= xmlXPathNewContext(response
);
11374 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11378 result
= xmlXPathEvalExpression(
11379 BAD_CAST
"/deposit:saveDocumentResponse/return",
11385 /* Empty response */
11386 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11387 isds_printf_message(context
,
11388 _("Missing `return' element in Czech POINT deposit response"));
11392 /* More responses */
11393 if (result
->nodesetval
->nodeNr
> 1) {
11394 isds_printf_message(context
,
11395 _("Multiple `return' element in Czech POINT deposit response"));
11400 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11403 EXTRACT_LONGINT("status", status_ptr
, 1);
11405 EXTRACT_STRING("statusMsg", string
);
11406 char *string_locale
= _isds_utf82locale(string
);
11407 isds_printf_message(context
,
11408 _("Czech POINT deposit refused document for conversion "
11409 "(code=%ld, message=%s)"),
11410 status
, string_locale
);
11411 free(string_locale
);
11416 /* Get document ID */
11417 EXTRACT_STRING("documentID", *id
);
11419 /* Get submit date */
11420 EXTRACT_STRING("dateInserted", string
);
11422 *date
= calloc(1, sizeof(**date
));
11427 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11429 if (err
== IE_NOTSUP
) {
11431 char *string_locale
= _isds_utf82locale(string
);
11432 isds_printf_message(context
,
11433 _("Invalid dateInserted value: %s"), string_locale
);
11434 free(string_locale
);
11442 xmlXPathFreeObject(result
);
11443 xmlXPathFreeContext(xpath_ctx
);
11445 xmlFreeDoc(response
);
11446 xmlFreeNode(request
);
11449 char *id_locale
= _isds_utf82locale((char *) *id
);
11450 isds_log(ILF_ISDS
, ILL_DEBUG
,
11451 _("Document %s has been submitted for conversion "
11452 "to server successfully\n"), id_locale
);
11455 #else /* not HAVE_LIBCURL */
11462 /* Close possibly opened connection to Czech POINT document deposit.
11463 * @context is Czech POINT session context. */
11464 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11465 if (!context
) return IE_INVALID_CONTEXT
;
11466 zfree(context
->long_message
);
11468 return czp_do_close_connection(context
);
11475 /* Send request for new box creation in testing ISDS instance.
11476 * It's not possible to request for a production box currently, as it
11477 * communicates via e-mail.
11478 * XXX: This function does not work either. Server complains about invalid
11480 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11482 * @context is special session context for box creation request. DO NOT use
11483 * standard context as it could reveal your password. Use fresh new context or
11484 * context previously used by this function.
11485 * @box is box description to create including single primary user (in case of
11486 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11487 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11488 * box, or contact address of PFO box owner). The email member is mandatory as
11489 * it will be used to deliver credentials.
11490 * @former_names is former name of box owner. Pass NULL if you don't care.
11491 * @approval is optional external approval of box manipulation
11492 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11493 * NULL, if you don't care.*/
11494 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11495 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11496 const char *former_names
, const struct isds_approval
*approval
,
11497 char **refnumber
) {
11498 isds_error err
= IE_SUCCESS
;
11500 xmlNodePtr request
= NULL
;
11501 xmlDocPtr response
= NULL
;
11502 xmlXPathContextPtr xpath_ctx
= NULL
;
11503 xmlXPathObjectPtr result
= NULL
;
11507 if (!context
) return IE_INVALID_CONTEXT
;
11508 zfree(context
->long_message
);
11509 if (!box
) return IE_INVAL
;
11512 if (!box
->email
|| box
->email
[0] == '\0') {
11513 isds_log_message(context
, _("E-mail field is mandatory"));
11517 /* Scratch box ID */
11520 /* Store configuration */
11521 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11522 free(context
->url
);
11523 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11524 if (!(context
->url
))
11527 /* Prepare CURL handle if not yet connected */
11528 if (!context
->curl
) {
11529 context
->curl
= curl_easy_init();
11530 if (!(context
->curl
))
11534 /* Build CreateDataBox request */
11535 err
= build_CreateDBInput_request(context
,
11536 &request
, BAD_CAST
"CreateDataBox",
11537 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11538 if (err
) goto leave
;
11540 /* Send it to server and process response */
11541 err
= send_destroy_request_check_response(context
,
11542 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11543 &response
, (xmlChar
**) refnumber
, NULL
);
11544 if (err
) goto leave
;
11546 /* Extract box ID */
11547 xpath_ctx
= xmlXPathNewContext(response
);
11552 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11556 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11559 xmlXPathFreeObject(result
);
11560 xmlXPathFreeContext(xpath_ctx
);
11561 xmlFreeDoc(response
);
11562 xmlFreeNode(request
);
11565 isds_log(ILF_ISDS
, ILL_DEBUG
,
11566 _("CreateDataBox request processed by server successfully.\n"));
11568 #else /* not HAVE_LIBCURL */
11576 /* Submit CMS signed message to ISDS to verify its originality. This is
11577 * stronger form of isds_verify_message_hash() because ISDS does more checks
11578 * than simple one (potentialy old weak) hash comparison.
11579 * @context is session context
11580 * @message is memory with raw CMS signed message bit stream
11581 * @length is @message size in bytes
11583 * IE_SUCCESS if message originates in ISDS
11584 * IE_NOTEQUAL if message is unknown to ISDS
11585 * other code for other errors */
11586 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11587 const void *message
, size_t length
) {
11588 isds_error err
= IE_SUCCESS
;
11590 xmlNsPtr isds_ns
= NULL
;
11591 xmlNodePtr request
= NULL
;
11592 xmlDocPtr response
= NULL
;
11593 xmlXPathContextPtr xpath_ctx
= NULL
;
11594 xmlXPathObjectPtr result
= NULL
;
11595 _Bool
*authentic
= NULL
;
11598 if (!context
) return IE_INVALID_CONTEXT
;
11599 zfree(context
->long_message
);
11600 if (!message
|| length
== 0) return IE_INVAL
;
11603 /* Check if connection is established
11604 * TODO: This check should be done downstairs. */
11605 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11608 /* Build AuthenticateMessage request */
11609 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11611 isds_log_message(context
,
11612 _("Could not build AuthenticateMessage request"));
11615 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11617 isds_log_message(context
, _("Could not create ISDS name space"));
11618 xmlFreeNode(request
);
11621 xmlSetNs(request
, isds_ns
);
11623 /* Insert Base64 encoded message */
11624 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11626 if (err
) goto leave
;
11628 /* Send request to server and process response */
11629 err
= send_destroy_request_check_response(context
,
11630 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11631 &response
, NULL
, NULL
);
11632 if (err
) goto leave
;
11635 /* ISDS has decided */
11636 xpath_ctx
= xmlXPathNewContext(response
);
11641 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11646 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11649 isds_log_message(context
,
11650 _("Server did not return any response on "
11651 "AuthenticateMessage request"));
11656 isds_log(ILF_ISDS
, ILL_DEBUG
,
11657 _("ISDS authenticated the message successfully\n"));
11659 isds_log_message(context
, _("ISDS does not know the message"));
11666 xmlXPathFreeObject(result
);
11667 xmlXPathFreeContext(xpath_ctx
);
11669 xmlFreeDoc(response
);
11670 xmlFreeNode(request
);
11671 #else /* not HAVE_LIBCURL */
11679 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11680 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11682 * @context is session context
11683 * @input_data is memory with raw CMS signed message or delivery info bit
11684 * stream to re-sign
11685 * @input_length is @input_data size in bytes
11686 * @output_data is pointer to auto-allocated memory where to store re-signed
11687 * input data blob. Caller must free it.
11688 * @output_data is pointer where to store @output_data size in bytes
11689 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11690 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11692 * IE_SUCCESS if CMS blob has been re-signed successfully
11693 * other code for other errors */
11694 isds_error
isds_resign_message(struct isds_ctx
*context
,
11695 const void *input_data
, size_t input_length
,
11696 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11697 isds_error err
= IE_SUCCESS
;
11699 xmlNsPtr isds_ns
= NULL
;
11700 xmlNodePtr request
= NULL
;
11701 xmlDocPtr response
= NULL
;
11702 xmlXPathContextPtr xpath_ctx
= NULL
;
11703 xmlXPathObjectPtr result
= NULL
;
11704 char *string
= NULL
;
11705 const xmlChar
*codes
[] = {
11712 const char *meanings
[] = {
11714 "Message is not original",
11715 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11716 "Time stamp could not been generated in time"
11718 const isds_error errors
[] = {
11724 struct code_map_isds_error map
= {
11726 .meanings
= meanings
,
11731 if (NULL
!= output_data
) *output_data
= NULL
;
11732 if (NULL
!= output_length
) *output_length
= 0;
11733 if (NULL
!= valid_to
) *valid_to
= NULL
;
11735 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11736 zfree(context
->long_message
);
11737 if (NULL
== input_data
|| 0 == input_length
) {
11738 isds_log_message(context
, _("Empty CMS blob on input"));
11741 if (NULL
== output_data
|| NULL
== output_length
) {
11742 isds_log_message(context
,
11743 _("NULL pointer provided for output CMS blob"));
11748 /* Check if connection is established
11749 * TODO: This check should be done downstairs. */
11750 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11753 /* Build Re-signISDSDocument request */
11754 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11756 isds_log_message(context
,
11757 _("Could not build Re-signISDSDocument request"));
11760 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11762 isds_log_message(context
, _("Could not create ISDS name space"));
11763 xmlFreeNode(request
);
11766 xmlSetNs(request
, isds_ns
);
11768 /* Insert Base64 encoded CMS blob */
11769 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11770 input_data
, input_length
);
11771 if (err
) goto leave
;
11773 /* Send request to server and process response */
11774 err
= send_destroy_request_check_response(context
,
11775 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11776 &response
, NULL
, &map
);
11777 if (err
) goto leave
;
11780 /* Extract re-signed data */
11781 xpath_ctx
= xmlXPathNewContext(response
);
11786 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11790 result
= xmlXPathEvalExpression(
11791 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11796 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11797 isds_log_message(context
,
11798 _("Missing Re-signISDSDocumentResponse element"));
11802 if (result
->nodesetval
->nodeNr
> 1) {
11803 isds_log_message(context
,
11804 _("Multiple Re-signISDSDocumentResponse element"));
11808 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11809 xmlXPathFreeObject(result
); result
= NULL
;
11811 EXTRACT_STRING("isds:dmResultDoc", string
);
11812 /* Decode non-empty data */
11813 if (NULL
!= string
&& string
[0] != '\0') {
11814 *output_length
= _isds_b64decode(string
, output_data
);
11815 if (*output_length
== (size_t) -1) {
11816 isds_log_message(context
,
11817 _("Error while Base64-decoding re-signed data"));
11822 isds_log_message(context
, _("Server did not send re-signed data"));
11828 if (NULL
!= valid_to
) {
11829 /* Get time stamp expiration date */
11830 EXTRACT_STRING("isds:dmValidTo", string
);
11831 if (NULL
!= string
) {
11832 *valid_to
= calloc(1, sizeof(**valid_to
));
11837 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11839 if (err
== IE_NOTSUP
) {
11841 char *string_locale
= _isds_utf82locale(string
);
11842 isds_printf_message(context
,
11843 _("Invalid dmValidTo value: %s"), string_locale
);
11844 free(string_locale
);
11854 xmlXPathFreeObject(result
);
11855 xmlXPathFreeContext(xpath_ctx
);
11857 xmlFreeDoc(response
);
11858 xmlFreeNode(request
);
11859 #else /* not HAVE_LIBCURL */
11866 #undef INSERT_ELEMENT
11867 #undef CHECK_FOR_STRING_LENGTH
11868 #undef INSERT_STRING_ATTRIBUTE
11869 #undef INSERT_ULONGINTNOPTR
11870 #undef INSERT_ULONGINT
11871 #undef INSERT_LONGINT
11872 #undef INSERT_BOOLEAN
11873 #undef INSERT_SCALAR_BOOLEAN
11874 #undef INSERT_STRING
11875 #undef INSERT_STRING_WITH_NS
11876 #undef EXTRACT_STRING_ATTRIBUTE
11877 #undef EXTRACT_ULONGINT
11878 #undef EXTRACT_LONGINT
11879 #undef EXTRACT_BOOLEAN
11880 #undef EXTRACT_STRING
11883 /* Compute hash of message from raw representation and store it into envelope.
11884 * Original hash structure will be destroyed in envelope.
11885 * @context is session context
11886 * @message is message carrying raw XML message blob
11887 * @algorithm is desired hash algorithm to use */
11888 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11889 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11890 isds_error err
= IE_SUCCESS
;
11892 void *xml_stream
= NULL
;
11893 size_t xml_stream_length
;
11894 size_t phys_start
, phys_end
;
11895 char *phys_path
= NULL
;
11896 struct isds_hash
*new_hash
= NULL
;
11899 if (!context
) return IE_INVALID_CONTEXT
;
11900 zfree(context
->long_message
);
11901 if (!message
) return IE_INVAL
;
11903 if (!message
->raw
) {
11904 isds_log_message(context
,
11905 _("Message does not carry raw representation"));
11909 switch (message
->raw_type
) {
11910 case RAWTYPE_INCOMING_MESSAGE
:
11912 xml_stream
= message
->raw
;
11913 xml_stream_length
= message
->raw_length
;
11916 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11917 nsuri
= SISDS_INCOMING_NS
;
11918 xml_stream
= message
->raw
;
11919 xml_stream_length
= message
->raw_length
;
11922 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11923 nsuri
= SISDS_INCOMING_NS
;
11924 err
= _isds_extract_cms_data(context
,
11925 message
->raw
, message
->raw_length
,
11926 &xml_stream
, &xml_stream_length
);
11927 if (err
) goto leave
;
11930 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11931 nsuri
= SISDS_OUTGOING_NS
;
11932 xml_stream
= message
->raw
;
11933 xml_stream_length
= message
->raw_length
;
11936 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11937 nsuri
= SISDS_OUTGOING_NS
;
11938 err
= _isds_extract_cms_data(context
,
11939 message
->raw
, message
->raw_length
,
11940 &xml_stream
, &xml_stream_length
);
11941 if (err
) goto leave
;
11945 isds_log_message(context
, _("Bad raw representation type"));
11951 /* XXX: Hash is computed from original string representing isds:dmDm
11952 * subtree. That means no encoding, white space, xmlns attributes changes.
11953 * In other words, input for hash can be invalid XML stream. */
11954 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11955 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11956 PHYSXML_ELEMENT_SEPARATOR
,
11957 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11958 PHYSXML_ELEMENT_SEPARATOR
11959 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11963 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11964 phys_path
, &phys_start
, &phys_end
);
11967 isds_log_message(context
,
11968 _("Substring with isds:dmDM element could not be located "
11969 "in raw message"));
11975 new_hash
= calloc(1, sizeof(*new_hash
));
11980 new_hash
->algorithm
= algorithm
;
11981 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11984 isds_log_message(context
, _("Could not compute message hash"));
11988 /* Save computed hash */
11989 if (!message
->envelope
) {
11990 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11991 if (!message
->envelope
) {
11996 isds_hash_free(&message
->envelope
->hash
);
11997 message
->envelope
->hash
= new_hash
;
12001 isds_hash_free(&new_hash
);
12005 if (xml_stream
!= message
->raw
) free(xml_stream
);
12010 /* Compare two hashes.
12011 * @h1 is first hash
12012 * @h2 is another hash
12014 * IE_SUCCESS if hashes equal
12015 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12016 * IE_ENUM if not comparable, but both structures defined
12017 * IE_INVAL if some of the structures are undefined (NULL)
12018 * IE_ERROR if internal error occurs */
12019 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12020 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12021 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12022 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12023 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12024 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12026 for (size_t i
= 0; i
< h1
->length
; i
++) {
12027 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12028 return IE_NOTEQUAL
;
12034 /* Check message has gone through ISDS by comparing message hash stored in
12035 * ISDS and locally computed hash. You must provide message with valid raw
12036 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12037 * This is convenient wrapper for isds_download_message_hash(),
12038 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12039 * @context is session context
12040 * @message is message with valid raw and envelope member; envelope->hash
12041 * member will be changed during function run. Use envelope on heap only.
12043 * IE_SUCCESS if message originates in ISDS
12044 * IE_NOTEQUAL if message is unknown to ISDS
12045 * other code for other errors */
12046 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12047 struct isds_message
*message
) {
12048 isds_error err
= IE_SUCCESS
;
12049 struct isds_hash
*downloaded_hash
= NULL
;
12051 if (!context
) return IE_INVALID_CONTEXT
;
12052 zfree(context
->long_message
);
12053 if (!message
) return IE_INVAL
;
12055 if (!message
->envelope
) {
12056 isds_log_message(context
,
12057 _("Given message structure is missing envelope"));
12060 if (!message
->raw
) {
12061 isds_log_message(context
,
12062 _("Given message structure is missing raw representation"));
12066 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12068 if (err
) goto leave
;
12070 err
= isds_compute_message_hash(context
, message
,
12071 downloaded_hash
->algorithm
);
12072 if (err
) goto leave
;
12074 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12077 isds_hash_free(&downloaded_hash
);
12082 /* Search for document by document ID in list of documents. IDs are compared
12084 * @documents is list of isds_documents
12085 * @id is document identifier
12086 * @return first matching document or NULL. */
12087 const struct isds_document
*isds_find_document_by_id(
12088 const struct isds_list
*documents
, const char *id
) {
12089 const struct isds_list
*item
;
12090 const struct isds_document
*document
;
12092 for (item
= documents
; item
; item
= item
->next
) {
12093 document
= (struct isds_document
*) item
->data
;
12094 if (!document
) continue;
12096 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12104 /* Normalize @mime_type to be proper MIME type.
12105 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12106 * guess regular MIME type (e.g. "application/pdf").
12107 * @mime_type is UTF-8 encoded MIME type to fix
12108 * @return original @mime_type if no better interpretation exists, or
12109 * constant static UTF-8 encoded string with proper MIME type. */
12110 const char *isds_normalize_mime_type(const char *mime_type
) {
12111 if (!mime_type
) return NULL
;
12113 for (size_t offset
= 0;
12114 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12116 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12117 extension_map_mime
[offset
]))
12118 return (const char *) extension_map_mime
[offset
+ 1];
12125 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12126 struct isds_message **message);
12127 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12128 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12129 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12130 struct isds_address **address);
12132 int isds_message_free(struct isds_message **message);
12133 int isds_address_free(struct isds_address **address);
12137 /* Makes known all relevant namespaces to given XPath context
12138 * @xpath_ctx is XPath context
12139 * @message_ns selects proper message name space. Unsigned and signed
12140 * messages and delivery info's differ in prefix and URI. */
12141 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12142 const message_ns_type message_ns
) {
12143 const xmlChar
*message_namespace
= NULL
;
12145 if (!xpath_ctx
) return IE_ERROR
;
12147 switch(message_ns
) {
12149 message_namespace
= BAD_CAST ISDS1_NS
; break;
12150 case MESSAGE_NS_UNSIGNED
:
12151 message_namespace
= BAD_CAST ISDS_NS
; break;
12152 case MESSAGE_NS_SIGNED_INCOMING
:
12153 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12154 case MESSAGE_NS_SIGNED_OUTGOING
:
12155 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12156 case MESSAGE_NS_SIGNED_DELIVERY
:
12157 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12162 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12164 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12166 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12168 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12170 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12172 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))