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 offset
= (char*)string
+ i
;
2305 /* Parse date and time without subseconds and offset */
2306 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2313 /* Get subseconds */
2314 if (*offset
== '.' ) {
2317 /* Copy first 6 digits, pad it with zeros.
2318 * Current server implementation uses only millisecond resolution. */
2319 /* TODO: isdigit() is locale sensitive */
2321 i
< subsecond_resolution
&& isdigit(*offset
);
2323 subseconds
[i
] = *offset
;
2325 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2326 /* Check 7th digit for rounding */
2327 if (*offset
>= '5') round_up
= 1;
2330 for (; i
< subsecond_resolution
; i
++) {
2331 subseconds
[i
] = '0';
2333 subseconds
[subsecond_resolution
] = '\0';
2335 /* Convert it into integer */
2336 long_number
= strtol(subseconds
, &endptr
, 10);
2337 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2338 long_number
== LONG_MAX
) {
2342 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2343 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2344 * microseconds" and "the type shall be a signed integer capable of
2345 * storing values at least in the range [-1, 1000000]. */
2346 if (long_number
< -1 || long_number
>= 1000000) {
2350 (*time
)->tv_usec
= long_number
;
2352 /* Round the subseconds */
2354 if (999999 == (*time
)->tv_usec
) {
2355 (*time
)->tv_usec
= 0;
2362 /* move to the zone offset delimiter or signal NULL*/
2363 delim
= strchr(offset
, '-');
2365 delim
= strchr(offset
, '+');
2367 delim
= strchr(offset
, 'Z');
2371 /* Get zone offset */
2372 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2373 * "" equals to "Z" and it means UTC zone. */
2374 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2375 * colon separator */
2376 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2377 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2381 if (*offset
== '+') {
2382 broken
.tm_hour
-= offset_hours
;
2383 broken
.tm_min
-= offset_minutes
;
2385 broken
.tm_hour
+= offset_hours
;
2386 broken
.tm_min
+= offset_minutes
;
2390 /* Convert to time_t */
2391 (*time
)->tv_sec
= _isds_timegm(&broken
);
2392 if ((*time
)->tv_sec
== (time_t) -1) {
2401 /* Convert unsigned int into isds_message_status.
2402 * @context is session context
2403 * @number is pointer to number value. NULL will be treated as invalid value.
2404 * @status is automatically reallocated status
2405 * @return IE_SUCCESS, or error code and free status */
2406 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2407 const unsigned long int *number
, isds_message_status
**status
) {
2408 if (!context
) return IE_INVALID_CONTEXT
;
2409 if (!status
) return IE_INVAL
;
2411 free(*status
); *status
= NULL
;
2412 if (!number
) return IE_INVAL
;
2414 if (*number
< 1 || *number
> 10) {
2415 isds_printf_message(context
, _("Invalid message status value: %lu"),
2420 *status
= malloc(sizeof(**status
));
2421 if (!*status
) return IE_NOMEM
;
2423 **status
= 1 << *number
;
2428 /* Convert event description string into isds_event members type and
2430 * @string is raw event description starting with event prefix
2431 * @event is structure where to store type and stripped description to
2432 * @return standard error code, unknown prefix is not classified as an error.
2434 static isds_error
eventstring2event(const xmlChar
*string
,
2435 struct isds_event
* event
) {
2436 const xmlChar
*known_prefixes
[] = {
2447 const isds_event_type types
[] = {
2448 EVENT_ENTERED_SYSTEM
,
2449 EVENT_ACCEPTED_BY_RECIPIENT
,
2450 EVENT_ACCEPTED_BY_FICTION
,
2451 EVENT_UNDELIVERABLE
,
2452 EVENT_COMMERCIAL_ACCEPTED
,
2454 EVENT_PRIMARY_LOGIN
,
2455 EVENT_ENTRUSTED_LOGIN
,
2461 if (!string
|| !event
) return IE_INVAL
;
2464 event
->type
= malloc(sizeof(*event
->type
));
2465 if (!(event
->type
)) return IE_NOMEM
;
2467 zfree(event
->description
);
2469 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2471 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2473 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2474 /* Prefix is known */
2475 *event
->type
= types
[index
];
2477 /* Strip prefix from description and spaces */
2478 /* TODO: Recognize all white spaces from UCS blank class and
2479 * operate on UTF-8 chars. */
2480 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2481 event
->description
= strdup((char *) (string
+ length
));
2482 if (!(event
->description
)) return IE_NOMEM
;
2488 /* Unknown event prefix.
2489 * XSD allows any string */
2490 char *string_locale
= _isds_utf82locale((char *) string
);
2491 isds_log(ILF_ISDS
, ILL_WARNING
,
2492 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2493 free(string_locale
);
2495 *event
->type
= EVENT_UKNOWN
;
2496 event
->description
= strdup((char *) string
);
2497 if (!(event
->description
)) return IE_NOMEM
;
2503 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2504 * and leave label */
2505 #define EXTRACT_STRING(element, string) { \
2506 xmlXPathFreeObject(result); \
2507 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2508 if (NULL == (result)) { \
2512 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2513 if (result->nodesetval->nodeNr > 1) { \
2514 isds_printf_message(context, _("Multiple %s element"), element); \
2518 (string) = (char *) \
2519 xmlXPathCastNodeSetToString(result->nodesetval); \
2520 if (NULL == (string)) { \
2527 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2529 char *string = NULL; \
2530 EXTRACT_STRING(element, string); \
2533 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2534 if (!(booleanPtr)) { \
2540 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2541 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2542 *(booleanPtr) = 1; \
2543 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2544 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2545 *(booleanPtr) = 0; \
2547 char *string_locale = _isds_utf82locale((char*)string); \
2548 isds_printf_message(context, \
2549 _("%s value is not valid boolean: %s"), \
2550 element, string_locale); \
2551 free(string_locale); \
2561 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2563 char *string = NULL; \
2564 EXTRACT_STRING(element, string); \
2566 if (NULL == string) { \
2567 isds_printf_message(context, _("%s element is empty"), element); \
2571 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2572 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2574 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2575 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2578 char *string_locale = _isds_utf82locale((char*)string); \
2579 isds_printf_message(context, \
2580 _("%s value is not valid boolean: %s"), \
2581 element, string_locale); \
2582 free(string_locale); \
2591 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2593 char *string = NULL; \
2594 EXTRACT_STRING(element, string); \
2599 number = strtol((char*)string, &endptr, 10); \
2601 if (*endptr != '\0') { \
2602 char *string_locale = _isds_utf82locale((char *)string); \
2603 isds_printf_message(context, \
2604 _("%s is not valid integer: %s"), \
2605 element, string_locale); \
2606 free(string_locale); \
2612 if (number == LONG_MIN || number == LONG_MAX) { \
2613 char *string_locale = _isds_utf82locale((char *)string); \
2614 isds_printf_message(context, \
2615 _("%s value out of range of long int: %s"), \
2616 element, string_locale); \
2617 free(string_locale); \
2623 free(string); string = NULL; \
2625 if (!(preallocated)) { \
2626 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2627 if (!(longintPtr)) { \
2632 *(longintPtr) = number; \
2636 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2638 char *string = NULL; \
2639 EXTRACT_STRING(element, string); \
2644 number = strtol((char*)string, &endptr, 10); \
2646 if (*endptr != '\0') { \
2647 char *string_locale = _isds_utf82locale((char *)string); \
2648 isds_printf_message(context, \
2649 _("%s is not valid integer: %s"), \
2650 element, string_locale); \
2651 free(string_locale); \
2657 if (number == LONG_MIN || number == LONG_MAX) { \
2658 char *string_locale = _isds_utf82locale((char *)string); \
2659 isds_printf_message(context, \
2660 _("%s value out of range of long int: %s"), \
2661 element, string_locale); \
2662 free(string_locale); \
2668 free(string); string = NULL; \
2670 isds_printf_message(context, \
2671 _("%s value is negative: %ld"), element, number); \
2676 if (!(preallocated)) { \
2677 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2678 if (!(ulongintPtr)) { \
2683 *(ulongintPtr) = number; \
2687 #define EXTRACT_DATE(element, tmPtr) { \
2688 char *string = NULL; \
2689 EXTRACT_STRING(element, string); \
2690 if (NULL != string) { \
2691 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2692 if (NULL == (tmPtr)) { \
2697 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2699 if (err == IE_NOTSUP) { \
2701 char *string_locale = _isds_utf82locale(string); \
2702 char *element_locale = _isds_utf82locale(element); \
2703 isds_printf_message(context, _("Invalid %s value: %s"), \
2704 element_locale, string_locale); \
2705 free(string_locale); \
2706 free(element_locale); \
2715 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2716 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2718 if ((required) && (!string)) { \
2719 char *attribute_locale = _isds_utf82locale(attribute); \
2720 char *element_locale = \
2721 _isds_utf82locale((char *)xpath_ctx->node->name); \
2722 isds_printf_message(context, \
2723 _("Could not extract required %s attribute value from " \
2724 "%s element"), attribute_locale, element_locale); \
2725 free(element_locale); \
2726 free(attribute_locale); \
2733 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2735 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2736 (xmlChar *) (string)); \
2738 isds_printf_message(context, \
2739 _("Could not add %s child to %s element"), \
2740 element, (parent)->name); \
2746 #define INSERT_STRING(parent, element, string) \
2747 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2749 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2751 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2752 else { INSERT_STRING(parent, element, "false"); } \
2755 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2758 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2760 INSERT_STRING(parent, element, NULL); \
2764 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2765 if ((longintPtr)) { \
2766 /* FIXME: locale sensitive */ \
2767 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2771 INSERT_STRING(parent, element, buffer) \
2772 free(buffer); (buffer) = NULL; \
2773 } else { INSERT_STRING(parent, element, NULL) } \
2776 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2777 if ((ulongintPtr)) { \
2778 /* FIXME: locale sensitive */ \
2779 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2783 INSERT_STRING(parent, element, buffer) \
2784 free(buffer); (buffer) = NULL; \
2785 } else { INSERT_STRING(parent, element, NULL) } \
2788 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2790 /* FIXME: locale sensitive */ \
2791 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2795 INSERT_STRING(parent, element, buffer) \
2796 free(buffer); (buffer) = NULL; \
2799 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2801 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2803 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2804 (xmlChar *) (string)); \
2805 if (!attribute_node) { \
2806 isds_printf_message(context, _("Could not add %s " \
2807 "attribute to %s element"), \
2808 (attribute), (parent)->name); \
2814 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2816 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2817 if (length > (maximum)) { \
2818 isds_printf_message(context, \
2819 ngettext("%s has more than %d characters", \
2820 "%s has more than %d characters", (maximum)), \
2821 (name), (maximum)); \
2825 if (length < (minimum)) { \
2826 isds_printf_message(context, \
2827 ngettext("%s has less than %d characters", \
2828 "%s has less than %d characters", (minimum)), \
2829 (name), (minimum)); \
2836 #define INSERT_ELEMENT(child, parent, element) \
2838 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2840 isds_printf_message(context, \
2841 _("Could not add %s child to %s element"), \
2842 (element), (parent)->name); \
2849 /* Find child element by name in given XPath context and switch context onto
2850 * it. The child must be uniq and must exist. Otherwise fails.
2851 * @context is ISDS context
2852 * @child is child element name
2853 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2854 * into it child. In error case, the @xpath_ctx keeps original value. */
2855 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2856 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2857 isds_error err
= IE_SUCCESS
;
2858 xmlXPathObjectPtr result
= NULL
;
2860 if (!context
) return IE_INVALID_CONTEXT
;
2861 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2864 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2871 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2872 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2873 char *child_locale
= _isds_utf82locale((char*) child
);
2874 isds_printf_message(context
,
2875 _("%s element does not contain %s child"),
2876 parent_locale
, child_locale
);
2878 free(parent_locale
);
2884 if (result
->nodesetval
->nodeNr
> 1) {
2885 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2886 char *child_locale
= _isds_utf82locale((char*) child
);
2887 isds_printf_message(context
,
2888 _("%s element contains multiple %s children"),
2889 parent_locale
, child_locale
);
2891 free(parent_locale
);
2896 /* Switch context */
2897 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2900 xmlXPathFreeObject(result
);
2907 /* Find and convert XSD:gPersonName group in current node into structure
2908 * @context is ISDS context
2909 * @personName is automatically reallocated person name structure. If no member
2910 * value is found, will be freed.
2911 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2913 * In case of error @personName will be freed. */
2914 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2915 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2916 isds_error err
= IE_SUCCESS
;
2917 xmlXPathObjectPtr result
= NULL
;
2919 if (!context
) return IE_INVALID_CONTEXT
;
2920 if (!personName
) return IE_INVAL
;
2921 isds_PersonName_free(personName
);
2922 if (!xpath_ctx
) return IE_INVAL
;
2925 *personName
= calloc(1, sizeof(**personName
));
2931 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2932 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2933 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2934 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2936 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2937 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2938 isds_PersonName_free(personName
);
2941 if (err
) isds_PersonName_free(personName
);
2942 xmlXPathFreeObject(result
);
2947 /* Find and convert XSD:gAddress group in current node into structure
2948 * @context is ISDS context
2949 * @address is automatically reallocated address structure. If no member
2950 * value is found, will be freed.
2951 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2953 * In case of error @address will be freed. */
2954 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2955 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2956 isds_error err
= IE_SUCCESS
;
2957 xmlXPathObjectPtr result
= NULL
;
2959 if (!context
) return IE_INVALID_CONTEXT
;
2960 if (!address
) return IE_INVAL
;
2961 isds_Address_free(address
);
2962 if (!xpath_ctx
) return IE_INVAL
;
2965 *address
= calloc(1, sizeof(**address
));
2971 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2972 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2973 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2974 EXTRACT_STRING("isds:adNumberInMunicipality",
2975 (*address
)->adNumberInMunicipality
);
2976 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2977 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2979 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2980 !(*address
)->adNumberInStreet
&&
2981 !(*address
)->adNumberInMunicipality
&&
2982 !(*address
)->adZipCode
&& !(*address
)->adState
)
2983 isds_Address_free(address
);
2986 if (err
) isds_Address_free(address
);
2987 xmlXPathFreeObject(result
);
2992 /* Find and convert isds:biDate element in current node into structure
2993 * @context is ISDS context
2994 * @biDate is automatically reallocated birth date structure. If no member
2995 * value is found, will be freed.
2996 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2998 * In case of error @biDate will be freed. */
2999 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3000 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3001 isds_error err
= IE_SUCCESS
;
3002 xmlXPathObjectPtr result
= NULL
;
3003 char *string
= NULL
;
3005 if (!context
) return IE_INVALID_CONTEXT
;
3006 if (!biDate
) return IE_INVAL
;
3008 if (!xpath_ctx
) return IE_INVAL
;
3010 EXTRACT_STRING("isds:biDate", string
);
3012 *biDate
= calloc(1, sizeof(**biDate
));
3017 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3019 if (err
== IE_NOTSUP
) {
3021 char *string_locale
= _isds_utf82locale(string
);
3022 isds_printf_message(context
,
3023 _("Invalid isds:biDate value: %s"), string_locale
);
3024 free(string_locale
);
3031 if (err
) zfree(*biDate
);
3033 xmlXPathFreeObject(result
);
3038 /* Convert isds:dBOwnerInfo XML tree into structure
3039 * @context is ISDS context
3040 * @db_owner_info is automatically reallocated box owner info structure
3041 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3042 * In case of error @db_owner_info will be freed. */
3043 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3044 struct isds_DbOwnerInfo
**db_owner_info
,
3045 xmlXPathContextPtr xpath_ctx
) {
3046 isds_error err
= IE_SUCCESS
;
3047 xmlXPathObjectPtr result
= NULL
;
3048 char *string
= NULL
;
3050 if (!context
) return IE_INVALID_CONTEXT
;
3051 if (!db_owner_info
) return IE_INVAL
;
3052 isds_DbOwnerInfo_free(db_owner_info
);
3053 if (!xpath_ctx
) return IE_INVAL
;
3056 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3057 if (!*db_owner_info
) {
3062 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3064 EXTRACT_STRING("isds:dbType", string
);
3066 (*db_owner_info
)->dbType
=
3067 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3068 if (!(*db_owner_info
)->dbType
) {
3072 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3074 zfree((*db_owner_info
)->dbType
);
3075 if (err
== IE_ENUM
) {
3077 char *string_locale
= _isds_utf82locale(string
);
3078 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3080 free(string_locale
);
3087 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3089 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3091 if (err
) goto leave
;
3093 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3095 (*db_owner_info
)->birthInfo
=
3096 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3097 if (!(*db_owner_info
)->birthInfo
) {
3101 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3103 if (err
) goto leave
;
3104 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3105 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3106 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3107 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3108 !(*db_owner_info
)->birthInfo
->biCity
&&
3109 !(*db_owner_info
)->birthInfo
->biCounty
&&
3110 !(*db_owner_info
)->birthInfo
->biState
)
3111 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3113 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3114 if (err
) goto leave
;
3116 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3117 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3118 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3119 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3120 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3122 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3124 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3125 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3126 (*db_owner_info
)->dbOpenAddressing
);
3129 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3131 xmlXPathFreeObject(result
);
3136 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3137 * @context is session context
3138 * @owner is libisds structure with box description
3139 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3140 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3141 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3143 isds_error err
= IE_SUCCESS
;
3145 xmlChar
*string
= NULL
;
3147 if (!context
) return IE_INVALID_CONTEXT
;
3148 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3151 /* Build XSD:tDbOwnerInfo */
3152 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3153 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3156 if (owner
->dbType
) {
3157 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3159 isds_printf_message(context
, _("Invalid dbType value: %d"),
3164 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3166 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3167 if (owner
->personName
) {
3168 INSERT_STRING(db_owner_info
, "pnFirstName",
3169 owner
->personName
->pnFirstName
);
3170 INSERT_STRING(db_owner_info
, "pnMiddleName",
3171 owner
->personName
->pnMiddleName
);
3172 INSERT_STRING(db_owner_info
, "pnLastName",
3173 owner
->personName
->pnLastName
);
3174 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3175 owner
->personName
->pnLastNameAtBirth
);
3177 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3178 if (owner
->birthInfo
) {
3179 if (owner
->birthInfo
->biDate
) {
3180 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3181 INSERT_STRING(db_owner_info
, "biDate", string
);
3182 free(string
); string
= NULL
;
3184 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3185 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3186 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3188 if (owner
->address
) {
3189 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3190 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3191 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3192 owner
->address
->adNumberInStreet
);
3193 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3194 owner
->address
->adNumberInMunicipality
);
3195 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3196 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3198 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3199 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3200 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3202 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3203 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3205 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3206 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3208 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3210 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3211 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3212 owner
->dbOpenAddressing
);
3220 /* Convert XSD:tDbUserInfo XML tree into structure
3221 * @context is ISDS context
3222 * @db_user_info is automatically reallocated user info structure
3223 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3224 * In case of error @db_user_info will be freed. */
3225 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3226 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3227 isds_error err
= IE_SUCCESS
;
3228 xmlXPathObjectPtr result
= NULL
;
3229 char *string
= NULL
;
3231 if (!context
) return IE_INVALID_CONTEXT
;
3232 if (!db_user_info
) return IE_INVAL
;
3233 isds_DbUserInfo_free(db_user_info
);
3234 if (!xpath_ctx
) return IE_INVAL
;
3237 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3238 if (!*db_user_info
) {
3243 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3245 EXTRACT_STRING("isds:userType", string
);
3247 (*db_user_info
)->userType
=
3248 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3249 if (!(*db_user_info
)->userType
) {
3253 err
= string2isds_UserType((xmlChar
*)string
,
3254 (*db_user_info
)->userType
);
3256 zfree((*db_user_info
)->userType
);
3257 if (err
== IE_ENUM
) {
3259 char *string_locale
= _isds_utf82locale(string
);
3260 isds_printf_message(context
,
3261 _("Unknown isds:userType value: %s"), string_locale
);
3262 free(string_locale
);
3269 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3271 (*db_user_info
)->personName
=
3272 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3273 if (!(*db_user_info
)->personName
) {
3278 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3280 if (err
) goto leave
;
3282 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3283 if (err
) goto leave
;
3285 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3286 if (err
) goto leave
;
3288 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3289 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3291 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3292 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3293 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3295 /* ???: Default value is "CZ" according specification. Should we provide
3297 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3300 if (err
) isds_DbUserInfo_free(db_user_info
);
3302 xmlXPathFreeObject(result
);
3307 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3308 * @context is session context
3309 * @user is libisds structure with user description
3310 * @db_user_info is XML element of XSD:tDbUserInfo */
3311 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3312 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3314 isds_error err
= IE_SUCCESS
;
3316 xmlChar
*string
= NULL
;
3318 if (!context
) return IE_INVALID_CONTEXT
;
3319 if (!user
|| !db_user_info
) return IE_INVAL
;
3321 /* Build XSD:tDbUserInfo */
3322 if (user
->personName
) {
3323 INSERT_STRING(db_user_info
, "pnFirstName",
3324 user
->personName
->pnFirstName
);
3325 INSERT_STRING(db_user_info
, "pnMiddleName",
3326 user
->personName
->pnMiddleName
);
3327 INSERT_STRING(db_user_info
, "pnLastName",
3328 user
->personName
->pnLastName
);
3329 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3330 user
->personName
->pnLastNameAtBirth
);
3332 if (user
->address
) {
3333 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3334 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3335 INSERT_STRING(db_user_info
, "adNumberInStreet",
3336 user
->address
->adNumberInStreet
);
3337 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3338 user
->address
->adNumberInMunicipality
);
3339 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3340 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3343 if (!tm2datestring(user
->biDate
, &string
))
3344 INSERT_STRING(db_user_info
, "biDate", string
);
3347 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3348 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3351 if (user
->userType
) {
3352 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3354 isds_printf_message(context
, _("Invalid userType value: %d"),
3359 INSERT_STRING(db_user_info
, "userType", type_string
);
3362 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3363 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3364 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3365 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3366 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3367 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3368 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3369 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3370 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3378 /* Convert XSD:tPDZRec XML tree into structure
3379 * @context is ISDS context
3380 * @permission is automatically reallocated commercial permission structure
3381 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3382 * In case of error @permission will be freed. */
3383 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3384 struct isds_commercial_permission
**permission
,
3385 xmlXPathContextPtr xpath_ctx
) {
3386 isds_error err
= IE_SUCCESS
;
3387 xmlXPathObjectPtr result
= NULL
;
3388 char *string
= NULL
;
3390 if (!context
) return IE_INVALID_CONTEXT
;
3391 if (!permission
) return IE_INVAL
;
3392 isds_commercial_permission_free(permission
);
3393 if (!xpath_ctx
) return IE_INVAL
;
3396 *permission
= calloc(1, sizeof(**permission
));
3402 EXTRACT_STRING("isds:PDZType", string
);
3404 err
= string2isds_payment_type((xmlChar
*)string
,
3405 &(*permission
)->type
);
3407 if (err
== IE_ENUM
) {
3409 char *string_locale
= _isds_utf82locale(string
);
3410 isds_printf_message(context
,
3411 _("Unknown isds:PDZType value: %s"), string_locale
);
3412 free(string_locale
);
3419 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3420 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3422 EXTRACT_STRING("isds:PDZExpire", string
);
3424 err
= timestring2timeval((xmlChar
*) string
,
3425 &((*permission
)->expiration
));
3427 char *string_locale
= _isds_utf82locale(string
);
3428 if (err
== IE_DATE
) err
= IE_ISDS
;
3429 isds_printf_message(context
,
3430 _("Could not convert PDZExpire as ISO time: %s"),
3432 free(string_locale
);
3438 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3439 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3442 if (err
) isds_commercial_permission_free(permission
);
3444 xmlXPathFreeObject(result
);
3449 /* Convert XSD:tCiRecord XML tree into structure
3450 * @context is ISDS context
3451 * @event is automatically reallocated commercial credit event structure
3452 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3453 * In case of error @event will be freed. */
3454 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3455 struct isds_credit_event
**event
,
3456 xmlXPathContextPtr xpath_ctx
) {
3457 isds_error err
= IE_SUCCESS
;
3458 xmlXPathObjectPtr result
= NULL
;
3459 char *string
= NULL
;
3460 long int *number_ptr
;
3462 if (!context
) return IE_INVALID_CONTEXT
;
3463 if (!event
) return IE_INVAL
;
3464 isds_credit_event_free(event
);
3465 if (!xpath_ctx
) return IE_INVAL
;
3468 *event
= calloc(1, sizeof(**event
));
3474 EXTRACT_STRING("isds:ciEventTime", string
);
3476 err
= timestring2timeval((xmlChar
*) string
,
3479 char *string_locale
= _isds_utf82locale(string
);
3480 if (err
== IE_DATE
) err
= IE_ISDS
;
3481 isds_printf_message(context
,
3482 _("Could not convert ciEventTime as ISO time: %s"),
3484 free(string_locale
);
3490 EXTRACT_STRING("isds:ciEventType", string
);
3492 err
= string2isds_credit_event_type((xmlChar
*)string
,
3495 if (err
== IE_ENUM
) {
3497 char *string_locale
= _isds_utf82locale(string
);
3498 isds_printf_message(context
,
3499 _("Unknown isds:ciEventType value: %s"), string_locale
);
3500 free(string_locale
);
3507 number_ptr
= &((*event
)->credit_change
);
3508 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3509 number_ptr
= &(*event
)->new_credit
;
3510 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3512 switch((*event
)->type
) {
3513 case ISDS_CREDIT_CHARGED
:
3514 EXTRACT_STRING("isds:ciTransID",
3515 (*event
)->details
.charged
.transaction
);
3517 case ISDS_CREDIT_DISCHARGED
:
3518 EXTRACT_STRING("isds:ciTransID",
3519 (*event
)->details
.discharged
.transaction
);
3521 case ISDS_CREDIT_MESSAGE_SENT
:
3522 EXTRACT_STRING("isds:ciRecipientID",
3523 (*event
)->details
.message_sent
.recipient
);
3524 EXTRACT_STRING("isds:ciPDZID",
3525 (*event
)->details
.message_sent
.message_id
);
3527 case ISDS_CREDIT_STORAGE_SET
:
3528 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3529 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3530 EXTRACT_DATE("isds:ciNewFrom",
3531 (*event
)->details
.storage_set
.new_valid_from
);
3532 EXTRACT_DATE("isds:ciNewTo",
3533 (*event
)->details
.storage_set
.new_valid_to
);
3534 EXTRACT_LONGINT("isds:ciOldCapacity",
3535 (*event
)->details
.storage_set
.old_capacity
, 0);
3536 EXTRACT_DATE("isds:ciOldFrom",
3537 (*event
)->details
.storage_set
.old_valid_from
);
3538 EXTRACT_DATE("isds:ciOldTo",
3539 (*event
)->details
.storage_set
.old_valid_to
);
3540 EXTRACT_STRING("isds:ciDoneBy",
3541 (*event
)->details
.storage_set
.initiator
);
3543 case ISDS_CREDIT_EXPIRED
:
3548 if (err
) isds_credit_event_free(event
);
3550 xmlXPathFreeObject(result
);
3555 #endif /* HAVE_LIBCURL */
3558 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3559 * isds_envelope structure. The envelope is automatically allocated but not
3560 * reallocated. The date are just appended into envelope structure.
3561 * @context is ISDS context
3562 * @envelope is automatically allocated message envelope structure
3563 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3564 * In case of error @envelope will be freed. */
3565 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3566 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3567 isds_error err
= IE_SUCCESS
;
3568 xmlXPathObjectPtr result
= NULL
;
3570 if (!context
) return IE_INVALID_CONTEXT
;
3571 if (!envelope
) return IE_INVAL
;
3572 if (!xpath_ctx
) return IE_INVAL
;
3576 /* Allocate envelope */
3577 *envelope
= calloc(1, sizeof(**envelope
));
3583 /* Else free former data */
3584 zfree((*envelope
)->dmSenderOrgUnit
);
3585 zfree((*envelope
)->dmSenderOrgUnitNum
);
3586 zfree((*envelope
)->dbIDRecipient
);
3587 zfree((*envelope
)->dmRecipientOrgUnit
);
3588 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3589 zfree((*envelope
)->dmToHands
);
3590 zfree((*envelope
)->dmAnnotation
);
3591 zfree((*envelope
)->dmRecipientRefNumber
);
3592 zfree((*envelope
)->dmSenderRefNumber
);
3593 zfree((*envelope
)->dmRecipientIdent
);
3594 zfree((*envelope
)->dmSenderIdent
);
3595 zfree((*envelope
)->dmLegalTitleLaw
);
3596 zfree((*envelope
)->dmLegalTitleYear
);
3597 zfree((*envelope
)->dmLegalTitleSect
);
3598 zfree((*envelope
)->dmLegalTitlePar
);
3599 zfree((*envelope
)->dmLegalTitlePoint
);
3600 zfree((*envelope
)->dmPersonalDelivery
);
3601 zfree((*envelope
)->dmAllowSubstDelivery
);
3604 /* Extract envelope elements added by sender or ISDS
3605 * (XSD: gMessageEnvelopeSub type) */
3606 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3607 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3608 (*envelope
)->dmSenderOrgUnitNum
, 0);
3609 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3610 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3611 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3612 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3613 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3614 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3615 EXTRACT_STRING("isds:dmRecipientRefNumber",
3616 (*envelope
)->dmRecipientRefNumber
);
3617 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3618 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3619 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3621 /* Extract envelope elements regarding law reference */
3622 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3623 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3624 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3625 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3626 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3628 /* Extract envelope other elements */
3629 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3630 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3631 (*envelope
)->dmAllowSubstDelivery
);
3634 if (err
) isds_envelope_free(envelope
);
3635 xmlXPathFreeObject(result
);
3641 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3642 * isds_envelope structure. The envelope is automatically allocated but not
3643 * reallocated. The date are just appended into envelope structure.
3644 * @context is ISDS context
3645 * @envelope is automatically allocated message envelope structure
3646 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3647 * In case of error @envelope will be freed. */
3648 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3649 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3650 isds_error err
= IE_SUCCESS
;
3651 xmlXPathObjectPtr result
= NULL
;
3653 if (!context
) return IE_INVALID_CONTEXT
;
3654 if (!envelope
) return IE_INVAL
;
3655 if (!xpath_ctx
) return IE_INVAL
;
3659 /* Allocate envelope */
3660 *envelope
= calloc(1, sizeof(**envelope
));
3666 /* Else free former data */
3667 zfree((*envelope
)->dmID
);
3668 zfree((*envelope
)->dbIDSender
);
3669 zfree((*envelope
)->dmSender
);
3670 zfree((*envelope
)->dmSenderAddress
);
3671 zfree((*envelope
)->dmSenderType
);
3672 zfree((*envelope
)->dmRecipient
);
3673 zfree((*envelope
)->dmRecipientAddress
);
3674 zfree((*envelope
)->dmAmbiguousRecipient
);
3677 /* Extract envelope elements added by ISDS
3678 * (XSD: gMessageEnvelope type) */
3679 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3680 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3681 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3682 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3683 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3684 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3685 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3686 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3687 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3688 (*envelope
)->dmAmbiguousRecipient
);
3690 /* Extract envelope elements added by sender and ISDS
3691 * (XSD: gMessageEnvelope type) */
3692 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3693 if (err
) goto leave
;
3696 if (err
) isds_envelope_free(envelope
);
3697 xmlXPathFreeObject(result
);
3702 /* Convert other envelope elements from XML tree into isds_envelope structure:
3703 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3704 * The envelope is automatically allocated but not reallocated.
3705 * The data are just appended into envelope structure.
3706 * @context is ISDS context
3707 * @envelope is automatically allocated message envelope structure
3708 * @xpath_ctx is XPath context with current node as parent desired elements
3709 * In case of error @envelope will be freed. */
3710 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3711 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3712 isds_error err
= IE_SUCCESS
;
3713 xmlXPathObjectPtr result
= NULL
;
3714 char *string
= NULL
;
3715 unsigned long int *unumber
= NULL
;
3717 if (!context
) return IE_INVALID_CONTEXT
;
3718 if (!envelope
) return IE_INVAL
;
3719 if (!xpath_ctx
) return IE_INVAL
;
3724 *envelope
= calloc(1, sizeof(**envelope
));
3731 zfree((*envelope
)->dmMessageStatus
);
3732 zfree((*envelope
)->dmAttachmentSize
);
3733 zfree((*envelope
)->dmDeliveryTime
);
3734 zfree((*envelope
)->dmAcceptanceTime
);
3738 /* dmMessageStatus element is mandatory */
3739 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3741 isds_log_message(context
,
3742 _("Missing mandatory sisds:dmMessageStatus integer"));
3746 err
= uint2isds_message_status(context
, unumber
,
3747 &((*envelope
)->dmMessageStatus
));
3749 if (err
== IE_ENUM
) err
= IE_ISDS
;
3752 free(unumber
); unumber
= NULL
;
3754 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3757 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3759 err
= timestring2timeval((xmlChar
*) string
,
3760 &((*envelope
)->dmDeliveryTime
));
3762 char *string_locale
= _isds_utf82locale(string
);
3763 if (err
== IE_DATE
) err
= IE_ISDS
;
3764 isds_printf_message(context
,
3765 _("Could not convert dmDeliveryTime as ISO time: %s"),
3767 free(string_locale
);
3773 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3775 err
= timestring2timeval((xmlChar
*) string
,
3776 &((*envelope
)->dmAcceptanceTime
));
3778 char *string_locale
= _isds_utf82locale(string
);
3779 if (err
== IE_DATE
) err
= IE_ISDS
;
3780 isds_printf_message(context
,
3781 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3783 free(string_locale
);
3790 if (err
) isds_envelope_free(envelope
);
3793 xmlXPathFreeObject(result
);
3798 /* Convert message type attribute of current element into isds_envelope
3800 * TODO: This function can be incorporated into append_status_size_times() as
3801 * they are called always together.
3802 * The envelope is automatically allocated but not reallocated.
3803 * The data are just appended into envelope structure.
3804 * @context is ISDS context
3805 * @envelope is automatically allocated message envelope structure
3806 * @xpath_ctx is XPath context with current node as parent of attribute
3807 * carrying message type
3808 * In case of error @envelope will be freed. */
3809 static isds_error
append_message_type(struct isds_ctx
*context
,
3810 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3811 isds_error err
= IE_SUCCESS
;
3813 if (!context
) return IE_INVALID_CONTEXT
;
3814 if (!envelope
) return IE_INVAL
;
3815 if (!xpath_ctx
) return IE_INVAL
;
3820 *envelope
= calloc(1, sizeof(**envelope
));
3827 zfree((*envelope
)->dmType
);
3831 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3833 if (!(*envelope
)->dmType
) {
3834 /* Use default value */
3835 (*envelope
)->dmType
= strdup("V");
3836 if (!(*envelope
)->dmType
) {
3840 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3841 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3842 isds_printf_message(context
,
3843 _("Message type in dmType attribute is not 1 character long: "
3852 if (err
) isds_envelope_free(envelope
);
3858 /* Convert dmType isds_envelope member into XML attribute and append it to
3860 * @context is ISDS context
3861 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3862 * @dm_envelope is XML element the resulting attribute will be appended to.
3863 * @return error code, in case of error context' message is filled. */
3864 static isds_error
insert_message_type(struct isds_ctx
*context
,
3865 const char *type
, xmlNodePtr dm_envelope
) {
3866 isds_error err
= IE_SUCCESS
;
3867 xmlAttrPtr attribute_node
;
3869 if (!context
) return IE_INVALID_CONTEXT
;
3870 if (!dm_envelope
) return IE_INVAL
;
3872 /* Insert optional message type */
3874 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3875 char *type_locale
= _isds_utf82locale(type
);
3876 isds_printf_message(context
,
3877 _("Message type in envelope is not 1 character long: %s"),
3883 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3889 #endif /* HAVE_LIBCURL */
3892 /* Extract message document into reallocated document structure
3893 * @context is ISDS context
3894 * @document is automatically reallocated message documents structure
3895 * @xpath_ctx is XPath context with current node as isds:dmFile
3896 * In case of error @document will be freed. */
3897 static isds_error
extract_document(struct isds_ctx
*context
,
3898 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3899 isds_error err
= IE_SUCCESS
;
3900 xmlXPathObjectPtr result
= NULL
;
3901 xmlNodePtr file_node
;
3902 char *string
= NULL
;
3904 if (!context
) return IE_INVALID_CONTEXT
;
3905 if (!document
) return IE_INVAL
;
3906 isds_document_free(document
);
3907 if (!xpath_ctx
) return IE_INVAL
;
3908 file_node
= xpath_ctx
->node
;
3910 *document
= calloc(1, sizeof(**document
));
3916 /* Extract document meta data */
3917 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3918 if (context
->normalize_mime_type
) {
3919 const char *normalized_type
=
3920 isds_normalize_mime_type((*document
)->dmMimeType
);
3921 if (NULL
!= normalized_type
&&
3922 normalized_type
!= (*document
)->dmMimeType
) {
3923 char *new_type
= strdup(normalized_type
);
3924 if (NULL
== new_type
) {
3925 isds_printf_message(context
,
3926 _("Not enough memory to normalize document MIME type"));
3930 free((*document
)->dmMimeType
);
3931 (*document
)->dmMimeType
= new_type
;
3935 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3936 err
= string2isds_FileMetaType((xmlChar
*)string
,
3937 &((*document
)->dmFileMetaType
));
3939 char *meta_type_locale
= _isds_utf82locale(string
);
3940 isds_printf_message(context
,
3941 _("Document has invalid dmFileMetaType attribute value: %s"),
3943 free(meta_type_locale
);
3949 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3950 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3951 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3952 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3955 /* Extract document data.
3956 * Base64 encoded blob or XML subtree must be presented. */
3958 /* Check for dmEncodedContent */
3959 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3966 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3967 /* Here we have Base64 blob */
3968 (*document
)->is_xml
= 0;
3970 if (result
->nodesetval
->nodeNr
> 1) {
3971 isds_printf_message(context
,
3972 _("Document has more dmEncodedContent elements"));
3977 xmlXPathFreeObject(result
); result
= NULL
;
3978 EXTRACT_STRING("isds:dmEncodedContent", string
);
3980 /* Decode non-empty document */
3981 if (string
&& string
[0] != '\0') {
3982 (*document
)->data_length
=
3983 _isds_b64decode(string
, &((*document
)->data
));
3984 if ((*document
)->data_length
== (size_t) -1) {
3985 isds_printf_message(context
,
3986 _("Error while Base64-decoding document content"));
3992 /* No Base64 blob, try XML document */
3993 xmlXPathFreeObject(result
); result
= NULL
;
3994 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4001 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4002 /* Here we have XML document */
4003 (*document
)->is_xml
= 1;
4005 if (result
->nodesetval
->nodeNr
> 1) {
4006 isds_printf_message(context
,
4007 _("Document has more dmXMLContent elements"));
4012 /* XXX: We cannot serialize the content simply because:
4013 * - XML document may point out of its scope (e.g. to message
4015 * - isds:dmXMLContent can contain more elements, no element,
4017 * - it's not the XML way
4018 * Thus we provide the only right solution: XML DOM. Let's
4019 * application to cope with this hot potato :) */
4020 (*document
)->xml_node_list
=
4021 result
->nodesetval
->nodeTab
[0]->children
;
4023 /* No base64 blob, nor XML document */
4024 isds_printf_message(context
,
4025 _("Document has no dmEncodedContent, nor dmXMLContent "
4034 if (err
) isds_document_free(document
);
4036 xmlXPathFreeObject(result
);
4037 xpath_ctx
->node
= file_node
;
4043 /* Extract message documents into reallocated list of documents
4044 * @context is ISDS context
4045 * @documents is automatically reallocated message documents list structure
4046 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4047 * In case of error @documents will be freed. */
4048 static isds_error
extract_documents(struct isds_ctx
*context
,
4049 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4050 isds_error err
= IE_SUCCESS
;
4051 xmlXPathObjectPtr result
= NULL
;
4052 xmlNodePtr files_node
;
4053 struct isds_list
*document
, *prev_document
= NULL
;
4055 if (!context
) return IE_INVALID_CONTEXT
;
4056 if (!documents
) return IE_INVAL
;
4057 isds_list_free(documents
);
4058 if (!xpath_ctx
) return IE_INVAL
;
4059 files_node
= xpath_ctx
->node
;
4061 /* Find documents */
4062 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4069 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4070 isds_printf_message(context
,
4071 _("Message does not contain any document"));
4077 /* Iterate over documents */
4078 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4080 /* Allocate and append list item */
4081 document
= calloc(1, sizeof(*document
));
4086 document
->destructor
= (void (*)(void **))isds_document_free
;
4087 if (i
== 0) *documents
= document
;
4088 else prev_document
->next
= document
;
4089 prev_document
= document
;
4091 /* Extract document */
4092 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4093 err
= extract_document(context
,
4094 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4095 if (err
) goto leave
;
4100 if (err
) isds_list_free(documents
);
4101 xmlXPathFreeObject(result
);
4102 xpath_ctx
->node
= files_node
;
4108 /* Convert isds:dmRecord XML tree into structure
4109 * @context is ISDS context
4110 * @envelope is automatically reallocated message envelope structure
4111 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4112 * In case of error @envelope will be freed. */
4113 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4114 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4115 isds_error err
= IE_SUCCESS
;
4116 xmlXPathObjectPtr result
= NULL
;
4118 if (!context
) return IE_INVALID_CONTEXT
;
4119 if (!envelope
) return IE_INVAL
;
4120 isds_envelope_free(envelope
);
4121 if (!xpath_ctx
) return IE_INVAL
;
4124 *envelope
= calloc(1, sizeof(**envelope
));
4131 /* Extract tRecord data */
4132 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4134 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4135 * dmAcceptanceTime. */
4136 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4137 if (err
) goto leave
;
4139 /* Extract envelope elements added by sender and ISDS
4140 * (XSD: gMessageEnvelope type) */
4141 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4142 if (err
) goto leave
;
4144 /* Get message type */
4145 err
= append_message_type(context
, envelope
, xpath_ctx
);
4146 if (err
) goto leave
;
4150 if (err
) isds_envelope_free(envelope
);
4151 xmlXPathFreeObject(result
);
4156 /* Convert XSD:tStateChangesRecord type XML tree into structure
4157 * @context is ISDS context
4158 * @changed_status is automatically reallocated message state change structure
4159 * @xpath_ctx is XPath context with current node as element of
4160 * XSD:tStateChangesRecord type
4161 * In case of error @changed_status will be freed. */
4162 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4163 struct isds_message_status_change
**changed_status
,
4164 xmlXPathContextPtr xpath_ctx
) {
4165 isds_error err
= IE_SUCCESS
;
4166 xmlXPathObjectPtr result
= NULL
;
4167 unsigned long int *unumber
= NULL
;
4168 char *string
= NULL
;
4170 if (!context
) return IE_INVALID_CONTEXT
;
4171 if (!changed_status
) return IE_INVAL
;
4172 isds_message_status_change_free(changed_status
);
4173 if (!xpath_ctx
) return IE_INVAL
;
4176 *changed_status
= calloc(1, sizeof(**changed_status
));
4177 if (!*changed_status
) {
4183 /* Extract tGetStateChangesInput data */
4184 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4186 /* dmEventTime is mandatory */
4187 EXTRACT_STRING("isds:dmEventTime", string
);
4189 err
= timestring2timeval((xmlChar
*) string
,
4190 &((*changed_status
)->time
));
4192 char *string_locale
= _isds_utf82locale(string
);
4193 if (err
== IE_DATE
) err
= IE_ISDS
;
4194 isds_printf_message(context
,
4195 _("Could not convert dmEventTime as ISO time: %s"),
4197 free(string_locale
);
4203 /* dmMessageStatus element is mandatory */
4204 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4206 isds_log_message(context
,
4207 _("Missing mandatory isds:dmMessageStatus integer"));
4211 err
= uint2isds_message_status(context
, unumber
,
4212 &((*changed_status
)->dmMessageStatus
));
4214 if (err
== IE_ENUM
) err
= IE_ISDS
;
4223 if (err
) isds_message_status_change_free(changed_status
);
4224 xmlXPathFreeObject(result
);
4227 #endif /* HAVE_LIBCURL */
4230 /* Find and convert isds:dmHash XML tree into structure
4231 * @context is ISDS context
4232 * @envelope is automatically reallocated message hash structure
4233 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4234 * In case of error @hash will be freed. */
4235 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4236 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4237 isds_error err
= IE_SUCCESS
;
4238 xmlNodePtr old_ctx_node
;
4239 xmlXPathObjectPtr result
= NULL
;
4240 char *string
= NULL
;
4242 if (!context
) return IE_INVALID_CONTEXT
;
4243 if (!hash
) return IE_INVAL
;
4244 isds_hash_free(hash
);
4245 if (!xpath_ctx
) return IE_INVAL
;
4247 old_ctx_node
= xpath_ctx
->node
;
4249 *hash
= calloc(1, sizeof(**hash
));
4256 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4257 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4266 /* Get hash algorithm */
4267 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4268 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4270 if (err
== IE_ENUM
) {
4271 char *string_locale
= _isds_utf82locale(string
);
4272 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4274 free(string_locale
);
4280 /* Get hash value */
4281 EXTRACT_STRING(".", string
);
4283 isds_printf_message(context
,
4284 _("sisds:dmHash element is missing hash value"));
4288 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4289 if ((*hash
)->length
== (size_t) -1) {
4290 isds_printf_message(context
,
4291 _("Error while Base64-decoding hash value"));
4297 if (err
) isds_hash_free(hash
);
4299 xmlXPathFreeObject(result
);
4300 xpath_ctx
->node
= old_ctx_node
;
4305 /* Find and append isds:dmQTimestamp XML tree into envelope.
4306 * Because one service is allowed to miss time-stamp content, and we think
4307 * other could too (flaw in specification), this function is deliberated and
4308 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4309 * @context is ISDS context
4310 * @envelope is automatically allocated envelope structure
4311 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4313 * In case of error @envelope will be freed. */
4314 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4315 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4316 isds_error err
= IE_SUCCESS
;
4317 xmlXPathObjectPtr result
= NULL
;
4318 char *string
= NULL
;
4320 if (!context
) return IE_INVALID_CONTEXT
;
4321 if (!envelope
) return IE_INVAL
;
4323 isds_envelope_free(envelope
);
4328 *envelope
= calloc(1, sizeof(**envelope
));
4334 zfree((*envelope
)->timestamp
);
4335 (*envelope
)->timestamp_length
= 0;
4338 /* Get dmQTimestamp */
4339 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4341 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4344 (*envelope
)->timestamp_length
=
4345 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4346 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4347 isds_printf_message(context
,
4348 _("Error while Base64-decoding time stamp value"));
4354 if (err
) isds_envelope_free(envelope
);
4356 xmlXPathFreeObject(result
);
4361 /* Convert XSD tReturnedMessage XML tree into message structure.
4362 * It does not store serialized XML tree into message->raw.
4363 * It does store (pointer to) parsed XML tree into message->xml if needed.
4364 * @context is ISDS context
4365 * @include_documents Use true if documents must be extracted
4366 * (tReturnedMessage XSD type), use false if documents shall be omitted
4367 * (tReturnedMessageEnvelope).
4368 * @message is automatically reallocated message structure
4369 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4371 * In case of error @message will be freed. */
4372 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4373 const _Bool include_documents
, struct isds_message
**message
,
4374 xmlXPathContextPtr xpath_ctx
) {
4375 isds_error err
= IE_SUCCESS
;
4376 xmlNodePtr message_node
;
4378 if (!context
) return IE_INVALID_CONTEXT
;
4379 if (!message
) return IE_INVAL
;
4380 isds_message_free(message
);
4381 if (!xpath_ctx
) return IE_INVAL
;
4384 *message
= calloc(1, sizeof(**message
));
4390 /* Save message XPATH context node */
4391 message_node
= xpath_ctx
->node
;
4395 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4396 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4397 if (err
) { err
= IE_ERROR
; goto leave
; }
4398 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4399 if (err
) goto leave
;
4401 if (include_documents
) {
4402 struct isds_list
*item
;
4404 /* Extract dmFiles */
4405 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4407 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4408 err
= IE_ISDS
; goto leave
;
4410 if (err
) { err
= IE_ERROR
; goto leave
; }
4411 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4412 if (err
) goto leave
;
4414 /* Store xmlDoc of this message if needed */
4415 /* Only if we got a XML document in all the documents. */
4416 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4417 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4418 (*message
)->xml
= xpath_ctx
->doc
;
4425 /* Restore context to message */
4426 xpath_ctx
->node
= message_node
;
4428 /* Extract dmHash */
4429 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4431 if (err
) goto leave
;
4433 /* Extract dmQTimestamp, */
4434 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4436 if (err
) goto leave
;
4438 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4439 * dmAcceptanceTime. */
4440 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4441 if (err
) goto leave
;
4443 /* Get message type */
4444 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4445 if (err
) goto leave
;
4448 if (err
) isds_message_free(message
);
4453 /* Extract message event into reallocated isds_event structure
4454 * @context is ISDS context
4455 * @event is automatically reallocated message event structure
4456 * @xpath_ctx is XPath context with current node as isds:dmEvent
4457 * In case of error @event will be freed. */
4458 static isds_error
extract_event(struct isds_ctx
*context
,
4459 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4460 isds_error err
= IE_SUCCESS
;
4461 xmlXPathObjectPtr result
= NULL
;
4462 xmlNodePtr event_node
;
4463 char *string
= NULL
;
4465 if (!context
) return IE_INVALID_CONTEXT
;
4466 if (!event
) return IE_INVAL
;
4467 isds_event_free(event
);
4468 if (!xpath_ctx
) return IE_INVAL
;
4469 event_node
= xpath_ctx
->node
;
4471 *event
= calloc(1, sizeof(**event
));
4477 /* Extract event data.
4478 * All elements are optional according XSD. That's funny. */
4479 EXTRACT_STRING("sisds:dmEventTime", string
);
4481 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4483 char *string_locale
= _isds_utf82locale(string
);
4484 if (err
== IE_DATE
) err
= IE_ISDS
;
4485 isds_printf_message(context
,
4486 _("Could not convert dmEventTime as ISO time: %s"),
4488 free(string_locale
);
4494 /* dmEventDescr element has prefix and the rest */
4495 EXTRACT_STRING("sisds:dmEventDescr", string
);
4497 err
= eventstring2event((xmlChar
*) string
, *event
);
4498 if (err
) goto leave
;
4503 if (err
) isds_event_free(event
);
4505 xmlXPathFreeObject(result
);
4506 xpath_ctx
->node
= event_node
;
4511 /* Convert element of XSD tEventsArray type from XML tree into
4512 * isds_list of isds_event's structure. The list is automatically reallocated.
4513 * @context is ISDS context
4514 * @events is automatically reallocated list of event structures
4515 * @xpath_ctx is XPath context with current node as tEventsArray
4516 * In case of error @events will be freed. */
4517 static isds_error
extract_events(struct isds_ctx
*context
,
4518 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4519 isds_error err
= IE_SUCCESS
;
4520 xmlXPathObjectPtr result
= NULL
;
4521 xmlNodePtr events_node
;
4522 struct isds_list
*event
, *prev_event
= NULL
;
4524 if (!context
) return IE_INVALID_CONTEXT
;
4525 if (!events
) return IE_INVAL
;
4526 if (!xpath_ctx
) return IE_INVAL
;
4527 events_node
= xpath_ctx
->node
;
4530 isds_list_free(events
);
4533 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4540 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4541 isds_printf_message(context
,
4542 _("Delivery info does not contain any event"));
4548 /* Iterate over events */
4549 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4551 /* Allocate and append list item */
4552 event
= calloc(1, sizeof(*event
));
4557 event
->destructor
= (void (*)(void **))isds_event_free
;
4558 if (i
== 0) *events
= event
;
4559 else prev_event
->next
= event
;
4563 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4564 err
= extract_event(context
,
4565 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4566 if (err
) goto leave
;
4571 if (err
) isds_list_free(events
);
4572 xmlXPathFreeObject(result
);
4573 xpath_ctx
->node
= events_node
;
4579 /* Insert Base64 encoded data as element with text child.
4580 * @context is session context
4581 * @parent is XML node to append @element with @data as child
4582 * @ns is XML namespace of @element, use NULL to inherit from @parent
4583 * @element is UTF-8 encoded name of new element
4584 * @data is bit stream to encode into @element
4585 * @length is size of @data in bytes
4586 * @return standard error code and fill long error message if needed */
4587 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4588 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4589 const void *data
, size_t length
) {
4590 isds_error err
= IE_SUCCESS
;
4593 if (!context
) return IE_INVALID_CONTEXT
;
4594 if (!data
&& length
> 0) return IE_INVAL
;
4595 if (!parent
|| !element
) return IE_INVAL
;
4597 xmlChar
*base64data
= NULL
;
4598 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4600 isds_printf_message(context
,
4601 ngettext("Not enough memory to encode %zd byte into Base64",
4602 "Not enough memory to encode %zd bytes into Base64",
4608 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4616 /* Convert isds_document structure into XML tree and append to dmFiles node.
4617 * @context is session context
4618 * @document is ISDS document
4619 * @dm_files is XML element the resulting tree will be appended to as a child.
4620 * @return error code, in case of error context' message is filled. */
4621 static isds_error
insert_document(struct isds_ctx
*context
,
4622 struct isds_document
*document
, xmlNodePtr dm_files
) {
4623 isds_error err
= IE_SUCCESS
;
4624 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4625 xmlAttrPtr attribute_node
;
4627 if (!context
) return IE_INVALID_CONTEXT
;
4628 if (!document
|| !dm_files
) return IE_INVAL
;
4630 /* Allocate new dmFile */
4631 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4633 isds_printf_message(context
, _("Could not allocate main dmFile"));
4637 /* Append the new dmFile.
4638 * XXX: Main document must go first */
4639 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4640 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4642 file
= xmlAddChild(dm_files
, new_file
);
4645 xmlFreeNode(new_file
); new_file
= NULL
;
4646 isds_printf_message(context
, _("Could not add dmFile child to "
4647 "%s element"), dm_files
->name
);
4652 /* @dmMimeType is required */
4653 if (!document
->dmMimeType
) {
4654 isds_log_message(context
,
4655 _("Document is missing mandatory MIME type definition"));
4659 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4661 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4663 isds_printf_message(context
,
4664 _("Document has unknown dmFileMetaType: %ld"),
4665 document
->dmFileMetaType
);
4669 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4671 if (document
->dmFileGuid
) {
4672 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4674 if (document
->dmUpFileGuid
) {
4675 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4678 /* @dmFileDescr is required */
4679 if (!document
->dmFileDescr
) {
4680 isds_log_message(context
,
4681 _("Document is missing mandatory description (title)"));
4685 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4687 if (document
->dmFormat
) {
4688 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4692 /* Insert content (body) of the document. */
4693 if (document
->is_xml
) {
4694 /* XML document requested */
4696 /* Allocate new dmXMLContent */
4697 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4699 isds_printf_message(context
,
4700 _("Could not allocate dmXMLContent element"));
4705 node
= xmlAddChild(file
, xmlcontent
);
4707 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4708 isds_printf_message(context
,
4709 _("Could not add dmXMLContent child to %s element"),
4715 /* Copy non-empty node list */
4716 if (document
->xml_node_list
) {
4717 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4718 document
->xml_node_list
);
4720 isds_printf_message(context
,
4721 _("Not enough memory to copy XML document"));
4726 if (!xmlAddChildList(node
, content
)) {
4727 xmlFreeNodeList(content
);
4728 isds_printf_message(context
,
4729 _("Error while adding XML document into dmXMLContent"));
4733 /* XXX: We cannot free the content here because it's part of node's
4734 * document since now. It will be freed with it automatically. */
4737 /* Binary document requested */
4738 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4739 document
->data
, document
->data_length
);
4740 if (err
) goto leave
;
4748 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4749 * The copy must be preallocated, the date are just appended into structure.
4750 * @context is ISDS context
4751 * @copy is message copy structure
4752 * @xpath_ctx is XPath context with current node as tMStatus */
4753 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4754 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4755 isds_error err
= IE_SUCCESS
;
4756 xmlXPathObjectPtr result
= NULL
;
4757 char *code
= NULL
, *message
= NULL
;
4759 if (!context
) return IE_INVALID_CONTEXT
;
4760 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4762 /* Free old values */
4763 zfree(copy
->dmStatus
);
4766 /* Get error specific to this copy */
4767 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4769 isds_log_message(context
,
4770 _("Missing isds:dmStatusCode under "
4771 "XSD:tMStatus type element"));
4776 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4777 /* This copy failed */
4778 copy
->error
= IE_ISDS
;
4779 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4781 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4782 if (!copy
->dmStatus
) {
4783 copy
->dmStatus
= code
;
4787 copy
->dmStatus
= code
;
4791 /* This copy succeeded. In this case only, message ID is valid */
4792 copy
->error
= IE_SUCCESS
;
4794 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4796 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4797 "but did not returned assigned message ID\n"));
4805 xmlXPathFreeObject(result
);
4810 /* Insert struct isds_approval data (box approval) into XML tree
4811 * @context is session context
4812 * @approval is libisds structure with approval description. NULL is
4814 * @parent is XML element to append @approval to */
4815 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4816 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4818 isds_error err
= IE_SUCCESS
;
4821 if (!context
) return IE_INVALID_CONTEXT
;
4822 if (!parent
) return IE_INVAL
;
4824 if (!approval
) return IE_SUCCESS
;
4826 /* Build XSD:gExtApproval */
4827 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4828 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4835 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4837 * @context is session context
4838 * @service_name is name of SERVICE_DB_ACCESS
4839 * @response is reallocated server SOAP body response as XML document
4840 * @raw_response is reallocated bit stream with response body. Use
4841 * NULL if you don't care
4842 * @raw_response_length is size of @raw_response in bytes
4843 * @code is reallocated ISDS status code
4844 * @status_message is reallocated ISDS status message
4845 * @return error coded from lower layer, context message will be set up
4847 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4848 const xmlChar
*service_name
,
4849 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4850 xmlChar
**code
, xmlChar
**status_message
) {
4852 isds_error err
= IE_SUCCESS
;
4853 char *service_name_locale
= NULL
;
4854 xmlNodePtr request
= NULL
, node
;
4855 xmlNsPtr isds_ns
= NULL
;
4857 if (!context
) return IE_INVALID_CONTEXT
;
4858 if (!service_name
) return IE_INVAL
;
4859 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4860 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4862 /* Free output argument */
4863 xmlFreeDoc(*response
); *response
= NULL
;
4864 if (raw_response
) zfree(*raw_response
);
4866 zfree(*status_message
);
4869 /* Check if connection is established
4870 * TODO: This check should be done downstairs. */
4871 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4873 service_name_locale
= _isds_utf82locale((char*)service_name
);
4874 if (!service_name_locale
) {
4880 request
= xmlNewNode(NULL
, service_name
);
4882 isds_printf_message(context
,
4883 _("Could not build %s request"), service_name_locale
);
4887 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4889 isds_log_message(context
, _("Could not create ISDS name space"));
4893 xmlSetNs(request
, isds_ns
);
4896 /* Add XSD:tDummyInput child */
4897 INSERT_STRING(request
, "dbDummy", NULL
);
4900 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4901 service_name_locale
);
4904 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4905 raw_response
, raw_response_length
);
4906 xmlFreeNode(request
); request
= NULL
;
4909 isds_log(ILF_ISDS
, ILL_DEBUG
,
4910 _("Processing ISDS response on %s request failed\n"),
4911 service_name_locale
);
4915 /* Check for response status */
4916 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4917 code
, status_message
, NULL
);
4919 isds_log(ILF_ISDS
, ILL_DEBUG
,
4920 _("ISDS response on %s request is missing status\n"),
4921 service_name_locale
);
4925 /* Request processed, but nothing found */
4926 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4927 char *code_locale
= _isds_utf82locale((char*) *code
);
4928 char *status_message_locale
=
4929 _isds_utf82locale((char*) *status_message
);
4930 isds_log(ILF_ISDS
, ILL_DEBUG
,
4931 _("Server refused %s request (code=%s, message=%s)\n"),
4932 service_name_locale
, code_locale
, status_message_locale
);
4933 isds_log_message(context
, status_message_locale
);
4935 free(status_message_locale
);
4941 free(service_name_locale
);
4942 xmlFreeNode(request
);
4948 /* Get data about logged in user and his box. */
4949 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4950 struct isds_DbOwnerInfo
**db_owner_info
) {
4951 isds_error err
= IE_SUCCESS
;
4953 xmlDocPtr response
= NULL
;
4954 xmlChar
*code
= NULL
, *message
= NULL
;
4955 xmlXPathContextPtr xpath_ctx
= NULL
;
4956 xmlXPathObjectPtr result
= NULL
;
4957 char *string
= NULL
;
4960 if (!context
) return IE_INVALID_CONTEXT
;
4961 zfree(context
->long_message
);
4962 if (!db_owner_info
) return IE_INVAL
;
4963 isds_DbOwnerInfo_free(db_owner_info
);
4966 /* Check if connection is established */
4967 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4970 /* Do request and check for success */
4971 err
= build_send_check_dbdummy_request(context
,
4972 BAD_CAST
"GetOwnerInfoFromLogin",
4973 &response
, NULL
, NULL
, &code
, &message
);
4974 if (err
) goto leave
;
4978 /* Prepare structure */
4979 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4980 if (!*db_owner_info
) {
4984 xpath_ctx
= xmlXPathNewContext(response
);
4989 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4994 /* Set context node */
4995 result
= xmlXPathEvalExpression(BAD_CAST
4996 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5001 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5002 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5006 if (result
->nodesetval
->nodeNr
> 1) {
5007 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5011 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5012 xmlXPathFreeObject(result
); result
= NULL
;
5015 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5020 isds_DbOwnerInfo_free(db_owner_info
);
5024 xmlXPathFreeObject(result
);
5025 xmlXPathFreeContext(xpath_ctx
);
5029 xmlFreeDoc(response
);
5032 isds_log(ILF_ISDS
, ILL_DEBUG
,
5033 _("GetOwnerInfoFromLogin request processed by server "
5034 "successfully.\n"));
5035 #else /* not HAVE_LIBCURL */
5043 /* Get data about logged in user. */
5044 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5045 struct isds_DbUserInfo
**db_user_info
) {
5046 isds_error err
= IE_SUCCESS
;
5048 xmlDocPtr response
= NULL
;
5049 xmlChar
*code
= NULL
, *message
= NULL
;
5050 xmlXPathContextPtr xpath_ctx
= NULL
;
5051 xmlXPathObjectPtr result
= NULL
;
5054 if (!context
) return IE_INVALID_CONTEXT
;
5055 zfree(context
->long_message
);
5056 if (!db_user_info
) return IE_INVAL
;
5057 isds_DbUserInfo_free(db_user_info
);
5060 /* Check if connection is established */
5061 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5064 /* Do request and check for success */
5065 err
= build_send_check_dbdummy_request(context
,
5066 BAD_CAST
"GetUserInfoFromLogin",
5067 &response
, NULL
, NULL
, &code
, &message
);
5068 if (err
) goto leave
;
5072 /* Prepare structure */
5073 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5074 if (!*db_user_info
) {
5078 xpath_ctx
= xmlXPathNewContext(response
);
5083 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5088 /* Set context node */
5089 result
= xmlXPathEvalExpression(BAD_CAST
5090 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5095 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5096 isds_log_message(context
, _("Missing dbUserInfo element"));
5100 if (result
->nodesetval
->nodeNr
> 1) {
5101 isds_log_message(context
, _("Multiple dbUserInfo element"));
5105 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5106 xmlXPathFreeObject(result
); result
= NULL
;
5109 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5113 isds_DbUserInfo_free(db_user_info
);
5116 xmlXPathFreeObject(result
);
5117 xmlXPathFreeContext(xpath_ctx
);
5121 xmlFreeDoc(response
);
5124 isds_log(ILF_ISDS
, ILL_DEBUG
,
5125 _("GetUserInfoFromLogin request processed by server "
5126 "successfully.\n"));
5127 #else /* not HAVE_LIBCURL */
5135 /* Get expiration time of current password
5136 * @context is session context
5137 * @expiration is automatically reallocated time when password expires. If
5138 * password expiration is disabled, NULL will be returned. In case of error
5139 * it will be nulled too. */
5140 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5141 struct timeval
**expiration
) {
5142 isds_error err
= IE_SUCCESS
;
5144 xmlDocPtr response
= NULL
;
5145 xmlChar
*code
= NULL
, *message
= NULL
;
5146 xmlXPathContextPtr xpath_ctx
= NULL
;
5147 xmlXPathObjectPtr result
= NULL
;
5148 char *string
= NULL
;
5151 if (!context
) return IE_INVALID_CONTEXT
;
5152 zfree(context
->long_message
);
5153 if (!expiration
) return IE_INVAL
;
5157 /* Check if connection is established */
5158 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5161 /* Do request and check for success */
5162 err
= build_send_check_dbdummy_request(context
,
5163 BAD_CAST
"GetPasswordInfo",
5164 &response
, NULL
, NULL
, &code
, &message
);
5165 if (err
) goto leave
;
5169 xpath_ctx
= xmlXPathNewContext(response
);
5174 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5179 /* Set context node */
5180 result
= xmlXPathEvalExpression(BAD_CAST
5181 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5186 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5187 isds_log_message(context
,
5188 _("Missing GetPasswordInfoResponse element"));
5192 if (result
->nodesetval
->nodeNr
> 1) {
5193 isds_log_message(context
,
5194 _("Multiple GetPasswordInfoResponse element"));
5198 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5199 xmlXPathFreeObject(result
); result
= NULL
;
5201 /* Extract expiration date */
5202 EXTRACT_STRING("isds:pswExpDate", string
);
5204 /* And convert it if any returned. Otherwise expiration is disabled. */
5205 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5207 char *string_locale
= _isds_utf82locale(string
);
5208 if (err
== IE_DATE
) err
= IE_ISDS
;
5209 isds_printf_message(context
,
5210 _("Could not convert pswExpDate as ISO time: %s"),
5212 free(string_locale
);
5225 xmlXPathFreeObject(result
);
5226 xmlXPathFreeContext(xpath_ctx
);
5230 xmlFreeDoc(response
);
5233 isds_log(ILF_ISDS
, ILL_DEBUG
,
5234 _("GetPasswordInfo request processed by server "
5235 "successfully.\n"));
5236 #else /* not HAVE_LIBCURL */
5245 /* Request delivering new TOTP code from ISDS through side channel before
5246 * changing password.
5247 * @context is session context
5248 * @password is current password.
5249 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5250 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5251 * function for more details.
5252 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5253 * NULL, if you don't care.
5254 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5256 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5257 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5258 isds_error err
= IE_SUCCESS
;
5259 char *saved_url
= NULL
; /* No copy */
5260 #if HAVE_CURL_REAUTHORIZATION_BUG
5261 CURL
*saved_curl
= NULL
; /* No copy */
5263 xmlNsPtr isds_ns
= NULL
;
5264 xmlNodePtr request
= NULL
;
5265 xmlDocPtr response
= NULL
;
5266 xmlChar
*code
= NULL
, *message
= NULL
;
5267 const xmlChar
*codes
[] = {
5272 const char *meanings
[] = {
5273 N_("Unexpected error"),
5274 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5275 N_("One-time code could not been sent. Try later again.")
5277 const isds_otp_resolution resolutions
[] = {
5278 OTP_RESOLUTION_UNKNOWN
,
5279 OTP_RESOLUTION_TO_FAST
,
5280 OTP_RESOLUTION_TOTP_NOT_SENT
5283 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5284 zfree(context
->long_message
);
5285 if (NULL
== password
) {
5286 isds_log_message(context
,
5287 _("Second argument (password) of isds_change_password() "
5292 /* Check if connection is established
5293 * TODO: This check should be done downstairs. */
5294 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5296 if (!context
->otp
) {
5297 isds_log_message(context
, _("This function requires OTP-authenticated "
5299 return IE_INVALID_CONTEXT
;
5302 isds_log_message(context
, _("If one-time password authentication "
5303 "method is in use, requesting new OTP code requires "
5304 "one-time credentials argument either"));
5307 if (otp
->method
!= OTP_TIME
) {
5308 isds_log_message(context
, _("Requesting new time-based OTP code from "
5309 "server requires one-time password authentication "
5313 if (otp
->otp_code
!= NULL
) {
5314 isds_log_message(context
, _("Requesting new time-based OTP code from "
5315 "server requires undefined OTP code member in "
5316 "one-time credentials argument"));
5322 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5324 isds_log_message(context
, _("Could not build SendSMSCode request"));
5327 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5329 isds_log_message(context
, _("Could not create ISDS name space"));
5330 xmlFreeNode(request
);
5333 xmlSetNs(request
, isds_ns
);
5335 /* Change URL temporarily for sending this request only */
5337 char *new_url
= NULL
;
5338 if ((err
= _isds_build_url_from_context(context
,
5339 "%1$.*2$sasws/changePassword", &new_url
))) {
5342 saved_url
= context
->url
;
5343 context
->url
= new_url
;
5346 /* Store credentials for sending this request only */
5347 context
->otp_credentials
= otp
;
5348 _isds_discard_credentials(context
, 0);
5349 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5351 _isds_discard_credentials(context
, 0);
5354 #if HAVE_CURL_REAUTHORIZATION_BUG
5355 saved_curl
= context
->curl
;
5356 context
->curl
= curl_easy_init();
5357 if (NULL
== context
->curl
) {
5361 if (context
->timeout
) {
5362 err
= isds_set_timeout(context
, context
->timeout
);
5363 if (err
) goto leave
;
5367 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5370 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5372 /* Remove temporal credentials */
5373 _isds_discard_credentials(context
, 0);
5374 /* Detach pointer to OTP credentials from context */
5375 context
->otp_credentials
= NULL
;
5376 /* Keep context->otp true to keep signaling this is OTP session */
5378 /* Destroy request */
5379 xmlFreeNode(request
); request
= NULL
;
5382 isds_log(ILF_ISDS
, ILL_DEBUG
,
5383 _("Processing ISDS response on SendSMSCode request failed\n"));
5387 /* Check for response status */
5388 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5389 &code
, &message
, (xmlChar
**)refnumber
);
5391 isds_log(ILF_ISDS
, ILL_DEBUG
,
5392 _("ISDS response on SendSMSCode request is missing "
5397 /* Check for error */
5398 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5399 char *code_locale
= _isds_utf82locale((char*)code
);
5400 char *message_locale
= _isds_utf82locale((char*)message
);
5402 isds_log(ILF_ISDS
, ILL_DEBUG
,
5403 _("Server refused to send new code on SendSMSCode "
5404 "request (code=%s, message=%s)\n"),
5405 code_locale
, message_locale
);
5407 /* Check for known error codes */
5408 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5409 if (!xmlStrcmp(code
, codes
[i
])) break;
5411 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5412 isds_log_message(context
, _(meanings
[i
]));
5413 /* Mimic otp->resolution according to the code, specification does
5414 * prescribe OTP header to be available. */
5415 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5416 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5417 otp
->resolution
= resolutions
[i
];
5419 isds_log_message(context
, message_locale
);
5422 free(message_locale
);
5428 /* Otherwise new code sent successfully */
5429 /* Mimic otp->resolution according to the code, specification does
5430 * prescribe OTP header to be available. */
5431 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5432 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5435 if (NULL
!= saved_url
) {
5436 /* Revert URL to original one */
5437 zfree(context
->url
);
5438 context
->url
= saved_url
;
5440 #if HAVE_CURL_REAUTHORIZATION_BUG
5441 if (NULL
!= saved_curl
) {
5442 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5443 context
->curl
= saved_curl
;
5449 xmlFreeDoc(response
);
5450 xmlFreeNode(request
);
5453 isds_log(ILF_ISDS
, ILL_DEBUG
,
5454 _("New OTP code has been sent successfully on SendSMSCode "
5460 /* Convert response status code to isds_error code and set long message
5461 * @context is context to save long message to
5462 * @map is mapping from codes to errors and messages. Pass NULL for generic
5464 * @code is status code to translate
5465 * @message is non-localized status message to put into long message in case
5466 * of uknown error. It can be NULL if server did not provide any.
5467 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5468 * invalid invocation. */
5469 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5470 const struct code_map_isds_error
*map
,
5471 const xmlChar
*code
, const xmlChar
*message
) {
5473 isds_log_message(context
,
5474 _("NULL status code passed to statuscode2isds_error()"));
5479 /* Check for known error codes */
5480 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5481 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5482 isds_log_message(context
, _(map
->meanings
[i
]));
5483 return map
->errors
[i
];
5489 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5490 char *message_locale
= _isds_utf82locale((char*)message
);
5491 if (NULL
== message_locale
)
5492 isds_log_message(context
, _("ISDS server returned unknown error"));
5494 isds_log_message(context
, message_locale
);
5495 free(message_locale
);
5504 /* Change user password in ISDS.
5505 * User must supply old password, new password will takes effect after some
5506 * time, current session can continue. Password must fulfill some constraints.
5507 * @context is session context
5508 * @old_password is current password.
5509 * @new_password is requested new password
5510 * @otp auxiliary data required if one-time password authentication is in use,
5511 * defines OTP code (if known) and returns fine grade resolution of OTP
5512 * procedure. Pass NULL, if one-time password authentication is not needed.
5513 * Please note the @otp argument must match OTP method used at log-in time. See
5514 * isds_login() function for more details.
5515 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5516 * NULL, if you don't care.
5517 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5518 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5519 * awaiting OTP code that has been delivered by side channel to the user. */
5520 isds_error
isds_change_password(struct isds_ctx
*context
,
5521 const char *old_password
, const char *new_password
,
5522 struct isds_otp
*otp
, char **refnumber
) {
5523 isds_error err
= IE_SUCCESS
;
5525 char *saved_url
= NULL
; /* No copy */
5526 #if HAVE_CURL_REAUTHORIZATION_BUG
5527 CURL
*saved_curl
= NULL
; /* No copy */
5529 xmlNsPtr isds_ns
= NULL
;
5530 xmlNodePtr request
= NULL
, node
;
5531 xmlDocPtr response
= NULL
;
5532 xmlChar
*code
= NULL
, *message
= NULL
;
5533 const xmlChar
*codes
[] = {
5546 const char *meanings
[] = {
5547 N_("Password length must be between 8 and 32 characters"),
5548 N_("Password cannot be reused"), /* Server does not distinguish 1067
5549 and 1091 on ChangePasswordOTP */
5550 N_("Password contains forbidden character"),
5551 N_("Password must contain at least one upper-case letter, "
5552 "one lower-case, and one digit"),
5553 N_("Password cannot contain sequence of three identical characters"),
5554 N_("Password cannot contain user identifier"),
5555 N_("Password is too simmple"),
5556 N_("Old password is not valid"),
5557 N_("Password cannot be reused"),
5558 N_("Unexpected error"),
5559 N_("LDAP update error")
5563 if (!context
) return IE_INVALID_CONTEXT
;
5564 zfree(context
->long_message
);
5565 if (NULL
!= refnumber
)
5567 if (NULL
== old_password
) {
5568 isds_log_message(context
,
5569 _("Second argument (old password) of isds_change_password() "
5573 if (NULL
== otp
&& NULL
== new_password
) {
5574 isds_log_message(context
,
5575 _("Third argument (new password) of isds_change_password() "
5581 /* Check if connection is established
5582 * TODO: This check should be done downstairs. */
5583 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5585 if (context
->otp
&& NULL
== otp
) {
5586 isds_log_message(context
, _("If one-time password authentication "
5587 "method is in use, changing password requires one-time "
5588 "credentials either"));
5592 /* Build ChangeISDSPassword request */
5593 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5594 BAD_CAST
"ChangePasswordOTP");
5596 isds_log_message(context
, (NULL
== otp
) ?
5597 _("Could not build ChangeISDSPassword request") :
5598 _("Could not build ChangePasswordOTP request"));
5601 isds_ns
= xmlNewNs(request
,
5602 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5605 isds_log_message(context
, _("Could not create ISDS name space"));
5606 xmlFreeNode(request
);
5609 xmlSetNs(request
, isds_ns
);
5611 INSERT_STRING(request
, "dbOldPassword", old_password
);
5612 INSERT_STRING(request
, "dbNewPassword", new_password
);
5615 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5616 switch (otp
->method
) {
5618 isds_log(ILF_SEC
, ILL_INFO
,
5619 _("Selected authentication method: "
5620 "HMAC-based one-time password\n"));
5621 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5624 isds_log(ILF_SEC
, ILL_INFO
,
5625 _("Selected authentication method: "
5626 "Time-based one-time password\n"));
5627 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5628 if (otp
->otp_code
== NULL
) {
5629 isds_log(ILF_SEC
, ILL_INFO
,
5630 _("OTP code has not been provided by "
5631 "application, requesting server for "
5633 err
= _isds_request_totp_code(context
, old_password
, otp
,
5635 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5639 isds_log(ILF_SEC
, ILL_INFO
,
5640 _("OTP code has been provided by "
5641 "application, not requesting server "
5646 isds_log_message(context
,
5647 _("Unknown one-time password authentication "
5648 "method requested by application"));
5653 /* Change URL temporarily for sending this request only */
5655 char *new_url
= NULL
;
5656 if ((err
= _isds_build_url_from_context(context
,
5657 "%1$.*2$sasws/changePassword", &new_url
))) {
5660 saved_url
= context
->url
;
5661 context
->url
= new_url
;
5664 /* Store credentials for sending this request only */
5665 context
->otp_credentials
= otp
;
5666 _isds_discard_credentials(context
, 0);
5667 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5668 old_password
, NULL
))) {
5669 _isds_discard_credentials(context
, 0);
5672 #if HAVE_CURL_REAUTHORIZATION_BUG
5673 saved_curl
= context
->curl
;
5674 context
->curl
= curl_easy_init();
5675 if (NULL
== context
->curl
) {
5679 if (context
->timeout
) {
5680 err
= isds_set_timeout(context
, context
->timeout
);
5681 if (err
) goto leave
;
5686 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5687 _("Sending ChangeISDSPassword request to ISDS\n") :
5688 _("Sending ChangePasswordOTP request to ISDS\n"));
5691 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5692 request
, &response
, NULL
, NULL
);
5695 /* Remove temporal credentials */
5696 _isds_discard_credentials(context
, 0);
5697 /* Detach pointer to OTP credentials from context */
5698 context
->otp_credentials
= NULL
;
5699 /* Keep context->otp true to keep signaling this is OTP session */
5702 /* Destroy request */
5703 xmlFreeNode(request
); request
= NULL
;
5706 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5707 _("Processing ISDS response on ChangeISDSPassword "
5708 "request failed\n") :
5709 _("Processing ISDS response on ChangePasswordOTP "
5710 "request failed\n"));
5714 /* Check for response status */
5715 err
= isds_response_status(context
,
5716 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5717 &code
, &message
, (xmlChar
**)refnumber
);
5719 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5720 _("ISDS response on ChangeISDSPassword request is missing "
5722 _("ISDS response on ChangePasswordOTP request is missing "
5727 /* Check for known error codes */
5728 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5729 if (!xmlStrcmp(code
, codes
[i
])) {
5730 char *code_locale
= _isds_utf82locale((char*)code
);
5731 char *message_locale
= _isds_utf82locale((char*)message
);
5732 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5733 _("Server refused to change password on ChangeISDSPassword "
5734 "request (code=%s, message=%s)\n") :
5735 _("Server refused to change password on ChangePasswordOTP "
5736 "request (code=%s, message=%s)\n"),
5737 code_locale
, message_locale
);
5739 free(message_locale
);
5740 isds_log_message(context
, _(meanings
[i
]));
5747 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5748 char *code_locale
= _isds_utf82locale((char*)code
);
5749 char *message_locale
= _isds_utf82locale((char*)message
);
5750 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5751 _("Server refused to change password on ChangeISDSPassword "
5752 "request (code=%s, message=%s)\n") :
5753 _("Server refused to change password on ChangePasswordOTP "
5754 "request (code=%s, message=%s)\n"),
5755 code_locale
, message_locale
);
5756 isds_log_message(context
, message_locale
);
5758 free(message_locale
);
5763 /* Otherwise password changed successfully */
5766 if (NULL
!= saved_url
) {
5767 /* Revert URL to original one */
5768 zfree(context
->url
);
5769 context
->url
= saved_url
;
5771 #if HAVE_CURL_REAUTHORIZATION_BUG
5772 if (NULL
!= saved_curl
) {
5773 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5774 context
->curl
= saved_curl
;
5780 xmlFreeDoc(response
);
5781 xmlFreeNode(request
);
5784 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5785 _("Password changed successfully on ChangeISDSPassword "
5787 _("Password changed successfully on ChangePasswordOTP "
5789 #else /* not HAVE_LIBCURL */
5798 /* Generic middle part with request sending and response check.
5799 * It sends prepared request and checks for error code.
5800 * @context is ISDS session context.
5801 * @service is ISDS service handler
5802 * @service_name is name in scope of given @service
5803 * @request is XML tree with request. Will be freed to save memory.
5804 * @response is XML document outputting ISDS response.
5805 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5806 * @map is mapping from status code to library error. Pass NULL if no special
5807 * handling is requested.
5808 * NULL, if you don't care. */
5809 static isds_error
send_destroy_request_check_response(
5810 struct isds_ctx
*context
,
5811 const isds_service service
, const xmlChar
*service_name
,
5812 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5813 const struct code_map_isds_error
*map
) {
5814 isds_error err
= IE_SUCCESS
;
5815 char *service_name_locale
= NULL
;
5816 xmlChar
*code
= NULL
, *message
= NULL
;
5819 if (!context
) return IE_INVALID_CONTEXT
;
5820 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5824 /* Check if connection is established
5825 * TODO: This check should be done downstairs. */
5826 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5828 service_name_locale
= _isds_utf82locale((char*) service_name
);
5829 if (!service_name_locale
) {
5834 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5835 service_name_locale
);
5838 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5839 xmlFreeNode(*request
); *request
= NULL
;
5842 isds_log(ILF_ISDS
, ILL_DEBUG
,
5843 _("Processing ISDS response on %s request failed\n"),
5844 service_name_locale
);
5848 /* Check for response status */
5849 err
= isds_response_status(context
, service
, *response
,
5850 &code
, &message
, refnumber
);
5852 isds_log(ILF_ISDS
, ILL_DEBUG
,
5853 _("ISDS response on %s request is missing status\n"),
5854 service_name_locale
);
5858 err
= statuscode2isds_error(context
, map
, code
, message
);
5860 /* Request processed, but server failed */
5861 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5862 char *code_locale
= _isds_utf82locale((char*) code
);
5863 char *message_locale
= _isds_utf82locale((char*) message
);
5864 isds_log(ILF_ISDS
, ILL_DEBUG
,
5865 _("Server refused %s request (code=%s, message=%s)\n"),
5866 service_name_locale
, code_locale
, message_locale
);
5868 free(message_locale
);
5876 if (err
&& *response
) {
5877 xmlFreeDoc(*response
);
5881 xmlFreeNode(*request
);
5884 free(service_name_locale
);
5890 /* Generic bottom half with request sending.
5891 * It sends prepared request, checks for error code, destroys response and
5892 * request and log success or failure.
5893 * @context is ISDS session context.
5894 * @service is ISDS service handler
5895 * @service_name is name in scope of given @service
5896 * @request is XML tree with request. Will be freed to save memory.
5897 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5898 * NULL, if you don't care. */
5899 static isds_error
send_request_check_drop_response(
5900 struct isds_ctx
*context
,
5901 const isds_service service
, const xmlChar
*service_name
,
5902 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5903 isds_error err
= IE_SUCCESS
;
5904 xmlDocPtr response
= NULL
;
5907 if (!context
) return IE_INVALID_CONTEXT
;
5908 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5911 /* Send request and check response*/
5912 err
= send_destroy_request_check_response(context
,
5913 service
, service_name
, request
, &response
, refnumber
, NULL
);
5915 xmlFreeDoc(response
);
5918 xmlFreeNode(*request
);
5923 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5924 isds_log(ILF_ISDS
, ILL_DEBUG
,
5925 _("%s request processed by server successfully.\n"),
5926 service_name_locale
);
5927 free(service_name_locale
);
5934 /* Insert isds_credentials_delivery structure into XML request if not NULL
5935 * @context is session context
5936 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5937 * credentials delivery. The email field is passed.
5938 * @parent is XML element where to insert */
5939 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5940 const struct isds_credentials_delivery
*credentials_delivery
,
5941 xmlNodePtr parent
) {
5942 isds_error err
= IE_SUCCESS
;
5945 if (!context
) return IE_INVALID_CONTEXT
;
5946 if (!parent
) return IE_INVAL
;
5948 if (credentials_delivery
) {
5949 /* Following elements are valid only for services:
5950 * NewAccessData, AddDataBoxUser, CreateDataBox */
5951 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5952 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5960 /* Extract credentials delivery from ISDS response.
5961 * @context is session context
5962 * @credentials_delivery is pointer to valid structure to fill in returned
5963 * user's password (and new log-in name). If NULL, do not extract the data.
5964 * @response is pointer to XML document with ISDS response
5965 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5966 * @return IE_SUCCESS even if new user name has not been found because it's not
5967 * clear whether it's returned always. */
5968 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5969 struct isds_credentials_delivery
*credentials_delivery
,
5970 xmlDocPtr response
, const char *request_name
) {
5971 isds_error err
= IE_SUCCESS
;
5972 xmlXPathContextPtr xpath_ctx
= NULL
;
5973 xmlXPathObjectPtr result
= NULL
;
5974 char *xpath_query
= NULL
;
5976 if (!context
) return IE_INVALID_CONTEXT
;
5977 if (credentials_delivery
) {
5978 zfree(credentials_delivery
->token
);
5979 zfree(credentials_delivery
->new_user_name
);
5981 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5984 /* Extract optional token */
5985 if (credentials_delivery
) {
5986 xpath_ctx
= xmlXPathNewContext(response
);
5991 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5996 /* Verify root element */
5997 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6002 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6007 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6008 char *request_name_locale
= _isds_utf82locale(request_name
);
6009 isds_log(ILF_ISDS
, ILL_WARNING
,
6010 _("Wrong element in ISDS response for %s request "
6011 "while extracting credentials delivery details\n"),
6012 request_name_locale
);
6013 free(request_name_locale
);
6017 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6020 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6022 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6024 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6025 if (!credentials_delivery
->token
) {
6026 char *request_name_locale
= _isds_utf82locale(request_name
);
6027 isds_log(ILF_ISDS
, ILL_ERR
,
6028 _("ISDS did not return token on %s request "
6029 "even if requested\n"), request_name_locale
);
6030 free(request_name_locale
);
6037 xmlXPathFreeObject(result
);
6038 xmlXPathFreeContext(xpath_ctx
);
6044 /* Build XSD:tCreateDBInput request type for box creating.
6045 * @context is session context
6046 * @request outputs built XML tree
6047 * @service_name is request name of SERVICE_DB_MANIPULATION service
6048 * @box is box description to create including single primary user (in case of
6050 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6051 * box, or contact address of PFO box owner)
6052 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6053 * @upper_box_id is optional ID of supper box if currently created box is
6055 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6057 * @credentials_delivery is valid pointer if ISDS should return token that box
6058 * owner can use to obtain his new credentials in on-line way. Then valid email
6059 * member value should be supplied.
6060 * @approval is optional external approval of box manipulation */
6061 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6062 xmlNodePtr
*request
, const xmlChar
*service_name
,
6063 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6064 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6065 const xmlChar
*ceo_label
,
6066 const struct isds_credentials_delivery
*credentials_delivery
,
6067 const struct isds_approval
*approval
) {
6068 isds_error err
= IE_SUCCESS
;
6069 xmlNsPtr isds_ns
= NULL
;
6070 xmlNodePtr node
, dbPrimaryUsers
;
6071 xmlChar
*string
= NULL
;
6072 const struct isds_list
*item
;
6075 if (!context
) return IE_INVALID_CONTEXT
;
6076 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6080 /* Build CreateDataBox-similar request */
6081 *request
= xmlNewNode(NULL
, service_name
);
6083 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6084 isds_printf_message(context
, _("Could build %s request"),
6085 service_name_locale
);
6086 free(service_name_locale
);
6089 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6090 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6092 isds_log_message(context
, _("Could not create ISDS1 name space"));
6093 xmlFreeNode(*request
);
6097 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6099 isds_log_message(context
, _("Could not create ISDS name space"));
6100 xmlFreeNode(*request
);
6104 xmlSetNs(*request
, isds_ns
);
6106 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6107 err
= insert_DbOwnerInfo(context
, box
, node
);
6108 if (err
) goto leave
;
6111 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6112 * verbose documentation allows none dbUserInfo */
6113 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6114 for (item
= users
; item
; item
= item
->next
) {
6116 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6117 err
= insert_DbUserInfo(context
,
6118 (struct isds_DbUserInfo
*) item
->data
, node
);
6119 if (err
) goto leave
;
6123 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6124 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6125 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6127 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6128 if (err
) goto leave
;
6130 err
= insert_GExtApproval(context
, approval
, *request
);
6131 if (err
) goto leave
;
6135 xmlFreeNode(*request
);
6141 #endif /* HAVE_LIBCURL */
6145 * @context is session context
6146 * @box is box description to create including single primary user (in case of
6147 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6148 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6149 * box, or contact address of PFO box owner)
6150 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6151 * @upper_box_id is optional ID of supper box if currently created box is
6153 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6154 * @credentials_delivery is NULL if new password should be delivered off-line
6155 * to box owner. It is valid pointer if owner should obtain new password on-line
6156 * on dedicated web server. Then input @credentials_delivery.email value is
6157 * his e-mail address he must provide to dedicated web server together
6158 * with output reallocated @credentials_delivery.token member. Output
6159 * member @credentials_delivery.new_user_name is unused up on this call.
6160 * @approval is optional external approval of box manipulation
6161 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6162 * NULL, if you don't care.*/
6163 isds_error
isds_add_box(struct isds_ctx
*context
,
6164 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6165 const char *former_names
, const char *upper_box_id
,
6166 const char *ceo_label
,
6167 struct isds_credentials_delivery
*credentials_delivery
,
6168 const struct isds_approval
*approval
, char **refnumber
) {
6169 isds_error err
= IE_SUCCESS
;
6171 xmlNodePtr request
= NULL
;
6172 xmlDocPtr response
= NULL
;
6173 xmlXPathContextPtr xpath_ctx
= NULL
;
6174 xmlXPathObjectPtr result
= NULL
;
6178 if (!context
) return IE_INVALID_CONTEXT
;
6179 zfree(context
->long_message
);
6180 if (credentials_delivery
) {
6181 zfree(credentials_delivery
->token
);
6182 zfree(credentials_delivery
->new_user_name
);
6184 if (!box
) return IE_INVAL
;
6187 /* Scratch box ID */
6190 /* Build CreateDataBox request */
6191 err
= build_CreateDBInput_request(context
,
6192 &request
, BAD_CAST
"CreateDataBox",
6193 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6194 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6195 if (err
) goto leave
;
6197 /* Send it to server and process response */
6198 err
= send_destroy_request_check_response(context
,
6199 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6200 &response
, (xmlChar
**) refnumber
, NULL
);
6202 /* Extract box ID */
6203 xpath_ctx
= xmlXPathNewContext(response
);
6208 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6212 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6214 /* Extract optional token */
6215 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6219 xmlXPathFreeObject(result
);
6220 xmlXPathFreeContext(xpath_ctx
);
6221 xmlFreeDoc(response
);
6222 xmlFreeNode(request
);
6225 isds_log(ILF_ISDS
, ILL_DEBUG
,
6226 _("CreateDataBox request processed by server successfully.\n"));
6228 #else /* not HAVE_LIBCURL */
6236 /* Notify ISDS about new PFO entity.
6237 * This function has no real effect.
6238 * @context is session context
6239 * @box is PFO description including single primary user.
6240 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6241 * @former_names is optional undocumented string. Pass NULL if you don't care.
6242 * @upper_box_id is optional ID of supper box if currently created box is
6244 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6245 * @approval is optional external approval of box manipulation
6246 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6247 * NULL, if you don't care.*/
6248 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6249 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6250 const char *former_names
, const char *upper_box_id
,
6251 const char *ceo_label
, const struct isds_approval
*approval
,
6253 isds_error err
= IE_SUCCESS
;
6255 xmlNodePtr request
= NULL
;
6258 if (!context
) return IE_INVALID_CONTEXT
;
6259 zfree(context
->long_message
);
6260 if (!box
) return IE_INVAL
;
6263 /* Build CreateDataBoxPFOInfo request */
6264 err
= build_CreateDBInput_request(context
,
6265 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6266 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6267 (xmlChar
*) ceo_label
, NULL
, approval
);
6268 if (err
) goto leave
;
6270 /* Send it to server and process response */
6271 err
= send_request_check_drop_response(context
,
6272 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6273 (xmlChar
**) refnumber
);
6274 /* XXX: XML Schema names output dbID element but textual documentation
6275 * states no box identifier is returned. */
6277 xmlFreeNode(request
);
6278 #else /* not HAVE_LIBCURL */
6285 /* Common implementation for removing given box.
6286 * @context is session context
6287 * @service_name is UTF-8 encoded name fo ISDS service
6288 * @box is box description to delete
6289 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6290 * carry sane value. If NULL, do not inject this information into request.
6291 * @approval is optional external approval of box manipulation
6292 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6293 * NULL, if you don't care.*/
6294 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6295 const xmlChar
*service_name
,
6296 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6297 const struct isds_approval
*approval
, char **refnumber
) {
6298 isds_error err
= IE_SUCCESS
;
6300 xmlNsPtr isds_ns
= NULL
;
6301 xmlNodePtr request
= NULL
;
6303 xmlChar
*string
= NULL
;
6307 if (!context
) return IE_INVALID_CONTEXT
;
6308 zfree(context
->long_message
);
6309 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6313 /* Build DeleteDataBox(Promptly) request */
6314 request
= xmlNewNode(NULL
, service_name
);
6316 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6317 isds_printf_message(context
,
6318 _("Could build %s request"), service_name_locale
);
6319 free(service_name_locale
);
6322 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6324 isds_log_message(context
, _("Could not create ISDS name space"));
6325 xmlFreeNode(request
);
6328 xmlSetNs(request
, isds_ns
);
6330 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6331 err
= insert_DbOwnerInfo(context
, box
, node
);
6332 if (err
) goto leave
;
6335 err
= tm2datestring(since
, &string
);
6337 isds_log_message(context
,
6338 _("Could not convert `since' argument to ISO date string"));
6341 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6345 err
= insert_GExtApproval(context
, approval
, request
);
6346 if (err
) goto leave
;
6349 /* Send it to server and process response */
6350 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6351 service_name
, &request
, (xmlChar
**) refnumber
);
6354 xmlFreeNode(request
);
6356 #else /* not HAVE_LIBCURL */
6363 /* Remove given box permanently.
6364 * @context is session context
6365 * @box is box description to delete
6366 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6368 * @approval is optional external approval of box manipulation
6369 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6370 * NULL, if you don't care.*/
6371 isds_error
isds_delete_box(struct isds_ctx
*context
,
6372 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6373 const struct isds_approval
*approval
, char **refnumber
) {
6374 if (!context
) return IE_INVALID_CONTEXT
;
6375 zfree(context
->long_message
);
6376 if (!box
|| !since
) return IE_INVAL
;
6378 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6379 box
, since
, approval
, refnumber
);
6383 /* Undocumented function.
6384 * @context is session context
6385 * @box is box description to delete
6386 * @approval is optional external approval of box manipulation
6387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6388 * NULL, if you don't care.*/
6389 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6390 const struct isds_DbOwnerInfo
*box
,
6391 const struct isds_approval
*approval
, char **refnumber
) {
6392 if (!context
) return IE_INVALID_CONTEXT
;
6393 zfree(context
->long_message
);
6394 if (!box
) return IE_INVAL
;
6396 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6397 box
, NULL
, approval
, refnumber
);
6401 /* Update data about given box.
6402 * @context is session context
6403 * @old_box current box description
6404 * @new_box are updated data about @old_box
6405 * @approval is optional external approval of box manipulation
6406 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6407 * NULL, if you don't care.*/
6408 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6409 const struct isds_DbOwnerInfo
*old_box
,
6410 const struct isds_DbOwnerInfo
*new_box
,
6411 const struct isds_approval
*approval
, char **refnumber
) {
6412 isds_error err
= IE_SUCCESS
;
6414 xmlNsPtr isds_ns
= NULL
;
6415 xmlNodePtr request
= NULL
;
6420 if (!context
) return IE_INVALID_CONTEXT
;
6421 zfree(context
->long_message
);
6422 if (!old_box
|| !new_box
) return IE_INVAL
;
6426 /* Build UpdateDataBoxDescr request */
6427 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6429 isds_log_message(context
,
6430 _("Could build UpdateDataBoxDescr request"));
6433 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6435 isds_log_message(context
, _("Could not create ISDS name space"));
6436 xmlFreeNode(request
);
6439 xmlSetNs(request
, isds_ns
);
6441 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6442 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6443 if (err
) goto leave
;
6445 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6446 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6447 if (err
) goto leave
;
6449 err
= insert_GExtApproval(context
, approval
, request
);
6450 if (err
) goto leave
;
6453 /* Send it to server and process response */
6454 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6455 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6458 xmlFreeNode(request
);
6459 #else /* not HAVE_LIBCURL */
6468 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6470 * @context is session context
6471 * @service is SOAP service
6472 * @service_name is name of request in @service
6473 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6474 * @box_id is box ID of interest
6475 * @approval is optional external approval of box manipulation
6476 * @response is server SOAP body response as XML document
6477 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6478 * NULL, if you don't care.
6479 * @return error coded from lower layer, context message will be set up
6481 static isds_error
build_send_dbid_request_check_response(
6482 struct isds_ctx
*context
, const isds_service service
,
6483 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6484 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6485 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6487 isds_error err
= IE_SUCCESS
;
6488 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6489 xmlNodePtr request
= NULL
, node
;
6490 xmlNsPtr isds_ns
= NULL
;
6492 if (!context
) return IE_INVALID_CONTEXT
;
6493 if (!service_name
|| !box_id
) return IE_INVAL
;
6494 if (!response
) return IE_INVAL
;
6496 /* Free output argument */
6497 xmlFreeDoc(*response
); *response
= NULL
;
6499 /* Prepare strings */
6500 service_name_locale
= _isds_utf82locale((char*)service_name
);
6501 if (!service_name_locale
) {
6505 box_id_locale
= _isds_utf82locale((char*)box_id
);
6506 if (!box_id_locale
) {
6512 request
= xmlNewNode(NULL
, service_name
);
6514 isds_printf_message(context
,
6515 _("Could not build %s request for %s box"), service_name_locale
,
6520 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6522 isds_log_message(context
, _("Could not create ISDS name space"));
6526 xmlSetNs(request
, isds_ns
);
6528 /* Add XSD:tIdDbInput children */
6529 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6530 INSERT_STRING(request
, box_id_element
, box_id
);
6531 err
= insert_GExtApproval(context
, approval
, request
);
6532 if (err
) goto leave
;
6534 /* Send request and check response*/
6535 err
= send_destroy_request_check_response(context
,
6536 service
, service_name
, &request
, response
, refnumber
, NULL
);
6539 free(service_name_locale
);
6540 free(box_id_locale
);
6541 xmlFreeNode(request
);
6544 #endif /* HAVE_LIBCURL */
6547 /* Get data about all users assigned to given box.
6548 * @context is session context
6550 * @users is automatically reallocated list of struct isds_DbUserInfo */
6551 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6552 struct isds_list
**users
) {
6553 isds_error err
= IE_SUCCESS
;
6555 xmlDocPtr response
= NULL
;
6556 xmlXPathContextPtr xpath_ctx
= NULL
;
6557 xmlXPathObjectPtr result
= NULL
;
6559 struct isds_list
*item
, *prev_item
= NULL
;
6562 if (!context
) return IE_INVALID_CONTEXT
;
6563 zfree(context
->long_message
);
6564 if (!users
|| !box_id
) return IE_INVAL
;
6565 isds_list_free(users
);
6569 /* Do request and check for success */
6570 err
= build_send_dbid_request_check_response(context
,
6571 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6572 BAD_CAST box_id
, NULL
, &response
, NULL
);
6573 if (err
) goto leave
;
6577 /* Prepare structure */
6578 xpath_ctx
= xmlXPathNewContext(response
);
6583 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6588 /* Set context node */
6589 result
= xmlXPathEvalExpression(BAD_CAST
6590 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6596 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6597 /* Iterate over all users */
6598 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6600 /* Prepare structure */
6601 item
= calloc(1, sizeof(*item
));
6606 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6607 if (i
== 0) *users
= item
;
6608 else prev_item
->next
= item
;
6612 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6613 err
= extract_DbUserInfo(context
,
6614 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6615 if (err
) goto leave
;
6621 isds_list_free(users
);
6624 xmlXPathFreeObject(result
);
6625 xmlXPathFreeContext(xpath_ctx
);
6626 xmlFreeDoc(response
);
6629 isds_log(ILF_ISDS
, ILL_DEBUG
,
6630 _("GetDataBoxUsers request processed by server "
6631 "successfully.\n"));
6632 #else /* not HAVE_LIBCURL */
6640 /* Update data about user assigned to given box.
6641 * @context is session context
6642 * @box is box identification
6643 * @old_user identifies user to update
6644 * @new_user are updated data about @old_user
6645 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6646 * NULL, if you don't care.*/
6647 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6648 const struct isds_DbOwnerInfo
*box
,
6649 const struct isds_DbUserInfo
*old_user
,
6650 const struct isds_DbUserInfo
*new_user
,
6652 isds_error err
= IE_SUCCESS
;
6654 xmlNsPtr isds_ns
= NULL
;
6655 xmlNodePtr request
= NULL
;
6660 if (!context
) return IE_INVALID_CONTEXT
;
6661 zfree(context
->long_message
);
6662 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6666 /* Build UpdateDataBoxUser request */
6667 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6669 isds_log_message(context
,
6670 _("Could build UpdateDataBoxUser request"));
6673 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6675 isds_log_message(context
, _("Could not create ISDS name space"));
6676 xmlFreeNode(request
);
6679 xmlSetNs(request
, isds_ns
);
6681 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6682 err
= insert_DbOwnerInfo(context
, box
, node
);
6683 if (err
) goto leave
;
6685 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6686 err
= insert_DbUserInfo(context
, old_user
, node
);
6687 if (err
) goto leave
;
6689 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6690 err
= insert_DbUserInfo(context
, new_user
, node
);
6691 if (err
) goto leave
;
6693 /* Send it to server and process response */
6694 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6695 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6698 xmlFreeNode(request
);
6699 #else /* not HAVE_LIBCURL */
6707 /* Undocumented function.
6708 * @context is session context
6709 * @box_id is UTF-8 encoded box identifier
6710 * @token is UTF-8 encoded temporary password
6711 * @user_id outputs UTF-8 encoded reallocated user identifier
6712 * @password outpus UTF-8 encoded reallocated user password
6713 * Output arguments will be nulled in case of error */
6714 isds_error
isds_activate(struct isds_ctx
*context
,
6715 const char *box_id
, const char *token
,
6716 char **user_id
, char **password
) {
6717 isds_error err
= IE_SUCCESS
;
6719 xmlNsPtr isds_ns
= NULL
;
6720 xmlNodePtr request
= NULL
, node
;
6721 xmlDocPtr response
= NULL
;
6722 xmlXPathContextPtr xpath_ctx
= NULL
;
6723 xmlXPathObjectPtr result
= NULL
;
6727 if (!context
) return IE_INVALID_CONTEXT
;
6728 zfree(context
->long_message
);
6730 if (user_id
) zfree(*user_id
);
6731 if (password
) zfree(*password
);
6733 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6737 /* Build Activate request */
6738 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6740 isds_log_message(context
, _("Could build Activate request"));
6743 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6745 isds_log_message(context
, _("Could not create ISDS name space"));
6746 xmlFreeNode(request
);
6749 xmlSetNs(request
, isds_ns
);
6751 INSERT_STRING(request
, "dbAccessDataId", token
);
6752 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6753 INSERT_STRING(request
, "dbID", box_id
);
6756 /* Send request and check response*/
6757 err
= send_destroy_request_check_response(context
,
6758 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6759 &response
, NULL
, NULL
);
6760 if (err
) goto leave
;
6764 xpath_ctx
= xmlXPathNewContext(response
);
6769 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6773 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6779 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6780 isds_log_message(context
, _("Missing ActivateResponse element"));
6784 if (result
->nodesetval
->nodeNr
> 1) {
6785 isds_log_message(context
, _("Multiple ActivateResponse element"));
6789 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6790 xmlXPathFreeObject(result
); result
= NULL
;
6792 EXTRACT_STRING("isds:userId", *user_id
);
6794 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6795 "but did not return `userId' element.\n"));
6797 EXTRACT_STRING("isds:password", *password
);
6799 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6800 "but did not return `password' element.\n"));
6803 xmlXPathFreeObject(result
);
6804 xmlXPathFreeContext(xpath_ctx
);
6805 xmlFreeDoc(response
);
6806 xmlFreeNode(request
);
6809 isds_log(ILF_ISDS
, ILL_DEBUG
,
6810 _("Activate request processed by server successfully.\n"));
6811 #else /* not HAVE_LIBCURL */
6819 /* Reset credentials of user assigned to given box.
6820 * @context is session context
6821 * @box is box identification
6822 * @user identifies user to reset password
6823 * @fee_paid is true if fee has been paid, false otherwise
6824 * @approval is optional external approval of box manipulation
6825 * @credentials_delivery is NULL if new password should be delivered off-line
6826 * to the user. It is valid pointer if user should obtain new password on-line
6827 * on dedicated web server. Then input @credentials_delivery.email value is
6828 * user's e-mail address user must provide to dedicated web server together
6829 * with @credentials_delivery.token. The output reallocated token user needs
6830 * to use to authorize on the web server to view his new password. Output
6831 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6832 * ISDS changed up on this call. (No reason why server could change the name
6834 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6835 * NULL, if you don't care.*/
6836 isds_error
isds_reset_password(struct isds_ctx
*context
,
6837 const struct isds_DbOwnerInfo
*box
,
6838 const struct isds_DbUserInfo
*user
,
6839 const _Bool fee_paid
, const struct isds_approval
*approval
,
6840 struct isds_credentials_delivery
*credentials_delivery
,
6842 isds_error err
= IE_SUCCESS
;
6844 xmlNsPtr isds_ns
= NULL
;
6845 xmlNodePtr request
= NULL
, node
;
6846 xmlDocPtr response
= NULL
;
6850 if (!context
) return IE_INVALID_CONTEXT
;
6851 zfree(context
->long_message
);
6853 if (credentials_delivery
) {
6854 zfree(credentials_delivery
->token
);
6855 zfree(credentials_delivery
->new_user_name
);
6857 if (!box
|| !user
) return IE_INVAL
;
6861 /* Build NewAccessData request */
6862 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6864 isds_log_message(context
,
6865 _("Could build NewAccessData request"));
6868 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6870 isds_log_message(context
, _("Could not create ISDS name space"));
6871 xmlFreeNode(request
);
6874 xmlSetNs(request
, isds_ns
);
6876 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6877 err
= insert_DbOwnerInfo(context
, box
, node
);
6878 if (err
) goto leave
;
6880 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6881 err
= insert_DbUserInfo(context
, user
, node
);
6882 if (err
) goto leave
;
6884 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6886 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6887 if (err
) goto leave
;
6889 err
= insert_GExtApproval(context
, approval
, request
);
6890 if (err
) goto leave
;
6892 /* Send request and check response*/
6893 err
= send_destroy_request_check_response(context
,
6894 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6895 &response
, (xmlChar
**) refnumber
, NULL
);
6896 if (err
) goto leave
;
6899 /* Extract optional token */
6900 err
= extract_credentials_delivery(context
, credentials_delivery
,
6901 response
, "NewAccessData");
6904 xmlFreeDoc(response
);
6905 xmlFreeNode(request
);
6908 isds_log(ILF_ISDS
, ILL_DEBUG
,
6909 _("NewAccessData request processed by server "
6910 "successfully.\n"));
6911 #else /* not HAVE_LIBCURL */
6919 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6920 * code, destroy response and log success.
6921 * @context is ISDS session context.
6922 * @service_name is name of SERVICE_DB_MANIPULATION service
6923 * @box is box identification
6924 * @user identifies user to remove
6925 * @credentials_delivery is NULL if new user's password should be delivered
6926 * off-line to the user. It is valid pointer if user should obtain new
6927 * password on-line on dedicated web server. Then input
6928 * @credentials_delivery.email value is user's e-mail address user must
6929 * provide to dedicated web server together with @credentials_delivery.token.
6930 * The output reallocated token user needs to use to authorize on the web
6931 * server to view his new password. Output reallocated
6932 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6933 * assingned or changed up on this call.
6934 * @approval is optional external approval of box manipulation
6935 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6936 * NULL, if you don't care. */
6937 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6938 struct isds_ctx
*context
, const xmlChar
*service_name
,
6939 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6940 struct isds_credentials_delivery
*credentials_delivery
,
6941 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6942 isds_error err
= IE_SUCCESS
;
6944 xmlNsPtr isds_ns
= NULL
;
6945 xmlNodePtr request
= NULL
, node
;
6946 xmlDocPtr response
= NULL
;
6950 if (!context
) return IE_INVALID_CONTEXT
;
6951 zfree(context
->long_message
);
6952 if (credentials_delivery
) {
6953 zfree(credentials_delivery
->token
);
6954 zfree(credentials_delivery
->new_user_name
);
6956 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6961 /* Build NewAccessData or similar request */
6962 request
= xmlNewNode(NULL
, service_name
);
6964 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6965 isds_printf_message(context
, _("Could not build %s request"),
6966 service_name_locale
);
6967 free(service_name_locale
);
6970 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6972 isds_log_message(context
, _("Could not create ISDS name space"));
6973 xmlFreeNode(request
);
6976 xmlSetNs(request
, isds_ns
);
6978 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6979 err
= insert_DbOwnerInfo(context
, box
, node
);
6980 if (err
) goto leave
;
6982 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6983 err
= insert_DbUserInfo(context
, user
, node
);
6984 if (err
) goto leave
;
6986 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6987 if (err
) goto leave
;
6989 err
= insert_GExtApproval(context
, approval
, request
);
6990 if (err
) goto leave
;
6993 /* Send request and check response*/
6994 err
= send_destroy_request_check_response(context
,
6995 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6998 xmlFreeNode(request
);
7001 /* Pick up credentials_delivery if requested */
7002 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7003 (char *)service_name
);
7006 xmlFreeDoc(response
);
7007 if (request
) xmlFreeNode(request
);
7010 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7011 isds_log(ILF_ISDS
, ILL_DEBUG
,
7012 _("%s request processed by server successfully.\n"),
7013 service_name_locale
);
7014 free(service_name_locale
);
7016 #else /* not HAVE_LIBCURL */
7024 /* Assign new user to given box.
7025 * @context is session context
7026 * @box is box identification
7027 * @user defines new user to add
7028 * @credentials_delivery is NULL if new user's password should be delivered
7029 * off-line to the user. It is valid pointer if user should obtain new
7030 * password on-line on dedicated web server. Then input
7031 * @credentials_delivery.email value is user's e-mail address user must
7032 * provide to dedicated web server together with @credentials_delivery.token.
7033 * The output reallocated token user needs to use to authorize on the web
7034 * server to view his new password. Output reallocated
7035 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7036 * assingned up on this call.
7037 * @approval is optional external approval of box manipulation
7038 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7039 * NULL, if you don't care.*/
7040 isds_error
isds_add_user(struct isds_ctx
*context
,
7041 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7042 struct isds_credentials_delivery
*credentials_delivery
,
7043 const struct isds_approval
*approval
, char **refnumber
) {
7044 return build_send_manipulationboxuser_request_check_drop_response(context
,
7045 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
7046 approval
, (xmlChar
**) refnumber
);
7050 /* Remove user assigned to given box.
7051 * @context is session context
7052 * @box is box identification
7053 * @user identifies user to remove
7054 * @approval is optional external approval of box manipulation
7055 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7056 * NULL, if you don't care.*/
7057 isds_error
isds_delete_user(struct isds_ctx
*context
,
7058 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7059 const struct isds_approval
*approval
, char **refnumber
) {
7060 return build_send_manipulationboxuser_request_check_drop_response(context
,
7061 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
7062 (xmlChar
**) refnumber
);
7066 /* Get list of boxes in ZIP archive.
7067 * @context is session context
7068 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7069 * System recognizes following values currently: ALL (all boxes), UPG
7070 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7071 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7072 * commercial messages). This argument is a string because specification
7073 * states new values can appear in the future. Not all list types are
7074 * available to all users.
7075 * @buffer is automatically reallocated memory to store the list of boxes. The
7076 * list is zipped CSV file.
7077 * @buffer_length is size of @buffer data in bytes.
7078 * In case of error @buffer will be freed and @buffer_length will be
7080 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7081 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7082 isds_error err
= IE_SUCCESS
;
7084 xmlNsPtr isds_ns
= NULL
;
7085 xmlNodePtr request
= NULL
, node
;
7086 xmlDocPtr response
= NULL
;
7087 xmlXPathContextPtr xpath_ctx
= NULL
;
7088 xmlXPathObjectPtr result
= NULL
;
7089 char *string
= NULL
;
7093 if (!context
) return IE_INVALID_CONTEXT
;
7094 zfree(context
->long_message
);
7095 if (buffer
) zfree(*buffer
);
7096 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7100 /* Check if connection is established
7101 * TODO: This check should be done downstairs. */
7102 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7105 /* Build AuthenticateMessage request */
7106 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7108 isds_log_message(context
,
7109 _("Could not build GetDataBoxList request"));
7112 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7114 isds_log_message(context
, _("Could not create ISDS name space"));
7115 xmlFreeNode(request
);
7118 xmlSetNs(request
, isds_ns
);
7119 INSERT_STRING(request
, "dblType", list_identifier
);
7121 /* Send request to server and process response */
7122 err
= send_destroy_request_check_response(context
,
7123 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7124 &response
, NULL
, NULL
);
7125 if (err
) goto leave
;
7128 /* Extract Base-64 encoded ZIP file */
7129 xpath_ctx
= xmlXPathNewContext(response
);
7134 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7138 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7140 /* Decode non-empty archive */
7141 if (string
&& string
[0] != '\0') {
7142 *buffer_length
= _isds_b64decode(string
, buffer
);
7143 if (*buffer_length
== (size_t) -1) {
7144 isds_printf_message(context
,
7145 _("Error while Base64-decoding box list archive"));
7154 xmlXPathFreeObject(result
);
7155 xmlXPathFreeContext(xpath_ctx
);
7156 xmlFreeDoc(response
);
7157 xmlFreeNode(request
);
7160 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7161 "processed by server successfully.\n"));
7163 #else /* not HAVE_LIBCURL */
7171 /* Find boxes suiting given criteria.
7172 * @criteria is filter. You should fill in at least some members.
7173 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7174 * possibly empty. Input NULL or valid old structure.
7176 * IE_SUCCESS if search succeeded, @boxes contains useful data
7177 * IE_NOEXIST if no such box exists, @boxes will be NULL
7178 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7179 * contains still valid data
7180 * other code if something bad happens. @boxes will be NULL. */
7181 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7182 const struct isds_DbOwnerInfo
*criteria
,
7183 struct isds_list
**boxes
) {
7184 isds_error err
= IE_SUCCESS
;
7186 _Bool truncated
= 0;
7187 xmlNsPtr isds_ns
= NULL
;
7188 xmlNodePtr request
= NULL
;
7189 xmlDocPtr response
= NULL
;
7190 xmlChar
*code
= NULL
, *message
= NULL
;
7191 xmlNodePtr db_owner_info
;
7192 xmlXPathContextPtr xpath_ctx
= NULL
;
7193 xmlXPathObjectPtr result
= NULL
;
7194 xmlChar
*string
= NULL
;
7198 if (!context
) return IE_INVALID_CONTEXT
;
7199 zfree(context
->long_message
);
7200 if (!boxes
) return IE_INVAL
;
7201 isds_list_free(boxes
);
7208 /* Check if connection is established
7209 * TODO: This check should be done downstairs. */
7210 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7213 /* Build FindDataBox request */
7214 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7216 isds_log_message(context
,
7217 _("Could build FindDataBox request"));
7220 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7222 isds_log_message(context
, _("Could not create ISDS name space"));
7223 xmlFreeNode(request
);
7226 xmlSetNs(request
, isds_ns
);
7227 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7228 if (!db_owner_info
) {
7229 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7230 "FindDataBox element"));
7231 xmlFreeNode(request
);
7235 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7236 if (err
) goto leave
;
7239 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7242 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7244 /* Destroy request */
7245 xmlFreeNode(request
); request
= NULL
;
7248 isds_log(ILF_ISDS
, ILL_DEBUG
,
7249 _("Processing ISDS response on FindDataBox "
7250 "request failed\n"));
7254 /* Check for response status */
7255 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7256 &code
, &message
, NULL
);
7258 isds_log(ILF_ISDS
, ILL_DEBUG
,
7259 _("ISDS response on FindDataBox request is missing status\n"));
7263 /* Request processed, but nothing found */
7264 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7265 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7266 char *code_locale
= _isds_utf82locale((char*)code
);
7267 char *message_locale
= _isds_utf82locale((char*)message
);
7268 isds_log(ILF_ISDS
, ILL_DEBUG
,
7269 _("Server did not found any box on FindDataBox request "
7270 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7271 isds_log_message(context
, message_locale
);
7273 free(message_locale
);
7278 /* Warning, not a error */
7279 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7280 char *code_locale
= _isds_utf82locale((char*)code
);
7281 char *message_locale
= _isds_utf82locale((char*)message
);
7282 isds_log(ILF_ISDS
, ILL_DEBUG
,
7283 _("Server truncated response on FindDataBox request "
7284 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7285 isds_log_message(context
, message_locale
);
7287 free(message_locale
);
7292 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7293 char *code_locale
= _isds_utf82locale((char*)code
);
7294 char *message_locale
= _isds_utf82locale((char*)message
);
7295 isds_log(ILF_ISDS
, ILL_DEBUG
,
7296 _("Server refused FindDataBox request "
7297 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7298 isds_log_message(context
, message_locale
);
7300 free(message_locale
);
7305 xpath_ctx
= xmlXPathNewContext(response
);
7310 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7315 /* Extract boxes if they present */
7316 result
= xmlXPathEvalExpression(BAD_CAST
7317 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7323 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7324 struct isds_list
*item
, *prev_item
= NULL
;
7325 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7326 item
= calloc(1, sizeof(*item
));
7332 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7333 if (i
== 0) *boxes
= item
;
7334 else prev_item
->next
= item
;
7337 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7338 err
= extract_DbOwnerInfo(context
,
7339 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7340 if (err
) goto leave
;
7346 isds_list_free(boxes
);
7348 if (truncated
) err
= IE_2BIG
;
7352 xmlFreeNode(request
);
7353 xmlXPathFreeObject(result
);
7354 xmlXPathFreeContext(xpath_ctx
);
7358 xmlFreeDoc(response
);
7361 isds_log(ILF_ISDS
, ILL_DEBUG
,
7362 _("FindDataBox request processed by server successfully.\n"));
7363 #else /* not HAVE_LIBCURL */
7372 /* Convert a string with match markers into a plain string with list of
7373 * pointers to the matches
7374 * @string is an UTF-8 encoded non-constant string with match markers
7375 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7376 * The markers will be removed from the string.
7377 * @starts is a reallocated list of static pointers into the @string pointing
7378 * to places where match start markers occured.
7379 * @ends is a reallocated list of static pointers into the @string pointing
7380 * to places where match end markers occured.
7381 * @return IE_SUCCESS in case of no failure. */
7382 static isds_error
interpret_matches(xmlChar
*string
,
7383 struct isds_list
**starts
, struct isds_list
**ends
) {
7384 isds_error err
= IE_SUCCESS
;
7385 xmlChar
*pointer
, *destination
, *source
;
7386 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7388 isds_list_free(starts
);
7389 isds_list_free(ends
);
7390 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7391 if (NULL
== string
) return IE_SUCCESS
;
7393 for (pointer
= string
; *pointer
!= '\0';) {
7394 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7395 /* Remove the start marker */
7396 for (source
= pointer
+ 14, destination
= pointer
;
7397 *source
!= '\0'; source
++, destination
++) {
7398 *destination
= *source
;
7400 *destination
= '\0';
7401 /* Append the pointer into the list */
7402 item
= calloc(1, sizeof(*item
));
7407 item
->destructor
= (void (*)(void **))NULL
;
7408 item
->data
= pointer
;
7409 if (NULL
== prev_start
) *starts
= item
;
7410 else prev_start
->next
= item
;
7412 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7413 /* Remove the end marker */
7414 for (source
= pointer
+ 12, destination
= pointer
;
7415 *source
!= '\0'; source
++, destination
++) {
7416 *destination
= *source
;
7418 *destination
= '\0';
7419 /* Append the pointer into the list */
7420 item
= calloc(1, sizeof(*item
));
7425 item
->destructor
= (void (*)(void **))NULL
;
7426 item
->data
= pointer
;
7427 if (NULL
== prev_end
) *ends
= item
;
7428 else prev_end
->next
= item
;
7437 isds_list_free(starts
);
7438 isds_list_free(ends
);
7444 /* Convert isds:dbResult XML tree into structure
7445 * @context is ISDS context.
7446 * @fulltext_result is automatically reallocated found box structure.
7447 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7448 * @collect_matches is true to interpret match markers.
7449 * In case of error @result will be freed. */
7450 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7451 struct isds_fulltext_result
**fulltext_result
,
7452 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7453 isds_error err
= IE_SUCCESS
;
7454 xmlXPathObjectPtr result
= NULL
;
7455 char *string
= NULL
;
7457 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7458 if (NULL
== fulltext_result
) return IE_INVAL
;
7459 isds_fulltext_result_free(fulltext_result
);
7460 if (!xpath_ctx
) return IE_INVAL
;
7463 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7464 if (NULL
== *fulltext_result
) {
7470 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7472 EXTRACT_STRING("isds:dbType", string
);
7473 if (NULL
== string
) {
7475 isds_log_message(context
, _("Empty isds:dbType element"));
7478 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7480 if (err
== IE_ENUM
) {
7482 char *string_locale
= _isds_utf82locale(string
);
7483 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7485 free(string_locale
);
7491 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7492 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7494 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7495 if (err
) goto leave
;
7497 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7498 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7499 (*fulltext_result
)->dbEffectiveOVM
);
7501 EXTRACT_STRING("isds:dbSendOptions", string
);
7502 if (NULL
== string
) {
7504 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7507 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7508 (*fulltext_result
)->active
= 1;
7509 (*fulltext_result
)->public_sending
= 1;
7510 (*fulltext_result
)->commercial_sending
= 0;
7511 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7512 (*fulltext_result
)->active
= 1;
7513 (*fulltext_result
)->public_sending
= 1;
7514 (*fulltext_result
)->commercial_sending
= 1;
7515 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7516 (*fulltext_result
)->active
= 1;
7517 (*fulltext_result
)->public_sending
= 0;
7518 (*fulltext_result
)->commercial_sending
= 1;
7519 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7520 (*fulltext_result
)->active
= 1;
7521 (*fulltext_result
)->public_sending
= 0;
7522 (*fulltext_result
)->commercial_sending
= 0;
7523 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7524 (*fulltext_result
)->active
= 0;
7525 (*fulltext_result
)->public_sending
= 0;
7526 (*fulltext_result
)->commercial_sending
= 0;
7529 char *string_locale
= _isds_utf82locale(string
);
7530 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7532 free(string_locale
);
7537 /* Interpret match marks */
7538 if (collect_matches
) {
7539 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7540 &((*fulltext_result
)->name_match_start
),
7541 &((*fulltext_result
)->name_match_end
));
7542 if (err
) goto leave
;
7543 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7544 &((*fulltext_result
)->address_match_start
),
7545 &((*fulltext_result
)->address_match_end
));
7546 if (err
) goto leave
;
7550 if (err
) isds_fulltext_result_free(fulltext_result
);
7552 xmlXPathFreeObject(result
);
7555 #endif /* HAVE_LIBCURL */
7558 /* Find boxes matching a given full-text criteria.
7559 * @context is a session context
7560 * @query is a non-empty string which consists of words to search
7561 * @target selects box attributes to search for @query words. Pass NULL if you
7563 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7564 * to search in all box types. Pass NULL to let server to use default value
7565 * which is DBTYPE_SYSTEM.
7566 * @page_size defines count of boxes to constitute a response page. It counts
7567 * from zero. Pass NULL to let server to use a default value (50 now).
7568 * @page_number defines ordinar number of the response page to return. It
7569 * counts from zero. Pass NULL to let server to use a default value (0 now).
7570 * @track_matches points to true for marking @query words found in the box
7571 * attributes. It points to false for not marking. Pass NULL to let the server
7572 * to use default value (false now).
7573 * @total_matching_boxes outputs reallocated number of all boxes matching the
7574 * query. Will be pointer to NULL if server did not provide the value.
7575 * Pass NULL if you don't care.
7576 * @current_page_beginning outputs reallocated ordinar number of the first box
7577 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7578 * server did not provide the value. Pass NULL if you don't care.
7579 * @current_page_size outputs reallocated count of boxes in the this @boxes
7580 * page. It will be pointer to NULL if the server did not provide the value.
7581 * Pass NULL if you don't care.
7582 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7583 * is the last one, false if more boxes match, NULL if the server did not
7584 * provude the value. Pass NULL if you don't care.
7585 * @boxes outputs reallocated list of isds_fulltext_result structures,
7588 * IE_SUCCESS if search succeeded
7589 * IE_2BIG if @page_size is too large
7590 * other code if something bad happens; output arguments will be NULL. */
7591 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7593 const isds_fulltext_target
*target
,
7594 const isds_DbType
*box_type
,
7595 const unsigned long int *page_size
,
7596 const unsigned long int *page_number
,
7597 const _Bool
*track_matches
,
7598 unsigned long int **total_matching_boxes
,
7599 unsigned long int **current_page_beginning
,
7600 unsigned long int **current_page_size
,
7602 struct isds_list
**boxes
) {
7603 isds_error err
= IE_SUCCESS
;
7605 xmlNsPtr isds_ns
= NULL
;
7606 xmlNodePtr request
= NULL
;
7607 xmlDocPtr response
= NULL
;
7609 xmlXPathContextPtr xpath_ctx
= NULL
;
7610 xmlXPathObjectPtr result
= NULL
;
7611 const xmlChar
*static_string
= NULL
;
7612 xmlChar
*string
= NULL
;
7614 const xmlChar
*codes
[] = {
7624 const char *meanings
[] = {
7625 N_("You are not allowed to perform the search"),
7626 N_("The query string is empty"),
7627 N_("Searched box ID is malformed"),
7628 N_("Searched organization ID is malformed"),
7629 N_("Invalid input"),
7630 N_("Requested page size is too large"),
7631 N_("Search engine internal error")
7633 const isds_error errors
[] = {
7642 struct code_map_isds_error map
= {
7644 .meanings
= meanings
,
7650 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7651 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7652 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7653 if (NULL
!= last_page
) zfree(*last_page
);
7654 isds_list_free(boxes
);
7656 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7657 zfree(context
->long_message
);
7659 if (NULL
== boxes
) return IE_INVAL
;
7661 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7662 isds_log_message(context
, _("Query string must be non-empty"));
7667 /* Check if connection is established
7668 * TODO: This check should be done downstairs. */
7669 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7671 /* Build FindDataBox request */
7672 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7673 if (NULL
== request
) {
7674 isds_log_message(context
,
7675 _("Could not build ISDSSearch2 request"));
7678 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7679 if(NULL
== isds_ns
) {
7680 isds_log_message(context
, _("Could not create ISDS name space"));
7681 xmlFreeNode(request
);
7684 xmlSetNs(request
, isds_ns
);
7686 INSERT_STRING(request
, "searchText", query
);
7688 if (NULL
!= target
) {
7689 static_string
= isds_fulltext_target2string(*(target
));
7690 if (NULL
== static_string
) {
7691 isds_printf_message(context
, _("Invalid target value: %d"),
7697 INSERT_STRING(request
, "searchType", static_string
);
7698 static_string
= NULL
;
7700 if (NULL
!= box_type
) {
7701 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7702 if (DBTYPE_SYSTEM
== *box_type
) {
7703 static_string
= BAD_CAST
"ALL";
7705 static_string
= isds_DbType2string(*(box_type
));
7706 if (NULL
== static_string
) {
7707 isds_printf_message(context
, _("Invalid box type value: %d"),
7714 INSERT_STRING(request
, "searchScope", static_string
);
7715 static_string
= NULL
;
7717 INSERT_ULONGINT(request
, "page", page_number
, string
);
7718 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7719 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7721 /* Send request and check response */
7722 err
= send_destroy_request_check_response(context
,
7723 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7724 &request
, &response
, NULL
, &map
);
7725 if (err
) goto leave
;
7727 /* Parse response */
7728 xpath_ctx
= xmlXPathNewContext(response
);
7729 if (NULL
== xpath_ctx
) {
7733 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7737 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7743 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7744 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7748 if (result
->nodesetval
->nodeNr
> 1) {
7749 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7753 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7754 xmlXPathFreeObject(result
); result
= NULL
;
7757 /* Extract counters */
7758 if (NULL
!= total_matching_boxes
) {
7759 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7761 if (NULL
!= current_page_size
) {
7762 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7764 if (NULL
!= current_page_beginning
) {
7765 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7767 if (NULL
!= last_page
) {
7768 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7770 xmlXPathFreeObject(result
); result
= NULL
;
7772 /* Extract boxes if they present */
7773 result
= xmlXPathEvalExpression(BAD_CAST
7774 "isds:dbResults/isds:dbResult", xpath_ctx
);
7775 if (NULL
== result
) {
7779 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7780 struct isds_list
*item
, *prev_item
= NULL
;
7781 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7782 item
= calloc(1, sizeof(*item
));
7788 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7789 if (i
== 0) *boxes
= item
;
7790 else prev_item
->next
= item
;
7793 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7794 err
= extract_dbResult(context
,
7795 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7796 (NULL
== track_matches
) ? 0 : *track_matches
);
7797 if (err
) goto leave
;
7803 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7804 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7805 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7806 if (NULL
!= last_page
) zfree(*last_page
);
7807 isds_list_free(boxes
);
7811 xmlFreeNode(request
);
7812 xmlXPathFreeObject(result
);
7813 xmlXPathFreeContext(xpath_ctx
);
7814 xmlFreeDoc(response
);
7817 isds_log(ILF_ISDS
, ILL_DEBUG
,
7818 _("ISDSSearch2 request processed by server successfully.\n"));
7819 #else /* not HAVE_LIBCURL */
7827 /* Get status of a box.
7828 * @context is ISDS session context.
7829 * @box_id is UTF-8 encoded box identifier as zero terminated string
7830 * @box_status is return value of box status.
7832 * IE_SUCCESS if box has been found and its status retrieved
7833 * IE_NOEXIST if box is not known to ISDS server
7834 * or other appropriate error.
7835 * You can use isds_DbState to enumerate box status. However out of enum
7836 * range value can be returned too. This is feature because ISDS
7837 * specification leaves the set of values open.
7838 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7839 * the box has been deleted, but ISDS still lists its former existence. */
7840 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7841 long int *box_status
) {
7842 isds_error err
= IE_SUCCESS
;
7844 xmlNsPtr isds_ns
= NULL
;
7845 xmlNodePtr request
= NULL
, db_id
;
7846 xmlDocPtr response
= NULL
;
7847 xmlXPathContextPtr xpath_ctx
= NULL
;
7848 xmlXPathObjectPtr result
= NULL
;
7849 xmlChar
*string
= NULL
;
7851 const xmlChar
*codes
[] = {
7857 const char *meanings
[] = {
7858 "The box does not exist",
7859 "Box ID is malformed",
7862 const isds_error errors
[] = {
7867 struct code_map_isds_error map
= {
7869 .meanings
= meanings
,
7874 if (!context
) return IE_INVALID_CONTEXT
;
7875 zfree(context
->long_message
);
7876 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7879 /* Check if connection is established
7880 * TODO: This check should be done downstairs. */
7881 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7884 /* Build CheckDataBox request */
7885 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7887 isds_log_message(context
,
7888 _("Could build CheckDataBox request"));
7891 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7893 isds_log_message(context
, _("Could not create ISDS name space"));
7894 xmlFreeNode(request
);
7897 xmlSetNs(request
, isds_ns
);
7898 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7900 isds_log_message(context
, _("Could not add dbID child to "
7901 "CheckDataBox element"));
7902 xmlFreeNode(request
);
7907 /* Send request and check response*/
7908 err
= send_destroy_request_check_response(context
,
7909 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7910 &request
, &response
, NULL
, &map
);
7911 if (err
) goto leave
;
7915 xpath_ctx
= xmlXPathNewContext(response
);
7920 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7924 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7930 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7931 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7935 if (result
->nodesetval
->nodeNr
> 1) {
7936 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7940 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7941 xmlXPathFreeObject(result
); result
= NULL
;
7943 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7948 xmlXPathFreeObject(result
);
7949 xmlXPathFreeContext(xpath_ctx
);
7951 xmlFreeDoc(response
);
7954 isds_log(ILF_ISDS
, ILL_DEBUG
,
7955 _("CheckDataBox request processed by server successfully.\n"));
7956 #else /* not HAVE_LIBCURL */
7964 /* Get list of permissions to send commercial messages.
7965 * @context is ISDS session context.
7966 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7967 * @permissions is a reallocated list of permissions (struct
7968 * isds_commercial_permission*) to send commercial messages from @box_id. The
7969 * order of permissions is significant as the server applies the permissions
7970 * and associated pre-paid credits in the order. Empty list means no
7973 * IE_SUCCESS if the list has been obtained correctly,
7974 * or other appropriate error. */
7975 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7976 const char *box_id
, struct isds_list
**permissions
) {
7977 isds_error err
= IE_SUCCESS
;
7979 xmlDocPtr response
= NULL
;
7980 xmlXPathContextPtr xpath_ctx
= NULL
;
7981 xmlXPathObjectPtr result
= NULL
;
7984 if (!context
) return IE_INVALID_CONTEXT
;
7985 zfree(context
->long_message
);
7986 if (NULL
== permissions
) return IE_INVAL
;
7987 isds_list_free(permissions
);
7988 if (NULL
== box_id
) return IE_INVAL
;
7991 /* Check if connection is established */
7992 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7994 /* Do request and check for success */
7995 err
= build_send_dbid_request_check_response(context
,
7996 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7997 BAD_CAST box_id
, NULL
, &response
, NULL
);
7999 isds_log(ILF_ISDS
, ILL_DEBUG
,
8000 _("PDZInfo request processed by server successfully.\n"));
8004 /* Prepare structure */
8005 xpath_ctx
= xmlXPathNewContext(response
);
8010 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8015 /* Set context node */
8016 result
= xmlXPathEvalExpression(BAD_CAST
8017 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8023 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8024 struct isds_list
*prev_item
= NULL
;
8026 /* Iterate over all permission records */
8027 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8028 struct isds_list
*item
;
8030 /* Prepare structure */
8031 item
= calloc(1, sizeof(*item
));
8036 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8037 if (i
== 0) *permissions
= item
;
8038 else prev_item
->next
= item
;
8042 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8043 err
= extract_DbPDZRecord(context
,
8044 (struct isds_commercial_permission
**) (&item
->data
),
8046 if (err
) goto leave
;
8052 isds_list_free(permissions
);
8055 xmlXPathFreeObject(result
);
8056 xmlXPathFreeContext(xpath_ctx
);
8057 xmlFreeDoc(response
);
8059 #else /* not HAVE_LIBCURL */
8067 /* Get details about credit for sending pre-paid commercial messages.
8068 * @context is ISDS session context.
8069 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8070 * @from_date is first day of credit history to return in @history. Only
8071 * tm_year, tm_mon and tm_mday carry sane value.
8072 * @to_date is last day of credit history to return in @history. Only
8073 * tm_year, tm_mon and tm_mday carry sane value.
8074 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8075 * if you don't care. This and all other credit values are integers in
8076 * hundredths of Czech Crowns.
8077 * @email outputs notification e-mail address where notifications about credit
8078 * are sent. This is automatically reallocated string. Pass NULL if you don't
8079 * care. It can return NULL if no address is defined.
8080 * @history outputs auto-reallocated list of pointers to struct
8081 * isds_credit_event. Events in closed interval @from_time to @to_time are
8082 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8083 * are sorted by time.
8085 * IE_SUCCESS if the credit details have been obtained correctly,
8086 * or other appropriate error. Please note that server allows to retrieve
8087 * only limited history of events. */
8088 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8090 const struct tm
*from_date
, const struct tm
*to_date
,
8091 long int *credit
, char **email
, struct isds_list
**history
) {
8092 isds_error err
= IE_SUCCESS
;
8094 char *box_id_locale
= NULL
;
8095 xmlNodePtr request
= NULL
, node
;
8096 xmlNsPtr isds_ns
= NULL
;
8097 xmlChar
*string
= NULL
;
8099 xmlDocPtr response
= NULL
;
8100 xmlXPathContextPtr xpath_ctx
= NULL
;
8101 xmlXPathObjectPtr result
= NULL
;
8103 const xmlChar
*codes
[] = {
8111 const char *meanings
[] = {
8112 "Insufficient priviledges for the box",
8113 "The box does not exist",
8114 "Date is too long (history is not available after 15 months)",
8115 "Interval is too long (limit is 3 months)",
8118 const isds_error errors
[] = {
8125 struct code_map_isds_error map
= {
8127 .meanings
= meanings
,
8132 if (!context
) return IE_INVALID_CONTEXT
;
8133 zfree(context
->long_message
);
8135 /* Free output argument */
8136 if (NULL
!= credit
) *credit
= 0;
8137 if (NULL
!= email
) zfree(*email
);
8138 isds_list_free(history
);
8140 if (NULL
== box_id
) return IE_INVAL
;
8143 /* Check if connection is established */
8144 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8146 box_id_locale
= _isds_utf82locale((char*)box_id
);
8147 if (NULL
== box_id_locale
) {
8153 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8154 if (NULL
== request
) {
8155 isds_printf_message(context
,
8156 _("Could not build DataBoxCreditInfo request for %s box"),
8161 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8163 isds_log_message(context
, _("Could not create ISDS name space"));
8167 xmlSetNs(request
, isds_ns
);
8169 /* Add mandatory XSD:tIdDbInput child */
8170 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8171 /* Add mandatory dates elements with optional values */
8173 err
= tm2datestring(from_date
, &string
);
8175 isds_log_message(context
,
8176 _("Could not convert `from_date' argument to ISO date "
8180 INSERT_STRING(request
, "ciFromDate", string
);
8183 INSERT_STRING(request
, "ciFromDate", NULL
);
8186 err
= tm2datestring(to_date
, &string
);
8188 isds_log_message(context
,
8189 _("Could not convert `to_date' argument to ISO date "
8193 INSERT_STRING(request
, "ciTodate", string
);
8196 INSERT_STRING(request
, "ciTodate", NULL
);
8199 /* Send request and check response*/
8200 err
= send_destroy_request_check_response(context
,
8201 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8202 &request
, &response
, NULL
, &map
);
8203 if (err
) goto leave
;
8207 /* Set context to the root */
8208 xpath_ctx
= xmlXPathNewContext(response
);
8213 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8217 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8223 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8224 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8228 if (result
->nodesetval
->nodeNr
> 1) {
8229 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8233 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8234 xmlXPathFreeObject(result
); result
= NULL
;
8236 /* Extract common data */
8237 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8238 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8240 /* Extract records */
8241 if (NULL
== history
) goto leave
;
8242 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8248 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8249 struct isds_list
*prev_item
= NULL
;
8251 /* Iterate over all records */
8252 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8253 struct isds_list
*item
;
8255 /* Prepare structure */
8256 item
= calloc(1, sizeof(*item
));
8261 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8262 if (i
== 0) *history
= item
;
8263 else prev_item
->next
= item
;
8267 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8268 err
= extract_CiRecord(context
,
8269 (struct isds_credit_event
**) (&item
->data
),
8271 if (err
) goto leave
;
8277 isds_log(ILF_ISDS
, ILL_DEBUG
,
8278 _("DataBoxCreditInfo request processed by server successfully.\n"));
8281 isds_list_free(history
);
8282 if (NULL
!= email
) zfree(*email
)
8285 free(box_id_locale
);
8286 xmlXPathFreeObject(result
);
8287 xmlXPathFreeContext(xpath_ctx
);
8288 xmlFreeDoc(response
);
8290 #else /* not HAVE_LIBCURL */
8298 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8299 * code, destroy response and log success.
8300 * @context is ISDS session context.
8301 * @service_name is name of SERVICE_DB_MANIPULATION service
8302 * @box_id is UTF-8 encoded box identifier as zero terminated string
8303 * @approval is optional external approval of box manipulation
8304 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8305 * NULL, if you don't care. */
8306 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8307 struct isds_ctx
*context
, const xmlChar
*service_name
,
8308 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8309 xmlChar
**refnumber
) {
8310 isds_error err
= IE_SUCCESS
;
8312 xmlDocPtr response
= NULL
;
8315 if (!context
) return IE_INVALID_CONTEXT
;
8316 zfree(context
->long_message
);
8317 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8320 /* Check if connection is established */
8321 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8323 /* Do request and check for success */
8324 err
= build_send_dbid_request_check_response(context
,
8325 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8326 &response
, refnumber
);
8327 xmlFreeDoc(response
);
8330 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8331 isds_log(ILF_ISDS
, ILL_DEBUG
,
8332 _("%s request processed by server successfully.\n"),
8333 service_name_locale
);
8334 free(service_name_locale
);
8336 #else /* not HAVE_LIBCURL */
8344 /* Switch box into state where box can receive commercial messages (off by
8346 * @context is ISDS session context.
8347 * @box_id is UTF-8 encoded box identifier as zero terminated string
8348 * @allow is true for enable, false for disable commercial messages income
8349 * @approval is optional external approval of box manipulation
8350 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8351 * NULL, if you don't care. */
8352 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8353 const char *box_id
, const _Bool allow
,
8354 const struct isds_approval
*approval
, char **refnumber
) {
8355 return build_send_manipulationdbid_request_check_drop_response(context
,
8356 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8357 BAD_CAST
"ClearOpenAddressing",
8358 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8362 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8363 * message acceptance). This is just a box permission. Sender must apply
8364 * such role by sending each message.
8365 * @context is ISDS session context.
8366 * @box_id is UTF-8 encoded box identifier as zero terminated string
8367 * @allow is true for enable, false for disable OVM role permission
8368 * @approval is optional external approval of box manipulation
8369 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8370 * NULL, if you don't care. */
8371 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8372 const char *box_id
, const _Bool allow
,
8373 const struct isds_approval
*approval
, char **refnumber
) {
8374 return build_send_manipulationdbid_request_check_drop_response(context
,
8375 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8376 BAD_CAST
"ClearEffectiveOVM",
8377 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8381 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8382 * code, destroy response and log success.
8383 * @context is ISDS session context.
8384 * @service_name is name of SERVICE_DB_MANIPULATION service
8385 * @owner is structure describing box
8386 * @approval is optional external approval of box manipulation
8387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8388 * NULL, if you don't care. */
8389 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8390 struct isds_ctx
*context
, const xmlChar
*service_name
,
8391 const struct isds_DbOwnerInfo
*owner
,
8392 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8393 isds_error err
= IE_SUCCESS
;
8395 char *service_name_locale
= NULL
;
8396 xmlNodePtr request
= NULL
, db_owner_info
;
8397 xmlNsPtr isds_ns
= NULL
;
8401 if (!context
) return IE_INVALID_CONTEXT
;
8402 zfree(context
->long_message
);
8403 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8406 service_name_locale
= _isds_utf82locale((char*)service_name
);
8407 if (!service_name_locale
) {
8413 request
= xmlNewNode(NULL
, service_name
);
8415 isds_printf_message(context
,
8416 _("Could not build %s request"), service_name_locale
);
8420 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8422 isds_log_message(context
, _("Could not create ISDS name space"));
8426 xmlSetNs(request
, isds_ns
);
8429 /* Add XSD:tOwnerInfoInput child*/
8430 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8431 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8432 if (err
) goto leave
;
8434 /* Add XSD:gExtApproval*/
8435 err
= insert_GExtApproval(context
, approval
, request
);
8436 if (err
) goto leave
;
8438 /* Send it to server and process response */
8439 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8440 service_name
, &request
, refnumber
);
8443 xmlFreeNode(request
);
8444 free(service_name_locale
);
8445 #else /* not HAVE_LIBCURL */
8453 /* Switch box accessibility state on request of box owner.
8454 * Despite the name, owner must do the request off-line. This function is
8455 * designed for such off-line meeting points (e.g. Czech POINT).
8456 * @context is ISDS session context.
8457 * @box identifies box to switch accessibility state.
8458 * @allow is true for making accessible, false to disallow access.
8459 * @approval is optional external approval of box manipulation
8460 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8461 * NULL, if you don't care. */
8462 isds_error
isds_switch_box_accessibility_on_owner_request(
8463 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8464 const _Bool allow
, const struct isds_approval
*approval
,
8466 return build_send_manipulationdbowner_request_check_drop_response(context
,
8467 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8468 BAD_CAST
"DisableOwnDataBox",
8469 box
, approval
, (xmlChar
**) refnumber
);
8473 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8475 * @context is ISDS session context.
8476 * @box identifies box to switch accessibility state.
8477 * @since is date since accessibility has been denied. This can be past too.
8478 * Only tm_year, tm_mon and tm_mday carry sane value.
8479 * @approval is optional external approval of box manipulation
8480 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8481 * NULL, if you don't care. */
8482 isds_error
isds_disable_box_accessibility_externaly(
8483 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8484 const struct tm
*since
, const struct isds_approval
*approval
,
8486 isds_error err
= IE_SUCCESS
;
8488 char *service_name_locale
= NULL
;
8489 xmlNodePtr request
= NULL
, node
;
8490 xmlNsPtr isds_ns
= NULL
;
8491 xmlChar
*string
= NULL
;
8495 if (!context
) return IE_INVALID_CONTEXT
;
8496 zfree(context
->long_message
);
8497 if (!box
|| !since
) return IE_INVAL
;
8501 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8503 isds_printf_message(context
,
8504 _("Could not build %s request"), "DisableDataBoxExternally");
8508 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8510 isds_log_message(context
, _("Could not create ISDS name space"));
8514 xmlSetNs(request
, isds_ns
);
8517 /* Add @box identification */
8518 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8519 err
= insert_DbOwnerInfo(context
, box
, node
);
8520 if (err
) goto leave
;
8522 /* Add @since date */
8523 err
= tm2datestring(since
, &string
);
8525 isds_log_message(context
,
8526 _("Could not convert `since' argument to ISO date string"));
8529 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8533 err
= insert_GExtApproval(context
, approval
, request
);
8534 if (err
) goto leave
;
8536 /* Send it to server and process response */
8537 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8538 BAD_CAST
"DisableDataBoxExternally", &request
,
8539 (xmlChar
**) refnumber
);
8543 xmlFreeNode(request
);
8544 free(service_name_locale
);
8545 #else /* not HAVE_LIBCURL */
8554 /* Insert struct isds_message data (envelope (recipient data optional) and
8555 * documents into XML tree
8556 * @context is session context
8557 * @outgoing_message is libisds structure with message data
8558 * @create_message is XML CreateMessage or CreateMultipleMessage element
8559 * @process_recipient true for recipient data serialization, false for no
8561 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8562 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8563 const _Bool process_recipient
) {
8565 isds_error err
= IE_SUCCESS
;
8566 xmlNodePtr envelope
, dm_files
, node
;
8567 xmlChar
*string
= NULL
;
8569 if (!context
) return IE_INVALID_CONTEXT
;
8570 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8573 /* Build envelope */
8574 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8576 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8577 "%s element"), create_message
->name
);
8581 if (!outgoing_message
->envelope
) {
8582 isds_log_message(context
, _("Outgoing message is missing envelope"));
8587 /* Insert optional message type */
8588 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8590 if (err
) goto leave
;
8592 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8593 outgoing_message
->envelope
->dmSenderOrgUnit
);
8594 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8595 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8597 if (process_recipient
) {
8598 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8599 isds_log_message(context
,
8600 _("Outgoing message is missing recipient box identifier"));
8604 INSERT_STRING(envelope
, "dbIDRecipient",
8605 outgoing_message
->envelope
->dbIDRecipient
);
8607 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8608 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8609 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8610 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8611 INSERT_STRING(envelope
, "dmToHands",
8612 outgoing_message
->envelope
->dmToHands
);
8615 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8617 INSERT_STRING(envelope
, "dmAnnotation",
8618 outgoing_message
->envelope
->dmAnnotation
);
8620 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8621 0, 50, "dmRecipientRefNumber");
8622 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8623 outgoing_message
->envelope
->dmRecipientRefNumber
);
8625 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8626 0, 50, "dmSenderRefNumber");
8627 INSERT_STRING(envelope
, "dmSenderRefNumber",
8628 outgoing_message
->envelope
->dmSenderRefNumber
);
8630 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8631 0, 50, "dmRecipientIdent");
8632 INSERT_STRING(envelope
, "dmRecipientIdent",
8633 outgoing_message
->envelope
->dmRecipientIdent
);
8635 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8636 0, 50, "dmSenderIdent");
8637 INSERT_STRING(envelope
, "dmSenderIdent",
8638 outgoing_message
->envelope
->dmSenderIdent
);
8640 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8641 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8642 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8643 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8644 INSERT_STRING(envelope
, "dmLegalTitleSect",
8645 outgoing_message
->envelope
->dmLegalTitleSect
);
8646 INSERT_STRING(envelope
, "dmLegalTitlePar",
8647 outgoing_message
->envelope
->dmLegalTitlePar
);
8648 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8649 outgoing_message
->envelope
->dmLegalTitlePoint
);
8651 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8652 outgoing_message
->envelope
->dmPersonalDelivery
);
8653 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8654 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8656 /* ???: Should we require value for dbEffectiveOVM sender?
8657 * ISDS has default as true */
8658 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8659 INSERT_BOOLEAN(envelope
, "dmOVM",
8660 outgoing_message
->envelope
->dmPublishOwnID
);
8663 /* Append dmFiles */
8664 if (!outgoing_message
->documents
) {
8665 isds_log_message(context
,
8666 _("Outgoing message is missing list of documents"));
8670 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8672 isds_printf_message(context
, _("Could not add dmFiles child to "
8673 "%s element"), create_message
->name
);
8678 /* Check for document hierarchy */
8679 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8680 if (err
) goto leave
;
8682 /* Process each document */
8683 for (struct isds_list
*item
=
8684 (struct isds_list
*) outgoing_message
->documents
;
8685 item
; item
= item
->next
) {
8687 isds_log_message(context
,
8688 _("List of documents contains empty item"));
8692 /* FIXME: Check for dmFileMetaType and for document references.
8693 * Only first document can be of MAIN type */
8694 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8697 if (err
) goto leave
;
8704 #endif /* HAVE_LIBCURL */
8707 /* Send a message via ISDS to a recipient
8708 * @context is session context
8709 * @outgoing_message is message to send; Some members are mandatory (like
8710 * dbIDRecipient), some are optional and some are irrelevant (especially data
8711 * about sender). Included pointer to isds_list documents must contain at
8712 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8713 * members will be filled with valid data from ISDS. Exact list of write
8714 * members is subject to change. Currently dmID is changed.
8715 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8716 isds_error
isds_send_message(struct isds_ctx
*context
,
8717 struct isds_message
*outgoing_message
) {
8719 isds_error err
= IE_SUCCESS
;
8721 xmlNsPtr isds_ns
= NULL
;
8722 xmlNodePtr request
= NULL
;
8723 xmlDocPtr response
= NULL
;
8724 xmlChar
*code
= NULL
, *message
= NULL
;
8725 xmlXPathContextPtr xpath_ctx
= NULL
;
8726 xmlXPathObjectPtr result
= NULL
;
8727 /*_Bool message_is_complete = 0;*/
8730 if (!context
) return IE_INVALID_CONTEXT
;
8731 zfree(context
->long_message
);
8732 if (!outgoing_message
) return IE_INVAL
;
8735 /* Check if connection is established
8736 * TODO: This check should be done downstairs. */
8737 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8740 /* Build CreateMessage request */
8741 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8743 isds_log_message(context
,
8744 _("Could not build CreateMessage request"));
8747 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8749 isds_log_message(context
, _("Could not create ISDS name space"));
8750 xmlFreeNode(request
);
8753 xmlSetNs(request
, isds_ns
);
8755 /* Append envelope and files */
8756 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8757 if (err
) goto leave
;
8760 /* Signal we can serialize message since now */
8761 /*message_is_complete = 1;*/
8764 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8767 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8769 /* Don't' destroy request, we want to provide it to application later */
8772 isds_log(ILF_ISDS
, ILL_DEBUG
,
8773 _("Processing ISDS response on CreateMessage "
8774 "request failed\n"));
8778 /* Check for response status */
8779 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8780 &code
, &message
, NULL
);
8782 isds_log(ILF_ISDS
, ILL_DEBUG
,
8783 _("ISDS response on CreateMessage request "
8784 "is missing status\n"));
8788 /* Request processed, but refused by server or server failed */
8789 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8790 char *box_id_locale
=
8791 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8792 char *code_locale
= _isds_utf82locale((char*)code
);
8793 char *message_locale
= _isds_utf82locale((char*)message
);
8794 isds_log(ILF_ISDS
, ILL_DEBUG
,
8795 _("Server did not accept message for %s on CreateMessage "
8796 "request (code=%s, message=%s)\n"),
8797 box_id_locale
, code_locale
, message_locale
);
8798 isds_log_message(context
, message_locale
);
8799 free(box_id_locale
);
8801 free(message_locale
);
8808 xpath_ctx
= xmlXPathNewContext(response
);
8813 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8817 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8823 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8824 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8828 if (result
->nodesetval
->nodeNr
> 1) {
8829 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8833 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8834 xmlXPathFreeObject(result
); result
= NULL
;
8836 if (outgoing_message
->envelope
->dmID
) {
8837 free(outgoing_message
->envelope
->dmID
);
8838 outgoing_message
->envelope
->dmID
= NULL
;
8840 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8841 if (!outgoing_message
->envelope
->dmID
) {
8842 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8843 "but did not return assigned message ID\n"));
8847 /* TODO: Serialize message into structure member raw */
8848 /* XXX: Each web service transport message in different format.
8849 * Therefore it's not possible to save them directly.
8850 * To save them, one must figure out common format.
8851 * We can leave it on application, or we can implement the ESS format. */
8852 /*if (message_is_complete) {
8853 if (outgoing_message->envelope->dmID) {
8855 /* Add assigned message ID as first child*/
8856 /*xmlNodePtr dmid_text = xmlNewText(
8857 (xmlChar *) outgoing_message->envelope->dmID);
8858 if (!dmid_text) goto serialization_failed;
8860 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8862 if (!dmid_element) {
8863 xmlFreeNode(dmid_text);
8864 goto serialization_failed;
8867 xmlNodePtr dmid_element_with_text =
8868 xmlAddChild(dmid_element, dmid_text);
8869 if (!dmid_element_with_text) {
8870 xmlFreeNode(dmid_element);
8871 xmlFreeNode(dmid_text);
8872 goto serialization_failed;
8875 node = xmlAddPrevSibling(envelope->childern,
8876 dmid_element_with_text);
8878 xmlFreeNodeList(dmid_element_with_text);
8879 goto serialization_failed;
8883 /* Serialize message with ID into raw */
8884 /*buffer = serialize_element(envelope)*/
8887 serialization_failed:
8892 xmlXPathFreeObject(result
);
8893 xmlXPathFreeContext(xpath_ctx
);
8897 xmlFreeDoc(response
);
8898 xmlFreeNode(request
);
8901 isds_log(ILF_ISDS
, ILL_DEBUG
,
8902 _("CreateMessage request processed by server "
8903 "successfully.\n"));
8904 #else /* not HAVE_LIBCURL */
8912 /* Send a message via ISDS to a multiple recipients
8913 * @context is session context
8914 * @outgoing_message is message to send; Some members are mandatory,
8915 * some are optional and some are irrelevant (especially data
8916 * about sender). Data about recipient will be substituted by ISDS from
8917 * @copies. Included pointer to isds_list documents must
8918 * contain at least one document of FILEMETATYPE_MAIN.
8919 * @copies is list of isds_message_copy structures addressing all desired
8920 * recipients. This is read-write structure, some members will be filled with
8921 * valid data from ISDS (message IDs, error codes, error descriptions).
8923 * ISDS_SUCCESS if all messages have been sent
8924 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8925 * succeeded messages can be identified by copies->data->error),
8926 * or other error code if something other goes wrong. */
8927 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8928 const struct isds_message
*outgoing_message
,
8929 struct isds_list
*copies
) {
8931 isds_error err
= IE_SUCCESS
;
8933 isds_error append_err
;
8934 xmlNsPtr isds_ns
= NULL
;
8935 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8936 struct isds_list
*item
;
8937 struct isds_message_copy
*copy
;
8938 xmlDocPtr response
= NULL
;
8939 xmlChar
*code
= NULL
, *message
= NULL
;
8940 xmlXPathContextPtr xpath_ctx
= NULL
;
8941 xmlXPathObjectPtr result
= NULL
;
8942 xmlChar
*string
= NULL
;
8946 if (!context
) return IE_INVALID_CONTEXT
;
8947 zfree(context
->long_message
);
8948 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8951 /* Check if connection is established
8952 * TODO: This check should be done downstairs. */
8953 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8956 /* Build CreateMultipleMessage request */
8957 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8959 isds_log_message(context
,
8960 _("Could not build CreateMultipleMessage request"));
8963 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8965 isds_log_message(context
, _("Could not create ISDS name space"));
8966 xmlFreeNode(request
);
8969 xmlSetNs(request
, isds_ns
);
8972 /* Build recipients */
8973 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8975 isds_log_message(context
, _("Could not add dmRecipients child to "
8976 "CreateMultipleMessage element"));
8977 xmlFreeNode(request
);
8981 /* Insert each recipient */
8982 for (item
= copies
; item
; item
= item
->next
) {
8983 copy
= (struct isds_message_copy
*) item
->data
;
8985 isds_log_message(context
,
8986 _("`copies' list item contains empty data"));
8991 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8993 isds_log_message(context
, _("Could not add dmRecipient child to "
8994 "dmRecipients element"));
8999 if (!copy
->dbIDRecipient
) {
9000 isds_log_message(context
,
9001 _("Message copy is missing recipient box identifier"));
9005 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9006 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9007 copy
->dmRecipientOrgUnit
);
9008 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9009 copy
->dmRecipientOrgUnitNum
, string
);
9010 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9013 /* Append envelope and files */
9014 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9015 if (err
) goto leave
;
9018 isds_log(ILF_ISDS
, ILL_DEBUG
,
9019 _("Sending CreateMultipleMessage request to ISDS\n"));
9022 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9024 isds_log(ILF_ISDS
, ILL_DEBUG
,
9025 _("Processing ISDS response on CreateMultipleMessage "
9026 "request failed\n"));
9030 /* Check for response status */
9031 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9032 &code
, &message
, NULL
);
9034 isds_log(ILF_ISDS
, ILL_DEBUG
,
9035 _("ISDS response on CreateMultipleMessage request "
9036 "is missing status\n"));
9040 /* Request processed, but some copies failed */
9041 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9042 char *box_id_locale
=
9043 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9044 char *code_locale
= _isds_utf82locale((char*)code
);
9045 char *message_locale
= _isds_utf82locale((char*)message
);
9046 isds_log(ILF_ISDS
, ILL_DEBUG
,
9047 _("Server did accept message for multiple recipients "
9048 "on CreateMultipleMessage request but delivery to "
9049 "some of them failed (code=%s, message=%s)\n"),
9050 box_id_locale
, code_locale
, message_locale
);
9051 isds_log_message(context
, message_locale
);
9052 free(box_id_locale
);
9054 free(message_locale
);
9055 err
= IE_PARTIAL_SUCCESS
;
9058 /* Request refused by server as whole */
9059 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9060 char *box_id_locale
=
9061 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9062 char *code_locale
= _isds_utf82locale((char*)code
);
9063 char *message_locale
= _isds_utf82locale((char*)message
);
9064 isds_log(ILF_ISDS
, ILL_DEBUG
,
9065 _("Server did not accept message for multiple recipients "
9066 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9067 box_id_locale
, code_locale
, message_locale
);
9068 isds_log_message(context
, message_locale
);
9069 free(box_id_locale
);
9071 free(message_locale
);
9078 xpath_ctx
= xmlXPathNewContext(response
);
9083 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9087 result
= xmlXPathEvalExpression(
9088 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9089 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9095 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9096 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9101 /* Extract message ID and delivery status for each copy */
9102 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9103 item
= item
->next
, i
++) {
9104 copy
= (struct isds_message_copy
*) item
->data
;
9105 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9107 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9113 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9114 isds_printf_message(context
, _("ISDS returned unexpected number of "
9115 "message copy delivery states: %d"),
9116 result
->nodesetval
->nodeNr
);
9125 xmlXPathFreeObject(result
);
9126 xmlXPathFreeContext(xpath_ctx
);
9130 xmlFreeDoc(response
);
9131 xmlFreeNode(request
);
9134 isds_log(ILF_ISDS
, ILL_DEBUG
,
9135 _("CreateMultipleMessageResponse request processed by server "
9136 "successfully.\n"));
9137 #else /* not HAVE_LIBCURL */
9145 /* Get list of messages. This is common core for getting sent or received
9147 * Any criterion argument can be NULL, if you don't care about it.
9148 * @context is session context. Must not be NULL.
9149 * @outgoing_direction is true if you want list of outgoing messages,
9150 * it's false if you want incoming messages.
9151 * @from_time is minimal time and date of message sending inclusive.
9152 * @to_time is maximal time and date of message sending inclusive
9153 * @organization_unit_number is number of sender/recipient respectively.
9154 * @status_filter is bit field of isds_message_status values. Use special
9155 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9156 * all values, you can use bit-wise arithmetic if you want.)
9157 * @offset is index of first message we are interested in. First message is 1.
9158 * Set to 0 (or 1) if you don't care.
9159 * @number is maximal length of list you want to get as input value, outputs
9160 * number of messages matching these criteria. Can be NULL if you don't care
9161 * (applies to output value either).
9162 * @messages is automatically reallocated list of isds_message's. Be ware that
9163 * it returns only brief overview (envelope and some other fields) about each
9164 * message, not the complete message. FIXME: Specify exact fields.
9165 * The list is sorted by delivery time in ascending order.
9166 * Use NULL if you don't care about don't need the data (useful if you want to
9167 * know only the @number). If you provide &NULL, list will be allocated on
9168 * heap, if you provide pointer to non-NULL, list will be freed automatically
9169 * at first. Also in case of error the list will be NULLed.
9170 * @return IE_SUCCESS or appropriate error code. */
9171 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9172 _Bool outgoing_direction
,
9173 const struct timeval
*from_time
, const struct timeval
*to_time
,
9174 const long int *organization_unit_number
,
9175 const unsigned int status_filter
,
9176 const unsigned long int offset
, unsigned long int *number
,
9177 struct isds_list
**messages
) {
9179 isds_error err
= IE_SUCCESS
;
9181 xmlNsPtr isds_ns
= NULL
;
9182 xmlNodePtr request
= NULL
, node
;
9183 xmlDocPtr response
= NULL
;
9184 xmlChar
*code
= NULL
, *message
= NULL
;
9185 xmlXPathContextPtr xpath_ctx
= NULL
;
9186 xmlXPathObjectPtr result
= NULL
;
9187 xmlChar
*string
= NULL
;
9191 if (!context
) return IE_INVALID_CONTEXT
;
9192 zfree(context
->long_message
);
9194 /* Free former message list if any */
9195 if (messages
) isds_list_free(messages
);
9198 /* Check if connection is established
9199 * TODO: This check should be done downstairs. */
9200 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9202 /* Build GetListOf*Messages request */
9203 request
= xmlNewNode(NULL
,
9204 (outgoing_direction
) ?
9205 BAD_CAST
"GetListOfSentMessages" :
9206 BAD_CAST
"GetListOfReceivedMessages"
9209 isds_log_message(context
,
9210 (outgoing_direction
) ?
9211 _("Could not build GetListOfSentMessages request") :
9212 _("Could not build GetListOfReceivedMessages request")
9216 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9218 isds_log_message(context
, _("Could not create ISDS name space"));
9219 xmlFreeNode(request
);
9222 xmlSetNs(request
, isds_ns
);
9226 err
= timeval2timestring(from_time
, &string
);
9227 if (err
) goto leave
;
9229 INSERT_STRING(request
, "dmFromTime", string
);
9230 free(string
); string
= NULL
;
9233 err
= timeval2timestring(to_time
, &string
);
9234 if (err
) goto leave
;
9236 INSERT_STRING(request
, "dmToTime", string
);
9237 free(string
); string
= NULL
;
9239 if (outgoing_direction
) {
9240 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9241 organization_unit_number
, string
);
9243 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9244 organization_unit_number
, string
);
9247 if (status_filter
> MESSAGESTATE_ANY
) {
9248 isds_printf_message(context
,
9249 _("Invalid message state filter value: %ld"), status_filter
);
9253 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9256 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9258 INSERT_STRING(request
, "dmOffset", "1");
9261 /* number 0 means no limit */
9262 if (number
&& *number
== 0) {
9263 INSERT_STRING(request
, "dmLimit", NULL
);
9265 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9269 isds_log(ILF_ISDS
, ILL_DEBUG
,
9270 (outgoing_direction
) ?
9271 _("Sending GetListOfSentMessages request to ISDS\n") :
9272 _("Sending GetListOfReceivedMessages request to ISDS\n")
9276 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9277 xmlFreeNode(request
); request
= NULL
;
9280 isds_log(ILF_ISDS
, ILL_DEBUG
,
9281 (outgoing_direction
) ?
9282 _("Processing ISDS response on GetListOfSentMessages "
9283 "request failed\n") :
9284 _("Processing ISDS response on GetListOfReceivedMessages "
9290 /* Check for response status */
9291 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9292 &code
, &message
, NULL
);
9294 isds_log(ILF_ISDS
, ILL_DEBUG
,
9295 (outgoing_direction
) ?
9296 _("ISDS response on GetListOfSentMessages request "
9297 "is missing status\n") :
9298 _("ISDS response on GetListOfReceivedMessages request "
9299 "is missing status\n")
9304 /* Request processed, but nothing found */
9305 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9306 char *code_locale
= _isds_utf82locale((char*)code
);
9307 char *message_locale
= _isds_utf82locale((char*)message
);
9308 isds_log(ILF_ISDS
, ILL_DEBUG
,
9309 (outgoing_direction
) ?
9310 _("Server refused GetListOfSentMessages request "
9311 "(code=%s, message=%s)\n") :
9312 _("Server refused GetListOfReceivedMessages request "
9313 "(code=%s, message=%s)\n"),
9314 code_locale
, message_locale
);
9315 isds_log_message(context
, message_locale
);
9317 free(message_locale
);
9324 xpath_ctx
= xmlXPathNewContext(response
);
9329 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9333 result
= xmlXPathEvalExpression(
9334 (outgoing_direction
) ?
9335 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9336 "isds:dmRecords/isds:dmRecord" :
9337 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9338 "isds:dmRecords/isds:dmRecord",
9345 /* Fill output arguments in */
9346 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9347 struct isds_envelope
*envelope
;
9348 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9350 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9351 /* Create new message */
9352 item
= calloc(1, sizeof(*item
));
9357 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9358 item
->data
= calloc(1, sizeof(struct isds_message
));
9360 isds_list_free(&item
);
9365 /* Extract envelope data */
9366 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9368 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9370 isds_list_free(&item
);
9374 /* Attach extracted envelope */
9375 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9377 /* Append new message into the list */
9379 *messages
= last_item
= item
;
9381 last_item
->next
= item
;
9386 if (number
) *number
= count
;
9390 isds_list_free(messages
);
9394 xmlXPathFreeObject(result
);
9395 xmlXPathFreeContext(xpath_ctx
);
9399 xmlFreeDoc(response
);
9400 xmlFreeNode(request
);
9403 isds_log(ILF_ISDS
, ILL_DEBUG
,
9404 (outgoing_direction
) ?
9405 _("GetListOfSentMessages request processed by server "
9406 "successfully.\n") :
9407 _("GetListOfReceivedMessages request processed by server "
9410 #else /* not HAVE_LIBCURL */
9417 /* Get list of outgoing (already sent) messages.
9418 * Any criterion argument can be NULL, if you don't care about it.
9419 * @context is session context. Must not be NULL.
9420 * @from_time is minimal time and date of message sending inclusive.
9421 * @to_time is maximal time and date of message sending inclusive
9422 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9423 * @status_filter is bit field of isds_message_status values. Use special
9424 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9425 * all values, you can use bit-wise arithmetic if you want.)
9426 * @offset is index of first message we are interested in. First message is 1.
9427 * Set to 0 (or 1) if you don't care.
9428 * @number is maximal length of list you want to get as input value, outputs
9429 * number of messages matching these criteria. Can be NULL if you don't care
9430 * (applies to output value either).
9431 * @messages is automatically reallocated list of isds_message's. Be ware that
9432 * it returns only brief overview (envelope and some other fields) about each
9433 * message, not the complete message. FIXME: Specify exact fields.
9434 * The list is sorted by delivery time in ascending order.
9435 * Use NULL if you don't care about the meta data (useful if you want to know
9436 * only the @number). If you provide &NULL, list will be allocated on heap,
9437 * if you provide pointer to non-NULL, list will be freed automatically at
9438 * first. Also in case of error the list will be NULLed.
9439 * @return IE_SUCCESS or appropriate error code. */
9440 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9441 const struct timeval
*from_time
, const struct timeval
*to_time
,
9442 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9443 const unsigned long int offset
, unsigned long int *number
,
9444 struct isds_list
**messages
) {
9446 return isds_get_list_of_messages(
9448 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9454 /* Get list of incoming (addressed to you) messages.
9455 * Any criterion argument can be NULL, if you don't care about it.
9456 * @context is session context. Must not be NULL.
9457 * @from_time is minimal time and date of message sending inclusive.
9458 * @to_time is maximal time and date of message sending inclusive
9459 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9460 * @status_filter is bit field of isds_message_status values. Use special
9461 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9462 * all values, you can use bit-wise arithmetic if you want.)
9463 * @offset is index of first message we are interested in. First message is 1.
9464 * Set to 0 (or 1) if you don't care.
9465 * @number is maximal length of list you want to get as input value, outputs
9466 * number of messages matching these criteria. Can be NULL if you don't care
9467 * (applies to output value either).
9468 * @messages is automatically reallocated list of isds_message's. Be ware that
9469 * it returns only brief overview (envelope and some other fields) about each
9470 * message, not the complete message. FIXME: Specify exact fields.
9471 * Use NULL if you don't care about the meta data (useful if you want to know
9472 * only the @number). If you provide &NULL, list will be allocated on heap,
9473 * if you provide pointer to non-NULL, list will be freed automatically at
9474 * first. Also in case of error the list will be NULLed.
9475 * @return IE_SUCCESS or appropriate error code. */
9476 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9477 const struct timeval
*from_time
, const struct timeval
*to_time
,
9478 const long int *dmRecipientOrgUnitNum
,
9479 const unsigned int status_filter
,
9480 const unsigned long int offset
, unsigned long int *number
,
9481 struct isds_list
**messages
) {
9483 return isds_get_list_of_messages(
9485 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9491 /* Get list of sent message state changes.
9492 * Any criterion argument can be NULL, if you don't care about it.
9493 * @context is session context. Must not be NULL.
9494 * @from_time is minimal time and date of status changes inclusive
9495 * @to_time is maximal time and date of status changes inclusive
9496 * @changed_states is automatically reallocated list of
9497 * isds_message_status_change's. If you provide &NULL, list will be allocated
9498 * on heap, if you provide pointer to non-NULL, list will be freed
9499 * automatically at first. Also in case of error the list will be NULLed.
9500 * XXX: The list item ordering is not specified.
9501 * XXX: Server provides only `recent' changes.
9502 * @return IE_SUCCESS or appropriate error code. */
9503 isds_error
isds_get_list_of_sent_message_state_changes(
9504 struct isds_ctx
*context
,
9505 const struct timeval
*from_time
, const struct timeval
*to_time
,
9506 struct isds_list
**changed_states
) {
9508 isds_error err
= IE_SUCCESS
;
9510 xmlNsPtr isds_ns
= NULL
;
9511 xmlNodePtr request
= NULL
, node
;
9512 xmlDocPtr response
= NULL
;
9513 xmlXPathContextPtr xpath_ctx
= NULL
;
9514 xmlXPathObjectPtr result
= NULL
;
9515 xmlChar
*string
= NULL
;
9519 if (!context
) return IE_INVALID_CONTEXT
;
9520 zfree(context
->long_message
);
9522 /* Free former message list if any */
9523 isds_list_free(changed_states
);
9526 /* Check if connection is established
9527 * TODO: This check should be done downstairs. */
9528 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9530 /* Build GetMessageStateChanges request */
9531 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9533 isds_log_message(context
,
9534 _("Could not build GetMessageStateChanges request"));
9537 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9539 isds_log_message(context
, _("Could not create ISDS name space"));
9540 xmlFreeNode(request
);
9543 xmlSetNs(request
, isds_ns
);
9547 err
= timeval2timestring(from_time
, &string
);
9548 if (err
) goto leave
;
9550 INSERT_STRING(request
, "dmFromTime", string
);
9554 err
= timeval2timestring(to_time
, &string
);
9555 if (err
) goto leave
;
9557 INSERT_STRING(request
, "dmToTime", string
);
9562 err
= send_destroy_request_check_response(context
,
9563 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9564 &response
, NULL
, NULL
);
9565 if (err
) goto leave
;
9569 xpath_ctx
= xmlXPathNewContext(response
);
9574 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9578 result
= xmlXPathEvalExpression(
9579 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9580 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9586 /* Fill output arguments in */
9587 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9588 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9590 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9591 /* Create new status change */
9592 item
= calloc(1, sizeof(*item
));
9598 (void(*)(void**)) &isds_message_status_change_free
;
9600 /* Extract message status change */
9601 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9602 err
= extract_StateChangesRecord(context
,
9603 (struct isds_message_status_change
**) &item
->data
,
9606 isds_list_free(&item
);
9610 /* Append new message status change into the list */
9611 if (!*changed_states
) {
9612 *changed_states
= last_item
= item
;
9614 last_item
->next
= item
;
9622 isds_list_free(changed_states
);
9626 xmlXPathFreeObject(result
);
9627 xmlXPathFreeContext(xpath_ctx
);
9628 xmlFreeDoc(response
);
9629 xmlFreeNode(request
);
9632 isds_log(ILF_ISDS
, ILL_DEBUG
,
9633 _("GetMessageStateChanges request processed by server "
9634 "successfully.\n"));
9635 #else /* not HAVE_LIBCURL */
9643 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9645 * @context is session context
9646 * @service is ISDS WS service handler
9647 * @service_name is name of SERVICE_DM_OPERATIONS
9648 * @message_id is message ID to send as service argument to ISDS
9649 * @response is reallocated server SOAP body response as XML document
9650 * @raw_response is reallocated bit stream with response body. Use
9651 * NULL if you don't care
9652 * @raw_response_length is size of @raw_response in bytes
9653 * @code is reallocated ISDS status code
9654 * @status_message is reallocated ISDS status message
9655 * @return error coded from lower layer, context message will be set up
9657 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9658 const isds_service service
, const xmlChar
*service_name
,
9659 const char *message_id
,
9660 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9661 xmlChar
**code
, xmlChar
**status_message
) {
9663 isds_error err
= IE_SUCCESS
;
9664 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9665 xmlNodePtr request
= NULL
, node
;
9666 xmlNsPtr isds_ns
= NULL
;
9668 if (!context
) return IE_INVALID_CONTEXT
;
9669 if (!service_name
|| !message_id
) return IE_INVAL
;
9670 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9671 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9673 /* Free output argument */
9674 xmlFreeDoc(*response
); *response
= NULL
;
9675 if (raw_response
) zfree(*raw_response
);
9677 zfree(*status_message
);
9680 /* Check if connection is established
9681 * TODO: This check should be done downstairs. */
9682 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9684 service_name_locale
= _isds_utf82locale((char*)service_name
);
9685 message_id_locale
= _isds_utf82locale(message_id
);
9686 if (!service_name_locale
|| !message_id_locale
) {
9692 request
= xmlNewNode(NULL
, service_name
);
9694 isds_printf_message(context
,
9695 _("Could not build %s request for %s message ID"),
9696 service_name_locale
, message_id_locale
);
9700 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9702 isds_log_message(context
, _("Could not create ISDS name space"));
9706 xmlSetNs(request
, isds_ns
);
9709 /* Add requested ID */
9710 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9711 if (err
) goto leave
;
9712 INSERT_STRING(request
, "dmID", message_id
);
9715 isds_log(ILF_ISDS
, ILL_DEBUG
,
9716 _("Sending %s request for %s message ID to ISDS\n"),
9717 service_name_locale
, message_id_locale
);
9720 err
= _isds(context
, service
, request
, response
,
9721 raw_response
, raw_response_length
);
9722 xmlFreeNode(request
); request
= NULL
;
9725 isds_log(ILF_ISDS
, ILL_DEBUG
,
9726 _("Processing ISDS response on %s request failed\n"),
9727 service_name_locale
);
9731 /* Check for response status */
9732 err
= isds_response_status(context
, service
, *response
,
9733 code
, status_message
, NULL
);
9735 isds_log(ILF_ISDS
, ILL_DEBUG
,
9736 _("ISDS response on %s request is missing status\n"),
9737 service_name_locale
);
9741 /* Request processed, but nothing found */
9742 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9743 char *code_locale
= _isds_utf82locale((char*) *code
);
9744 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9745 isds_log(ILF_ISDS
, ILL_DEBUG
,
9746 _("Server refused %s request for %s message ID "
9747 "(code=%s, message=%s)\n"),
9748 service_name_locale
, message_id_locale
,
9749 code_locale
, status_message_locale
);
9750 isds_log_message(context
, status_message_locale
);
9752 free(status_message_locale
);
9758 free(message_id_locale
);
9759 free(service_name_locale
);
9760 xmlFreeNode(request
);
9765 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9766 * signed data and free ISDS response.
9767 * @context is session context
9768 * @message_id is UTF-8 encoded message ID for logging purpose
9769 * @response is parsed XML document. It will be freed and NULLed in the middle
9770 * of function run to save memory. This is not guaranteed in case of error.
9771 * @request_name is name of ISDS request used to construct response root
9772 * element name and for logging purpose.
9773 * @raw is reallocated output buffer with DER encoded CMS data
9774 * @raw_length is size of @raw buffer in bytes
9775 * @returns standard error codes, in case of error, @raw will be freed and
9776 * NULLed, @response sometimes. */
9777 static isds_error
find_extract_signed_data_free_response(
9778 struct isds_ctx
*context
, const xmlChar
*message_id
,
9779 xmlDocPtr
*response
, const xmlChar
*request_name
,
9780 void **raw
, size_t *raw_length
) {
9782 isds_error err
= IE_SUCCESS
;
9783 char *xpath_expression
= NULL
;
9784 xmlXPathContextPtr xpath_ctx
= NULL
;
9785 xmlXPathObjectPtr result
= NULL
;
9786 char *encoded_structure
= NULL
;
9788 if (!context
) return IE_INVALID_CONTEXT
;
9789 if (!raw
) return IE_INVAL
;
9791 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9794 /* Build XPath expression */
9795 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9796 "Response/isds:dmSignature");
9797 if (!xpath_expression
) return IE_NOMEM
;
9800 xpath_ctx
= xmlXPathNewContext(*response
);
9805 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9809 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9814 /* Empty response */
9815 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9816 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9817 isds_printf_message(context
,
9818 _("Server did not return any signed data for message ID `%s' "
9820 message_id_locale
, request_name
);
9821 free(message_id_locale
);
9825 /* More responses */
9826 if (result
->nodesetval
->nodeNr
> 1) {
9827 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9828 isds_printf_message(context
,
9829 _("Server did return more signed data for message ID `%s' "
9831 message_id_locale
, request_name
);
9832 free(message_id_locale
);
9837 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9839 /* Extract PKCS#7 structure */
9840 EXTRACT_STRING(".", encoded_structure
);
9841 if (!encoded_structure
) {
9842 isds_log_message(context
, _("dmSignature element is empty"));
9845 /* Here we have delivery info as standalone CMS in encoded_structure.
9846 * We don't need any other data, free them: */
9847 xmlXPathFreeObject(result
); result
= NULL
;
9848 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9849 xmlFreeDoc(*response
); *response
= NULL
;
9852 /* Decode PKCS#7 to DER format */
9853 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9854 if (*raw_length
== (size_t) -1) {
9855 isds_log_message(context
,
9856 _("Error while Base64-decoding PKCS#7 structure"));
9867 free(encoded_structure
);
9868 xmlXPathFreeObject(result
);
9869 xmlXPathFreeContext(xpath_ctx
);
9870 free(xpath_expression
);
9874 #endif /* HAVE_LIBCURL */
9877 /* Download incoming message envelope identified by ID.
9878 * @context is session context
9879 * @message_id is message identifier (you can get them from
9880 * isds_get_list_of_received_messages())
9881 * @message is automatically reallocated message retrieved from ISDS.
9882 * It will miss documents per se. Use isds_get_received_message(), if you are
9883 * interested in documents (content) too.
9884 * Returned hash and timestamp require documents to be verifiable. */
9885 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9886 const char *message_id
, struct isds_message
**message
) {
9888 isds_error err
= IE_SUCCESS
;
9890 xmlDocPtr response
= NULL
;
9891 xmlChar
*code
= NULL
, *status_message
= NULL
;
9892 xmlXPathContextPtr xpath_ctx
= NULL
;
9893 xmlXPathObjectPtr result
= NULL
;
9896 if (!context
) return IE_INVALID_CONTEXT
;
9897 zfree(context
->long_message
);
9899 /* Free former message if any */
9900 if (!message
) return IE_INVAL
;
9901 isds_message_free(message
);
9904 /* Do request and check for success */
9905 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9906 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9907 &response
, NULL
, NULL
, &code
, &status_message
);
9908 if (err
) goto leave
;
9911 xpath_ctx
= xmlXPathNewContext(response
);
9916 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9920 result
= xmlXPathEvalExpression(
9921 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9922 "isds:dmReturnedMessageEnvelope",
9928 /* Empty response */
9929 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9930 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9931 isds_printf_message(context
,
9932 _("Server did not return any envelope for ID `%s' "
9933 "on MessageEnvelopeDownload request"), message_id_locale
);
9934 free(message_id_locale
);
9939 if (result
->nodesetval
->nodeNr
> 1) {
9940 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9941 isds_printf_message(context
,
9942 _("Server did return more envelopes for ID `%s' "
9943 "on MessageEnvelopeDownload request"), message_id_locale
);
9944 free(message_id_locale
);
9949 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9951 /* Extract the envelope (= message without documents, hence 0) */
9952 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9953 if (err
) goto leave
;
9956 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9957 &(*message
)->raw_length
);
9961 isds_message_free(message
);
9964 xmlXPathFreeObject(result
);
9965 xmlXPathFreeContext(xpath_ctx
);
9968 free(status_message
);
9969 if (!*message
|| !(*message
)->xml
) {
9970 xmlFreeDoc(response
);
9974 isds_log(ILF_ISDS
, ILL_DEBUG
,
9975 _("MessageEnvelopeDownload request processed by server "
9978 #else /* not HAVE_LIBCURL */
9985 /* Load delivery info of any format from buffer.
9986 * @context is session context
9987 * @raw_type advertises format of @buffer content. Only delivery info types
9989 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9990 * retrieve such data from message->raw after calling
9991 * isds_get_signed_delivery_info().
9992 * @length is length of buffer in bytes.
9993 * @message is automatically reallocated message parsed from @buffer.
9994 * @strategy selects how buffer will be attached into raw isds_message member.
9996 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9997 const isds_raw_type raw_type
,
9998 const void *buffer
, const size_t length
,
9999 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10001 isds_error err
= IE_SUCCESS
;
10002 message_ns_type message_ns
;
10003 xmlDocPtr message_doc
= NULL
;
10004 xmlXPathContextPtr xpath_ctx
= NULL
;
10005 xmlXPathObjectPtr result
= NULL
;
10006 void *xml_stream
= NULL
;
10007 size_t xml_stream_length
= 0;
10009 if (!context
) return IE_INVALID_CONTEXT
;
10010 zfree(context
->long_message
);
10011 if (!message
) return IE_INVAL
;
10012 isds_message_free(message
);
10013 if (!buffer
) return IE_INVAL
;
10016 /* Select buffer format and extract XML from CMS*/
10017 switch (raw_type
) {
10018 case RAWTYPE_DELIVERYINFO
:
10019 message_ns
= MESSAGE_NS_UNSIGNED
;
10020 xml_stream
= (void *) buffer
;
10021 xml_stream_length
= length
;
10024 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10025 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10026 xml_stream
= (void *) buffer
;
10027 xml_stream_length
= length
;
10030 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10031 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10032 err
= _isds_extract_cms_data(context
, buffer
, length
,
10033 &xml_stream
, &xml_stream_length
);
10034 if (err
) goto leave
;
10038 isds_log_message(context
, _("Bad raw delivery representation type"));
10043 isds_log(ILF_ISDS
, ILL_DEBUG
,
10044 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10045 xml_stream_length
, xml_stream
);
10047 /* Convert delivery info XML stream into XPath context */
10048 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10049 if (!message_doc
) {
10053 xpath_ctx
= xmlXPathNewContext(message_doc
);
10058 /* XXX: Name spaces mangled for signed delivery info:
10059 * http://isds.czechpoint.cz/v20/delivery:
10061 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10063 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10064 * <p:dmID>170272</p:dmID>
10067 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10069 * </q:dmEvents>...</q:dmEvents>
10071 * </q:GetDeliveryInfoResponse>
10073 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10077 result
= xmlXPathEvalExpression(
10078 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10084 /* Empty delivery info */
10085 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10086 isds_printf_message(context
,
10087 _("XML document is not sisds:dmDelivery document"));
10091 /* More delivery info's */
10092 if (result
->nodesetval
->nodeNr
> 1) {
10093 isds_printf_message(context
,
10094 _("XML document has more sisds:dmDelivery elements"));
10098 /* One delivery info */
10099 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10101 /* Extract the envelope (= message without documents, hence 0).
10102 * XXX: extract_TReturnedMessage() can obtain attachments size,
10103 * but delivery info carries none. It's coded as option elements,
10104 * so it should work. */
10105 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10106 if (err
) goto leave
;
10108 /* Extract events */
10109 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10110 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10111 if (err
) { err
= IE_ERROR
; goto leave
; }
10112 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10113 if (err
) goto leave
;
10115 /* Append raw CMS structure into message */
10116 (*message
)->raw_type
= raw_type
;
10117 switch (strategy
) {
10118 case BUFFER_DONT_STORE
:
10121 (*message
)->raw
= malloc(length
);
10122 if (!(*message
)->raw
) {
10126 memcpy((*message
)->raw
, buffer
, length
);
10127 (*message
)->raw_length
= length
;
10130 (*message
)->raw
= (void *) buffer
;
10131 (*message
)->raw_length
= length
;
10140 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10141 isds_message_free(message
);
10144 xmlXPathFreeObject(result
);
10145 xmlXPathFreeContext(xpath_ctx
);
10146 if (!*message
|| !(*message
)->xml
) {
10147 xmlFreeDoc(message_doc
);
10149 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10152 isds_log(ILF_ISDS
, ILL_DEBUG
,
10153 _("Delivery info loaded successfully.\n"));
10158 /* Download signed delivery info-sheet of given message identified by ID.
10159 * @context is session context
10160 * @message_id is message identifier (you can get them from
10161 * isds_get_list_of_{sent,received}_messages())
10162 * @message is automatically reallocated message retrieved from ISDS.
10163 * It will miss documents per se. Use isds_get_signed_received_message(),
10164 * if you are interested in documents (content). OTOH, only this function
10165 * can get list events message has gone through. */
10166 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10167 const char *message_id
, struct isds_message
**message
) {
10169 isds_error err
= IE_SUCCESS
;
10171 xmlDocPtr response
= NULL
;
10172 xmlChar
*code
= NULL
, *status_message
= NULL
;
10174 size_t raw_length
= 0;
10177 if (!context
) return IE_INVALID_CONTEXT
;
10178 zfree(context
->long_message
);
10180 /* Free former message if any */
10181 if (!message
) return IE_INVAL
;
10182 isds_message_free(message
);
10185 /* Do request and check for success */
10186 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10187 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10188 &response
, NULL
, NULL
, &code
, &status_message
);
10189 if (err
) goto leave
;
10191 /* Find signed delivery info, extract it into raw and maybe free
10193 err
= find_extract_signed_data_free_response(context
,
10194 (xmlChar
*)message_id
, &response
,
10195 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10196 if (err
) goto leave
;
10198 /* Parse delivery info */
10199 err
= isds_load_delivery_info(context
,
10200 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10201 message
, BUFFER_MOVE
);
10202 if (err
) goto leave
;
10208 isds_message_free(message
);
10213 free(status_message
);
10214 xmlFreeDoc(response
);
10217 isds_log(ILF_ISDS
, ILL_DEBUG
,
10218 _("GetSignedDeliveryInfo request processed by server "
10221 #else /* not HAVE_LIBCURL */
10228 /* Download delivery info-sheet of given message identified by ID.
10229 * @context is session context
10230 * @message_id is message identifier (you can get them from
10231 * isds_get_list_of_{sent,received}_messages())
10232 * @message is automatically reallocated message retrieved from ISDS.
10233 * It will miss documents per se. Use isds_get_received_message(), if you are
10234 * interested in documents (content). OTOH, only this function can get list
10235 * of events message has gone through. */
10236 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10237 const char *message_id
, struct isds_message
**message
) {
10239 isds_error err
= IE_SUCCESS
;
10241 xmlDocPtr response
= NULL
;
10242 xmlChar
*code
= NULL
, *status_message
= NULL
;
10243 xmlNodePtr delivery_node
= NULL
;
10245 size_t raw_length
= 0;
10248 if (!context
) return IE_INVALID_CONTEXT
;
10249 zfree(context
->long_message
);
10251 /* Free former message if any */
10252 if (!message
) return IE_INVAL
;
10253 isds_message_free(message
);
10256 /* Do request and check for success */
10257 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10258 BAD_CAST
"GetDeliveryInfo", message_id
,
10259 &response
, NULL
, NULL
, &code
, &status_message
);
10260 if (err
) goto leave
;
10263 /* Serialize delivery info */
10264 delivery_node
= xmlDocGetRootElement(response
);
10265 if (!delivery_node
) {
10266 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10267 isds_printf_message(context
,
10268 _("Server did not return any delivery info for ID `%s' "
10269 "on GetDeliveryInfo request"), message_id_locale
);
10270 free(message_id_locale
);
10274 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10275 if (err
) goto leave
;
10277 /* Parse delivery info */
10278 /* TODO: Here we parse the response second time. We could single delivery
10279 * parser from isds_load_delivery_info() to make things faster. */
10280 err
= isds_load_delivery_info(context
,
10281 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10282 message
, BUFFER_MOVE
);
10283 if (err
) goto leave
;
10290 isds_message_free(message
);
10295 free(status_message
);
10296 xmlFreeDoc(response
);
10299 isds_log(ILF_ISDS
, ILL_DEBUG
,
10300 _("GetDeliveryInfo request processed by server "
10303 #else /* not HAVE_LIBCURL */
10310 /* Download incoming message identified by ID.
10311 * @context is session context
10312 * @message_id is message identifier (you can get them from
10313 * isds_get_list_of_received_messages())
10314 * @message is automatically reallocated message retrieved from ISDS */
10315 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10316 const char *message_id
, struct isds_message
**message
) {
10318 isds_error err
= IE_SUCCESS
;
10320 xmlDocPtr response
= NULL
;
10321 void *xml_stream
= NULL
;
10322 size_t xml_stream_length
;
10323 xmlChar
*code
= NULL
, *status_message
= NULL
;
10324 xmlXPathContextPtr xpath_ctx
= NULL
;
10325 xmlXPathObjectPtr result
= NULL
;
10326 char *phys_path
= NULL
;
10327 size_t phys_start
, phys_end
;
10330 if (!context
) return IE_INVALID_CONTEXT
;
10331 zfree(context
->long_message
);
10333 /* Free former message if any */
10334 if (NULL
== message
) return IE_INVAL
;
10335 if (message
) isds_message_free(message
);
10338 /* Do request and check for success */
10339 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10340 BAD_CAST
"MessageDownload", message_id
,
10341 &response
, &xml_stream
, &xml_stream_length
,
10342 &code
, &status_message
);
10343 if (err
) goto leave
;
10346 xpath_ctx
= xmlXPathNewContext(response
);
10351 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10355 result
= xmlXPathEvalExpression(
10356 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10362 /* Empty response */
10363 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10364 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10365 isds_printf_message(context
,
10366 _("Server did not return any message for ID `%s' "
10367 "on MessageDownload request"), message_id_locale
);
10368 free(message_id_locale
);
10372 /* More messages */
10373 if (result
->nodesetval
->nodeNr
> 1) {
10374 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10375 isds_printf_message(context
,
10376 _("Server did return more messages for ID `%s' "
10377 "on MessageDownload request"), message_id_locale
);
10378 free(message_id_locale
);
10383 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10385 /* Extract the message */
10386 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10387 if (err
) goto leave
;
10389 /* Locate raw XML blob */
10390 phys_path
= strdup(
10391 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10392 PHYSXML_ELEMENT_SEPARATOR
10393 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10394 PHYSXML_ELEMENT_SEPARATOR
10395 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10401 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10402 phys_path
, &phys_start
, &phys_end
);
10405 isds_log_message(context
,
10406 _("Substring with isds:MessageDownloadResponse element "
10407 "could not be located in raw SOAP message"));
10410 /* Save XML blob */
10411 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10412 &(*message)->raw_length);*/
10413 /* TODO: Store name space declarations from ancestors */
10414 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10415 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10416 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10417 (*message
)->raw
= malloc((*message
)->raw_length
);
10418 if (!(*message
)->raw
) {
10422 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10427 isds_message_free(message
);
10432 xmlXPathFreeObject(result
);
10433 xmlXPathFreeContext(xpath_ctx
);
10436 free(status_message
);
10438 if (!*message
|| !(*message
)->xml
) {
10439 xmlFreeDoc(response
);
10443 isds_log(ILF_ISDS
, ILL_DEBUG
,
10444 _("MessageDownload request processed by server "
10447 #else /* not HAVE_LIBCURL */
10454 /* Load message of any type from buffer.
10455 * @context is session context
10456 * @raw_type defines content type of @buffer. Only message types are allowed.
10457 * @buffer is message raw representation. Format (CMS, plain signed,
10458 * message direction) is defined in @raw_type. You can retrieve such data
10459 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10460 * @length is length of buffer in bytes.
10461 * @message is automatically reallocated message parsed from @buffer.
10462 * @strategy selects how buffer will be attached into raw isds_message member.
10464 isds_error
isds_load_message(struct isds_ctx
*context
,
10465 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10466 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10468 isds_error err
= IE_SUCCESS
;
10469 void *xml_stream
= NULL
;
10470 size_t xml_stream_length
= 0;
10471 message_ns_type message_ns
;
10472 xmlDocPtr message_doc
= NULL
;
10473 xmlXPathContextPtr xpath_ctx
= NULL
;
10474 xmlXPathObjectPtr result
= NULL
;
10476 if (!context
) return IE_INVALID_CONTEXT
;
10477 zfree(context
->long_message
);
10478 if (!message
) return IE_INVAL
;
10479 isds_message_free(message
);
10480 if (!buffer
) return IE_INVAL
;
10483 /* Select buffer format and extract XML from CMS*/
10484 switch (raw_type
) {
10485 case RAWTYPE_INCOMING_MESSAGE
:
10486 message_ns
= MESSAGE_NS_UNSIGNED
;
10487 xml_stream
= (void *) buffer
;
10488 xml_stream_length
= length
;
10491 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10492 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10493 xml_stream
= (void *) buffer
;
10494 xml_stream_length
= length
;
10497 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10498 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10499 err
= _isds_extract_cms_data(context
, buffer
, length
,
10500 &xml_stream
, &xml_stream_length
);
10501 if (err
) goto leave
;
10504 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10505 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10506 xml_stream
= (void *) buffer
;
10507 xml_stream_length
= length
;
10510 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10511 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10512 err
= _isds_extract_cms_data(context
, buffer
, length
,
10513 &xml_stream
, &xml_stream_length
);
10514 if (err
) goto leave
;
10518 isds_log_message(context
, _("Bad raw message representation type"));
10523 isds_log(ILF_ISDS
, ILL_DEBUG
,
10524 _("Loading message:\n%.*s\nEnd of message\n"),
10525 xml_stream_length
, xml_stream
);
10527 /* Convert messages XML stream into XPath context */
10528 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10529 if (!message_doc
) {
10533 xpath_ctx
= xmlXPathNewContext(message_doc
);
10538 /* XXX: Standard name space for unsigned incoming direction:
10539 * http://isds.czechpoint.cz/v20/
10541 * XXX: Name spaces mangled for signed outgoing direction:
10542 * http://isds.czechpoint.cz/v20/SentMessage:
10544 * <q:MessageDownloadResponse
10545 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10546 * <q:dmReturnedMessage>
10547 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10548 * <p:dmID>151916</p:dmID>
10551 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10553 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10554 * </q:dmReturnedMessage>
10555 * </q:MessageDownloadResponse>
10557 * XXX: Name spaces mangled for signed incoming direction:
10558 * http://isds.czechpoint.cz/v20/message:
10560 * <q:MessageDownloadResponse
10561 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10562 * <q:dmReturnedMessage>
10563 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10564 * <p:dmID>151916</p:dmID>
10567 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10569 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10570 * </q:dmReturnedMessage>
10571 * </q:MessageDownloadResponse>
10573 * Stupidity of ISDS developers is unlimited */
10574 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10578 result
= xmlXPathEvalExpression(
10579 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10585 /* Empty message */
10586 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10587 isds_printf_message(context
,
10588 _("XML document does not contain "
10589 "sisds:dmReturnedMessage element"));
10593 /* More messages */
10594 if (result
->nodesetval
->nodeNr
> 1) {
10595 isds_printf_message(context
,
10596 _("XML document has more sisds:dmReturnedMessage elements"));
10601 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10603 /* Extract the message */
10604 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10605 if (err
) goto leave
;
10607 /* Append raw buffer into message */
10608 (*message
)->raw_type
= raw_type
;
10609 switch (strategy
) {
10610 case BUFFER_DONT_STORE
:
10613 (*message
)->raw
= malloc(length
);
10614 if (!(*message
)->raw
) {
10618 memcpy((*message
)->raw
, buffer
, length
);
10619 (*message
)->raw_length
= length
;
10622 (*message
)->raw
= (void *) buffer
;
10623 (*message
)->raw_length
= length
;
10633 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10634 isds_message_free(message
);
10637 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10638 xmlXPathFreeObject(result
);
10639 xmlXPathFreeContext(xpath_ctx
);
10640 if (!*message
|| !(*message
)->xml
) {
10641 xmlFreeDoc(message_doc
);
10645 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10650 /* Determine type of raw message or delivery info according some heuristics.
10651 * It does not validate the raw blob.
10652 * @context is session context
10653 * @raw_type returns content type of @buffer. Valid only if exit code of this
10654 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10655 * reallocated memory.
10656 * @buffer is message raw representation.
10657 * @length is length of buffer in bytes. */
10658 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10659 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10661 void *xml_stream
= NULL
;
10662 size_t xml_stream_length
= 0;
10663 xmlDocPtr document
= NULL
;
10664 xmlNodePtr root
= NULL
;
10666 if (!context
) return IE_INVALID_CONTEXT
;
10667 zfree(context
->long_message
);
10668 if (length
== 0 || !buffer
) return IE_INVAL
;
10669 if (!raw_type
) return IE_INVAL
;
10672 err
= _isds_extract_cms_data(context
, buffer
, length
,
10673 &xml_stream
, &xml_stream_length
);
10675 xml_stream
= (void *) buffer
;
10676 xml_stream_length
= (size_t) length
;
10681 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10683 isds_printf_message(context
,
10684 _("Could not parse data as XML document"));
10689 /* Get root element */
10690 root
= xmlDocGetRootElement(document
);
10692 isds_printf_message(context
,
10693 _("XML document is missing root element"));
10698 if (!root
->ns
|| !root
->ns
->href
) {
10699 isds_printf_message(context
,
10700 _("Root element does not belong to any name space"));
10705 /* Test name space */
10706 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10707 if (xml_stream
== buffer
)
10708 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10710 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10711 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10712 if (xml_stream
== buffer
)
10713 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10715 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10716 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10717 if (xml_stream
== buffer
)
10718 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10720 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10721 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10722 if (xml_stream
!= buffer
) {
10723 isds_printf_message(context
,
10724 _("Document in ISDS name space is encapsulated into CMS" ));
10726 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10727 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10728 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10729 *raw_type
= RAWTYPE_DELIVERYINFO
;
10731 isds_printf_message(context
,
10732 _("Unknown root element in ISDS name space"));
10736 isds_printf_message(context
,
10737 _("Unknown name space"));
10742 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10743 xmlFreeDoc(document
);
10748 /* Download signed incoming/outgoing message identified by ID.
10749 * @context is session context
10750 * @output is true for outgoing message, false for incoming message
10751 * @message_id is message identifier (you can get them from
10752 * isds_get_list_of_{sent,received}_messages())
10753 * @message is automatically reallocated message retrieved from ISDS. The raw
10754 * member will be filled with PKCS#7 structure in DER format. */
10755 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10756 const _Bool outgoing
, const char *message_id
,
10757 struct isds_message
**message
) {
10759 isds_error err
= IE_SUCCESS
;
10761 xmlDocPtr response
= NULL
;
10762 xmlChar
*code
= NULL
, *status_message
= NULL
;
10763 xmlXPathContextPtr xpath_ctx
= NULL
;
10764 xmlXPathObjectPtr result
= NULL
;
10765 char *encoded_structure
= NULL
;
10767 size_t raw_length
= 0;
10770 if (!context
) return IE_INVALID_CONTEXT
;
10771 zfree(context
->long_message
);
10772 if (!message
) return IE_INVAL
;
10773 isds_message_free(message
);
10776 /* Do request and check for success */
10777 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10778 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10779 BAD_CAST
"SignedMessageDownload",
10780 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10781 if (err
) goto leave
;
10783 /* Find signed message, extract it into raw and maybe free
10785 err
= find_extract_signed_data_free_response(context
,
10786 (xmlChar
*)message_id
, &response
,
10787 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10788 BAD_CAST
"SignedMessageDownload",
10789 &raw
, &raw_length
);
10790 if (err
) goto leave
;
10792 /* Parse message */
10793 err
= isds_load_message(context
,
10794 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10795 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10796 raw
, raw_length
, message
, BUFFER_MOVE
);
10797 if (err
) goto leave
;
10803 isds_message_free(message
);
10806 free(encoded_structure
);
10807 xmlXPathFreeObject(result
);
10808 xmlXPathFreeContext(xpath_ctx
);
10812 free(status_message
);
10813 xmlFreeDoc(response
);
10816 isds_log(ILF_ISDS
, ILL_DEBUG
,
10818 _("SignedSentMessageDownload request processed by server "
10819 "successfully.\n") :
10820 _("SignedMessageDownload request processed by server "
10823 #else /* not HAVE_LIBCURL */
10830 /* Download signed incoming message identified by ID.
10831 * @context is session context
10832 * @message_id is message identifier (you can get them from
10833 * isds_get_list_of_received_messages())
10834 * @message is automatically reallocated message retrieved from ISDS. The raw
10835 * member will be filled with PKCS#7 structure in DER format. */
10836 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10837 const char *message_id
, struct isds_message
**message
) {
10838 return isds_get_signed_message(context
, 0, message_id
, message
);
10842 /* Download signed outgoing message identified by ID.
10843 * @context is session context
10844 * @message_id is message identifier (you can get them from
10845 * isds_get_list_of_sent_messages())
10846 * @message is automatically reallocated message retrieved from ISDS. The raw
10847 * member will be filled with PKCS#7 structure in DER format. */
10848 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10849 const char *message_id
, struct isds_message
**message
) {
10850 return isds_get_signed_message(context
, 1, message_id
, message
);
10854 /* Get type and name of user who sent a message identified by ID.
10855 * @context is session context
10856 * @message_id is message identifier
10857 * @sender_type is pointer to automatically allocated type of sender detected
10858 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10859 * library or to the server, NULL will be returned. Pass NULL if you don't
10861 * @raw_sender_type is automatically reallocated UTF-8 string describing
10862 * sender type or NULL if not known to server. Pass NULL if you don't care.
10863 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10864 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10865 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10866 const char *message_id
, isds_sender_type
**sender_type
,
10867 char **raw_sender_type
, char **sender_name
) {
10868 isds_error err
= IE_SUCCESS
;
10870 xmlDocPtr response
= NULL
;
10871 xmlChar
*code
= NULL
, *status_message
= NULL
;
10872 xmlXPathContextPtr xpath_ctx
= NULL
;
10873 xmlXPathObjectPtr result
= NULL
;
10874 char *type_string
= NULL
;
10877 if (!context
) return IE_INVALID_CONTEXT
;
10878 zfree(context
->long_message
);
10879 if (sender_type
) zfree(*sender_type
);
10880 if (raw_sender_type
) zfree(*raw_sender_type
);
10881 if (sender_name
) zfree(*sender_name
);
10882 if (!message_id
) return IE_INVAL
;
10885 /* Do request and check for success */
10886 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10887 BAD_CAST
"GetMessageAuthor",
10888 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10889 if (err
) goto leave
;
10892 xpath_ctx
= xmlXPathNewContext(response
);
10897 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10901 result
= xmlXPathEvalExpression(
10902 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10907 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10908 isds_log_message(context
,
10909 _("Missing GetMessageAuthorResponse element"));
10913 if (result
->nodesetval
->nodeNr
> 1) {
10914 isds_log_message(context
,
10915 _("Multiple GetMessageAuthorResponse element"));
10919 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10920 xmlXPathFreeObject(result
); result
= NULL
;
10922 /* Fill output arguments in */
10923 EXTRACT_STRING("isds:userType", type_string
);
10924 if (NULL
!= type_string
) {
10925 if (NULL
!= sender_type
) {
10926 *sender_type
= calloc(1, sizeof(**sender_type
));
10927 if (NULL
== *sender_type
) {
10932 err
= string2isds_sender_type((xmlChar
*)type_string
,
10935 zfree(*sender_type
);
10936 if (err
== IE_ENUM
) {
10938 char *type_string_locale
= _isds_utf82locale(type_string
);
10939 isds_log(ILF_ISDS
, ILL_WARNING
,
10940 _("Unknown isds:userType value: %s"),
10941 type_string_locale
);
10942 free(type_string_locale
);
10947 if (NULL
!= sender_name
)
10948 EXTRACT_STRING("isds:authorName", *sender_name
);
10952 if (NULL
!= sender_type
) zfree(*sender_type
);
10953 zfree(type_string
);
10954 if (NULL
!= sender_name
) zfree(*sender_name
);
10956 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10958 xmlXPathFreeObject(result
);
10959 xmlXPathFreeContext(xpath_ctx
);
10962 free(status_message
);
10963 xmlFreeDoc(response
);
10966 isds_log(ILF_ISDS
, ILL_DEBUG
,
10967 _("GetMessageAuthor request processed by server "
10968 "successfully.\n"));
10969 #else /* not HAVE_LIBCURL */
10976 /* Retrieve hash of message identified by ID stored in ISDS.
10977 * @context is session context
10978 * @message_id is message identifier
10979 * @hash is automatically reallocated message hash downloaded from ISDS.
10980 * Message must exist in system and must not be deleted. */
10981 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10982 const char *message_id
, struct isds_hash
**hash
) {
10984 isds_error err
= IE_SUCCESS
;
10986 xmlDocPtr response
= NULL
;
10987 xmlChar
*code
= NULL
, *status_message
= NULL
;
10988 xmlXPathContextPtr xpath_ctx
= NULL
;
10989 xmlXPathObjectPtr result
= NULL
;
10992 if (!context
) return IE_INVALID_CONTEXT
;
10993 zfree(context
->long_message
);
10995 isds_hash_free(hash
);
10998 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10999 BAD_CAST
"VerifyMessage", message_id
,
11000 &response
, NULL
, NULL
, &code
, &status_message
);
11001 if (err
) goto leave
;
11005 xpath_ctx
= xmlXPathNewContext(response
);
11010 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11014 result
= xmlXPathEvalExpression(
11015 BAD_CAST
"/isds:VerifyMessageResponse",
11021 /* Empty response */
11022 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11023 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11024 isds_printf_message(context
,
11025 _("Server did not return any response for ID `%s' "
11026 "on VerifyMessage request"), message_id_locale
);
11027 free(message_id_locale
);
11031 /* More responses */
11032 if (result
->nodesetval
->nodeNr
> 1) {
11033 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11034 isds_printf_message(context
,
11035 _("Server did return more responses for ID `%s' "
11036 "on VerifyMessage request"), message_id_locale
);
11037 free(message_id_locale
);
11042 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11044 /* Extract the hash */
11045 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11049 isds_hash_free(hash
);
11052 xmlXPathFreeObject(result
);
11053 xmlXPathFreeContext(xpath_ctx
);
11056 free(status_message
);
11057 xmlFreeDoc(response
);
11060 isds_log(ILF_ISDS
, ILL_DEBUG
,
11061 _("VerifyMessage request processed by server "
11064 #else /* not HAVE_LIBCURL */
11071 /* Erase message specified by @message_id from long term storage. Other
11072 * message cannot be erased on user request.
11073 * @context is session context
11074 * @message_id is message identifier.
11075 * @incoming is true for incoming message, false for outgoing message.
11077 * IE_SUCCESS if message has ben removed
11078 * IE_INVAL if message does not exist in long term storage or message
11079 * belongs to different box
11080 * TODO: IE_NOEPRM if user has no permission to erase a message */
11081 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11082 const char *message_id
, _Bool incoming
) {
11083 isds_error err
= IE_SUCCESS
;
11085 xmlNodePtr request
= NULL
, node
;
11086 xmlNsPtr isds_ns
= NULL
;
11087 xmlDocPtr response
= NULL
;
11088 xmlChar
*code
= NULL
, *status_message
= NULL
;
11091 if (!context
) return IE_INVALID_CONTEXT
;
11092 zfree(context
->long_message
);
11093 if (NULL
== message_id
) return IE_INVAL
;
11095 /* Check if connection is established
11096 * TODO: This check should be done downstairs. */
11097 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11100 /* Build request */
11101 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11103 isds_log_message(context
,
11104 _("Could build EraseMessage request"));
11107 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11109 isds_log_message(context
, _("Could not create ISDS name space"));
11110 xmlFreeNode(request
);
11113 xmlSetNs(request
, isds_ns
);
11115 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11116 if (err
) goto leave
;
11117 INSERT_STRING(request
, "dmID", message_id
);
11119 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11123 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11124 "message ID %s to ISDS\n"), message_id
);
11125 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11126 xmlFreeNode(request
); request
= NULL
;
11129 isds_log(ILF_ISDS
, ILL_DEBUG
,
11130 _("Processing ISDS response on EraseMessage request "
11135 /* Check for response status */
11136 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11137 &code
, &status_message
, NULL
);
11139 isds_log(ILF_ISDS
, ILL_DEBUG
,
11140 _("ISDS response on EraseMessage request is missing "
11145 /* Check server status code */
11146 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11147 isds_log_message(context
, _("Message to erase belongs to other box"));
11149 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11150 isds_log_message(context
, _("Message to erase is not saved in "
11151 "long term storage or the direction does not match"));
11153 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11154 char *code_locale
= _isds_utf82locale((char*) code
);
11155 char *message_locale
= _isds_utf82locale((char*) status_message
);
11156 isds_log(ILF_ISDS
, ILL_DEBUG
,
11157 _("Server refused EraseMessage request "
11158 "(code=%s, message=%s)\n"),
11159 code_locale
, message_locale
);
11160 isds_log_message(context
, message_locale
);
11162 free(message_locale
);
11169 free(status_message
);
11170 xmlFreeDoc(response
);
11171 xmlFreeNode(request
);
11174 isds_log(ILF_ISDS
, ILL_DEBUG
,
11175 _("EraseMessage request processed by server "
11178 #else /* not HAVE_LIBCURL */
11185 /* Mark message as read. This is a transactional commit function to acknowledge
11186 * to ISDS the message has been downloaded and processed by client properly.
11187 * @context is session context
11188 * @message_id is message identifier. */
11189 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11190 const char *message_id
) {
11192 isds_error err
= IE_SUCCESS
;
11194 xmlDocPtr response
= NULL
;
11195 xmlChar
*code
= NULL
, *status_message
= NULL
;
11198 if (!context
) return IE_INVALID_CONTEXT
;
11199 zfree(context
->long_message
);
11202 /* Do request and check for success */
11203 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11204 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11205 &response
, NULL
, NULL
, &code
, &status_message
);
11208 free(status_message
);
11209 xmlFreeDoc(response
);
11212 isds_log(ILF_ISDS
, ILL_DEBUG
,
11213 _("MarkMessageAsDownloaded request processed by server "
11216 #else /* not HAVE_LIBCURL */
11223 /* Mark message as received by recipient. This is applicable only to
11224 * commercial message. Use envelope->dmType message member to distinguish
11225 * commercial message from government message. Government message is
11226 * received automatically (by law), commercial message on recipient request.
11227 * @context is session context
11228 * @message_id is message identifier. */
11229 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11230 const char *message_id
) {
11232 isds_error err
= IE_SUCCESS
;
11234 xmlDocPtr response
= NULL
;
11235 xmlChar
*code
= NULL
, *status_message
= NULL
;
11238 if (!context
) return IE_INVALID_CONTEXT
;
11239 zfree(context
->long_message
);
11242 /* Do request and check for success */
11243 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11244 BAD_CAST
"ConfirmDelivery", message_id
,
11245 &response
, NULL
, NULL
, &code
, &status_message
);
11248 free(status_message
);
11249 xmlFreeDoc(response
);
11252 isds_log(ILF_ISDS
, ILL_DEBUG
,
11253 _("ConfirmDelivery request processed by server "
11256 #else /* not HAVE_LIBCURL */
11263 /* Send document for authorized conversion into Czech POINT system.
11264 * This is public anonymous service, no log-in necessary. Special context is
11265 * used to reuse keep-a-live HTTPS connection.
11266 * @context is Czech POINT session context. DO NOT use context connected to
11267 * ISDS server. Use new context or context used by this function previously.
11268 * @document is document to convert. Only data, data_length, dmFileDescr and
11269 * is_xml members are significant. Be ware that not all document formats can be
11270 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11271 * @id is reallocated identifier assigned by Czech POINT system to
11272 * your document on submit. Use is to tell it to Czech POINT officer.
11273 * @date is reallocated document submit date (submitted documents
11274 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11276 isds_error
czp_convert_document(struct isds_ctx
*context
,
11277 const struct isds_document
*document
,
11278 char **id
, struct tm
**date
) {
11279 isds_error err
= IE_SUCCESS
;
11281 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11282 xmlNodePtr request
= NULL
, node
;
11283 xmlDocPtr response
= NULL
;
11285 xmlXPathContextPtr xpath_ctx
= NULL
;
11286 xmlXPathObjectPtr result
= NULL
;
11287 long int status
= -1;
11288 long int *status_ptr
= &status
;
11289 char *string
= NULL
;
11293 if (!context
) return IE_INVALID_CONTEXT
;
11294 zfree(context
->long_message
);
11295 if (!document
|| !id
|| !date
) return IE_INVAL
;
11297 if (document
->is_xml
) {
11298 isds_log_message(context
,
11299 _("XML documents cannot be submitted to conversion"));
11303 /* Free output arguments */
11308 /* Store configuration */
11309 context
->type
= CTX_TYPE_CZP
;
11310 free(context
->url
);
11311 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11312 if (!(context
->url
))
11315 /* Prepare CURL handle if not yet connected */
11316 if (!context
->curl
) {
11317 context
->curl
= curl_easy_init();
11318 if (!(context
->curl
))
11322 /* Build conversion request */
11323 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11325 isds_log_message(context
,
11326 _("Could not build Czech POINT conversion request"));
11329 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11331 isds_log_message(context
,
11332 _("Could not create Czech POINT deposit name space"));
11333 xmlFreeNode(request
);
11336 xmlSetNs(request
, deposit_ns
);
11338 /* Insert children. They are in empty namespace! */
11339 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11341 isds_log_message(context
, _("Could not create empty name space"));
11345 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11346 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11347 document
->dmFileDescr
);
11349 /* Document encoded in Base64 */
11350 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11351 document
->data
, document
->data_length
);
11352 if (err
) goto leave
;
11354 isds_log(ILF_ISDS
, ILL_DEBUG
,
11355 _("Submitting document for conversion into Czech POINT deposit"));
11357 /* Send conversion request */
11358 err
= _czp_czpdeposit(context
, request
, &response
);
11359 xmlFreeNode(request
); request
= NULL
;
11362 czp_do_close_connection(context
);
11367 /* Extract response */
11368 xpath_ctx
= xmlXPathNewContext(response
);
11373 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11377 result
= xmlXPathEvalExpression(
11378 BAD_CAST
"/deposit:saveDocumentResponse/return",
11384 /* Empty response */
11385 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11386 isds_printf_message(context
,
11387 _("Missing `return' element in Czech POINT deposit response"));
11391 /* More responses */
11392 if (result
->nodesetval
->nodeNr
> 1) {
11393 isds_printf_message(context
,
11394 _("Multiple `return' element in Czech POINT deposit response"));
11399 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11402 EXTRACT_LONGINT("status", status_ptr
, 1);
11404 EXTRACT_STRING("statusMsg", string
);
11405 char *string_locale
= _isds_utf82locale(string
);
11406 isds_printf_message(context
,
11407 _("Czech POINT deposit refused document for conversion "
11408 "(code=%ld, message=%s)"),
11409 status
, string_locale
);
11410 free(string_locale
);
11415 /* Get document ID */
11416 EXTRACT_STRING("documentID", *id
);
11418 /* Get submit date */
11419 EXTRACT_STRING("dateInserted", string
);
11421 *date
= calloc(1, sizeof(**date
));
11426 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11428 if (err
== IE_NOTSUP
) {
11430 char *string_locale
= _isds_utf82locale(string
);
11431 isds_printf_message(context
,
11432 _("Invalid dateInserted value: %s"), string_locale
);
11433 free(string_locale
);
11441 xmlXPathFreeObject(result
);
11442 xmlXPathFreeContext(xpath_ctx
);
11444 xmlFreeDoc(response
);
11445 xmlFreeNode(request
);
11448 char *id_locale
= _isds_utf82locale((char *) *id
);
11449 isds_log(ILF_ISDS
, ILL_DEBUG
,
11450 _("Document %s has been submitted for conversion "
11451 "to server successfully\n"), id_locale
);
11454 #else /* not HAVE_LIBCURL */
11461 /* Close possibly opened connection to Czech POINT document deposit.
11462 * @context is Czech POINT session context. */
11463 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11464 if (!context
) return IE_INVALID_CONTEXT
;
11465 zfree(context
->long_message
);
11467 return czp_do_close_connection(context
);
11474 /* Send request for new box creation in testing ISDS instance.
11475 * It's not possible to request for a production box currently, as it
11476 * communicates via e-mail.
11477 * XXX: This function does not work either. Server complains about invalid
11479 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11481 * @context is special session context for box creation request. DO NOT use
11482 * standard context as it could reveal your password. Use fresh new context or
11483 * context previously used by this function.
11484 * @box is box description to create including single primary user (in case of
11485 * FO box type). It outputs box ID assigned by ISDS in dbID element.
11486 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11487 * box, or contact address of PFO box owner). The email member is mandatory as
11488 * it will be used to deliver credentials.
11489 * @former_names is former name of box owner. Pass NULL if you don't care.
11490 * @approval is optional external approval of box manipulation
11491 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11492 * NULL, if you don't care.*/
11493 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11494 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11495 const char *former_names
, const struct isds_approval
*approval
,
11496 char **refnumber
) {
11497 isds_error err
= IE_SUCCESS
;
11499 xmlNodePtr request
= NULL
;
11500 xmlDocPtr response
= NULL
;
11501 xmlXPathContextPtr xpath_ctx
= NULL
;
11502 xmlXPathObjectPtr result
= NULL
;
11506 if (!context
) return IE_INVALID_CONTEXT
;
11507 zfree(context
->long_message
);
11508 if (!box
) return IE_INVAL
;
11511 if (!box
->email
|| box
->email
[0] == '\0') {
11512 isds_log_message(context
, _("E-mail field is mandatory"));
11516 /* Scratch box ID */
11519 /* Store configuration */
11520 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11521 free(context
->url
);
11522 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11523 if (!(context
->url
))
11526 /* Prepare CURL handle if not yet connected */
11527 if (!context
->curl
) {
11528 context
->curl
= curl_easy_init();
11529 if (!(context
->curl
))
11533 /* Build CreateDataBox request */
11534 err
= build_CreateDBInput_request(context
,
11535 &request
, BAD_CAST
"CreateDataBox",
11536 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11537 if (err
) goto leave
;
11539 /* Send it to server and process response */
11540 err
= send_destroy_request_check_response(context
,
11541 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11542 &response
, (xmlChar
**) refnumber
, NULL
);
11543 if (err
) goto leave
;
11545 /* Extract box ID */
11546 xpath_ctx
= xmlXPathNewContext(response
);
11551 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11555 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11558 xmlXPathFreeObject(result
);
11559 xmlXPathFreeContext(xpath_ctx
);
11560 xmlFreeDoc(response
);
11561 xmlFreeNode(request
);
11564 isds_log(ILF_ISDS
, ILL_DEBUG
,
11565 _("CreateDataBox request processed by server successfully.\n"));
11567 #else /* not HAVE_LIBCURL */
11575 /* Submit CMS signed message to ISDS to verify its originality. This is
11576 * stronger form of isds_verify_message_hash() because ISDS does more checks
11577 * than simple one (potentialy old weak) hash comparison.
11578 * @context is session context
11579 * @message is memory with raw CMS signed message bit stream
11580 * @length is @message size in bytes
11582 * IE_SUCCESS if message originates in ISDS
11583 * IE_NOTEQUAL if message is unknown to ISDS
11584 * other code for other errors */
11585 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11586 const void *message
, size_t length
) {
11587 isds_error err
= IE_SUCCESS
;
11589 xmlNsPtr isds_ns
= NULL
;
11590 xmlNodePtr request
= NULL
;
11591 xmlDocPtr response
= NULL
;
11592 xmlXPathContextPtr xpath_ctx
= NULL
;
11593 xmlXPathObjectPtr result
= NULL
;
11594 _Bool
*authentic
= NULL
;
11597 if (!context
) return IE_INVALID_CONTEXT
;
11598 zfree(context
->long_message
);
11599 if (!message
|| length
== 0) return IE_INVAL
;
11602 /* Check if connection is established
11603 * TODO: This check should be done downstairs. */
11604 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11607 /* Build AuthenticateMessage request */
11608 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11610 isds_log_message(context
,
11611 _("Could not build AuthenticateMessage request"));
11614 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11616 isds_log_message(context
, _("Could not create ISDS name space"));
11617 xmlFreeNode(request
);
11620 xmlSetNs(request
, isds_ns
);
11622 /* Insert Base64 encoded message */
11623 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11625 if (err
) goto leave
;
11627 /* Send request to server and process response */
11628 err
= send_destroy_request_check_response(context
,
11629 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11630 &response
, NULL
, NULL
);
11631 if (err
) goto leave
;
11634 /* ISDS has decided */
11635 xpath_ctx
= xmlXPathNewContext(response
);
11640 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11645 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11648 isds_log_message(context
,
11649 _("Server did not return any response on "
11650 "AuthenticateMessage request"));
11655 isds_log(ILF_ISDS
, ILL_DEBUG
,
11656 _("ISDS authenticated the message successfully\n"));
11658 isds_log_message(context
, _("ISDS does not know the message"));
11665 xmlXPathFreeObject(result
);
11666 xmlXPathFreeContext(xpath_ctx
);
11668 xmlFreeDoc(response
);
11669 xmlFreeNode(request
);
11670 #else /* not HAVE_LIBCURL */
11678 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11679 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11681 * @context is session context
11682 * @input_data is memory with raw CMS signed message or delivery info bit
11683 * stream to re-sign
11684 * @input_length is @input_data size in bytes
11685 * @output_data is pointer to auto-allocated memory where to store re-signed
11686 * input data blob. Caller must free it.
11687 * @output_data is pointer where to store @output_data size in bytes
11688 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11689 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11691 * IE_SUCCESS if CMS blob has been re-signed successfully
11692 * other code for other errors */
11693 isds_error
isds_resign_message(struct isds_ctx
*context
,
11694 const void *input_data
, size_t input_length
,
11695 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11696 isds_error err
= IE_SUCCESS
;
11698 xmlNsPtr isds_ns
= NULL
;
11699 xmlNodePtr request
= NULL
;
11700 xmlDocPtr response
= NULL
;
11701 xmlXPathContextPtr xpath_ctx
= NULL
;
11702 xmlXPathObjectPtr result
= NULL
;
11703 char *string
= NULL
;
11704 const xmlChar
*codes
[] = {
11711 const char *meanings
[] = {
11713 "Message is not original",
11714 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11715 "Time stamp could not been generated in time"
11717 const isds_error errors
[] = {
11723 struct code_map_isds_error map
= {
11725 .meanings
= meanings
,
11730 if (NULL
!= output_data
) *output_data
= NULL
;
11731 if (NULL
!= output_length
) *output_length
= 0;
11732 if (NULL
!= valid_to
) *valid_to
= NULL
;
11734 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11735 zfree(context
->long_message
);
11736 if (NULL
== input_data
|| 0 == input_length
) {
11737 isds_log_message(context
, _("Empty CMS blob on input"));
11740 if (NULL
== output_data
|| NULL
== output_length
) {
11741 isds_log_message(context
,
11742 _("NULL pointer provided for output CMS blob"));
11747 /* Check if connection is established
11748 * TODO: This check should be done downstairs. */
11749 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11752 /* Build Re-signISDSDocument request */
11753 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11755 isds_log_message(context
,
11756 _("Could not build Re-signISDSDocument request"));
11759 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11761 isds_log_message(context
, _("Could not create ISDS name space"));
11762 xmlFreeNode(request
);
11765 xmlSetNs(request
, isds_ns
);
11767 /* Insert Base64 encoded CMS blob */
11768 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11769 input_data
, input_length
);
11770 if (err
) goto leave
;
11772 /* Send request to server and process response */
11773 err
= send_destroy_request_check_response(context
,
11774 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11775 &response
, NULL
, &map
);
11776 if (err
) goto leave
;
11779 /* Extract re-signed data */
11780 xpath_ctx
= xmlXPathNewContext(response
);
11785 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11789 result
= xmlXPathEvalExpression(
11790 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11795 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11796 isds_log_message(context
,
11797 _("Missing Re-signISDSDocumentResponse element"));
11801 if (result
->nodesetval
->nodeNr
> 1) {
11802 isds_log_message(context
,
11803 _("Multiple Re-signISDSDocumentResponse element"));
11807 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11808 xmlXPathFreeObject(result
); result
= NULL
;
11810 EXTRACT_STRING("isds:dmResultDoc", string
);
11811 /* Decode non-empty data */
11812 if (NULL
!= string
&& string
[0] != '\0') {
11813 *output_length
= _isds_b64decode(string
, output_data
);
11814 if (*output_length
== (size_t) -1) {
11815 isds_log_message(context
,
11816 _("Error while Base64-decoding re-signed data"));
11821 isds_log_message(context
, _("Server did not send re-signed data"));
11827 if (NULL
!= valid_to
) {
11828 /* Get time stamp expiration date */
11829 EXTRACT_STRING("isds:dmValidTo", string
);
11830 if (NULL
!= string
) {
11831 *valid_to
= calloc(1, sizeof(**valid_to
));
11836 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11838 if (err
== IE_NOTSUP
) {
11840 char *string_locale
= _isds_utf82locale(string
);
11841 isds_printf_message(context
,
11842 _("Invalid dmValidTo value: %s"), string_locale
);
11843 free(string_locale
);
11853 xmlXPathFreeObject(result
);
11854 xmlXPathFreeContext(xpath_ctx
);
11856 xmlFreeDoc(response
);
11857 xmlFreeNode(request
);
11858 #else /* not HAVE_LIBCURL */
11865 #undef INSERT_ELEMENT
11866 #undef CHECK_FOR_STRING_LENGTH
11867 #undef INSERT_STRING_ATTRIBUTE
11868 #undef INSERT_ULONGINTNOPTR
11869 #undef INSERT_ULONGINT
11870 #undef INSERT_LONGINT
11871 #undef INSERT_BOOLEAN
11872 #undef INSERT_SCALAR_BOOLEAN
11873 #undef INSERT_STRING
11874 #undef INSERT_STRING_WITH_NS
11875 #undef EXTRACT_STRING_ATTRIBUTE
11876 #undef EXTRACT_ULONGINT
11877 #undef EXTRACT_LONGINT
11878 #undef EXTRACT_BOOLEAN
11879 #undef EXTRACT_STRING
11882 /* Compute hash of message from raw representation and store it into envelope.
11883 * Original hash structure will be destroyed in envelope.
11884 * @context is session context
11885 * @message is message carrying raw XML message blob
11886 * @algorithm is desired hash algorithm to use */
11887 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11888 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11889 isds_error err
= IE_SUCCESS
;
11891 void *xml_stream
= NULL
;
11892 size_t xml_stream_length
;
11893 size_t phys_start
, phys_end
;
11894 char *phys_path
= NULL
;
11895 struct isds_hash
*new_hash
= NULL
;
11898 if (!context
) return IE_INVALID_CONTEXT
;
11899 zfree(context
->long_message
);
11900 if (!message
) return IE_INVAL
;
11902 if (!message
->raw
) {
11903 isds_log_message(context
,
11904 _("Message does not carry raw representation"));
11908 switch (message
->raw_type
) {
11909 case RAWTYPE_INCOMING_MESSAGE
:
11911 xml_stream
= message
->raw
;
11912 xml_stream_length
= message
->raw_length
;
11915 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11916 nsuri
= SISDS_INCOMING_NS
;
11917 xml_stream
= message
->raw
;
11918 xml_stream_length
= message
->raw_length
;
11921 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11922 nsuri
= SISDS_INCOMING_NS
;
11923 err
= _isds_extract_cms_data(context
,
11924 message
->raw
, message
->raw_length
,
11925 &xml_stream
, &xml_stream_length
);
11926 if (err
) goto leave
;
11929 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11930 nsuri
= SISDS_OUTGOING_NS
;
11931 xml_stream
= message
->raw
;
11932 xml_stream_length
= message
->raw_length
;
11935 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11936 nsuri
= SISDS_OUTGOING_NS
;
11937 err
= _isds_extract_cms_data(context
,
11938 message
->raw
, message
->raw_length
,
11939 &xml_stream
, &xml_stream_length
);
11940 if (err
) goto leave
;
11944 isds_log_message(context
, _("Bad raw representation type"));
11950 /* XXX: Hash is computed from original string representing isds:dmDm
11951 * subtree. That means no encoding, white space, xmlns attributes changes.
11952 * In other words, input for hash can be invalid XML stream. */
11953 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11954 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11955 PHYSXML_ELEMENT_SEPARATOR
,
11956 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11957 PHYSXML_ELEMENT_SEPARATOR
11958 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11962 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11963 phys_path
, &phys_start
, &phys_end
);
11966 isds_log_message(context
,
11967 _("Substring with isds:dmDM element could not be located "
11968 "in raw message"));
11974 new_hash
= calloc(1, sizeof(*new_hash
));
11979 new_hash
->algorithm
= algorithm
;
11980 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11983 isds_log_message(context
, _("Could not compute message hash"));
11987 /* Save computed hash */
11988 if (!message
->envelope
) {
11989 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11990 if (!message
->envelope
) {
11995 isds_hash_free(&message
->envelope
->hash
);
11996 message
->envelope
->hash
= new_hash
;
12000 isds_hash_free(&new_hash
);
12004 if (xml_stream
!= message
->raw
) free(xml_stream
);
12009 /* Compare two hashes.
12010 * @h1 is first hash
12011 * @h2 is another hash
12013 * IE_SUCCESS if hashes equal
12014 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12015 * IE_ENUM if not comparable, but both structures defined
12016 * IE_INVAL if some of the structures are undefined (NULL)
12017 * IE_ERROR if internal error occurs */
12018 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12019 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12020 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12021 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12022 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12023 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12025 for (size_t i
= 0; i
< h1
->length
; i
++) {
12026 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12027 return IE_NOTEQUAL
;
12033 /* Check message has gone through ISDS by comparing message hash stored in
12034 * ISDS and locally computed hash. You must provide message with valid raw
12035 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12036 * This is convenient wrapper for isds_download_message_hash(),
12037 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12038 * @context is session context
12039 * @message is message with valid raw and envelope member; envelope->hash
12040 * member will be changed during function run. Use envelope on heap only.
12042 * IE_SUCCESS if message originates in ISDS
12043 * IE_NOTEQUAL if message is unknown to ISDS
12044 * other code for other errors */
12045 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12046 struct isds_message
*message
) {
12047 isds_error err
= IE_SUCCESS
;
12048 struct isds_hash
*downloaded_hash
= NULL
;
12050 if (!context
) return IE_INVALID_CONTEXT
;
12051 zfree(context
->long_message
);
12052 if (!message
) return IE_INVAL
;
12054 if (!message
->envelope
) {
12055 isds_log_message(context
,
12056 _("Given message structure is missing envelope"));
12059 if (!message
->raw
) {
12060 isds_log_message(context
,
12061 _("Given message structure is missing raw representation"));
12065 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12067 if (err
) goto leave
;
12069 err
= isds_compute_message_hash(context
, message
,
12070 downloaded_hash
->algorithm
);
12071 if (err
) goto leave
;
12073 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12076 isds_hash_free(&downloaded_hash
);
12081 /* Search for document by document ID in list of documents. IDs are compared
12083 * @documents is list of isds_documents
12084 * @id is document identifier
12085 * @return first matching document or NULL. */
12086 const struct isds_document
*isds_find_document_by_id(
12087 const struct isds_list
*documents
, const char *id
) {
12088 const struct isds_list
*item
;
12089 const struct isds_document
*document
;
12091 for (item
= documents
; item
; item
= item
->next
) {
12092 document
= (struct isds_document
*) item
->data
;
12093 if (!document
) continue;
12095 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12103 /* Normalize @mime_type to be proper MIME type.
12104 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12105 * guess regular MIME type (e.g. "application/pdf").
12106 * @mime_type is UTF-8 encoded MIME type to fix
12107 * @return original @mime_type if no better interpretation exists, or
12108 * constant static UTF-8 encoded string with proper MIME type. */
12109 const char *isds_normalize_mime_type(const char *mime_type
) {
12110 if (!mime_type
) return NULL
;
12112 for (size_t offset
= 0;
12113 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12115 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12116 extension_map_mime
[offset
]))
12117 return (const char *) extension_map_mime
[offset
+ 1];
12124 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12125 struct isds_message **message);
12126 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12127 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12128 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12129 struct isds_address **address);
12131 int isds_message_free(struct isds_message **message);
12132 int isds_address_free(struct isds_address **address);
12136 /* Makes known all relevant namespaces to given XPath context
12137 * @xpath_ctx is XPath context
12138 * @message_ns selects proper message name space. Unsigned and signed
12139 * messages and delivery info's differ in prefix and URI. */
12140 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12141 const message_ns_type message_ns
) {
12142 const xmlChar
*message_namespace
= NULL
;
12144 if (!xpath_ctx
) return IE_ERROR
;
12146 switch(message_ns
) {
12148 message_namespace
= BAD_CAST ISDS1_NS
; break;
12149 case MESSAGE_NS_UNSIGNED
:
12150 message_namespace
= BAD_CAST ISDS_NS
; break;
12151 case MESSAGE_NS_SIGNED_INCOMING
:
12152 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12153 case MESSAGE_NS_SIGNED_OUTGOING
:
12154 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12155 case MESSAGE_NS_SIGNED_DELIVERY
:
12156 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12161 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12163 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12165 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12167 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12169 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12171 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))