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
)->adCode
);
191 free((*address
)->adCity
);
192 free((*address
)->adDistrict
);
193 free((*address
)->adStreet
);
194 free((*address
)->adNumberInStreet
);
195 free((*address
)->adNumberInMunicipality
);
196 free((*address
)->adZipCode
);
197 free((*address
)->adState
);
204 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
205 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
206 if (!db_owner_info
|| !*db_owner_info
) return;
208 free((*db_owner_info
)->dbID
);
209 free((*db_owner_info
)->dbType
);
210 free((*db_owner_info
)->ic
);
211 isds_PersonName_free(&((*db_owner_info
)->personName
));
212 free((*db_owner_info
)->firmName
);
213 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
214 isds_Address_free(&((*db_owner_info
)->address
));
215 free((*db_owner_info
)->nationality
);
216 free((*db_owner_info
)->email
);
217 free((*db_owner_info
)->telNumber
);
218 free((*db_owner_info
)->identifier
);
219 free((*db_owner_info
)->aifoIsds
);
220 free((*db_owner_info
)->registryCode
);
221 free((*db_owner_info
)->dbState
);
222 free((*db_owner_info
)->dbEffectiveOVM
);
223 free((*db_owner_info
)->dbOpenAddressing
);
225 free(*db_owner_info
);
226 *db_owner_info
= NULL
;
229 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
230 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
231 if (!db_user_info
|| !*db_user_info
) return;
233 free((*db_user_info
)->userID
);
234 free((*db_user_info
)->userType
);
235 free((*db_user_info
)->userPrivils
);
236 isds_PersonName_free(&((*db_user_info
)->personName
));
237 isds_Address_free(&((*db_user_info
)->address
));
238 free((*db_user_info
)->biDate
);
239 free((*db_user_info
)->ic
);
240 free((*db_user_info
)->firmName
);
241 free((*db_user_info
)->caStreet
);
242 free((*db_user_info
)->caCity
);
243 free((*db_user_info
)->caZipCode
);
244 free((*db_user_info
)->caState
);
245 free((*db_user_info
)->aifo_ticket
);
247 zfree(*db_user_info
);
251 /* Deallocate struct isds_event recursively and NULL it */
252 void isds_event_free(struct isds_event
**event
) {
253 if (!event
|| !*event
) return;
255 free((*event
)->time
);
256 free((*event
)->type
);
257 free((*event
)->description
);
262 /* Deallocate struct isds_envelope recursively and NULL it */
263 void isds_envelope_free(struct isds_envelope
**envelope
) {
264 if (!envelope
|| !*envelope
) return;
266 free((*envelope
)->dmID
);
267 free((*envelope
)->dbIDSender
);
268 free((*envelope
)->dmSender
);
269 free((*envelope
)->dmSenderAddress
);
270 free((*envelope
)->dmSenderType
);
271 free((*envelope
)->dmRecipient
);
272 free((*envelope
)->dmRecipientAddress
);
273 free((*envelope
)->dmAmbiguousRecipient
);
274 free((*envelope
)->dmType
);
276 free((*envelope
)->dmOrdinal
);
277 free((*envelope
)->dmMessageStatus
);
278 free((*envelope
)->dmDeliveryTime
);
279 free((*envelope
)->dmAcceptanceTime
);
280 isds_hash_free(&(*envelope
)->hash
);
281 free((*envelope
)->timestamp
);
282 isds_list_free(&(*envelope
)->events
);
284 free((*envelope
)->dmSenderOrgUnit
);
285 free((*envelope
)->dmSenderOrgUnitNum
);
286 free((*envelope
)->dbIDRecipient
);
287 free((*envelope
)->dmRecipientOrgUnit
);
288 free((*envelope
)->dmRecipientOrgUnitNum
);
289 free((*envelope
)->dmToHands
);
290 free((*envelope
)->dmAnnotation
);
291 free((*envelope
)->dmRecipientRefNumber
);
292 free((*envelope
)->dmSenderRefNumber
);
293 free((*envelope
)->dmRecipientIdent
);
294 free((*envelope
)->dmSenderIdent
);
296 free((*envelope
)->dmLegalTitleLaw
);
297 free((*envelope
)->dmLegalTitleYear
);
298 free((*envelope
)->dmLegalTitleSect
);
299 free((*envelope
)->dmLegalTitlePar
);
300 free((*envelope
)->dmLegalTitlePoint
);
302 free((*envelope
)->dmPersonalDelivery
);
303 free((*envelope
)->dmAllowSubstDelivery
);
305 free((*envelope
)->dmOVM
);
306 free((*envelope
)->dmPublishOwnID
);
313 /* Deallocate struct isds_message recursively and NULL it */
314 void isds_message_free(struct isds_message
**message
) {
315 if (!message
|| !*message
) return;
317 free((*message
)->raw
);
318 isds_envelope_free(&((*message
)->envelope
));
319 isds_list_free(&((*message
)->documents
));
320 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
327 /* Deallocate struct isds_document recursively and NULL it */
328 void isds_document_free(struct isds_document
**document
) {
329 if (!document
|| !*document
) return;
331 if (!(*document
)->is_xml
) {
332 free((*document
)->data
);
334 free((*document
)->dmMimeType
);
335 free((*document
)->dmFileGuid
);
336 free((*document
)->dmUpFileGuid
);
337 free((*document
)->dmFileDescr
);
338 free((*document
)->dmFormat
);
345 /* Deallocate struct isds_message_copy recursively and NULL it */
346 void isds_message_copy_free(struct isds_message_copy
**copy
) {
347 if (!copy
|| !*copy
) return;
349 free((*copy
)->dbIDRecipient
);
350 free((*copy
)->dmRecipientOrgUnit
);
351 free((*copy
)->dmRecipientOrgUnitNum
);
352 free((*copy
)->dmToHands
);
354 free((*copy
)->dmStatus
);
361 /* Deallocate struct isds_message_status_change recursively and NULL it */
362 void isds_message_status_change_free(
363 struct isds_message_status_change
**message_status_change
) {
364 if (!message_status_change
|| !*message_status_change
) return;
366 free((*message_status_change
)->dmID
);
367 free((*message_status_change
)->time
);
368 free((*message_status_change
)->dmMessageStatus
);
370 zfree(*message_status_change
);
374 /* Deallocate struct isds_approval recursively and NULL it */
375 void isds_approval_free(struct isds_approval
**approval
) {
376 if (!approval
|| !*approval
) return;
378 free((*approval
)->refference
);
384 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
385 * The email string is deallocated too. */
386 void isds_credentials_delivery_free(
387 struct isds_credentials_delivery
**credentials_delivery
) {
388 if (!credentials_delivery
|| !*credentials_delivery
) return;
390 free((*credentials_delivery
)->email
);
391 free((*credentials_delivery
)->token
);
392 free((*credentials_delivery
)->new_user_name
);
394 zfree(*credentials_delivery
);
398 /* Deallocate struct isds_commercial_permission recursively and NULL it */
399 void isds_commercial_permission_free(
400 struct isds_commercial_permission
**permission
) {
401 if (NULL
== permission
|| NULL
== *permission
) return;
403 free((*permission
)->recipient
);
404 free((*permission
)->payer
);
405 free((*permission
)->expiration
);
406 free((*permission
)->count
);
407 free((*permission
)->reply_identifier
);
413 /* Deallocate struct isds_credit_event recursively and NULL it */
414 void isds_credit_event_free(struct isds_credit_event
**event
) {
415 if (NULL
== event
|| NULL
== *event
) return;
417 free((*event
)->time
);
418 switch ((*event
)->type
) {
419 case ISDS_CREDIT_CHARGED
:
420 free((*event
)->details
.charged
.transaction
);
422 case ISDS_CREDIT_DISCHARGED
:
423 free((*event
)->details
.discharged
.transaction
);
425 case ISDS_CREDIT_MESSAGE_SENT
:
426 free((*event
)->details
.message_sent
.recipient
);
427 free((*event
)->details
.message_sent
.message_id
);
429 case ISDS_CREDIT_STORAGE_SET
:
430 free((*event
)->details
.storage_set
.new_valid_from
);
431 free((*event
)->details
.storage_set
.new_valid_to
);
432 free((*event
)->details
.storage_set
.old_capacity
);
433 free((*event
)->details
.storage_set
.old_valid_from
);
434 free((*event
)->details
.storage_set
.old_valid_to
);
435 free((*event
)->details
.storage_set
.initiator
);
437 case ISDS_CREDIT_EXPIRED
:
445 /* Deallocate struct isds_fulltext_result recursively and NULL it */
446 void isds_fulltext_result_free(
447 struct isds_fulltext_result
**result
) {
448 if (NULL
== result
|| NULL
== *result
) return;
450 free((*result
)->dbID
);
451 free((*result
)->name
);
452 isds_list_free(&((*result
)->name_match_start
));
453 isds_list_free(&((*result
)->name_match_end
));
454 free((*result
)->address
);
455 isds_list_free(&((*result
)->address_match_start
));
456 isds_list_free(&((*result
)->address_match_end
));
458 free((*result
)->biDate
);
464 /* *DUP_OR_ERROR macros needs error label */
465 #define STRDUP_OR_ERROR(new, template) { \
469 (new) = strdup(template); \
470 if (!new) goto error; \
474 #define FLATDUP_OR_ERROR(new, template) { \
478 (new) = malloc(sizeof(*(new))); \
479 if (!new) goto error; \
480 memcpy((new), (template), sizeof(*(template))); \
484 /* Copy structure isds_pki_credentials recursively. */
485 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
486 const struct isds_pki_credentials
*template) {
487 struct isds_pki_credentials
*new = NULL
;
489 if(!template) return NULL
;
491 new = calloc(1, sizeof(*new));
492 if (!new) return NULL
;
494 STRDUP_OR_ERROR(new->engine
, template->engine
);
495 new->certificate_format
= template->certificate_format
;
496 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
497 new->key_format
= template->key_format
;
498 STRDUP_OR_ERROR(new->key
, template->key
);
499 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
504 isds_pki_credentials_free(&new);
509 /* Copy structure isds_PersonName recursively */
510 struct isds_PersonName
*isds_PersonName_duplicate(
511 const struct isds_PersonName
*src
) {
512 struct isds_PersonName
*new = NULL
;
514 if (!src
) return NULL
;
516 new = calloc(1, sizeof(*new));
517 if (!new) return NULL
;
519 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
520 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
521 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
522 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
527 isds_PersonName_free(&new);
532 /* Copy structure isds_BirthInfo recursively */
533 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
534 const struct isds_BirthInfo
*template) {
535 struct isds_BirthInfo
*new = NULL
;
537 if (!template) return NULL
;
539 new = calloc(1, sizeof(*new));
540 if (!new) return NULL
;
542 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
543 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
544 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
545 STRDUP_OR_ERROR(new->biState
, template->biState
);
550 isds_BirthInfo_free(&new);
555 /* Copy structure isds_Address recursively */
556 struct isds_Address
*isds_Address_duplicate(
557 const struct isds_Address
*src
) {
558 struct isds_Address
*new = NULL
;
560 if (!src
) return NULL
;
562 new = calloc(1, sizeof(*new));
563 if (!new) return NULL
;
565 FLATDUP_OR_ERROR(new->adCode
, src
->adCode
);
566 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
567 STRDUP_OR_ERROR(new->adDistrict
, src
->adDistrict
);
568 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
569 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
570 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
571 src
->adNumberInMunicipality
);
572 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
573 STRDUP_OR_ERROR(new->adState
, src
->adState
);
578 isds_Address_free(&new);
583 /* Copy structure isds_DbOwnerInfo recursively */
584 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
585 const struct isds_DbOwnerInfo
*src
) {
586 struct isds_DbOwnerInfo
*new = NULL
;
587 if (!src
) return NULL
;
589 new = calloc(1, sizeof(*new));
590 if (!new) return NULL
;
592 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
593 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
594 STRDUP_OR_ERROR(new->ic
, src
->ic
);
596 if (src
->personName
) {
597 if (!(new->personName
=
598 isds_PersonName_duplicate(src
->personName
)))
602 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
604 if (src
->birthInfo
) {
605 if (!(new->birthInfo
=
606 isds_BirthInfo_duplicate(src
->birthInfo
)))
611 if (!(new->address
= isds_Address_duplicate(src
->address
)))
615 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
616 STRDUP_OR_ERROR(new->email
, src
->email
);
617 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
618 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
619 FLATDUP_OR_ERROR(new->aifoIsds
, src
->aifoIsds
);
620 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
621 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
622 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
623 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
628 isds_DbOwnerInfo_free(&new);
633 /* Copy structure isds_DbUserInfo recursively */
634 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
635 const struct isds_DbUserInfo
*src
) {
636 struct isds_DbUserInfo
*new = NULL
;
637 if (!src
) return NULL
;
639 new = calloc(1, sizeof(*new));
640 if (!new) return NULL
;
642 STRDUP_OR_ERROR(new->userID
, src
->userID
);
643 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
644 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
646 if (src
->personName
) {
647 if (!(new->personName
=
648 isds_PersonName_duplicate(src
->personName
)))
653 if (!(new->address
= isds_Address_duplicate(src
->address
)))
657 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
658 STRDUP_OR_ERROR(new->ic
, src
->ic
);
659 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
660 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
661 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
662 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
663 STRDUP_OR_ERROR(new->caState
, src
->caState
);
664 STRDUP_OR_ERROR(new->aifo_ticket
, src
->aifo_ticket
);
669 isds_DbUserInfo_free(&new);
673 #undef FLATDUP_OR_ERROR
674 #undef STRDUP_OR_ERROR
677 /* Logs libxml2 errors. Should be registered to libxml2 library.
678 * @ctx is unused currently
679 * @msg is printf-like formated message from libxml2 (UTF-8?)
680 * @... are variadic arguments for @msg */
681 static void log_xml(void *ctx
, const char *msg
, ...) {
685 /* Silent warning for unused function argument.
686 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
692 isds_vasprintf(&text
, msg
, ap
);
696 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
701 /* Initialize ISDS library.
702 * Global function, must be called before other functions.
703 * If it fails you can not use ISDS library and must call isds_cleanup() to
704 * free partially initialized global variables. */
705 isds_error
isds_init(void) {
706 /* NULL global variables */
707 log_facilities
= ILF_ALL
;
708 log_level
= ILL_WARNING
;
710 log_callback_data
= NULL
;
713 /* Initialize gettext */
714 bindtextdomain(PACKAGE
, LOCALEDIR
);
718 /* Initialize CURL */
719 if (curl_global_init(CURL_GLOBAL_ALL
)) {
720 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
723 #endif /* HAVE_LIBCURL */
725 /* Initialise cryptographic back-ends. */
726 if (IE_SUCCESS
!= _isds_init_crypto()) {
727 isds_log(ILF_ISDS
, ILL_CRIT
,
728 _("Initialization of cryptographic back-end failed\n"));
732 /* This can _exit() current program. Find not so assertive check. */
734 xmlSetGenericErrorFunc(NULL
, log_xml
);
737 if (_isds_init_expat(&version_expat
)) {
738 isds_log(ILF_ISDS
, ILL_CRIT
,
739 _("expat library initialization failed\n"));
743 /* Allocate global variables */
750 /* Deinitialize ISDS library.
751 * Global function, must be called as last library function. */
752 isds_error
isds_cleanup(void) {
758 curl_global_cleanup();
765 /* Return version string of this library. Version of dependencies can be
766 * embedded. Do no try to parse it. You must free it. */
767 char *isds_version(void) {
770 isds_asprintf(&buffer
,
772 # ifndef USE_OPENSSL_BACKEND
773 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
775 _("%s (%s, %s, %s, libxml2 %s)"),
778 # ifndef USE_OPENSSL_BACKEND
779 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
781 _("%s (%s, %s, libxml2 %s)"),
788 #ifndef USE_OPENSSL_BACKEND
789 version_gpgme
, version_gcrypt
,
793 version_expat
, xmlParserVersion
);
798 /* Return text description of ISDS error */
799 const char *isds_strerror(const isds_error error
) {
802 return(_("Success")); break;
804 return(_("Unspecified error")); break;
806 return(_("Not supported")); break;
808 return(_("Invalid value")); break;
809 case IE_INVALID_CONTEXT
:
810 return(_("Invalid context")); break;
811 case IE_NOT_LOGGED_IN
:
812 return(_("Not logged in")); break;
813 case IE_CONNECTION_CLOSED
:
814 return(_("Connection closed")); break;
816 return(_("Timed out")); break;
818 return(_("Not exist")); break;
820 return(_("Out of memory")); break;
822 return(_("Network problem")); break;
824 return(_("HTTP problem")); break;
826 return(_("SOAP problem")); break;
828 return(_("XML problem")); break;
830 return(_("ISDS server problem")); break;
832 return(_("Invalid enum value")); break;
834 return(_("Invalid date value")); break;
836 return(_("Too big")); break;
838 return(_("Too small")); break;
840 return(_("Value not unique")); break;
842 return(_("Values not equal")); break;
843 case IE_PARTIAL_SUCCESS
:
844 return(_("Some suboperations failed")); break;
846 return(_("Operation aborted")); break;
848 return(_("Security problem")); break;
850 return(_("Unknown error"));
855 /* Create ISDS context.
856 * Each context can be used for different sessions to (possibly) different
857 * ISDS server with different credentials. */
858 struct isds_ctx
*isds_ctx_create(void) {
859 struct isds_ctx
*context
;
860 context
= malloc(sizeof(*context
));
861 if (context
) memset(context
, 0, sizeof(*context
));
866 /* Close possibly opened connection to Czech POINT document deposit without
867 * resetting long_message buffer.
868 * XXX: Do not use czp_close_connection() if you do not want to destroy log
870 * @context is Czech POINT session context. */
871 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
872 if (!context
) return IE_INVALID_CONTEXT
;
873 _isds_close_connection(context
);
878 /* Discard credentials.
879 * @context is ISDS context
880 * @discard_saved_username is true for removing saved username, false for
882 * Only that. It does not cause log out, connection close or similar. */
883 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
884 _Bool discard_saved_username
) {
885 if(!context
) return IE_INVALID_CONTEXT
;
887 if (context
->username
) {
888 memset(context
->username
, 0, strlen(context
->username
));
889 zfree(context
->username
);
891 if (context
->password
) {
892 memset(context
->password
, 0, strlen(context
->password
));
893 zfree(context
->password
);
895 isds_pki_credentials_free(&context
->pki_credentials
);
896 if (discard_saved_username
&& context
->saved_username
) {
897 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
898 zfree(context
->saved_username
);
903 #endif /* HAVE_LIBCURL */
906 /* Destroy ISDS context and free memory.
907 * @context will be NULLed on success. */
908 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
909 if (!context
|| !*context
) {
910 return IE_INVALID_CONTEXT
;
914 /* Discard credentials and close connection */
915 switch ((*context
)->type
) {
916 case CTX_TYPE_NONE
: break;
917 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
919 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
920 czp_do_close_connection(*context
); break;
924 _isds_discard_credentials(*context
, 1);
926 /* Free other structures */
927 free((*context
)->url
);
928 free((*context
)->tls_verify_server
);
929 free((*context
)->tls_ca_file
);
930 free((*context
)->tls_ca_dir
);
931 free((*context
)->tls_crl_file
);
932 #endif /* HAVE_LIBCURL */
933 free((*context
)->long_message
);
941 /* Return long message text produced by library function, e.g. detailed error
942 * message. Returned pointer is only valid until new library function is
943 * called for the same context. Could be NULL, especially if NULL context is
944 * supplied. Return string is locale encoded. */
945 char *isds_long_message(const struct isds_ctx
*context
) {
946 if (!context
) return NULL
;
947 return context
->long_message
;
951 /* Stores message into context' long_message buffer.
952 * Application can pick the message up using isds_long_message().
953 * NULL @message truncates the buffer but does not deallocate it.
954 * @message is coded in locale encoding */
955 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
956 const char *message
) {
960 if (!context
) return IE_INVALID_CONTEXT
;
962 /* FIXME: Check for integer overflow */
963 length
= 1 + ((message
) ? strlen(message
) : 0);
964 buffer
= realloc(context
->long_message
, length
);
965 if (!buffer
) return IE_NOMEM
;
968 strcpy(buffer
, message
);
972 context
->long_message
= buffer
;
977 /* Appends message into context' long_message buffer.
978 * Application can pick the message up using isds_long_message().
979 * NULL message has void effect. */
980 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
981 const char *message
) {
983 size_t old_length
, length
;
985 if (!context
) return IE_INVALID_CONTEXT
;
986 if (!message
) return IE_SUCCESS
;
987 if (!context
->long_message
)
988 return isds_log_message(context
, message
);
990 old_length
= strlen(context
->long_message
);
991 /* FIXME: Check for integer overflow */
992 length
= 1 + old_length
+ strlen(message
);
993 buffer
= realloc(context
->long_message
, length
);
994 if (!buffer
) return IE_NOMEM
;
996 strcpy(buffer
+ old_length
, message
);
998 context
->long_message
= buffer
;
1003 /* Stores formatted message into context' long_message buffer.
1004 * Application can pick the message up using isds_long_message(). */
1005 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
1006 const char *format
, ...) {
1010 if (!context
) return IE_INVALID_CONTEXT
;
1011 va_start(ap
, format
);
1012 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
1015 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
1020 * @facilities is bit mask of isds_log_facility values,
1021 * @level is verbosity level. */
1022 void isds_set_logging(const unsigned int facilities
,
1023 const isds_log_level level
) {
1024 log_facilities
= facilities
;
1029 /* Register callback function libisds calls when new global log message is
1030 * produced by library. Library logs to stderr by default.
1031 * @callback is function provided by application libisds will call. See type
1032 * definition for @callback argument explanation. Pass NULL to revert logging to
1033 * default behaviour.
1034 * @data is application specific data @callback gets as last argument */
1035 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1036 log_callback
= callback
;
1037 log_callback_data
= data
;
1041 /* Log @message in class @facility with log @level into global log. @message
1042 * is printf(3) formatting string, variadic arguments may be necessary.
1043 * For debugging purposes. */
1044 _hidden isds_error
isds_log(const isds_log_facility facility
,
1045 const isds_log_level level
, const char *message
, ...) {
1047 char *buffer
= NULL
;
1050 if (level
> log_level
) return IE_SUCCESS
;
1051 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1052 if (!message
) return IE_INVAL
;
1055 /* Pass message to application supplied callback function */
1056 va_start(ap
, message
);
1057 length
= isds_vasprintf(&buffer
, message
, ap
);
1064 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1068 /* Default: Log it to stderr */
1069 va_start(ap
, message
);
1070 vfprintf(stderr
, message
, ap
);
1072 /* Line buffered printf is default.
1080 /* Set timeout in milliseconds for each network job like connecting to server
1081 * or sending message. Use 0 to disable timeout limits. */
1082 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1083 const unsigned int timeout
) {
1084 if (!context
) return IE_INVALID_CONTEXT
;
1085 zfree(context
->long_message
);
1088 context
->timeout
= timeout
;
1090 if (context
->curl
) {
1093 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1095 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1096 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1099 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1100 context
->timeout
/ 1000);
1101 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1102 if (curl_err
) return IE_ERROR
;
1106 #else /* not HAVE_LIBCURL */
1112 /* Register callback function libisds calls periodically during HTTP data
1114 * @context is session context
1115 * @callback is function provided by application libisds will call. See type
1116 * definition for @callback argument explanation.
1117 * @data is application specific data @callback gets as last argument */
1118 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1119 isds_progress_callback callback
, void *data
) {
1120 if (!context
) return IE_INVALID_CONTEXT
;
1121 zfree(context
->long_message
);
1124 context
->progress_callback
= callback
;
1125 context
->progress_callback_data
= data
;
1128 #else /* not HAVE_LIBCURL */
1134 /* Change context settings.
1135 * @context is context which setting will be applied to
1136 * @option is name of option. It determines the type of last argument. See
1137 * isds_option definition for more info.
1138 * @... is value of new setting. Type is determined by @option
1140 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1142 isds_error err
= IE_SUCCESS
;
1145 char *pointer
, *string
;
1148 if (!context
) return IE_INVALID_CONTEXT
;
1149 zfree(context
->long_message
);
1151 va_start(ap
, option
);
1153 #define REPLACE_VA_BOOLEAN(destination) { \
1154 if (!(destination)) { \
1155 (destination) = malloc(sizeof(*(destination))); \
1156 if (!(destination)) { \
1157 err = IE_NOMEM; goto leave; \
1160 *(destination) = (_Bool) !!va_arg(ap, int); \
1163 #define REPLACE_VA_STRING(destination) { \
1164 string = va_arg(ap, char *); \
1166 pointer = realloc((destination), 1 + strlen(string)); \
1167 if (!pointer) { err = IE_NOMEM; goto leave; } \
1168 strcpy(pointer, string); \
1169 (destination) = pointer; \
1171 free(destination); \
1172 (destination) = NULL; \
1177 case IOPT_TLS_VERIFY_SERVER
:
1179 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1181 err
= IE_NOTSUP
; goto leave
;
1184 case IOPT_TLS_CA_FILE
:
1186 REPLACE_VA_STRING(context
->tls_ca_file
);
1188 err
= IE_NOTSUP
; goto leave
;
1191 case IOPT_TLS_CA_DIRECTORY
:
1193 REPLACE_VA_STRING(context
->tls_ca_dir
);
1195 err
= IE_NOTSUP
; goto leave
;
1198 case IOPT_TLS_CRL_FILE
:
1200 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1201 REPLACE_VA_STRING(context
->tls_crl_file
);
1203 isds_log_message(context
,
1204 _("Curl library does not support CRL definition"));
1206 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1208 err
= IE_NOTSUP
; goto leave
;
1209 #endif /* not HAVE_LIBCURL */
1211 case IOPT_NORMALIZE_MIME_TYPE
:
1212 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1216 err
= IE_ENUM
; goto leave
;
1219 #undef REPLACE_VA_STRING
1220 #undef REPLACE_VA_BOOLEAN
1229 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1230 * Destination for NULL argument will not be touched.
1231 * Destination pointers must be freed before calling this function.
1232 * If @username is @context->saved_username, the saved_username will not be
1233 * replaced. The saved_username is clobbered only if context has set otp
1235 * Return IE_SUCCESS on success. */
1236 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1237 const char *username
, const char *password
,
1238 const struct isds_pki_credentials
*pki_credentials
) {
1239 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1241 /* FIXME: mlock password
1242 * (I have a library) */
1245 context
->username
= strdup(username
);
1246 if (context
->otp
&& context
->saved_username
!= username
)
1247 context
->saved_username
= strdup(username
);
1250 if (NULL
== context
->otp_credentials
)
1251 context
->password
= strdup(password
);
1253 context
->password
= _isds_astrcat(password
,
1254 context
->otp_credentials
->otp_code
);
1256 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1258 if ((NULL
!= username
&& NULL
== context
->username
) ||
1259 (NULL
!= password
&& NULL
== context
->password
) ||
1260 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1261 (context
->otp
&& NULL
!= context
->username
&&
1262 NULL
== context
->saved_username
)) {
1271 /* Connect and log into ISDS server.
1272 * All required arguments will be copied, you do not have to keep them after
1274 * ISDS supports six different authentication methods. Exact method is
1275 * selected on @username, @password, @pki_credentials, and @otp arguments:
1276 * - If @pki_credentials == NULL, @username and @password must be supplied
1278 * - If @otp == NULL, simple authentication by username and password will
1280 * - If @otp != NULL, authentication by username and password and OTP
1282 * - If @pki_credentials != NULL, then
1283 * - If @username == NULL, only certificate will be used
1284 * - If @username != NULL, then
1285 * - If @password == NULL, then certificate will be used and
1286 * @username shifts meaning to box ID. This is used for hosted
1288 * - Otherwise all three arguments will be used.
1289 * Please note, that different cases require different certificate type
1290 * (system qualified one or commercial non qualified one). This library
1291 * does not check such political issues. Please see ISDS Specification
1293 * @url is base address of ISDS web service. Pass extern isds_locator
1294 * variable to use production ISDS instance without client certificate
1295 * authentication (or extern isds_cert_locator with client certificate
1296 * authentication or extern isds_otp_locators with OTP authentication).
1297 * Passing NULL has the same effect, autoselection between isds_locator,
1298 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1299 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1300 * isds_otp_testing_locator) variable to select testing instance.
1301 * @username is user name of ISDS user or box ID
1302 * @password is user's secret password
1303 * @pki_credentials defines public key cryptographic material to use in client
1305 * @otp selects one-time password authentication method to use, defines OTP
1306 * code (if known) and returns fine grade resolution of OTP procedure.
1308 * IE_SUCCESS if authentication succeeds
1309 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1310 * requested, fine grade reason will be set into @otp->resolution. Error
1311 * message from server can be obtained by isds_long_message() call.
1312 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1313 * server has sent OTP code through side channel. Application is expected to
1314 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1315 * this call to complete second phase of TOTP authentication;
1316 * or other appropriate error. */
1317 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1318 const char *username
, const char *password
,
1319 const struct isds_pki_credentials
*pki_credentials
,
1320 struct isds_otp
*otp
) {
1322 isds_error err
= IE_NOT_LOGGED_IN
;
1323 isds_error soap_err
;
1324 xmlNsPtr isds_ns
= NULL
;
1325 xmlNodePtr request
= NULL
;
1326 #endif /* HAVE_LIBCURL */
1328 if (!context
) return IE_INVALID_CONTEXT
;
1329 zfree(context
->long_message
);
1332 /* Close connection if already logged in */
1333 if (context
->curl
) {
1334 _isds_close_connection(context
);
1337 /* Store configuration */
1338 context
->type
= CTX_TYPE_ISDS
;
1339 zfree(context
->url
);
1341 /* Mangle base URI according to requested authentication method */
1342 if (NULL
== pki_credentials
) {
1343 isds_log(ILF_SEC
, ILL_INFO
,
1344 _("Selected authentication method: no certificate, "
1345 "username and password\n"));
1346 if (!username
|| !password
) {
1347 isds_log_message(context
,
1348 _("Both username and password must be supplied"));
1351 context
->otp_credentials
= otp
;
1352 context
->otp
= (NULL
!= context
->otp_credentials
);
1354 if (!context
->otp
) {
1355 /* Default locator is official system (without certificate or
1357 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1359 const char *authenticator_uri
= NULL
;
1360 if (!url
) url
= isds_otp_locator
;
1361 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1362 switch (context
->otp_credentials
->method
) {
1364 isds_log(ILF_SEC
, ILL_INFO
,
1365 _("Selected authentication method: "
1366 "HMAC-based one-time password\n"));
1368 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1371 isds_log(ILF_SEC
, ILL_INFO
,
1372 _("Selected authentication method: "
1373 "Time-based one-time password\n"));
1374 if (context
->otp_credentials
->otp_code
== NULL
) {
1375 isds_log(ILF_SEC
, ILL_INFO
,
1376 _("OTP code has not been provided by "
1377 "application, requesting server for "
1380 "%1$sas/processLogin?type=totp&sendSms=true&"
1383 isds_log(ILF_SEC
, ILL_INFO
,
1384 _("OTP code has been provided by "
1385 "application, not requesting server "
1388 "%1$sas/processLogin?type=totp&"
1393 isds_log_message(context
,
1394 _("Unknown one-time password authentication "
1395 "method requested by application"));
1398 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1402 /* Default locator is official system (with client certificate) */
1404 context
->otp_credentials
= NULL
;
1405 if (!url
) url
= isds_cert_locator
;
1408 isds_log(ILF_SEC
, ILL_INFO
,
1409 _("Selected authentication method: system certificate, "
1410 "no username and no password\n"));
1412 context
->url
= _isds_astrcat(url
, "cert/");
1415 isds_log(ILF_SEC
, ILL_INFO
,
1416 _("Selected authentication method: system certificate, "
1417 "box ID and no password\n"));
1418 context
->url
= _isds_astrcat(url
, "hspis/");
1420 isds_log(ILF_SEC
, ILL_INFO
,
1421 _("Selected authentication method: commercial "
1422 "certificate, username and password\n"));
1423 context
->url
= _isds_astrcat(url
, "certds/");
1427 if (!(context
->url
))
1430 /* Prepare CURL handle */
1431 context
->curl
= curl_easy_init();
1432 if (!(context
->curl
))
1435 /* Build log-in request */
1436 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1438 isds_log_message(context
, _("Could not build ISDS log-in request"));
1441 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1443 isds_log_message(context
, _("Could not create ISDS name space"));
1444 xmlFreeNode(request
);
1447 xmlSetNs(request
, isds_ns
);
1449 /* Store credentials */
1450 _isds_discard_credentials(context
, 1);
1451 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1452 _isds_discard_credentials(context
, 1);
1453 xmlFreeNode(request
);
1457 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1460 /* XXX: ISDS documentation does not specify response body for
1461 * DummyOperation request. However real server sends back
1462 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1463 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1464 * SOAP body content, e.g. the dmStatus element. */
1466 /* Send log-in request */
1467 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1470 /* Revert context URL from OTP authentication service URL to OTP web
1471 * service base URL for subsequent calls. Potenial isds_login() retry
1472 * will re-set context URL again. */
1473 zfree(context
->url
);
1474 context
->url
= _isds_astrcat(url
, "apps/");
1475 if (context
->url
== NULL
) {
1476 soap_err
= IE_NOMEM
;
1478 /* Detach pointer to OTP credentials from context */
1479 context
->otp_credentials
= NULL
;
1482 /* Remove credentials */
1483 _isds_discard_credentials(context
, 0);
1485 /* Destroy log-in request */
1486 xmlFreeNode(request
);
1489 _isds_close_connection(context
);
1493 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1494 * authentication succeeded if soap_err == IE_SUCCESS */
1498 isds_log(ILF_ISDS
, ILL_DEBUG
,
1499 _("User %s has been logged into server %s successfully\n"),
1502 #else /* not HAVE_LIBCURL */
1508 /* Log out from ISDS server discards credentials and connection configuration. */
1509 isds_error
isds_logout(struct isds_ctx
*context
) {
1510 if (!context
) return IE_INVALID_CONTEXT
;
1511 zfree(context
->long_message
);
1514 if (context
->curl
) {
1516 isds_error err
= _isds_invalidate_otp_cookie(context
);
1517 if (err
) return err
;
1520 /* Close connection */
1521 _isds_close_connection(context
);
1523 /* Discard credentials for sure. They should not survive isds_login(),
1524 * even successful .*/
1525 _isds_discard_credentials(context
, 1);
1527 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1529 _isds_discard_credentials(context
, 1);
1531 zfree(context
->url
);
1533 #else /* not HAVE_LIBCURL */
1539 /* Verify connection to ISDS is alive and server is responding.
1540 * Send dummy request to ISDS and expect dummy response. */
1541 isds_error
isds_ping(struct isds_ctx
*context
) {
1543 isds_error soap_err
;
1544 xmlNsPtr isds_ns
= NULL
;
1545 xmlNodePtr request
= NULL
;
1546 #endif /* HAVE_LIBCURL */
1548 if (!context
) return IE_INVALID_CONTEXT
;
1549 zfree(context
->long_message
);
1552 /* Check if connection is established */
1553 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1556 /* Build dummy request */
1557 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1559 isds_log_message(context
, _("Could build ISDS dummy request"));
1562 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1564 isds_log_message(context
, _("Could not create ISDS name space"));
1565 xmlFreeNode(request
);
1568 xmlSetNs(request
, isds_ns
);
1570 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1572 /* XXX: ISDS documentation does not specify response body for
1573 * DummyOperation request. However real server sends back
1574 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1575 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1576 * SOAP body content, e.g. the dmStatus element. */
1578 /* Send dummy request */
1579 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1581 /* Destroy log-in request */
1582 xmlFreeNode(request
);
1585 isds_log(ILF_ISDS
, ILL_DEBUG
,
1586 _("ISDS server could not be contacted\n"));
1590 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1591 * authentication succeeded if soap_err == IE_SUCCESS */
1594 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1597 #else /* not HAVE_LIBCURL */
1603 /* Send bogus request to ISDS.
1604 * Just for test purposes */
1605 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1608 xmlNsPtr isds_ns
= NULL
;
1609 xmlNodePtr request
= NULL
;
1610 xmlDocPtr response
= NULL
;
1611 xmlChar
*code
= NULL
, *message
= NULL
;
1614 if (!context
) return IE_INVALID_CONTEXT
;
1615 zfree(context
->long_message
);
1618 /* Check if connection is established */
1619 if (!context
->curl
) {
1620 /* Testing printf message */
1621 isds_printf_message(context
, "%s", _("I said connection closed"));
1622 return IE_CONNECTION_CLOSED
;
1626 /* Build dummy request */
1627 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1629 isds_log_message(context
, _("Could build ISDS bogus request"));
1632 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1634 isds_log_message(context
, _("Could not create ISDS name space"));
1635 xmlFreeNode(request
);
1638 xmlSetNs(request
, isds_ns
);
1640 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1642 /* Sent bogus request */
1643 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1645 /* Destroy request */
1646 xmlFreeNode(request
);
1649 isds_log(ILF_ISDS
, ILL_DEBUG
,
1650 _("Processing ISDS response on bogus request failed\n"));
1651 xmlFreeDoc(response
);
1655 /* Check for response status */
1656 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1657 &code
, &message
, NULL
);
1659 isds_log(ILF_ISDS
, ILL_DEBUG
,
1660 _("ISDS response on bogus request is missing status\n"));
1663 xmlFreeDoc(response
);
1666 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1667 char *code_locale
= _isds_utf82locale((char*)code
);
1668 char *message_locale
= _isds_utf82locale((char*)message
);
1669 isds_log(ILF_ISDS
, ILL_DEBUG
,
1670 _("Server refused bogus request (code=%s, message=%s)\n"),
1671 code_locale
, message_locale
);
1672 /* XXX: Literal error messages from ISDS are Czech messages
1673 * (English sometimes) in UTF-8. It's hard to catch them for
1674 * translation. Successfully gettextized would return in locale
1675 * encoding, unsuccessfully translated would pass in UTF-8. */
1676 isds_log_message(context
, message_locale
);
1678 free(message_locale
);
1681 xmlFreeDoc(response
);
1688 xmlFreeDoc(response
);
1690 isds_log(ILF_ISDS
, ILL_DEBUG
,
1691 _("Bogus message accepted by server. This should not happen.\n"));
1694 #else /* not HAVE_LIBCURL */
1701 /* Serialize XML subtree to buffer preserving XML indentation.
1702 * @context is session context
1703 * @subtree is XML element to be serialized (with children)
1704 * @buffer is automatically reallocated buffer where serialize to
1705 * @length is size of serialized stream in bytes
1706 * @return standard error code, free @buffer in case of error */
1707 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1708 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1709 isds_error err
= IE_SUCCESS
;
1710 xmlBufferPtr xml_buffer
= NULL
;
1711 xmlSaveCtxtPtr save_ctx
= NULL
;
1712 xmlDocPtr subtree_doc
= NULL
;
1713 xmlNodePtr subtree_copy
;
1717 if (!context
) return IE_INVALID_CONTEXT
;
1718 if (!buffer
) return IE_INVAL
;
1720 if (!subtree
|| !length
) return IE_INVAL
;
1722 /* Make temporary XML document with @subtree root element */
1723 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1724 * It can result in not well-formed on invalid XML tree (e.g. name space
1725 * prefix definition can miss. */
1728 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1730 isds_log_message(context
, _("Could not build temporary document"));
1735 /* XXX: Copy subtree and attach the copy to document.
1736 * One node can not bee attached into more document at the same time.
1737 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1739 * XXX: Check xmlSaveTree() too. */
1740 subtree_copy
= xmlCopyNodeList(subtree
);
1741 if (!subtree_copy
) {
1742 isds_log_message(context
, _("Could not copy subtree"));
1746 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1748 /* Only this way we get namespace definition as @xmlns:isds,
1749 * otherwise we get namespace prefix without definition */
1750 /* FIXME: Don't overwrite original default namespace */
1751 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1753 isds_log_message(context
, _("Could not create ISDS name space"));
1757 xmlSetNs(subtree_copy
, isds_ns
);
1760 /* Serialize the document into buffer */
1761 xml_buffer
= xmlBufferCreate();
1763 isds_log_message(context
, _("Could not create xmlBuffer"));
1767 /* Last argument 0 means to not format the XML tree */
1768 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1770 isds_log_message(context
, _("Could not create XML serializer"));
1774 /* XXX: According LibXML documentation, this function does not return
1775 * meaningful value yet */
1776 xmlSaveDoc(save_ctx
, subtree_doc
);
1777 if (-1 == xmlSaveFlush(save_ctx
)) {
1778 isds_log_message(context
,
1779 _("Could not serialize XML subtree"));
1783 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1784 * even after xmlSaveFlush(). Thus close it here */
1785 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1788 /* Store and detach buffer from xml_buffer */
1789 *buffer
= xml_buffer
->content
;
1790 *length
= xml_buffer
->use
;
1791 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1794 new_buffer
= realloc(*buffer
, *length
);
1795 if (new_buffer
) *buffer
= new_buffer
;
1803 xmlSaveClose(save_ctx
);
1804 xmlBufferFree(xml_buffer
);
1805 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1808 #endif /* HAVE_LIBCURL */
1812 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1813 * @context is session context
1814 * @document is original document where @nodeset points to
1815 * @nodeset is XPath node set to dump (recursively)
1816 * @buffer is automatically reallocated buffer where serialize to
1817 * @length is size of serialized stream in bytes
1818 * @return standard error code, free @buffer in case of error */
1819 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1820 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1821 void **buffer
, size_t *length
) {
1822 isds_error err
= IE_SUCCESS
;
1823 xmlBufferPtr xml_buffer
= NULL
;
1826 if (!context
) return IE_INVALID_CONTEXT
;
1827 if (!buffer
) return IE_INVAL
;
1829 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1832 /* Empty node set results into NULL buffer */
1833 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1837 /* Resulting the document into buffer */
1838 xml_buffer
= xmlBufferCreate();
1840 isds_log_message(context
, _("Could not create xmlBuffer"));
1845 /* Iterate over all nodes */
1846 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1848 * XXX: xmlNodeDump() appends to xml_buffer. */
1850 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1851 isds_log_message(context
, _("Could not dump XML node"));
1857 /* Store and detach buffer from xml_buffer */
1858 *buffer
= xml_buffer
->content
;
1859 *length
= xml_buffer
->use
;
1860 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1863 new_buffer
= realloc(*buffer
, *length
);
1864 if (new_buffer
) *buffer
= new_buffer
;
1873 xmlBufferFree(xml_buffer
);
1879 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1880 * @context is session context
1881 * @document is original document where @nodeset points to
1882 * @nodeset is XPath node set to dump (recursively)
1883 * @buffer is automatically reallocated buffer where serialize to
1884 * @length is size of serialized stream in bytes
1885 * @return standard error code, free @buffer in case of error */
1886 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1887 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1888 void **buffer
, size_t *length
) {
1889 isds_error err
= IE_SUCCESS
;
1890 xmlBufferPtr xml_buffer
= NULL
;
1891 xmlSaveCtxtPtr save_ctx
= NULL
;
1894 if (!context
) return IE_INVALID_CONTEXT
;
1895 if (!buffer
) return IE_INVAL
;
1897 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1900 /* Empty node set results into NULL buffer */
1901 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1905 /* Resulting the document into buffer */
1906 xml_buffer
= xmlBufferCreate();
1908 isds_log_message(context
, _("Could not create xmlBuffer"));
1912 if (xmlSubstituteEntitiesDefault(1)) {
1913 isds_log_message(context
, _("Could not disable attribute escaping"));
1917 /* Last argument means:
1918 * 0 to not format the XML tree
1919 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1920 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1921 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1923 isds_log_message(context
, _("Could not create XML serializer"));
1927 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1928 isds_log_message(context, _("Could not disable attribute escaping"));
1934 /* Iterate over all nodes */
1935 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1937 * XXX: xmlNodeDump() appends to xml_buffer. */
1939 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1941 /* XXX: According LibXML documentation, this function does not return
1942 * meaningful value yet */
1943 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1944 if (-1 == xmlSaveFlush(save_ctx
)) {
1945 isds_log_message(context
,
1946 _("Could not serialize XML subtree"));
1952 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1953 * even after xmlSaveFlush(). Thus close it here */
1954 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1956 /* Store and detach buffer from xml_buffer */
1957 *buffer
= xml_buffer
->content
;
1958 *length
= xml_buffer
->use
;
1959 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1962 new_buffer
= realloc(*buffer
, *length
);
1963 if (new_buffer
) *buffer
= new_buffer
;
1971 xmlSaveClose(save_ctx
);
1972 xmlBufferFree(xml_buffer
);
1979 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1980 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1981 if (!string
|| !type
) return IE_INVAL
;
1983 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1985 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1987 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1988 *type
= DBTYPE_PFO_ADVOK
;
1989 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1990 *type
= DBTYPE_PFO_DANPOR
;
1991 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1992 *type
= DBTYPE_PFO_INSSPR
;
1993 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1995 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1996 *type
= DBTYPE_PO_ZAK
;
1997 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1998 *type
= DBTYPE_PO_REQ
;
1999 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
2001 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
2002 *type
= DBTYPE_OVM_NOTAR
;
2003 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
2004 *type
= DBTYPE_OVM_EXEKUT
;
2005 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
2006 *type
= DBTYPE_OVM_REQ
;
2013 /* Convert ISDS dbType enum @type to UTF-8 string.
2014 * @Return pointer to static string, or NULL if unknown enum value */
2015 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2017 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2018 * of view of generic public SOAP interface. */
2019 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2020 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2021 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2022 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2023 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2024 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2025 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2026 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2027 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2028 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2029 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2030 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2031 default: return NULL
; break;
2036 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2037 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2038 if (!string
|| !type
) return IE_INVAL
;
2040 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2041 *type
= USERTYPE_PRIMARY
;
2042 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2043 *type
= USERTYPE_ENTRUSTED
;
2044 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2045 *type
= USERTYPE_ADMINISTRATOR
;
2046 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2047 *type
= USERTYPE_OFFICIAL
;
2048 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2049 *type
= USERTYPE_OFFICIAL_CERT
;
2050 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2051 *type
= USERTYPE_LIQUIDATOR
;
2058 /* Convert ISDS userType enum @type to UTF-8 string.
2059 * @Return pointer to static string, or NULL if unknown enum value */
2060 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2062 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2063 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2064 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2065 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2066 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2067 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2068 default: return NULL
; break;
2073 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2074 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2075 isds_sender_type
*type
) {
2076 if (!string
|| !type
) return IE_INVAL
;
2078 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2079 *type
= SENDERTYPE_PRIMARY
;
2080 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2081 *type
= SENDERTYPE_ENTRUSTED
;
2082 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2083 *type
= SENDERTYPE_ADMINISTRATOR
;
2084 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2085 *type
= SENDERTYPE_OFFICIAL
;
2086 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2087 *type
= SENDERTYPE_VIRTUAL
;
2088 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2089 *type
= SENDERTYPE_OFFICIAL_CERT
;
2090 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2091 *type
= SENDERTYPE_LIQUIDATOR
;
2098 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2099 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2100 isds_payment_type
*type
) {
2101 if (!string
|| !type
) return IE_INVAL
;
2103 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2104 *type
= PAYMENT_SENDER
;
2105 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2106 *type
= PAYMENT_RESPONSE
;
2107 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2108 *type
= PAYMENT_SPONSOR
;
2109 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2110 *type
= PAYMENT_SPONSOR_LIMITED
;
2111 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2112 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2113 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2114 *type
= PAYMENT_STAMP
;
2121 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2122 * ciEventType is integer but we convert it from string representation
2124 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2125 isds_credit_event_type
*type
) {
2126 if (!string
|| !type
) return IE_INVAL
;
2128 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2129 *type
= ISDS_CREDIT_CHARGED
;
2130 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2131 *type
= ISDS_CREDIT_DISCHARGED
;
2132 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2133 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2134 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2135 *type
= ISDS_CREDIT_STORAGE_SET
;
2136 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2137 *type
= ISDS_CREDIT_EXPIRED
;
2144 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2145 * @Return pointer to static string, or NULL if unknown enum value */
2146 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2148 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2149 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2150 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2151 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2152 default: return NULL
; break;
2157 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2158 * ISDSSearch2/searchType value.
2159 * @Return pointer to static string, or NULL if unknown enum value */
2160 static const xmlChar
*isds_fulltext_target2string(
2161 const isds_fulltext_target type
) {
2163 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2164 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2165 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2166 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2167 default: return NULL
; break;
2170 #endif /* HAVE_LIBCURL */
2173 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2174 * @Return IE_ENUM if @string is not valid enum member */
2175 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2176 isds_FileMetaType
*type
) {
2177 if (!string
|| !type
) return IE_INVAL
;
2179 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2180 *type
= FILEMETATYPE_MAIN
;
2181 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2182 *type
= FILEMETATYPE_ENCLOSURE
;
2183 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2184 *type
= FILEMETATYPE_SIGNATURE
;
2185 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2186 *type
= FILEMETATYPE_META
;
2193 /* Convert UTF-8 @string to ISDS hash @algorithm.
2194 * @Return IE_ENUM if @string is not valid enum member */
2195 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2196 isds_hash_algorithm
*algorithm
) {
2197 if (!string
|| !algorithm
) return IE_INVAL
;
2199 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2200 *algorithm
= HASH_ALGORITHM_MD5
;
2201 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2202 *algorithm
= HASH_ALGORITHM_SHA_1
;
2203 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2204 *algorithm
= HASH_ALGORITHM_SHA_224
;
2205 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2206 *algorithm
= HASH_ALGORITHM_SHA_256
;
2207 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2208 *algorithm
= HASH_ALGORITHM_SHA_384
;
2209 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2210 *algorithm
= HASH_ALGORITHM_SHA_512
;
2218 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2219 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2220 if (!time
|| !string
) return IE_INVAL
;
2222 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2223 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2230 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2231 * respects the @time microseconds too. */
2232 static isds_error
timeval2timestring(const struct timeval
*time
,
2235 time_t seconds_as_time_t
;
2237 if (!time
|| !string
) return IE_INVAL
;
2239 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2240 * 32-bit long in Microsoft API. Convert value to the type expected by
2242 seconds_as_time_t
= time
->tv_sec
;
2243 if (!gmtime_r(&seconds_as_time_t
, &broken
)) return IE_DATE
;
2244 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2246 /* TODO: small negative year should be formatted as "-0012". This is not
2247 * true for glibc "%04d". We should implement it.
2248 * time->tv_usec type is su_seconds_t which is required to be signed
2249 * integer to accomodate values from range [-1, 1000000].
2250 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2251 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2252 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2253 * of the range check above. */
2254 if (-1 == isds_asprintf((char **) string
,
2255 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32
,
2256 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2257 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2258 (int32_t)time
->tv_usec
))
2263 #endif /* HAVE_LIBCURL */
2266 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2267 * It respects microseconds too. Microseconds are rounded half up.
2268 * In case of error, @time will be freed. */
2269 static isds_error
timestring2timeval(const xmlChar
*string
,
2270 struct timeval
**time
) {
2272 char *offset
, *delim
, *endptr
;
2273 const int subsecond_resolution
= 6;
2274 char subseconds
[subsecond_resolution
+ 1];
2276 int offset_hours
, offset_minutes
;
2278 long int long_number
;
2283 if (!time
) return IE_INVAL
;
2289 memset(&broken
, 0, sizeof(broken
));
2292 *time
= calloc(1, sizeof(**time
));
2293 if (!*time
) return IE_NOMEM
;
2295 memset(*time
, 0, sizeof(**time
));
2299 /* xsd:date is ISO 8601 string, thus ASCII */
2300 /*TODO: negative year */
2304 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2305 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2306 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2312 broken
.tm_year
-= 1900;
2314 broken
.tm_isdst
= -1;
2315 offset
= (char*)string
+ i
;
2317 /* Parse date and time without subseconds and offset */
2318 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2325 /* Get subseconds */
2326 if (*offset
== '.' ) {
2329 /* Copy first 6 digits, pad it with zeros.
2330 * Current server implementation uses only millisecond resolution. */
2331 /* TODO: isdigit() is locale sensitive */
2333 i
< subsecond_resolution
&& isdigit(*offset
);
2335 subseconds
[i
] = *offset
;
2337 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2338 /* Check 7th digit for rounding */
2339 if (*offset
>= '5') round_up
= 1;
2342 for (; i
< subsecond_resolution
; i
++) {
2343 subseconds
[i
] = '0';
2345 subseconds
[subsecond_resolution
] = '\0';
2347 /* Convert it into integer */
2348 long_number
= strtol(subseconds
, &endptr
, 10);
2349 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2350 long_number
== LONG_MAX
) {
2354 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2355 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2356 * microseconds" and "the type shall be a signed integer capable of
2357 * storing values at least in the range [-1, 1000000]. */
2358 if (long_number
< -1 || long_number
>= 1000000) {
2362 (*time
)->tv_usec
= long_number
;
2364 /* Round the subseconds */
2366 if (999999 == (*time
)->tv_usec
) {
2367 (*time
)->tv_usec
= 0;
2374 /* move to the zone offset delimiter or signal NULL*/
2375 delim
= strchr(offset
, '-');
2377 delim
= strchr(offset
, '+');
2379 delim
= strchr(offset
, 'Z');
2383 /* Get zone offset */
2384 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2385 * "" equals to "Z" and it means UTC zone. */
2386 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2387 * colon separator */
2388 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2389 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2393 if (*offset
== '+') {
2394 broken
.tm_hour
-= offset_hours
;
2395 broken
.tm_min
-= offset_minutes
;
2397 broken
.tm_hour
+= offset_hours
;
2398 broken
.tm_min
+= offset_minutes
;
2402 /* Convert to time_t */
2403 (*time
)->tv_sec
= _isds_timegm(&broken
);
2404 if ((*time
)->tv_sec
== (time_t) -1) {
2413 /* Convert unsigned int into isds_message_status.
2414 * @context is session context
2415 * @number is pointer to number value. NULL will be treated as invalid value.
2416 * @status is automatically reallocated status
2417 * @return IE_SUCCESS, or error code and free status */
2418 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2419 const unsigned long int *number
, isds_message_status
**status
) {
2420 if (!context
) return IE_INVALID_CONTEXT
;
2421 if (!status
) return IE_INVAL
;
2423 free(*status
); *status
= NULL
;
2424 if (!number
) return IE_INVAL
;
2426 if (*number
< 1 || *number
> 10) {
2427 isds_printf_message(context
, _("Invalid message status value: %lu"),
2432 *status
= malloc(sizeof(**status
));
2433 if (!*status
) return IE_NOMEM
;
2435 **status
= 1 << *number
;
2440 /* Convert event description string into isds_event members type and
2442 * @string is raw event description starting with event prefix
2443 * @event is structure where to store type and stripped description to
2444 * @return standard error code, unknown prefix is not classified as an error.
2446 static isds_error
eventstring2event(const xmlChar
*string
,
2447 struct isds_event
* event
) {
2448 const xmlChar
*known_prefixes
[] = {
2459 const isds_event_type types
[] = {
2460 EVENT_ENTERED_SYSTEM
,
2461 EVENT_ACCEPTED_BY_RECIPIENT
,
2462 EVENT_ACCEPTED_BY_FICTION
,
2463 EVENT_UNDELIVERABLE
,
2464 EVENT_COMMERCIAL_ACCEPTED
,
2466 EVENT_PRIMARY_LOGIN
,
2467 EVENT_ENTRUSTED_LOGIN
,
2473 if (!string
|| !event
) return IE_INVAL
;
2476 event
->type
= malloc(sizeof(*event
->type
));
2477 if (!(event
->type
)) return IE_NOMEM
;
2479 zfree(event
->description
);
2481 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2483 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2485 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2486 /* Prefix is known */
2487 *event
->type
= types
[index
];
2489 /* Strip prefix from description and spaces */
2490 /* TODO: Recognize all white spaces from UCS blank class and
2491 * operate on UTF-8 chars. */
2492 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2493 event
->description
= strdup((char *) (string
+ length
));
2494 if (!(event
->description
)) return IE_NOMEM
;
2500 /* Unknown event prefix.
2501 * XSD allows any string */
2502 char *string_locale
= _isds_utf82locale((char *) string
);
2503 isds_log(ILF_ISDS
, ILL_WARNING
,
2504 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2505 free(string_locale
);
2507 *event
->type
= EVENT_UKNOWN
;
2508 event
->description
= strdup((char *) string
);
2509 if (!(event
->description
)) return IE_NOMEM
;
2515 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2516 * and leave label */
2517 #define EXTRACT_STRING(element, string) { \
2518 xmlXPathFreeObject(result); \
2519 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2520 if (NULL == (result)) { \
2524 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2525 if (result->nodesetval->nodeNr > 1) { \
2526 isds_printf_message(context, _("Multiple %s element"), element); \
2530 (string) = (char *) \
2531 xmlXPathCastNodeSetToString(result->nodesetval); \
2532 if (NULL == (string)) { \
2539 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2541 char *string = NULL; \
2542 EXTRACT_STRING(element, string); \
2545 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2546 if (!(booleanPtr)) { \
2552 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2553 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2554 *(booleanPtr) = 1; \
2555 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2556 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2557 *(booleanPtr) = 0; \
2559 char *string_locale = _isds_utf82locale((char*)string); \
2560 isds_printf_message(context, \
2561 _("%s value is not valid boolean: %s"), \
2562 element, string_locale); \
2563 free(string_locale); \
2573 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2575 char *string = NULL; \
2576 EXTRACT_STRING(element, string); \
2578 if (NULL == string) { \
2579 isds_printf_message(context, _("%s element is empty"), element); \
2583 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2584 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2586 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2587 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2590 char *string_locale = _isds_utf82locale((char*)string); \
2591 isds_printf_message(context, \
2592 _("%s value is not valid boolean: %s"), \
2593 element, string_locale); \
2594 free(string_locale); \
2603 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2605 char *string = NULL; \
2606 EXTRACT_STRING(element, string); \
2611 number = strtol((char*)string, &endptr, 10); \
2613 if (*endptr != '\0') { \
2614 char *string_locale = _isds_utf82locale((char *)string); \
2615 isds_printf_message(context, \
2616 _("%s is not valid integer: %s"), \
2617 element, string_locale); \
2618 free(string_locale); \
2624 if (number == LONG_MIN || number == LONG_MAX) { \
2625 char *string_locale = _isds_utf82locale((char *)string); \
2626 isds_printf_message(context, \
2627 _("%s value out of range of long int: %s"), \
2628 element, string_locale); \
2629 free(string_locale); \
2635 free(string); string = NULL; \
2637 if (!(preallocated)) { \
2638 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2639 if (!(longintPtr)) { \
2644 *(longintPtr) = number; \
2648 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2650 char *string = NULL; \
2651 EXTRACT_STRING(element, string); \
2656 number = strtol((char*)string, &endptr, 10); \
2658 if (*endptr != '\0') { \
2659 char *string_locale = _isds_utf82locale((char *)string); \
2660 isds_printf_message(context, \
2661 _("%s is not valid integer: %s"), \
2662 element, string_locale); \
2663 free(string_locale); \
2669 if (number == LONG_MIN || number == LONG_MAX) { \
2670 char *string_locale = _isds_utf82locale((char *)string); \
2671 isds_printf_message(context, \
2672 _("%s value out of range of long int: %s"), \
2673 element, string_locale); \
2674 free(string_locale); \
2680 free(string); string = NULL; \
2682 isds_printf_message(context, \
2683 _("%s value is negative: %ld"), element, number); \
2688 if (!(preallocated)) { \
2689 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2690 if (!(ulongintPtr)) { \
2695 *(ulongintPtr) = number; \
2699 #define EXTRACT_DATE(element, tmPtr) { \
2700 char *string = NULL; \
2701 EXTRACT_STRING(element, string); \
2702 if (NULL != string) { \
2703 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2704 if (NULL == (tmPtr)) { \
2709 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2711 if (err == IE_NOTSUP) { \
2713 char *string_locale = _isds_utf82locale(string); \
2714 char *element_locale = _isds_utf82locale(element); \
2715 isds_printf_message(context, _("Invalid %s value: %s"), \
2716 element_locale, string_locale); \
2717 free(string_locale); \
2718 free(element_locale); \
2727 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2728 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2730 if ((required) && (!string)) { \
2731 char *attribute_locale = _isds_utf82locale(attribute); \
2732 char *element_locale = \
2733 _isds_utf82locale((char *)xpath_ctx->node->name); \
2734 isds_printf_message(context, \
2735 _("Could not extract required %s attribute value from " \
2736 "%s element"), attribute_locale, element_locale); \
2737 free(element_locale); \
2738 free(attribute_locale); \
2745 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2747 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2748 (xmlChar *) (string)); \
2750 isds_printf_message(context, \
2751 _("Could not add %s child to %s element"), \
2752 element, (parent)->name); \
2758 #define INSERT_STRING(parent, element, string) \
2759 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2761 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2763 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2764 else { INSERT_STRING(parent, element, "false"); } \
2767 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2770 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2772 INSERT_STRING(parent, element, NULL); \
2776 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2777 if ((longintPtr)) { \
2778 /* FIXME: locale sensitive */ \
2779 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2783 INSERT_STRING(parent, element, buffer) \
2784 free(buffer); (buffer) = NULL; \
2785 } else { INSERT_STRING(parent, element, NULL) } \
2788 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2789 if ((ulongintPtr)) { \
2790 /* FIXME: locale sensitive */ \
2791 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2795 INSERT_STRING(parent, element, buffer) \
2796 free(buffer); (buffer) = NULL; \
2797 } else { INSERT_STRING(parent, element, NULL) } \
2800 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2802 /* FIXME: locale sensitive */ \
2803 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2807 INSERT_STRING(parent, element, buffer) \
2808 free(buffer); (buffer) = NULL; \
2811 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2813 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2815 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2816 (xmlChar *) (string)); \
2817 if (!attribute_node) { \
2818 isds_printf_message(context, _("Could not add %s " \
2819 "attribute to %s element"), \
2820 (attribute), (parent)->name); \
2826 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2828 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2829 if (length > (maximum)) { \
2830 isds_printf_message(context, \
2831 ngettext("%s has more than %d characters", \
2832 "%s has more than %d characters", (maximum)), \
2833 (name), (maximum)); \
2837 if (length < (minimum)) { \
2838 isds_printf_message(context, \
2839 ngettext("%s has less than %d characters", \
2840 "%s has less than %d characters", (minimum)), \
2841 (name), (minimum)); \
2848 #define INSERT_ELEMENT(child, parent, element) \
2850 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2852 isds_printf_message(context, \
2853 _("Could not add %s child to %s element"), \
2854 (element), (parent)->name); \
2861 /* Find child element by name in given XPath context and switch context onto
2862 * it. The child must be uniq and must exist. Otherwise fails.
2863 * @context is ISDS context
2864 * @child is child element name
2865 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2866 * into it child. In error case, the @xpath_ctx keeps original value. */
2867 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2868 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2869 isds_error err
= IE_SUCCESS
;
2870 xmlXPathObjectPtr result
= NULL
;
2872 if (!context
) return IE_INVALID_CONTEXT
;
2873 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2876 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2883 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2884 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2885 char *child_locale
= _isds_utf82locale((char*) child
);
2886 isds_printf_message(context
,
2887 _("%s element does not contain %s child"),
2888 parent_locale
, child_locale
);
2890 free(parent_locale
);
2896 if (result
->nodesetval
->nodeNr
> 1) {
2897 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2898 char *child_locale
= _isds_utf82locale((char*) child
);
2899 isds_printf_message(context
,
2900 _("%s element contains multiple %s children"),
2901 parent_locale
, child_locale
);
2903 free(parent_locale
);
2908 /* Switch context */
2909 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2912 xmlXPathFreeObject(result
);
2919 /* Find and convert XSD:gPersonName group in current node into structure
2920 * @context is ISDS context
2921 * @personName is automatically reallocated person name structure. If no member
2922 * value is found, will be freed.
2923 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2925 * In case of error @personName will be freed. */
2926 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2927 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2928 isds_error err
= IE_SUCCESS
;
2929 xmlXPathObjectPtr result
= NULL
;
2931 if (!context
) return IE_INVALID_CONTEXT
;
2932 if (!personName
) return IE_INVAL
;
2933 isds_PersonName_free(personName
);
2934 if (!xpath_ctx
) return IE_INVAL
;
2937 *personName
= calloc(1, sizeof(**personName
));
2943 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2944 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2945 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2946 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2948 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2949 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2950 isds_PersonName_free(personName
);
2953 if (err
) isds_PersonName_free(personName
);
2954 xmlXPathFreeObject(result
);
2959 /* Find and convert XSD:gAddress group in current node into structure
2960 * @context is ISDS context
2961 * @address is automatically reallocated address structure. If no member
2962 * value is found, will be freed.
2963 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2965 * In case of error @address will be freed. */
2966 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2967 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2968 isds_error err
= IE_SUCCESS
;
2969 xmlXPathObjectPtr result
= NULL
;
2971 if (!context
) return IE_INVALID_CONTEXT
;
2972 if (!address
) return IE_INVAL
;
2973 isds_Address_free(address
);
2974 if (!xpath_ctx
) return IE_INVAL
;
2977 *address
= calloc(1, sizeof(**address
));
2983 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2984 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2985 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2986 EXTRACT_STRING("isds:adNumberInMunicipality",
2987 (*address
)->adNumberInMunicipality
);
2988 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2989 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2991 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2992 !(*address
)->adNumberInStreet
&&
2993 !(*address
)->adNumberInMunicipality
&&
2994 !(*address
)->adZipCode
&& !(*address
)->adState
)
2995 isds_Address_free(address
);
2998 if (err
) isds_Address_free(address
);
2999 xmlXPathFreeObject(result
);
3004 /* Find and convert isds:biDate element in current node into structure
3005 * @context is ISDS context
3006 * @biDate is automatically reallocated birth date structure. If no member
3007 * value is found, will be freed.
3008 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3010 * In case of error @biDate will be freed. */
3011 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3012 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3013 isds_error err
= IE_SUCCESS
;
3014 xmlXPathObjectPtr result
= NULL
;
3015 char *string
= NULL
;
3017 if (!context
) return IE_INVALID_CONTEXT
;
3018 if (!biDate
) return IE_INVAL
;
3020 if (!xpath_ctx
) return IE_INVAL
;
3022 EXTRACT_STRING("isds:biDate", string
);
3024 *biDate
= calloc(1, sizeof(**biDate
));
3029 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3031 if (err
== IE_NOTSUP
) {
3033 char *string_locale
= _isds_utf82locale(string
);
3034 isds_printf_message(context
,
3035 _("Invalid isds:biDate value: %s"), string_locale
);
3036 free(string_locale
);
3043 if (err
) zfree(*biDate
);
3045 xmlXPathFreeObject(result
);
3050 /* Convert isds:dBOwnerInfo XML tree into structure
3051 * @context is ISDS context
3052 * @db_owner_info is automatically reallocated box owner info structure
3053 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3054 * In case of error @db_owner_info will be freed. */
3055 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3056 struct isds_DbOwnerInfo
**db_owner_info
,
3057 xmlXPathContextPtr xpath_ctx
) {
3058 isds_error err
= IE_SUCCESS
;
3059 xmlXPathObjectPtr result
= NULL
;
3060 char *string
= NULL
;
3062 if (!context
) return IE_INVALID_CONTEXT
;
3063 if (!db_owner_info
) return IE_INVAL
;
3064 isds_DbOwnerInfo_free(db_owner_info
);
3065 if (!xpath_ctx
) return IE_INVAL
;
3068 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3069 if (!*db_owner_info
) {
3074 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3076 EXTRACT_STRING("isds:dbType", string
);
3078 (*db_owner_info
)->dbType
=
3079 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3080 if (!(*db_owner_info
)->dbType
) {
3084 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3086 zfree((*db_owner_info
)->dbType
);
3087 if (err
== IE_ENUM
) {
3089 char *string_locale
= _isds_utf82locale(string
);
3090 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3092 free(string_locale
);
3099 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3101 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3103 if (err
) goto leave
;
3105 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3107 (*db_owner_info
)->birthInfo
=
3108 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3109 if (!(*db_owner_info
)->birthInfo
) {
3113 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3115 if (err
) goto leave
;
3116 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3117 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3118 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3119 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3120 !(*db_owner_info
)->birthInfo
->biCity
&&
3121 !(*db_owner_info
)->birthInfo
->biCounty
&&
3122 !(*db_owner_info
)->birthInfo
->biState
)
3123 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3125 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3126 if (err
) goto leave
;
3128 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3129 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3130 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3131 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3132 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3134 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3136 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3137 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3138 (*db_owner_info
)->dbOpenAddressing
);
3141 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3143 xmlXPathFreeObject(result
);
3148 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3149 * @context is session context
3150 * @owner is libisds structure with box description
3151 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3152 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3153 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3155 isds_error err
= IE_SUCCESS
;
3157 xmlChar
*string
= NULL
;
3159 if (!context
) return IE_INVALID_CONTEXT
;
3160 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3163 /* Build XSD:tDbOwnerInfo */
3164 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3165 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3168 if (owner
->dbType
) {
3169 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3171 isds_printf_message(context
, _("Invalid dbType value: %d"),
3176 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3178 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3179 if (owner
->personName
) {
3180 INSERT_STRING(db_owner_info
, "pnFirstName",
3181 owner
->personName
->pnFirstName
);
3182 INSERT_STRING(db_owner_info
, "pnMiddleName",
3183 owner
->personName
->pnMiddleName
);
3184 INSERT_STRING(db_owner_info
, "pnLastName",
3185 owner
->personName
->pnLastName
);
3186 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3187 owner
->personName
->pnLastNameAtBirth
);
3189 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3190 if (owner
->birthInfo
) {
3191 if (owner
->birthInfo
->biDate
) {
3192 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3193 INSERT_STRING(db_owner_info
, "biDate", string
);
3194 free(string
); string
= NULL
;
3196 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3197 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3198 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3200 if (owner
->address
) {
3201 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3202 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3203 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3204 owner
->address
->adNumberInStreet
);
3205 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3206 owner
->address
->adNumberInMunicipality
);
3207 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3208 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3210 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3211 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3212 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3214 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3215 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3217 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3218 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3220 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3222 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3223 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3224 owner
->dbOpenAddressing
);
3232 /* Convert XSD:tDbUserInfo XML tree into structure
3233 * @context is ISDS context
3234 * @db_user_info is automatically reallocated user info structure
3235 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3236 * In case of error @db_user_info will be freed. */
3237 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3238 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3239 isds_error err
= IE_SUCCESS
;
3240 xmlXPathObjectPtr result
= NULL
;
3241 char *string
= NULL
;
3243 if (!context
) return IE_INVALID_CONTEXT
;
3244 if (!db_user_info
) return IE_INVAL
;
3245 isds_DbUserInfo_free(db_user_info
);
3246 if (!xpath_ctx
) return IE_INVAL
;
3249 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3250 if (!*db_user_info
) {
3255 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info
)->aifo_ticket
, 0);
3257 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3259 EXTRACT_STRING("isds:userType", string
);
3261 (*db_user_info
)->userType
=
3262 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3263 if (!(*db_user_info
)->userType
) {
3267 err
= string2isds_UserType((xmlChar
*)string
,
3268 (*db_user_info
)->userType
);
3270 zfree((*db_user_info
)->userType
);
3271 if (err
== IE_ENUM
) {
3273 char *string_locale
= _isds_utf82locale(string
);
3274 isds_printf_message(context
,
3275 _("Unknown isds:userType value: %s"), string_locale
);
3276 free(string_locale
);
3283 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3285 (*db_user_info
)->personName
=
3286 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3287 if (!(*db_user_info
)->personName
) {
3292 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3294 if (err
) goto leave
;
3296 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3297 if (err
) goto leave
;
3299 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3300 if (err
) goto leave
;
3302 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3303 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3305 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3306 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3307 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3309 /* ???: Default value is "CZ" according specification. Should we provide
3311 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3314 if (err
) isds_DbUserInfo_free(db_user_info
);
3316 xmlXPathFreeObject(result
);
3321 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3322 * @context is session context
3323 * @user is libisds structure with user description
3324 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3325 * @db_user_info is XML element of XSD:tDbUserInfo */
3326 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3327 const struct isds_DbUserInfo
*user
, _Bool honor_aifo_ticket
,
3328 xmlNodePtr db_user_info
) {
3330 isds_error err
= IE_SUCCESS
;
3332 xmlAttrPtr attribute_node
;
3333 xmlChar
*string
= NULL
;
3335 if (!context
) return IE_INVALID_CONTEXT
;
3336 if (!user
|| !db_user_info
) return IE_INVAL
;
3338 /* Build XSD:tDbUserInfo */
3340 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3341 * allow it everywhere. */
3342 if (honor_aifo_ticket
&& user
->aifo_ticket
) {
3343 INSERT_STRING_ATTRIBUTE(db_user_info
, "AIFOTicket", user
->aifo_ticket
);
3346 if (user
->personName
) {
3347 INSERT_STRING(db_user_info
, "pnFirstName",
3348 user
->personName
->pnFirstName
);
3349 INSERT_STRING(db_user_info
, "pnMiddleName",
3350 user
->personName
->pnMiddleName
);
3351 INSERT_STRING(db_user_info
, "pnLastName",
3352 user
->personName
->pnLastName
);
3353 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3354 user
->personName
->pnLastNameAtBirth
);
3356 if (user
->address
) {
3357 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3358 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3359 INSERT_STRING(db_user_info
, "adNumberInStreet",
3360 user
->address
->adNumberInStreet
);
3361 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3362 user
->address
->adNumberInMunicipality
);
3363 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3364 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3367 if (!tm2datestring(user
->biDate
, &string
))
3368 INSERT_STRING(db_user_info
, "biDate", string
);
3371 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3372 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3375 if (user
->userType
) {
3376 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3378 isds_printf_message(context
, _("Invalid userType value: %d"),
3383 INSERT_STRING(db_user_info
, "userType", type_string
);
3386 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3387 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3388 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3389 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3390 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3391 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3392 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3393 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3394 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3402 /* Convert XSD:tPDZRec XML tree into structure
3403 * @context is ISDS context
3404 * @permission is automatically reallocated commercial permission structure
3405 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3406 * In case of error @permission will be freed. */
3407 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3408 struct isds_commercial_permission
**permission
,
3409 xmlXPathContextPtr xpath_ctx
) {
3410 isds_error err
= IE_SUCCESS
;
3411 xmlXPathObjectPtr result
= NULL
;
3412 char *string
= NULL
;
3414 if (!context
) return IE_INVALID_CONTEXT
;
3415 if (!permission
) return IE_INVAL
;
3416 isds_commercial_permission_free(permission
);
3417 if (!xpath_ctx
) return IE_INVAL
;
3420 *permission
= calloc(1, sizeof(**permission
));
3426 EXTRACT_STRING("isds:PDZType", string
);
3428 err
= string2isds_payment_type((xmlChar
*)string
,
3429 &(*permission
)->type
);
3431 if (err
== IE_ENUM
) {
3433 char *string_locale
= _isds_utf82locale(string
);
3434 isds_printf_message(context
,
3435 _("Unknown isds:PDZType value: %s"), string_locale
);
3436 free(string_locale
);
3443 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3444 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3446 EXTRACT_STRING("isds:PDZExpire", string
);
3448 err
= timestring2timeval((xmlChar
*) string
,
3449 &((*permission
)->expiration
));
3451 char *string_locale
= _isds_utf82locale(string
);
3452 if (err
== IE_DATE
) err
= IE_ISDS
;
3453 isds_printf_message(context
,
3454 _("Could not convert PDZExpire as ISO time: %s"),
3456 free(string_locale
);
3462 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3463 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3466 if (err
) isds_commercial_permission_free(permission
);
3468 xmlXPathFreeObject(result
);
3473 /* Convert XSD:tCiRecord XML tree into structure
3474 * @context is ISDS context
3475 * @event is automatically reallocated commercial credit event structure
3476 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3477 * In case of error @event will be freed. */
3478 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3479 struct isds_credit_event
**event
,
3480 xmlXPathContextPtr xpath_ctx
) {
3481 isds_error err
= IE_SUCCESS
;
3482 xmlXPathObjectPtr result
= NULL
;
3483 char *string
= NULL
;
3484 long int *number_ptr
;
3486 if (!context
) return IE_INVALID_CONTEXT
;
3487 if (!event
) return IE_INVAL
;
3488 isds_credit_event_free(event
);
3489 if (!xpath_ctx
) return IE_INVAL
;
3492 *event
= calloc(1, sizeof(**event
));
3498 EXTRACT_STRING("isds:ciEventTime", string
);
3500 err
= timestring2timeval((xmlChar
*) string
,
3503 char *string_locale
= _isds_utf82locale(string
);
3504 if (err
== IE_DATE
) err
= IE_ISDS
;
3505 isds_printf_message(context
,
3506 _("Could not convert ciEventTime as ISO time: %s"),
3508 free(string_locale
);
3514 EXTRACT_STRING("isds:ciEventType", string
);
3516 err
= string2isds_credit_event_type((xmlChar
*)string
,
3519 if (err
== IE_ENUM
) {
3521 char *string_locale
= _isds_utf82locale(string
);
3522 isds_printf_message(context
,
3523 _("Unknown isds:ciEventType value: %s"), string_locale
);
3524 free(string_locale
);
3531 number_ptr
= &((*event
)->credit_change
);
3532 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3533 number_ptr
= &(*event
)->new_credit
;
3534 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3536 switch((*event
)->type
) {
3537 case ISDS_CREDIT_CHARGED
:
3538 EXTRACT_STRING("isds:ciTransID",
3539 (*event
)->details
.charged
.transaction
);
3541 case ISDS_CREDIT_DISCHARGED
:
3542 EXTRACT_STRING("isds:ciTransID",
3543 (*event
)->details
.discharged
.transaction
);
3545 case ISDS_CREDIT_MESSAGE_SENT
:
3546 EXTRACT_STRING("isds:ciRecipientID",
3547 (*event
)->details
.message_sent
.recipient
);
3548 EXTRACT_STRING("isds:ciPDZID",
3549 (*event
)->details
.message_sent
.message_id
);
3551 case ISDS_CREDIT_STORAGE_SET
:
3552 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3553 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3554 EXTRACT_DATE("isds:ciNewFrom",
3555 (*event
)->details
.storage_set
.new_valid_from
);
3556 EXTRACT_DATE("isds:ciNewTo",
3557 (*event
)->details
.storage_set
.new_valid_to
);
3558 EXTRACT_LONGINT("isds:ciOldCapacity",
3559 (*event
)->details
.storage_set
.old_capacity
, 0);
3560 EXTRACT_DATE("isds:ciOldFrom",
3561 (*event
)->details
.storage_set
.old_valid_from
);
3562 EXTRACT_DATE("isds:ciOldTo",
3563 (*event
)->details
.storage_set
.old_valid_to
);
3564 EXTRACT_STRING("isds:ciDoneBy",
3565 (*event
)->details
.storage_set
.initiator
);
3567 case ISDS_CREDIT_EXPIRED
:
3572 if (err
) isds_credit_event_free(event
);
3574 xmlXPathFreeObject(result
);
3579 #endif /* HAVE_LIBCURL */
3582 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3583 * isds_envelope structure. The envelope is automatically allocated but not
3584 * reallocated. The date are just appended into envelope structure.
3585 * @context is ISDS context
3586 * @envelope is automatically allocated message envelope structure
3587 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3588 * In case of error @envelope will be freed. */
3589 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3590 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3591 isds_error err
= IE_SUCCESS
;
3592 xmlXPathObjectPtr result
= NULL
;
3594 if (!context
) return IE_INVALID_CONTEXT
;
3595 if (!envelope
) return IE_INVAL
;
3596 if (!xpath_ctx
) return IE_INVAL
;
3600 /* Allocate envelope */
3601 *envelope
= calloc(1, sizeof(**envelope
));
3607 /* Else free former data */
3608 zfree((*envelope
)->dmSenderOrgUnit
);
3609 zfree((*envelope
)->dmSenderOrgUnitNum
);
3610 zfree((*envelope
)->dbIDRecipient
);
3611 zfree((*envelope
)->dmRecipientOrgUnit
);
3612 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3613 zfree((*envelope
)->dmToHands
);
3614 zfree((*envelope
)->dmAnnotation
);
3615 zfree((*envelope
)->dmRecipientRefNumber
);
3616 zfree((*envelope
)->dmSenderRefNumber
);
3617 zfree((*envelope
)->dmRecipientIdent
);
3618 zfree((*envelope
)->dmSenderIdent
);
3619 zfree((*envelope
)->dmLegalTitleLaw
);
3620 zfree((*envelope
)->dmLegalTitleYear
);
3621 zfree((*envelope
)->dmLegalTitleSect
);
3622 zfree((*envelope
)->dmLegalTitlePar
);
3623 zfree((*envelope
)->dmLegalTitlePoint
);
3624 zfree((*envelope
)->dmPersonalDelivery
);
3625 zfree((*envelope
)->dmAllowSubstDelivery
);
3628 /* Extract envelope elements added by sender or ISDS
3629 * (XSD: gMessageEnvelopeSub type) */
3630 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3631 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3632 (*envelope
)->dmSenderOrgUnitNum
, 0);
3633 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3634 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3635 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3636 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3637 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3638 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3639 EXTRACT_STRING("isds:dmRecipientRefNumber",
3640 (*envelope
)->dmRecipientRefNumber
);
3641 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3642 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3643 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3645 /* Extract envelope elements regarding law reference */
3646 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3647 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3648 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3649 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3650 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3652 /* Extract envelope other elements */
3653 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3654 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3655 (*envelope
)->dmAllowSubstDelivery
);
3658 if (err
) isds_envelope_free(envelope
);
3659 xmlXPathFreeObject(result
);
3665 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3666 * isds_envelope structure. The envelope is automatically allocated but not
3667 * reallocated. The date are just appended into envelope structure.
3668 * @context is ISDS context
3669 * @envelope is automatically allocated message envelope structure
3670 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3671 * In case of error @envelope will be freed. */
3672 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3673 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3674 isds_error err
= IE_SUCCESS
;
3675 xmlXPathObjectPtr result
= NULL
;
3677 if (!context
) return IE_INVALID_CONTEXT
;
3678 if (!envelope
) return IE_INVAL
;
3679 if (!xpath_ctx
) return IE_INVAL
;
3683 /* Allocate envelope */
3684 *envelope
= calloc(1, sizeof(**envelope
));
3690 /* Else free former data */
3691 zfree((*envelope
)->dmID
);
3692 zfree((*envelope
)->dbIDSender
);
3693 zfree((*envelope
)->dmSender
);
3694 zfree((*envelope
)->dmSenderAddress
);
3695 zfree((*envelope
)->dmSenderType
);
3696 zfree((*envelope
)->dmRecipient
);
3697 zfree((*envelope
)->dmRecipientAddress
);
3698 zfree((*envelope
)->dmAmbiguousRecipient
);
3701 /* Extract envelope elements added by ISDS
3702 * (XSD: gMessageEnvelope type) */
3703 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3704 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3705 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3706 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3707 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3708 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3709 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3710 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3711 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3712 (*envelope
)->dmAmbiguousRecipient
);
3714 /* Extract envelope elements added by sender and ISDS
3715 * (XSD: gMessageEnvelope type) */
3716 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3717 if (err
) goto leave
;
3720 if (err
) isds_envelope_free(envelope
);
3721 xmlXPathFreeObject(result
);
3726 /* Convert other envelope elements from XML tree into isds_envelope structure:
3727 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3728 * The envelope is automatically allocated but not reallocated.
3729 * The data are just appended into envelope structure.
3730 * @context is ISDS context
3731 * @envelope is automatically allocated message envelope structure
3732 * @xpath_ctx is XPath context with current node as parent desired elements
3733 * In case of error @envelope will be freed. */
3734 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3735 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3736 isds_error err
= IE_SUCCESS
;
3737 xmlXPathObjectPtr result
= NULL
;
3738 char *string
= NULL
;
3739 unsigned long int *unumber
= NULL
;
3741 if (!context
) return IE_INVALID_CONTEXT
;
3742 if (!envelope
) return IE_INVAL
;
3743 if (!xpath_ctx
) return IE_INVAL
;
3748 *envelope
= calloc(1, sizeof(**envelope
));
3755 zfree((*envelope
)->dmMessageStatus
);
3756 zfree((*envelope
)->dmAttachmentSize
);
3757 zfree((*envelope
)->dmDeliveryTime
);
3758 zfree((*envelope
)->dmAcceptanceTime
);
3762 /* dmMessageStatus element is mandatory */
3763 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3765 isds_log_message(context
,
3766 _("Missing mandatory sisds:dmMessageStatus integer"));
3770 err
= uint2isds_message_status(context
, unumber
,
3771 &((*envelope
)->dmMessageStatus
));
3773 if (err
== IE_ENUM
) err
= IE_ISDS
;
3776 free(unumber
); unumber
= NULL
;
3778 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3781 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3783 err
= timestring2timeval((xmlChar
*) string
,
3784 &((*envelope
)->dmDeliveryTime
));
3786 char *string_locale
= _isds_utf82locale(string
);
3787 if (err
== IE_DATE
) err
= IE_ISDS
;
3788 isds_printf_message(context
,
3789 _("Could not convert dmDeliveryTime as ISO time: %s"),
3791 free(string_locale
);
3797 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3799 err
= timestring2timeval((xmlChar
*) string
,
3800 &((*envelope
)->dmAcceptanceTime
));
3802 char *string_locale
= _isds_utf82locale(string
);
3803 if (err
== IE_DATE
) err
= IE_ISDS
;
3804 isds_printf_message(context
,
3805 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3807 free(string_locale
);
3814 if (err
) isds_envelope_free(envelope
);
3817 xmlXPathFreeObject(result
);
3822 /* Convert message type attribute of current element into isds_envelope
3824 * TODO: This function can be incorporated into append_status_size_times() as
3825 * they are called always together.
3826 * The envelope is automatically allocated but not reallocated.
3827 * The data are just appended into envelope structure.
3828 * @context is ISDS context
3829 * @envelope is automatically allocated message envelope structure
3830 * @xpath_ctx is XPath context with current node as parent of attribute
3831 * carrying message type
3832 * In case of error @envelope will be freed. */
3833 static isds_error
append_message_type(struct isds_ctx
*context
,
3834 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3835 isds_error err
= IE_SUCCESS
;
3837 if (!context
) return IE_INVALID_CONTEXT
;
3838 if (!envelope
) return IE_INVAL
;
3839 if (!xpath_ctx
) return IE_INVAL
;
3844 *envelope
= calloc(1, sizeof(**envelope
));
3851 zfree((*envelope
)->dmType
);
3855 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3857 if (!(*envelope
)->dmType
) {
3858 /* Use default value */
3859 (*envelope
)->dmType
= strdup("V");
3860 if (!(*envelope
)->dmType
) {
3864 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3865 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3866 isds_printf_message(context
,
3867 _("Message type in dmType attribute is not 1 character long: "
3876 if (err
) isds_envelope_free(envelope
);
3882 /* Convert dmType isds_envelope member into XML attribute and append it to
3884 * @context is ISDS context
3885 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3886 * @dm_envelope is XML element the resulting attribute will be appended to.
3887 * @return error code, in case of error context' message is filled. */
3888 static isds_error
insert_message_type(struct isds_ctx
*context
,
3889 const char *type
, xmlNodePtr dm_envelope
) {
3890 isds_error err
= IE_SUCCESS
;
3891 xmlAttrPtr attribute_node
;
3893 if (!context
) return IE_INVALID_CONTEXT
;
3894 if (!dm_envelope
) return IE_INVAL
;
3896 /* Insert optional message type */
3898 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3899 char *type_locale
= _isds_utf82locale(type
);
3900 isds_printf_message(context
,
3901 _("Message type in envelope is not 1 character long: %s"),
3907 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3913 #endif /* HAVE_LIBCURL */
3916 /* Extract message document into reallocated document structure
3917 * @context is ISDS context
3918 * @document is automatically reallocated message documents structure
3919 * @xpath_ctx is XPath context with current node as isds:dmFile
3920 * In case of error @document will be freed. */
3921 static isds_error
extract_document(struct isds_ctx
*context
,
3922 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3923 isds_error err
= IE_SUCCESS
;
3924 xmlXPathObjectPtr result
= NULL
;
3925 xmlNodePtr file_node
;
3926 char *string
= NULL
;
3928 if (!context
) return IE_INVALID_CONTEXT
;
3929 if (!document
) return IE_INVAL
;
3930 isds_document_free(document
);
3931 if (!xpath_ctx
) return IE_INVAL
;
3932 file_node
= xpath_ctx
->node
;
3934 *document
= calloc(1, sizeof(**document
));
3940 /* Extract document meta data */
3941 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3942 if (context
->normalize_mime_type
) {
3943 const char *normalized_type
=
3944 isds_normalize_mime_type((*document
)->dmMimeType
);
3945 if (NULL
!= normalized_type
&&
3946 normalized_type
!= (*document
)->dmMimeType
) {
3947 char *new_type
= strdup(normalized_type
);
3948 if (NULL
== new_type
) {
3949 isds_printf_message(context
,
3950 _("Not enough memory to normalize document MIME type"));
3954 free((*document
)->dmMimeType
);
3955 (*document
)->dmMimeType
= new_type
;
3959 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3960 err
= string2isds_FileMetaType((xmlChar
*)string
,
3961 &((*document
)->dmFileMetaType
));
3963 char *meta_type_locale
= _isds_utf82locale(string
);
3964 isds_printf_message(context
,
3965 _("Document has invalid dmFileMetaType attribute value: %s"),
3967 free(meta_type_locale
);
3973 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3974 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3975 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3976 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3979 /* Extract document data.
3980 * Base64 encoded blob or XML subtree must be presented. */
3982 /* Check for dmEncodedContent */
3983 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3990 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3991 /* Here we have Base64 blob */
3992 (*document
)->is_xml
= 0;
3994 if (result
->nodesetval
->nodeNr
> 1) {
3995 isds_printf_message(context
,
3996 _("Document has more dmEncodedContent elements"));
4001 xmlXPathFreeObject(result
); result
= NULL
;
4002 EXTRACT_STRING("isds:dmEncodedContent", string
);
4004 /* Decode non-empty document */
4005 if (string
&& string
[0] != '\0') {
4006 (*document
)->data_length
=
4007 _isds_b64decode(string
, &((*document
)->data
));
4008 if ((*document
)->data_length
== (size_t) -1) {
4009 isds_printf_message(context
,
4010 _("Error while Base64-decoding document content"));
4016 /* No Base64 blob, try XML document */
4017 xmlXPathFreeObject(result
); result
= NULL
;
4018 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4025 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4026 /* Here we have XML document */
4027 (*document
)->is_xml
= 1;
4029 if (result
->nodesetval
->nodeNr
> 1) {
4030 isds_printf_message(context
,
4031 _("Document has more dmXMLContent elements"));
4036 /* XXX: We cannot serialize the content simply because:
4037 * - XML document may point out of its scope (e.g. to message
4039 * - isds:dmXMLContent can contain more elements, no element,
4041 * - it's not the XML way
4042 * Thus we provide the only right solution: XML DOM. Let's
4043 * application to cope with this hot potato :) */
4044 (*document
)->xml_node_list
=
4045 result
->nodesetval
->nodeTab
[0]->children
;
4047 /* No base64 blob, nor XML document */
4048 isds_printf_message(context
,
4049 _("Document has no dmEncodedContent, nor dmXMLContent "
4058 if (err
) isds_document_free(document
);
4060 xmlXPathFreeObject(result
);
4061 xpath_ctx
->node
= file_node
;
4067 /* Extract message documents into reallocated list of documents
4068 * @context is ISDS context
4069 * @documents is automatically reallocated message documents list structure
4070 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4071 * In case of error @documents will be freed. */
4072 static isds_error
extract_documents(struct isds_ctx
*context
,
4073 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4074 isds_error err
= IE_SUCCESS
;
4075 xmlXPathObjectPtr result
= NULL
;
4076 xmlNodePtr files_node
;
4077 struct isds_list
*document
, *prev_document
= NULL
;
4079 if (!context
) return IE_INVALID_CONTEXT
;
4080 if (!documents
) return IE_INVAL
;
4081 isds_list_free(documents
);
4082 if (!xpath_ctx
) return IE_INVAL
;
4083 files_node
= xpath_ctx
->node
;
4085 /* Find documents */
4086 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4093 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4094 isds_printf_message(context
,
4095 _("Message does not contain any document"));
4101 /* Iterate over documents */
4102 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4104 /* Allocate and append list item */
4105 document
= calloc(1, sizeof(*document
));
4110 document
->destructor
= (void (*)(void **))isds_document_free
;
4111 if (i
== 0) *documents
= document
;
4112 else prev_document
->next
= document
;
4113 prev_document
= document
;
4115 /* Extract document */
4116 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4117 err
= extract_document(context
,
4118 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4119 if (err
) goto leave
;
4124 if (err
) isds_list_free(documents
);
4125 xmlXPathFreeObject(result
);
4126 xpath_ctx
->node
= files_node
;
4132 /* Convert isds:dmRecord XML tree into structure
4133 * @context is ISDS context
4134 * @envelope is automatically reallocated message envelope structure
4135 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4136 * In case of error @envelope will be freed. */
4137 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4138 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4139 isds_error err
= IE_SUCCESS
;
4140 xmlXPathObjectPtr result
= NULL
;
4142 if (!context
) return IE_INVALID_CONTEXT
;
4143 if (!envelope
) return IE_INVAL
;
4144 isds_envelope_free(envelope
);
4145 if (!xpath_ctx
) return IE_INVAL
;
4148 *envelope
= calloc(1, sizeof(**envelope
));
4155 /* Extract tRecord data */
4156 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4158 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4159 * dmAcceptanceTime. */
4160 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4161 if (err
) goto leave
;
4163 /* Extract envelope elements added by sender and ISDS
4164 * (XSD: gMessageEnvelope type) */
4165 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4166 if (err
) goto leave
;
4168 /* Get message type */
4169 err
= append_message_type(context
, envelope
, xpath_ctx
);
4170 if (err
) goto leave
;
4174 if (err
) isds_envelope_free(envelope
);
4175 xmlXPathFreeObject(result
);
4180 /* Convert XSD:tStateChangesRecord type XML tree into structure
4181 * @context is ISDS context
4182 * @changed_status is automatically reallocated message state change structure
4183 * @xpath_ctx is XPath context with current node as element of
4184 * XSD:tStateChangesRecord type
4185 * In case of error @changed_status will be freed. */
4186 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4187 struct isds_message_status_change
**changed_status
,
4188 xmlXPathContextPtr xpath_ctx
) {
4189 isds_error err
= IE_SUCCESS
;
4190 xmlXPathObjectPtr result
= NULL
;
4191 unsigned long int *unumber
= NULL
;
4192 char *string
= NULL
;
4194 if (!context
) return IE_INVALID_CONTEXT
;
4195 if (!changed_status
) return IE_INVAL
;
4196 isds_message_status_change_free(changed_status
);
4197 if (!xpath_ctx
) return IE_INVAL
;
4200 *changed_status
= calloc(1, sizeof(**changed_status
));
4201 if (!*changed_status
) {
4207 /* Extract tGetStateChangesInput data */
4208 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4210 /* dmEventTime is mandatory */
4211 EXTRACT_STRING("isds:dmEventTime", string
);
4213 err
= timestring2timeval((xmlChar
*) string
,
4214 &((*changed_status
)->time
));
4216 char *string_locale
= _isds_utf82locale(string
);
4217 if (err
== IE_DATE
) err
= IE_ISDS
;
4218 isds_printf_message(context
,
4219 _("Could not convert dmEventTime as ISO time: %s"),
4221 free(string_locale
);
4227 /* dmMessageStatus element is mandatory */
4228 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4230 isds_log_message(context
,
4231 _("Missing mandatory isds:dmMessageStatus integer"));
4235 err
= uint2isds_message_status(context
, unumber
,
4236 &((*changed_status
)->dmMessageStatus
));
4238 if (err
== IE_ENUM
) err
= IE_ISDS
;
4247 if (err
) isds_message_status_change_free(changed_status
);
4248 xmlXPathFreeObject(result
);
4251 #endif /* HAVE_LIBCURL */
4254 /* Find and convert isds:dmHash XML tree into structure
4255 * @context is ISDS context
4256 * @envelope is automatically reallocated message hash structure
4257 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4258 * In case of error @hash will be freed. */
4259 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4260 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4261 isds_error err
= IE_SUCCESS
;
4262 xmlNodePtr old_ctx_node
;
4263 xmlXPathObjectPtr result
= NULL
;
4264 char *string
= NULL
;
4266 if (!context
) return IE_INVALID_CONTEXT
;
4267 if (!hash
) return IE_INVAL
;
4268 isds_hash_free(hash
);
4269 if (!xpath_ctx
) return IE_INVAL
;
4271 old_ctx_node
= xpath_ctx
->node
;
4273 *hash
= calloc(1, sizeof(**hash
));
4280 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4281 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4290 /* Get hash algorithm */
4291 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4292 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4294 if (err
== IE_ENUM
) {
4295 char *string_locale
= _isds_utf82locale(string
);
4296 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4298 free(string_locale
);
4304 /* Get hash value */
4305 EXTRACT_STRING(".", string
);
4307 isds_printf_message(context
,
4308 _("sisds:dmHash element is missing hash value"));
4312 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4313 if ((*hash
)->length
== (size_t) -1) {
4314 isds_printf_message(context
,
4315 _("Error while Base64-decoding hash value"));
4321 if (err
) isds_hash_free(hash
);
4323 xmlXPathFreeObject(result
);
4324 xpath_ctx
->node
= old_ctx_node
;
4329 /* Find and append isds:dmQTimestamp XML tree into envelope.
4330 * Because one service is allowed to miss time-stamp content, and we think
4331 * other could too (flaw in specification), this function is deliberated and
4332 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4333 * @context is ISDS context
4334 * @envelope is automatically allocated envelope structure
4335 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4337 * In case of error @envelope will be freed. */
4338 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4339 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4340 isds_error err
= IE_SUCCESS
;
4341 xmlXPathObjectPtr result
= NULL
;
4342 char *string
= NULL
;
4344 if (!context
) return IE_INVALID_CONTEXT
;
4345 if (!envelope
) return IE_INVAL
;
4347 isds_envelope_free(envelope
);
4352 *envelope
= calloc(1, sizeof(**envelope
));
4358 zfree((*envelope
)->timestamp
);
4359 (*envelope
)->timestamp_length
= 0;
4362 /* Get dmQTimestamp */
4363 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4365 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4368 (*envelope
)->timestamp_length
=
4369 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4370 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4371 isds_printf_message(context
,
4372 _("Error while Base64-decoding time stamp value"));
4378 if (err
) isds_envelope_free(envelope
);
4380 xmlXPathFreeObject(result
);
4385 /* Convert XSD tReturnedMessage XML tree into message structure.
4386 * It does not store serialized XML tree into message->raw.
4387 * It does store (pointer to) parsed XML tree into message->xml if needed.
4388 * @context is ISDS context
4389 * @include_documents Use true if documents must be extracted
4390 * (tReturnedMessage XSD type), use false if documents shall be omitted
4391 * (tReturnedMessageEnvelope).
4392 * @message is automatically reallocated message structure
4393 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4395 * In case of error @message will be freed. */
4396 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4397 const _Bool include_documents
, struct isds_message
**message
,
4398 xmlXPathContextPtr xpath_ctx
) {
4399 isds_error err
= IE_SUCCESS
;
4400 xmlNodePtr message_node
;
4402 if (!context
) return IE_INVALID_CONTEXT
;
4403 if (!message
) return IE_INVAL
;
4404 isds_message_free(message
);
4405 if (!xpath_ctx
) return IE_INVAL
;
4408 *message
= calloc(1, sizeof(**message
));
4414 /* Save message XPATH context node */
4415 message_node
= xpath_ctx
->node
;
4419 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4420 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4421 if (err
) { err
= IE_ERROR
; goto leave
; }
4422 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4423 if (err
) goto leave
;
4425 if (include_documents
) {
4426 struct isds_list
*item
;
4428 /* Extract dmFiles */
4429 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4431 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4432 err
= IE_ISDS
; goto leave
;
4434 if (err
) { err
= IE_ERROR
; goto leave
; }
4435 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4436 if (err
) goto leave
;
4438 /* Store xmlDoc of this message if needed */
4439 /* Only if we got a XML document in all the documents. */
4440 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4441 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4442 (*message
)->xml
= xpath_ctx
->doc
;
4449 /* Restore context to message */
4450 xpath_ctx
->node
= message_node
;
4452 /* Extract dmHash */
4453 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4455 if (err
) goto leave
;
4457 /* Extract dmQTimestamp, */
4458 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4460 if (err
) goto leave
;
4462 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4463 * dmAcceptanceTime. */
4464 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4465 if (err
) goto leave
;
4467 /* Get message type */
4468 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4469 if (err
) goto leave
;
4472 if (err
) isds_message_free(message
);
4477 /* Extract message event into reallocated isds_event structure
4478 * @context is ISDS context
4479 * @event is automatically reallocated message event structure
4480 * @xpath_ctx is XPath context with current node as isds:dmEvent
4481 * In case of error @event will be freed. */
4482 static isds_error
extract_event(struct isds_ctx
*context
,
4483 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4484 isds_error err
= IE_SUCCESS
;
4485 xmlXPathObjectPtr result
= NULL
;
4486 xmlNodePtr event_node
;
4487 char *string
= NULL
;
4489 if (!context
) return IE_INVALID_CONTEXT
;
4490 if (!event
) return IE_INVAL
;
4491 isds_event_free(event
);
4492 if (!xpath_ctx
) return IE_INVAL
;
4493 event_node
= xpath_ctx
->node
;
4495 *event
= calloc(1, sizeof(**event
));
4501 /* Extract event data.
4502 * All elements are optional according XSD. That's funny. */
4503 EXTRACT_STRING("sisds:dmEventTime", string
);
4505 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4507 char *string_locale
= _isds_utf82locale(string
);
4508 if (err
== IE_DATE
) err
= IE_ISDS
;
4509 isds_printf_message(context
,
4510 _("Could not convert dmEventTime as ISO time: %s"),
4512 free(string_locale
);
4518 /* dmEventDescr element has prefix and the rest */
4519 EXTRACT_STRING("sisds:dmEventDescr", string
);
4521 err
= eventstring2event((xmlChar
*) string
, *event
);
4522 if (err
) goto leave
;
4527 if (err
) isds_event_free(event
);
4529 xmlXPathFreeObject(result
);
4530 xpath_ctx
->node
= event_node
;
4535 /* Convert element of XSD tEventsArray type from XML tree into
4536 * isds_list of isds_event's structure. The list is automatically reallocated.
4537 * @context is ISDS context
4538 * @events is automatically reallocated list of event structures
4539 * @xpath_ctx is XPath context with current node as tEventsArray
4540 * In case of error @events will be freed. */
4541 static isds_error
extract_events(struct isds_ctx
*context
,
4542 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4543 isds_error err
= IE_SUCCESS
;
4544 xmlXPathObjectPtr result
= NULL
;
4545 xmlNodePtr events_node
;
4546 struct isds_list
*event
, *prev_event
= NULL
;
4548 if (!context
) return IE_INVALID_CONTEXT
;
4549 if (!events
) return IE_INVAL
;
4550 if (!xpath_ctx
) return IE_INVAL
;
4551 events_node
= xpath_ctx
->node
;
4554 isds_list_free(events
);
4557 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4564 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4565 isds_printf_message(context
,
4566 _("Delivery info does not contain any event"));
4572 /* Iterate over events */
4573 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4575 /* Allocate and append list item */
4576 event
= calloc(1, sizeof(*event
));
4581 event
->destructor
= (void (*)(void **))isds_event_free
;
4582 if (i
== 0) *events
= event
;
4583 else prev_event
->next
= event
;
4587 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4588 err
= extract_event(context
,
4589 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4590 if (err
) goto leave
;
4595 if (err
) isds_list_free(events
);
4596 xmlXPathFreeObject(result
);
4597 xpath_ctx
->node
= events_node
;
4603 /* Insert Base64 encoded data as element with text child.
4604 * @context is session context
4605 * @parent is XML node to append @element with @data as child
4606 * @ns is XML namespace of @element, use NULL to inherit from @parent
4607 * @element is UTF-8 encoded name of new element
4608 * @data is bit stream to encode into @element
4609 * @length is size of @data in bytes
4610 * @return standard error code and fill long error message if needed */
4611 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4612 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4613 const void *data
, size_t length
) {
4614 isds_error err
= IE_SUCCESS
;
4617 if (!context
) return IE_INVALID_CONTEXT
;
4618 if (!data
&& length
> 0) return IE_INVAL
;
4619 if (!parent
|| !element
) return IE_INVAL
;
4621 xmlChar
*base64data
= NULL
;
4622 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4624 isds_printf_message(context
,
4625 ngettext("Not enough memory to encode %zd byte into Base64",
4626 "Not enough memory to encode %zd bytes into Base64",
4632 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4640 /* Convert isds_document structure into XML tree and append to dmFiles node.
4641 * @context is session context
4642 * @document is ISDS document
4643 * @dm_files is XML element the resulting tree will be appended to as a child.
4644 * @return error code, in case of error context' message is filled. */
4645 static isds_error
insert_document(struct isds_ctx
*context
,
4646 struct isds_document
*document
, xmlNodePtr dm_files
) {
4647 isds_error err
= IE_SUCCESS
;
4648 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4649 xmlAttrPtr attribute_node
;
4651 if (!context
) return IE_INVALID_CONTEXT
;
4652 if (!document
|| !dm_files
) return IE_INVAL
;
4654 /* Allocate new dmFile */
4655 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4657 isds_printf_message(context
, _("Could not allocate main dmFile"));
4661 /* Append the new dmFile.
4662 * XXX: Main document must go first */
4663 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4664 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4666 file
= xmlAddChild(dm_files
, new_file
);
4669 xmlFreeNode(new_file
); new_file
= NULL
;
4670 isds_printf_message(context
, _("Could not add dmFile child to "
4671 "%s element"), dm_files
->name
);
4676 /* @dmMimeType is required */
4677 if (!document
->dmMimeType
) {
4678 isds_log_message(context
,
4679 _("Document is missing mandatory MIME type definition"));
4683 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4685 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4687 isds_printf_message(context
,
4688 _("Document has unknown dmFileMetaType: %ld"),
4689 document
->dmFileMetaType
);
4693 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4695 if (document
->dmFileGuid
) {
4696 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4698 if (document
->dmUpFileGuid
) {
4699 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4702 /* @dmFileDescr is required */
4703 if (!document
->dmFileDescr
) {
4704 isds_log_message(context
,
4705 _("Document is missing mandatory description (title)"));
4709 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4711 if (document
->dmFormat
) {
4712 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4716 /* Insert content (body) of the document. */
4717 if (document
->is_xml
) {
4718 /* XML document requested */
4720 /* Allocate new dmXMLContent */
4721 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4723 isds_printf_message(context
,
4724 _("Could not allocate dmXMLContent element"));
4729 node
= xmlAddChild(file
, xmlcontent
);
4731 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4732 isds_printf_message(context
,
4733 _("Could not add dmXMLContent child to %s element"),
4739 /* Copy non-empty node list */
4740 if (document
->xml_node_list
) {
4741 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4742 document
->xml_node_list
);
4744 isds_printf_message(context
,
4745 _("Not enough memory to copy XML document"));
4750 if (!xmlAddChildList(node
, content
)) {
4751 xmlFreeNodeList(content
);
4752 isds_printf_message(context
,
4753 _("Error while adding XML document into dmXMLContent"));
4757 /* XXX: We cannot free the content here because it's part of node's
4758 * document since now. It will be freed with it automatically. */
4761 /* Binary document requested */
4762 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4763 document
->data
, document
->data_length
);
4764 if (err
) goto leave
;
4772 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4773 * The copy must be preallocated, the date are just appended into structure.
4774 * @context is ISDS context
4775 * @copy is message copy structure
4776 * @xpath_ctx is XPath context with current node as tMStatus */
4777 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4778 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4779 isds_error err
= IE_SUCCESS
;
4780 xmlXPathObjectPtr result
= NULL
;
4781 char *code
= NULL
, *message
= NULL
;
4783 if (!context
) return IE_INVALID_CONTEXT
;
4784 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4786 /* Free old values */
4787 zfree(copy
->dmStatus
);
4790 /* Get error specific to this copy */
4791 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4793 isds_log_message(context
,
4794 _("Missing isds:dmStatusCode under "
4795 "XSD:tMStatus type element"));
4800 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4801 /* This copy failed */
4802 copy
->error
= IE_ISDS
;
4803 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4805 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4806 if (!copy
->dmStatus
) {
4807 copy
->dmStatus
= code
;
4811 copy
->dmStatus
= code
;
4815 /* This copy succeeded. In this case only, message ID is valid */
4816 copy
->error
= IE_SUCCESS
;
4818 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4820 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4821 "but did not returned assigned message ID\n"));
4829 xmlXPathFreeObject(result
);
4834 /* Insert struct isds_approval data (box approval) into XML tree
4835 * @context is session context
4836 * @approval is libisds structure with approval description. NULL is
4838 * @parent is XML element to append @approval to */
4839 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4840 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4842 isds_error err
= IE_SUCCESS
;
4845 if (!context
) return IE_INVALID_CONTEXT
;
4846 if (!parent
) return IE_INVAL
;
4848 if (!approval
) return IE_SUCCESS
;
4850 /* Build XSD:gExtApproval */
4851 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4852 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4859 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4861 * @context is session context
4862 * @service_name is name of SERVICE_DB_ACCESS
4863 * @response is reallocated server SOAP body response as XML document
4864 * @raw_response is reallocated bit stream with response body. Use
4865 * NULL if you don't care
4866 * @raw_response_length is size of @raw_response in bytes
4867 * @code is reallocated ISDS status code
4868 * @status_message is reallocated ISDS status message
4869 * @return error coded from lower layer, context message will be set up
4871 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4872 const xmlChar
*service_name
,
4873 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4874 xmlChar
**code
, xmlChar
**status_message
) {
4876 isds_error err
= IE_SUCCESS
;
4877 char *service_name_locale
= NULL
;
4878 xmlNodePtr request
= NULL
, node
;
4879 xmlNsPtr isds_ns
= NULL
;
4881 if (!context
) return IE_INVALID_CONTEXT
;
4882 if (!service_name
) return IE_INVAL
;
4883 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4884 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4886 /* Free output argument */
4887 xmlFreeDoc(*response
); *response
= NULL
;
4888 if (raw_response
) zfree(*raw_response
);
4890 zfree(*status_message
);
4893 /* Check if connection is established
4894 * TODO: This check should be done downstairs. */
4895 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4897 service_name_locale
= _isds_utf82locale((char*)service_name
);
4898 if (!service_name_locale
) {
4904 request
= xmlNewNode(NULL
, service_name
);
4906 isds_printf_message(context
,
4907 _("Could not build %s request"), service_name_locale
);
4911 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4913 isds_log_message(context
, _("Could not create ISDS name space"));
4917 xmlSetNs(request
, isds_ns
);
4920 /* Add XSD:tDummyInput child */
4921 INSERT_STRING(request
, "dbDummy", NULL
);
4924 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4925 service_name_locale
);
4928 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4929 raw_response
, raw_response_length
);
4930 xmlFreeNode(request
); request
= NULL
;
4933 isds_log(ILF_ISDS
, ILL_DEBUG
,
4934 _("Processing ISDS response on %s request failed\n"),
4935 service_name_locale
);
4939 /* Check for response status */
4940 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4941 code
, status_message
, NULL
);
4943 isds_log(ILF_ISDS
, ILL_DEBUG
,
4944 _("ISDS response on %s request is missing status\n"),
4945 service_name_locale
);
4949 /* Request processed, but nothing found */
4950 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4951 char *code_locale
= _isds_utf82locale((char*) *code
);
4952 char *status_message_locale
=
4953 _isds_utf82locale((char*) *status_message
);
4954 isds_log(ILF_ISDS
, ILL_DEBUG
,
4955 _("Server refused %s request (code=%s, message=%s)\n"),
4956 service_name_locale
, code_locale
, status_message_locale
);
4957 isds_log_message(context
, status_message_locale
);
4959 free(status_message_locale
);
4965 free(service_name_locale
);
4966 xmlFreeNode(request
);
4972 /* Get data about logged in user and his box.
4973 * @context is session context
4974 * @db_owner_info is reallocated box owner description. It will be freed on
4976 * @return error code from lower layer, context message will be set up
4978 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4979 struct isds_DbOwnerInfo
**db_owner_info
) {
4980 isds_error err
= IE_SUCCESS
;
4982 xmlDocPtr response
= NULL
;
4983 xmlChar
*code
= NULL
, *message
= NULL
;
4984 xmlXPathContextPtr xpath_ctx
= NULL
;
4985 xmlXPathObjectPtr result
= NULL
;
4986 char *string
= NULL
;
4989 if (!context
) return IE_INVALID_CONTEXT
;
4990 zfree(context
->long_message
);
4991 if (!db_owner_info
) return IE_INVAL
;
4992 isds_DbOwnerInfo_free(db_owner_info
);
4995 /* Check if connection is established */
4996 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4999 /* Do request and check for success */
5000 err
= build_send_check_dbdummy_request(context
,
5001 BAD_CAST
"GetOwnerInfoFromLogin",
5002 &response
, NULL
, NULL
, &code
, &message
);
5003 if (err
) goto leave
;
5007 /* Prepare structure */
5008 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5009 if (!*db_owner_info
) {
5013 xpath_ctx
= xmlXPathNewContext(response
);
5018 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5023 /* Set context node */
5024 result
= xmlXPathEvalExpression(BAD_CAST
5025 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5030 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5031 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5035 if (result
->nodesetval
->nodeNr
> 1) {
5036 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5040 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5041 xmlXPathFreeObject(result
); result
= NULL
;
5044 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5049 isds_DbOwnerInfo_free(db_owner_info
);
5053 xmlXPathFreeObject(result
);
5054 xmlXPathFreeContext(xpath_ctx
);
5058 xmlFreeDoc(response
);
5061 isds_log(ILF_ISDS
, ILL_DEBUG
,
5062 _("GetOwnerInfoFromLogin request processed by server "
5063 "successfully.\n"));
5064 #else /* not HAVE_LIBCURL */
5072 /* Get data about logged in user. */
5073 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5074 struct isds_DbUserInfo
**db_user_info
) {
5075 isds_error err
= IE_SUCCESS
;
5077 xmlDocPtr response
= NULL
;
5078 xmlChar
*code
= NULL
, *message
= NULL
;
5079 xmlXPathContextPtr xpath_ctx
= NULL
;
5080 xmlXPathObjectPtr result
= NULL
;
5083 if (!context
) return IE_INVALID_CONTEXT
;
5084 zfree(context
->long_message
);
5085 if (!db_user_info
) return IE_INVAL
;
5086 isds_DbUserInfo_free(db_user_info
);
5089 /* Check if connection is established */
5090 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5093 /* Do request and check for success */
5094 err
= build_send_check_dbdummy_request(context
,
5095 BAD_CAST
"GetUserInfoFromLogin",
5096 &response
, NULL
, NULL
, &code
, &message
);
5097 if (err
) goto leave
;
5101 /* Prepare structure */
5102 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5103 if (!*db_user_info
) {
5107 xpath_ctx
= xmlXPathNewContext(response
);
5112 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5117 /* Set context node */
5118 result
= xmlXPathEvalExpression(BAD_CAST
5119 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5124 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5125 isds_log_message(context
, _("Missing dbUserInfo element"));
5129 if (result
->nodesetval
->nodeNr
> 1) {
5130 isds_log_message(context
, _("Multiple dbUserInfo element"));
5134 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5135 xmlXPathFreeObject(result
); result
= NULL
;
5138 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5142 isds_DbUserInfo_free(db_user_info
);
5145 xmlXPathFreeObject(result
);
5146 xmlXPathFreeContext(xpath_ctx
);
5150 xmlFreeDoc(response
);
5153 isds_log(ILF_ISDS
, ILL_DEBUG
,
5154 _("GetUserInfoFromLogin request processed by server "
5155 "successfully.\n"));
5156 #else /* not HAVE_LIBCURL */
5164 /* Get expiration time of current password
5165 * @context is session context
5166 * @expiration is automatically reallocated time when password expires. If
5167 * password expiration is disabled, NULL will be returned. In case of error
5168 * it will be nulled too. */
5169 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5170 struct timeval
**expiration
) {
5171 isds_error err
= IE_SUCCESS
;
5173 xmlDocPtr response
= NULL
;
5174 xmlChar
*code
= NULL
, *message
= NULL
;
5175 xmlXPathContextPtr xpath_ctx
= NULL
;
5176 xmlXPathObjectPtr result
= NULL
;
5177 char *string
= NULL
;
5180 if (!context
) return IE_INVALID_CONTEXT
;
5181 zfree(context
->long_message
);
5182 if (!expiration
) return IE_INVAL
;
5186 /* Check if connection is established */
5187 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5190 /* Do request and check for success */
5191 err
= build_send_check_dbdummy_request(context
,
5192 BAD_CAST
"GetPasswordInfo",
5193 &response
, NULL
, NULL
, &code
, &message
);
5194 if (err
) goto leave
;
5198 xpath_ctx
= xmlXPathNewContext(response
);
5203 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5208 /* Set context node */
5209 result
= xmlXPathEvalExpression(BAD_CAST
5210 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5215 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5216 isds_log_message(context
,
5217 _("Missing GetPasswordInfoResponse element"));
5221 if (result
->nodesetval
->nodeNr
> 1) {
5222 isds_log_message(context
,
5223 _("Multiple GetPasswordInfoResponse element"));
5227 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5228 xmlXPathFreeObject(result
); result
= NULL
;
5230 /* Extract expiration date */
5231 EXTRACT_STRING("isds:pswExpDate", string
);
5233 /* And convert it if any returned. Otherwise expiration is disabled. */
5234 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5236 char *string_locale
= _isds_utf82locale(string
);
5237 if (err
== IE_DATE
) err
= IE_ISDS
;
5238 isds_printf_message(context
,
5239 _("Could not convert pswExpDate as ISO time: %s"),
5241 free(string_locale
);
5254 xmlXPathFreeObject(result
);
5255 xmlXPathFreeContext(xpath_ctx
);
5259 xmlFreeDoc(response
);
5262 isds_log(ILF_ISDS
, ILL_DEBUG
,
5263 _("GetPasswordInfo request processed by server "
5264 "successfully.\n"));
5265 #else /* not HAVE_LIBCURL */
5274 /* Request delivering new TOTP code from ISDS through side channel before
5275 * changing password.
5276 * @context is session context
5277 * @password is current password.
5278 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5279 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5280 * function for more details.
5281 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5282 * NULL, if you don't care.
5283 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5285 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5286 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5287 isds_error err
= IE_SUCCESS
;
5288 char *saved_url
= NULL
; /* No copy */
5289 #if HAVE_CURL_REAUTHORIZATION_BUG
5290 CURL
*saved_curl
= NULL
; /* No copy */
5292 xmlNsPtr isds_ns
= NULL
;
5293 xmlNodePtr request
= NULL
;
5294 xmlDocPtr response
= NULL
;
5295 xmlChar
*code
= NULL
, *message
= NULL
;
5296 const xmlChar
*codes
[] = {
5301 const char *meanings
[] = {
5302 N_("Unexpected error"),
5303 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5304 N_("One-time code could not been sent. Try later again.")
5306 const isds_otp_resolution resolutions
[] = {
5307 OTP_RESOLUTION_UNKNOWN
,
5308 OTP_RESOLUTION_TO_FAST
,
5309 OTP_RESOLUTION_TOTP_NOT_SENT
5312 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5313 zfree(context
->long_message
);
5314 if (NULL
== password
) {
5315 isds_log_message(context
,
5316 _("Second argument (password) of isds_change_password() "
5321 /* Check if connection is established
5322 * TODO: This check should be done downstairs. */
5323 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5325 if (!context
->otp
) {
5326 isds_log_message(context
, _("This function requires OTP-authenticated "
5328 return IE_INVALID_CONTEXT
;
5331 isds_log_message(context
, _("If one-time password authentication "
5332 "method is in use, requesting new OTP code requires "
5333 "one-time credentials argument either"));
5336 if (otp
->method
!= OTP_TIME
) {
5337 isds_log_message(context
, _("Requesting new time-based OTP code from "
5338 "server requires one-time password authentication "
5342 if (otp
->otp_code
!= NULL
) {
5343 isds_log_message(context
, _("Requesting new time-based OTP code from "
5344 "server requires undefined OTP code member in "
5345 "one-time credentials argument"));
5351 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5353 isds_log_message(context
, _("Could not build SendSMSCode request"));
5356 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5358 isds_log_message(context
, _("Could not create ISDS name space"));
5359 xmlFreeNode(request
);
5362 xmlSetNs(request
, isds_ns
);
5364 /* Change URL temporarily for sending this request only */
5366 char *new_url
= NULL
;
5367 if ((err
= _isds_build_url_from_context(context
,
5368 "%1$.*2$sasws/changePassword", &new_url
))) {
5371 saved_url
= context
->url
;
5372 context
->url
= new_url
;
5375 /* Store credentials for sending this request only */
5376 context
->otp_credentials
= otp
;
5377 _isds_discard_credentials(context
, 0);
5378 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5380 _isds_discard_credentials(context
, 0);
5383 #if HAVE_CURL_REAUTHORIZATION_BUG
5384 saved_curl
= context
->curl
;
5385 context
->curl
= curl_easy_init();
5386 if (NULL
== context
->curl
) {
5390 if (context
->timeout
) {
5391 err
= isds_set_timeout(context
, context
->timeout
);
5392 if (err
) goto leave
;
5396 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5399 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5401 /* Remove temporal credentials */
5402 _isds_discard_credentials(context
, 0);
5403 /* Detach pointer to OTP credentials from context */
5404 context
->otp_credentials
= NULL
;
5405 /* Keep context->otp true to keep signaling this is OTP session */
5407 /* Destroy request */
5408 xmlFreeNode(request
); request
= NULL
;
5411 isds_log(ILF_ISDS
, ILL_DEBUG
,
5412 _("Processing ISDS response on SendSMSCode request failed\n"));
5416 /* Check for response status */
5417 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5418 &code
, &message
, (xmlChar
**)refnumber
);
5420 isds_log(ILF_ISDS
, ILL_DEBUG
,
5421 _("ISDS response on SendSMSCode request is missing "
5426 /* Check for error */
5427 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5428 char *code_locale
= _isds_utf82locale((char*)code
);
5429 char *message_locale
= _isds_utf82locale((char*)message
);
5431 isds_log(ILF_ISDS
, ILL_DEBUG
,
5432 _("Server refused to send new code on SendSMSCode "
5433 "request (code=%s, message=%s)\n"),
5434 code_locale
, message_locale
);
5436 /* Check for known error codes */
5437 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5438 if (!xmlStrcmp(code
, codes
[i
])) break;
5440 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5441 isds_log_message(context
, _(meanings
[i
]));
5442 /* Mimic otp->resolution according to the code, specification does
5443 * prescribe OTP header to be available. */
5444 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5445 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5446 otp
->resolution
= resolutions
[i
];
5448 isds_log_message(context
, message_locale
);
5451 free(message_locale
);
5457 /* Otherwise new code sent successfully */
5458 /* Mimic otp->resolution according to the code, specification does
5459 * prescribe OTP header to be available. */
5460 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5461 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5464 if (NULL
!= saved_url
) {
5465 /* Revert URL to original one */
5466 zfree(context
->url
);
5467 context
->url
= saved_url
;
5469 #if HAVE_CURL_REAUTHORIZATION_BUG
5470 if (NULL
!= saved_curl
) {
5471 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5472 context
->curl
= saved_curl
;
5478 xmlFreeDoc(response
);
5479 xmlFreeNode(request
);
5482 isds_log(ILF_ISDS
, ILL_DEBUG
,
5483 _("New OTP code has been sent successfully on SendSMSCode "
5489 /* Convert response status code to isds_error code and set long message
5490 * @context is context to save long message to
5491 * @map is mapping from codes to errors and messages. Pass NULL for generic
5493 * @code is status code to translate
5494 * @message is non-localized status message to put into long message in case
5495 * of uknown error. It can be NULL if server did not provide any.
5496 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5497 * invalid invocation. */
5498 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5499 const struct code_map_isds_error
*map
,
5500 const xmlChar
*code
, const xmlChar
*message
) {
5502 isds_log_message(context
,
5503 _("NULL status code passed to statuscode2isds_error()"));
5508 /* Check for known error codes */
5509 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5510 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5511 isds_log_message(context
, _(map
->meanings
[i
]));
5512 return map
->errors
[i
];
5518 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5519 char *message_locale
= _isds_utf82locale((char*)message
);
5520 if (NULL
== message_locale
)
5521 isds_log_message(context
, _("ISDS server returned unknown error"));
5523 isds_log_message(context
, message_locale
);
5524 free(message_locale
);
5533 /* Change user password in ISDS.
5534 * User must supply old password, new password will takes effect after some
5535 * time, current session can continue. Password must fulfill some constraints.
5536 * @context is session context
5537 * @old_password is current password.
5538 * @new_password is requested new password
5539 * @otp auxiliary data required if one-time password authentication is in use,
5540 * defines OTP code (if known) and returns fine grade resolution of OTP
5541 * procedure. Pass NULL, if one-time password authentication is not needed.
5542 * Please note the @otp argument must match OTP method used at log-in time. See
5543 * isds_login() function for more details.
5544 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5545 * NULL, if you don't care.
5546 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5547 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5548 * awaiting OTP code that has been delivered by side channel to the user. */
5549 isds_error
isds_change_password(struct isds_ctx
*context
,
5550 const char *old_password
, const char *new_password
,
5551 struct isds_otp
*otp
, char **refnumber
) {
5552 isds_error err
= IE_SUCCESS
;
5554 char *saved_url
= NULL
; /* No copy */
5555 #if HAVE_CURL_REAUTHORIZATION_BUG
5556 CURL
*saved_curl
= NULL
; /* No copy */
5558 xmlNsPtr isds_ns
= NULL
;
5559 xmlNodePtr request
= NULL
, node
;
5560 xmlDocPtr response
= NULL
;
5561 xmlChar
*code
= NULL
, *message
= NULL
;
5562 const xmlChar
*codes
[] = {
5575 const char *meanings
[] = {
5576 N_("Password length must be between 8 and 32 characters"),
5577 N_("Password cannot be reused"), /* Server does not distinguish 1067
5578 and 1091 on ChangePasswordOTP */
5579 N_("Password contains forbidden character"),
5580 N_("Password must contain at least one upper-case letter, "
5581 "one lower-case, and one digit"),
5582 N_("Password cannot contain sequence of three identical characters"),
5583 N_("Password cannot contain user identifier"),
5584 N_("Password is too simmple"),
5585 N_("Old password is not valid"),
5586 N_("Password cannot be reused"),
5587 N_("Unexpected error"),
5588 N_("LDAP update error")
5592 if (!context
) return IE_INVALID_CONTEXT
;
5593 zfree(context
->long_message
);
5594 if (NULL
!= refnumber
)
5596 if (NULL
== old_password
) {
5597 isds_log_message(context
,
5598 _("Second argument (old password) of isds_change_password() "
5602 if (NULL
== otp
&& NULL
== new_password
) {
5603 isds_log_message(context
,
5604 _("Third argument (new password) of isds_change_password() "
5610 /* Check if connection is established
5611 * TODO: This check should be done downstairs. */
5612 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5614 if (context
->otp
&& NULL
== otp
) {
5615 isds_log_message(context
, _("If one-time password authentication "
5616 "method is in use, changing password requires one-time "
5617 "credentials either"));
5621 /* Build ChangeISDSPassword request */
5622 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5623 BAD_CAST
"ChangePasswordOTP");
5625 isds_log_message(context
, (NULL
== otp
) ?
5626 _("Could not build ChangeISDSPassword request") :
5627 _("Could not build ChangePasswordOTP request"));
5630 isds_ns
= xmlNewNs(request
,
5631 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5634 isds_log_message(context
, _("Could not create ISDS name space"));
5635 xmlFreeNode(request
);
5638 xmlSetNs(request
, isds_ns
);
5640 INSERT_STRING(request
, "dbOldPassword", old_password
);
5641 INSERT_STRING(request
, "dbNewPassword", new_password
);
5644 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5645 switch (otp
->method
) {
5647 isds_log(ILF_SEC
, ILL_INFO
,
5648 _("Selected authentication method: "
5649 "HMAC-based one-time password\n"));
5650 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5653 isds_log(ILF_SEC
, ILL_INFO
,
5654 _("Selected authentication method: "
5655 "Time-based one-time password\n"));
5656 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5657 if (otp
->otp_code
== NULL
) {
5658 isds_log(ILF_SEC
, ILL_INFO
,
5659 _("OTP code has not been provided by "
5660 "application, requesting server for "
5662 err
= _isds_request_totp_code(context
, old_password
, otp
,
5664 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5668 isds_log(ILF_SEC
, ILL_INFO
,
5669 _("OTP code has been provided by "
5670 "application, not requesting server "
5675 isds_log_message(context
,
5676 _("Unknown one-time password authentication "
5677 "method requested by application"));
5682 /* Change URL temporarily for sending this request only */
5684 char *new_url
= NULL
;
5685 if ((err
= _isds_build_url_from_context(context
,
5686 "%1$.*2$sasws/changePassword", &new_url
))) {
5689 saved_url
= context
->url
;
5690 context
->url
= new_url
;
5693 /* Store credentials for sending this request only */
5694 context
->otp_credentials
= otp
;
5695 _isds_discard_credentials(context
, 0);
5696 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5697 old_password
, NULL
))) {
5698 _isds_discard_credentials(context
, 0);
5701 #if HAVE_CURL_REAUTHORIZATION_BUG
5702 saved_curl
= context
->curl
;
5703 context
->curl
= curl_easy_init();
5704 if (NULL
== context
->curl
) {
5708 if (context
->timeout
) {
5709 err
= isds_set_timeout(context
, context
->timeout
);
5710 if (err
) goto leave
;
5715 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5716 _("Sending ChangeISDSPassword request to ISDS\n") :
5717 _("Sending ChangePasswordOTP request to ISDS\n"));
5720 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5721 request
, &response
, NULL
, NULL
);
5724 /* Remove temporal credentials */
5725 _isds_discard_credentials(context
, 0);
5726 /* Detach pointer to OTP credentials from context */
5727 context
->otp_credentials
= NULL
;
5728 /* Keep context->otp true to keep signaling this is OTP session */
5731 /* Destroy request */
5732 xmlFreeNode(request
); request
= NULL
;
5735 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5736 _("Processing ISDS response on ChangeISDSPassword "
5737 "request failed\n") :
5738 _("Processing ISDS response on ChangePasswordOTP "
5739 "request failed\n"));
5743 /* Check for response status */
5744 err
= isds_response_status(context
,
5745 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5746 &code
, &message
, (xmlChar
**)refnumber
);
5748 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5749 _("ISDS response on ChangeISDSPassword request is missing "
5751 _("ISDS response on ChangePasswordOTP request is missing "
5756 /* Check for known error codes */
5757 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5758 if (!xmlStrcmp(code
, codes
[i
])) {
5759 char *code_locale
= _isds_utf82locale((char*)code
);
5760 char *message_locale
= _isds_utf82locale((char*)message
);
5761 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5762 _("Server refused to change password on ChangeISDSPassword "
5763 "request (code=%s, message=%s)\n") :
5764 _("Server refused to change password on ChangePasswordOTP "
5765 "request (code=%s, message=%s)\n"),
5766 code_locale
, message_locale
);
5768 free(message_locale
);
5769 isds_log_message(context
, _(meanings
[i
]));
5776 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5777 char *code_locale
= _isds_utf82locale((char*)code
);
5778 char *message_locale
= _isds_utf82locale((char*)message
);
5779 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5780 _("Server refused to change password on ChangeISDSPassword "
5781 "request (code=%s, message=%s)\n") :
5782 _("Server refused to change password on ChangePasswordOTP "
5783 "request (code=%s, message=%s)\n"),
5784 code_locale
, message_locale
);
5785 isds_log_message(context
, message_locale
);
5787 free(message_locale
);
5792 /* Otherwise password changed successfully */
5795 if (NULL
!= saved_url
) {
5796 /* Revert URL to original one */
5797 zfree(context
->url
);
5798 context
->url
= saved_url
;
5800 #if HAVE_CURL_REAUTHORIZATION_BUG
5801 if (NULL
!= saved_curl
) {
5802 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5803 context
->curl
= saved_curl
;
5809 xmlFreeDoc(response
);
5810 xmlFreeNode(request
);
5813 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5814 _("Password changed successfully on ChangeISDSPassword "
5816 _("Password changed successfully on ChangePasswordOTP "
5818 #else /* not HAVE_LIBCURL */
5827 /* Generic middle part with request sending and response check.
5828 * It sends prepared request and checks for error code.
5829 * @context is ISDS session context.
5830 * @service is ISDS service handler
5831 * @service_name is name in scope of given @service
5832 * @request is XML tree with request. Will be freed to save memory.
5833 * @response is XML document outputting ISDS response.
5834 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5835 * @map is mapping from status code to library error. Pass NULL if no special
5836 * handling is requested.
5837 * NULL, if you don't care. */
5838 static isds_error
send_destroy_request_check_response(
5839 struct isds_ctx
*context
,
5840 const isds_service service
, const xmlChar
*service_name
,
5841 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5842 const struct code_map_isds_error
*map
) {
5843 isds_error err
= IE_SUCCESS
;
5844 char *service_name_locale
= NULL
;
5845 xmlChar
*code
= NULL
, *message
= NULL
;
5848 if (!context
) return IE_INVALID_CONTEXT
;
5849 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5853 /* Check if connection is established
5854 * TODO: This check should be done downstairs. */
5855 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5857 service_name_locale
= _isds_utf82locale((char*) service_name
);
5858 if (!service_name_locale
) {
5863 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5864 service_name_locale
);
5867 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5868 xmlFreeNode(*request
); *request
= NULL
;
5871 isds_log(ILF_ISDS
, ILL_DEBUG
,
5872 _("Processing ISDS response on %s request failed\n"),
5873 service_name_locale
);
5877 /* Check for response status */
5878 err
= isds_response_status(context
, service
, *response
,
5879 &code
, &message
, refnumber
);
5881 isds_log(ILF_ISDS
, ILL_DEBUG
,
5882 _("ISDS response on %s request is missing status\n"),
5883 service_name_locale
);
5887 err
= statuscode2isds_error(context
, map
, code
, message
);
5889 /* Request processed, but server failed */
5890 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5891 char *code_locale
= _isds_utf82locale((char*) code
);
5892 char *message_locale
= _isds_utf82locale((char*) message
);
5893 isds_log(ILF_ISDS
, ILL_DEBUG
,
5894 _("Server refused %s request (code=%s, message=%s)\n"),
5895 service_name_locale
, code_locale
, message_locale
);
5897 free(message_locale
);
5905 if (err
&& *response
) {
5906 xmlFreeDoc(*response
);
5910 xmlFreeNode(*request
);
5913 free(service_name_locale
);
5919 /* Generic bottom half with request sending.
5920 * It sends prepared request, checks for error code, destroys response and
5921 * request and log success or failure.
5922 * @context is ISDS session context.
5923 * @service is ISDS service handler
5924 * @service_name is name in scope of given @service
5925 * @request is XML tree with request. Will be freed to save memory.
5926 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5927 * NULL, if you don't care. */
5928 static isds_error
send_request_check_drop_response(
5929 struct isds_ctx
*context
,
5930 const isds_service service
, const xmlChar
*service_name
,
5931 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5932 isds_error err
= IE_SUCCESS
;
5933 xmlDocPtr response
= NULL
;
5936 if (!context
) return IE_INVALID_CONTEXT
;
5937 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5940 /* Send request and check response*/
5941 err
= send_destroy_request_check_response(context
,
5942 service
, service_name
, request
, &response
, refnumber
, NULL
);
5944 xmlFreeDoc(response
);
5947 xmlFreeNode(*request
);
5952 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5953 isds_log(ILF_ISDS
, ILL_DEBUG
,
5954 _("%s request processed by server successfully.\n"),
5955 service_name_locale
);
5956 free(service_name_locale
);
5963 /* Insert isds_credentials_delivery structure into XML request if not NULL
5964 * @context is session context
5965 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5966 * credentials delivery. The email field is passed.
5967 * @parent is XML element where to insert */
5968 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5969 const struct isds_credentials_delivery
*credentials_delivery
,
5970 xmlNodePtr parent
) {
5971 isds_error err
= IE_SUCCESS
;
5974 if (!context
) return IE_INVALID_CONTEXT
;
5975 if (!parent
) return IE_INVAL
;
5977 if (credentials_delivery
) {
5978 /* Following elements are valid only for services:
5979 * NewAccessData, AddDataBoxUser, CreateDataBox */
5980 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5981 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5989 /* Extract credentials delivery from ISDS response.
5990 * @context is session context
5991 * @credentials_delivery is pointer to valid structure to fill in returned
5992 * user's password (and new log-in name). If NULL, do not extract the data.
5993 * @response is pointer to XML document with ISDS response
5994 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5995 * @return IE_SUCCESS even if new user name has not been found because it's not
5996 * clear whether it's returned always. */
5997 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5998 struct isds_credentials_delivery
*credentials_delivery
,
5999 xmlDocPtr response
, const char *request_name
) {
6000 isds_error err
= IE_SUCCESS
;
6001 xmlXPathContextPtr xpath_ctx
= NULL
;
6002 xmlXPathObjectPtr result
= NULL
;
6003 char *xpath_query
= NULL
;
6005 if (!context
) return IE_INVALID_CONTEXT
;
6006 if (credentials_delivery
) {
6007 zfree(credentials_delivery
->token
);
6008 zfree(credentials_delivery
->new_user_name
);
6010 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6013 /* Extract optional token */
6014 if (credentials_delivery
) {
6015 xpath_ctx
= xmlXPathNewContext(response
);
6020 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6025 /* Verify root element */
6026 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6031 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6036 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6037 char *request_name_locale
= _isds_utf82locale(request_name
);
6038 isds_log(ILF_ISDS
, ILL_WARNING
,
6039 _("Wrong element in ISDS response for %s request "
6040 "while extracting credentials delivery details\n"),
6041 request_name_locale
);
6042 free(request_name_locale
);
6046 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6049 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6051 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6053 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6054 if (!credentials_delivery
->token
) {
6055 char *request_name_locale
= _isds_utf82locale(request_name
);
6056 isds_log(ILF_ISDS
, ILL_ERR
,
6057 _("ISDS did not return token on %s request "
6058 "even if requested\n"), request_name_locale
);
6059 free(request_name_locale
);
6066 xmlXPathFreeObject(result
);
6067 xmlXPathFreeContext(xpath_ctx
);
6073 /* Build XSD:tCreateDBInput request type for box creating.
6074 * @context is session context
6075 * @request outputs built XML tree
6076 * @service_name is request name of SERVICE_DB_MANIPULATION service
6077 * @box is box description to create including single primary user (in case of
6078 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6080 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6081 * box, or contact address of PFO box owner)
6082 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6083 * @upper_box_id is optional ID of supper box if currently created box is
6085 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6087 * @credentials_delivery is valid pointer if ISDS should return token that box
6088 * owner can use to obtain his new credentials in on-line way. Then valid email
6089 * member value should be supplied.
6090 * @approval is optional external approval of box manipulation */
6091 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6092 xmlNodePtr
*request
, const xmlChar
*service_name
,
6093 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6094 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6095 const xmlChar
*ceo_label
,
6096 const struct isds_credentials_delivery
*credentials_delivery
,
6097 const struct isds_approval
*approval
) {
6098 isds_error err
= IE_SUCCESS
;
6099 xmlNsPtr isds_ns
= NULL
;
6100 xmlNodePtr node
, dbPrimaryUsers
;
6101 xmlChar
*string
= NULL
;
6102 const struct isds_list
*item
;
6105 if (!context
) return IE_INVALID_CONTEXT
;
6106 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6110 /* Build CreateDataBox-similar request */
6111 *request
= xmlNewNode(NULL
, service_name
);
6113 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6114 isds_printf_message(context
, _("Could build %s request"),
6115 service_name_locale
);
6116 free(service_name_locale
);
6119 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6120 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6122 isds_log_message(context
, _("Could not create ISDS1 name space"));
6123 xmlFreeNode(*request
);
6127 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6129 isds_log_message(context
, _("Could not create ISDS name space"));
6130 xmlFreeNode(*request
);
6134 xmlSetNs(*request
, isds_ns
);
6136 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6137 err
= insert_DbOwnerInfo(context
, box
, node
);
6138 if (err
) goto leave
;
6141 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6142 * verbose documentation allows none dbUserInfo */
6143 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6144 for (item
= users
; item
; item
= item
->next
) {
6146 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6147 err
= insert_DbUserInfo(context
,
6148 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6149 if (err
) goto leave
;
6153 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6154 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6155 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6157 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6158 if (err
) goto leave
;
6160 err
= insert_GExtApproval(context
, approval
, *request
);
6161 if (err
) goto leave
;
6165 xmlFreeNode(*request
);
6171 #endif /* HAVE_LIBCURL */
6175 * @context is session context
6176 * @box is box description to create including single primary user (in case of
6177 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6178 * ignored. It outputs box ID assigned by ISDS in dbID element.
6179 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6180 * box, or contact address of PFO box owner)
6181 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6182 * @upper_box_id is optional ID of supper box if currently created box is
6184 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6185 * @credentials_delivery is NULL if new password should be delivered off-line
6186 * to box owner. It is valid pointer if owner should obtain new password on-line
6187 * on dedicated web server. Then input @credentials_delivery.email value is
6188 * his e-mail address he must provide to dedicated web server together
6189 * with output reallocated @credentials_delivery.token member. Output
6190 * member @credentials_delivery.new_user_name is unused up on this call.
6191 * @approval is optional external approval of box manipulation
6192 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6193 * NULL, if you don't care.*/
6194 isds_error
isds_add_box(struct isds_ctx
*context
,
6195 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6196 const char *former_names
, const char *upper_box_id
,
6197 const char *ceo_label
,
6198 struct isds_credentials_delivery
*credentials_delivery
,
6199 const struct isds_approval
*approval
, char **refnumber
) {
6200 isds_error err
= IE_SUCCESS
;
6202 xmlNodePtr request
= NULL
;
6203 xmlDocPtr response
= NULL
;
6204 xmlXPathContextPtr xpath_ctx
= NULL
;
6205 xmlXPathObjectPtr result
= NULL
;
6209 if (!context
) return IE_INVALID_CONTEXT
;
6210 zfree(context
->long_message
);
6211 if (credentials_delivery
) {
6212 zfree(credentials_delivery
->token
);
6213 zfree(credentials_delivery
->new_user_name
);
6215 if (!box
) return IE_INVAL
;
6218 /* Scratch box ID */
6221 /* Build CreateDataBox request */
6222 err
= build_CreateDBInput_request(context
,
6223 &request
, BAD_CAST
"CreateDataBox",
6224 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6225 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6226 if (err
) goto leave
;
6228 /* Send it to server and process response */
6229 err
= send_destroy_request_check_response(context
,
6230 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6231 &response
, (xmlChar
**) refnumber
, NULL
);
6233 /* Extract box ID */
6234 xpath_ctx
= xmlXPathNewContext(response
);
6239 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6243 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6245 /* Extract optional token */
6246 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6250 xmlXPathFreeObject(result
);
6251 xmlXPathFreeContext(xpath_ctx
);
6252 xmlFreeDoc(response
);
6253 xmlFreeNode(request
);
6256 isds_log(ILF_ISDS
, ILL_DEBUG
,
6257 _("CreateDataBox request processed by server successfully.\n"));
6259 #else /* not HAVE_LIBCURL */
6267 /* Notify ISDS about new PFO entity.
6268 * This function has no real effect.
6269 * @context is session context
6270 * @box is PFO description including single primary user. aifoIsds,
6271 * address->adCode, address->adDistrict members are ignored.
6272 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6273 * @former_names is optional undocumented string. Pass NULL if you don't care.
6274 * @upper_box_id is optional ID of supper box if currently created box is
6276 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6277 * @approval is optional external approval of box manipulation
6278 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6279 * NULL, if you don't care.*/
6280 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6281 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6282 const char *former_names
, const char *upper_box_id
,
6283 const char *ceo_label
, const struct isds_approval
*approval
,
6285 isds_error err
= IE_SUCCESS
;
6287 xmlNodePtr request
= NULL
;
6290 if (!context
) return IE_INVALID_CONTEXT
;
6291 zfree(context
->long_message
);
6292 if (!box
) return IE_INVAL
;
6295 /* Build CreateDataBoxPFOInfo request */
6296 err
= build_CreateDBInput_request(context
,
6297 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6298 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6299 (xmlChar
*) ceo_label
, NULL
, approval
);
6300 if (err
) goto leave
;
6302 /* Send it to server and process response */
6303 err
= send_request_check_drop_response(context
,
6304 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6305 (xmlChar
**) refnumber
);
6306 /* XXX: XML Schema names output dbID element but textual documentation
6307 * states no box identifier is returned. */
6309 xmlFreeNode(request
);
6310 #else /* not HAVE_LIBCURL */
6317 /* Common implementation for removing given box.
6318 * @context is session context
6319 * @service_name is UTF-8 encoded name fo ISDS service
6320 * @box is box description to delete. aifoIsds, address->adCode,
6321 * address->adDistrict members are ignored.
6322 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6323 * carry sane value. If NULL, do not inject this information into request.
6324 * @approval is optional external approval of box manipulation
6325 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6326 * NULL, if you don't care.*/
6327 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6328 const xmlChar
*service_name
,
6329 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6330 const struct isds_approval
*approval
, char **refnumber
) {
6331 isds_error err
= IE_SUCCESS
;
6333 xmlNsPtr isds_ns
= NULL
;
6334 xmlNodePtr request
= NULL
;
6336 xmlChar
*string
= NULL
;
6340 if (!context
) return IE_INVALID_CONTEXT
;
6341 zfree(context
->long_message
);
6342 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6346 /* Build DeleteDataBox(Promptly) request */
6347 request
= xmlNewNode(NULL
, service_name
);
6349 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6350 isds_printf_message(context
,
6351 _("Could build %s request"), service_name_locale
);
6352 free(service_name_locale
);
6355 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6357 isds_log_message(context
, _("Could not create ISDS name space"));
6358 xmlFreeNode(request
);
6361 xmlSetNs(request
, isds_ns
);
6363 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6364 err
= insert_DbOwnerInfo(context
, box
, node
);
6365 if (err
) goto leave
;
6368 err
= tm2datestring(since
, &string
);
6370 isds_log_message(context
,
6371 _("Could not convert `since' argument to ISO date string"));
6374 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6378 err
= insert_GExtApproval(context
, approval
, request
);
6379 if (err
) goto leave
;
6382 /* Send it to server and process response */
6383 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6384 service_name
, &request
, (xmlChar
**) refnumber
);
6387 xmlFreeNode(request
);
6389 #else /* not HAVE_LIBCURL */
6396 /* Remove given box permanently.
6397 * @context is session context
6398 * @box is box description to delete. aifoIsds, address->adCode,
6399 * address->adDistrict members are ignored.
6400 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6402 * @approval is optional external approval of box manipulation
6403 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6404 * NULL, if you don't care.*/
6405 isds_error
isds_delete_box(struct isds_ctx
*context
,
6406 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6407 const struct isds_approval
*approval
, char **refnumber
) {
6408 if (!context
) return IE_INVALID_CONTEXT
;
6409 zfree(context
->long_message
);
6410 if (!box
|| !since
) return IE_INVAL
;
6412 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6413 box
, since
, approval
, refnumber
);
6417 /* Undocumented function.
6418 * @context is session context
6419 * @box is box description to delete. aifoIsds, address->adCode,
6420 * address->adDistrict members are ignored.
6421 * @approval is optional external approval of box manipulation
6422 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6423 * NULL, if you don't care.*/
6424 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6425 const struct isds_DbOwnerInfo
*box
,
6426 const struct isds_approval
*approval
, char **refnumber
) {
6427 if (!context
) return IE_INVALID_CONTEXT
;
6428 zfree(context
->long_message
);
6429 if (!box
) return IE_INVAL
;
6431 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6432 box
, NULL
, approval
, refnumber
);
6436 /* Update data about given box.
6437 * @context is session context
6438 * @old_box current box description. aifoIsds, address->adCode,
6439 * address->adDistrict members are ignored.
6440 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6441 * address->adDistrict members are ignored.
6442 * @approval is optional external approval of box manipulation
6443 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6444 * NULL, if you don't care.*/
6445 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6446 const struct isds_DbOwnerInfo
*old_box
,
6447 const struct isds_DbOwnerInfo
*new_box
,
6448 const struct isds_approval
*approval
, char **refnumber
) {
6449 isds_error err
= IE_SUCCESS
;
6451 xmlNsPtr isds_ns
= NULL
;
6452 xmlNodePtr request
= NULL
;
6457 if (!context
) return IE_INVALID_CONTEXT
;
6458 zfree(context
->long_message
);
6459 if (!old_box
|| !new_box
) return IE_INVAL
;
6463 /* Build UpdateDataBoxDescr request */
6464 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6466 isds_log_message(context
,
6467 _("Could build UpdateDataBoxDescr request"));
6470 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6472 isds_log_message(context
, _("Could not create ISDS name space"));
6473 xmlFreeNode(request
);
6476 xmlSetNs(request
, isds_ns
);
6478 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6479 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6480 if (err
) goto leave
;
6482 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6483 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6484 if (err
) goto leave
;
6486 err
= insert_GExtApproval(context
, approval
, request
);
6487 if (err
) goto leave
;
6490 /* Send it to server and process response */
6491 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6492 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6495 xmlFreeNode(request
);
6496 #else /* not HAVE_LIBCURL */
6505 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6507 * @context is session context
6508 * @service is SOAP service
6509 * @service_name is name of request in @service
6510 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6511 * @box_id is box ID of interest
6512 * @approval is optional external approval of box manipulation
6513 * @response is server SOAP body response as XML document
6514 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6515 * NULL, if you don't care.
6516 * @return error coded from lower layer, context message will be set up
6518 static isds_error
build_send_dbid_request_check_response(
6519 struct isds_ctx
*context
, const isds_service service
,
6520 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6521 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6522 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6524 isds_error err
= IE_SUCCESS
;
6525 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6526 xmlNodePtr request
= NULL
, node
;
6527 xmlNsPtr isds_ns
= NULL
;
6529 if (!context
) return IE_INVALID_CONTEXT
;
6530 if (!service_name
|| !box_id
) return IE_INVAL
;
6531 if (!response
) return IE_INVAL
;
6533 /* Free output argument */
6534 xmlFreeDoc(*response
); *response
= NULL
;
6536 /* Prepare strings */
6537 service_name_locale
= _isds_utf82locale((char*)service_name
);
6538 if (!service_name_locale
) {
6542 box_id_locale
= _isds_utf82locale((char*)box_id
);
6543 if (!box_id_locale
) {
6549 request
= xmlNewNode(NULL
, service_name
);
6551 isds_printf_message(context
,
6552 _("Could not build %s request for %s box"), service_name_locale
,
6557 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6559 isds_log_message(context
, _("Could not create ISDS name space"));
6563 xmlSetNs(request
, isds_ns
);
6565 /* Add XSD:tIdDbInput children */
6566 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6567 INSERT_STRING(request
, box_id_element
, box_id
);
6568 err
= insert_GExtApproval(context
, approval
, request
);
6569 if (err
) goto leave
;
6571 /* Send request and check response*/
6572 err
= send_destroy_request_check_response(context
,
6573 service
, service_name
, &request
, response
, refnumber
, NULL
);
6576 free(service_name_locale
);
6577 free(box_id_locale
);
6578 xmlFreeNode(request
);
6581 #endif /* HAVE_LIBCURL */
6584 /* Get data about all users assigned to given box.
6585 * @context is session context
6587 * @users is automatically reallocated list of struct isds_DbUserInfo */
6588 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6589 struct isds_list
**users
) {
6590 isds_error err
= IE_SUCCESS
;
6592 xmlDocPtr response
= NULL
;
6593 xmlXPathContextPtr xpath_ctx
= NULL
;
6594 xmlXPathObjectPtr result
= NULL
;
6596 struct isds_list
*item
, *prev_item
= NULL
;
6599 if (!context
) return IE_INVALID_CONTEXT
;
6600 zfree(context
->long_message
);
6601 if (!users
|| !box_id
) return IE_INVAL
;
6602 isds_list_free(users
);
6606 /* Do request and check for success */
6607 err
= build_send_dbid_request_check_response(context
,
6608 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6609 BAD_CAST box_id
, NULL
, &response
, NULL
);
6610 if (err
) goto leave
;
6614 /* Prepare structure */
6615 xpath_ctx
= xmlXPathNewContext(response
);
6620 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6625 /* Set context node */
6626 result
= xmlXPathEvalExpression(BAD_CAST
6627 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6633 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6634 /* Iterate over all users */
6635 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6637 /* Prepare structure */
6638 item
= calloc(1, sizeof(*item
));
6643 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6644 if (i
== 0) *users
= item
;
6645 else prev_item
->next
= item
;
6649 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6650 err
= extract_DbUserInfo(context
,
6651 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6652 if (err
) goto leave
;
6658 isds_list_free(users
);
6661 xmlXPathFreeObject(result
);
6662 xmlXPathFreeContext(xpath_ctx
);
6663 xmlFreeDoc(response
);
6666 isds_log(ILF_ISDS
, ILL_DEBUG
,
6667 _("GetDataBoxUsers request processed by server "
6668 "successfully.\n"));
6669 #else /* not HAVE_LIBCURL */
6677 /* Update data about user assigned to given box.
6678 * @context is session context
6679 * @box is box identification. aifoIsds, address->adCode,
6680 * address->adDistrict members are ignored.
6681 * @old_user identifies user to update, aifo_ticket member is ignored
6682 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6683 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6684 * NULL, if you don't care.*/
6685 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6686 const struct isds_DbOwnerInfo
*box
,
6687 const struct isds_DbUserInfo
*old_user
,
6688 const struct isds_DbUserInfo
*new_user
,
6690 isds_error err
= IE_SUCCESS
;
6692 xmlNsPtr isds_ns
= NULL
;
6693 xmlNodePtr request
= NULL
;
6698 if (!context
) return IE_INVALID_CONTEXT
;
6699 zfree(context
->long_message
);
6700 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6704 /* Build UpdateDataBoxUser request */
6705 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6707 isds_log_message(context
,
6708 _("Could build UpdateDataBoxUser request"));
6711 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6713 isds_log_message(context
, _("Could not create ISDS name space"));
6714 xmlFreeNode(request
);
6717 xmlSetNs(request
, isds_ns
);
6719 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6720 err
= insert_DbOwnerInfo(context
, box
, node
);
6721 if (err
) goto leave
;
6723 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6724 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
6725 if (err
) goto leave
;
6727 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6728 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
6729 if (err
) goto leave
;
6731 /* Send it to server and process response */
6732 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6733 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6736 xmlFreeNode(request
);
6737 #else /* not HAVE_LIBCURL */
6745 /* Undocumented function.
6746 * @context is session context
6747 * @box_id is UTF-8 encoded box identifier
6748 * @token is UTF-8 encoded temporary password
6749 * @user_id outputs UTF-8 encoded reallocated user identifier
6750 * @password outpus UTF-8 encoded reallocated user password
6751 * Output arguments will be nulled in case of error */
6752 isds_error
isds_activate(struct isds_ctx
*context
,
6753 const char *box_id
, const char *token
,
6754 char **user_id
, char **password
) {
6755 isds_error err
= IE_SUCCESS
;
6757 xmlNsPtr isds_ns
= NULL
;
6758 xmlNodePtr request
= NULL
, node
;
6759 xmlDocPtr response
= NULL
;
6760 xmlXPathContextPtr xpath_ctx
= NULL
;
6761 xmlXPathObjectPtr result
= NULL
;
6765 if (!context
) return IE_INVALID_CONTEXT
;
6766 zfree(context
->long_message
);
6768 if (user_id
) zfree(*user_id
);
6769 if (password
) zfree(*password
);
6771 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6775 /* Build Activate request */
6776 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6778 isds_log_message(context
, _("Could build Activate request"));
6781 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6783 isds_log_message(context
, _("Could not create ISDS name space"));
6784 xmlFreeNode(request
);
6787 xmlSetNs(request
, isds_ns
);
6789 INSERT_STRING(request
, "dbAccessDataId", token
);
6790 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6791 INSERT_STRING(request
, "dbID", box_id
);
6794 /* Send request and check response*/
6795 err
= send_destroy_request_check_response(context
,
6796 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6797 &response
, NULL
, NULL
);
6798 if (err
) goto leave
;
6802 xpath_ctx
= xmlXPathNewContext(response
);
6807 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6811 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6817 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6818 isds_log_message(context
, _("Missing ActivateResponse element"));
6822 if (result
->nodesetval
->nodeNr
> 1) {
6823 isds_log_message(context
, _("Multiple ActivateResponse element"));
6827 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6828 xmlXPathFreeObject(result
); result
= NULL
;
6830 EXTRACT_STRING("isds:userId", *user_id
);
6832 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6833 "but did not return `userId' element.\n"));
6835 EXTRACT_STRING("isds:password", *password
);
6837 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6838 "but did not return `password' element.\n"));
6841 xmlXPathFreeObject(result
);
6842 xmlXPathFreeContext(xpath_ctx
);
6843 xmlFreeDoc(response
);
6844 xmlFreeNode(request
);
6847 isds_log(ILF_ISDS
, ILL_DEBUG
,
6848 _("Activate request processed by server successfully.\n"));
6849 #else /* not HAVE_LIBCURL */
6857 /* Reset credentials of user assigned to given box.
6858 * @context is session context
6859 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6860 * members are ignored.
6861 * @user identifies user to reset password, aifo_ticket member is ignored
6862 * @fee_paid is true if fee has been paid, false otherwise
6863 * @approval is optional external approval of box manipulation
6864 * @credentials_delivery is NULL if new password should be delivered off-line
6865 * to the user. It is valid pointer if user should obtain new password on-line
6866 * on dedicated web server. Then input @credentials_delivery.email value is
6867 * user's e-mail address user must provide to dedicated web server together
6868 * with @credentials_delivery.token. The output reallocated token user needs
6869 * to use to authorize on the web server to view his new password. Output
6870 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6871 * ISDS changed up on this call. (No reason why server could change the name
6873 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6874 * NULL, if you don't care.*/
6875 isds_error
isds_reset_password(struct isds_ctx
*context
,
6876 const struct isds_DbOwnerInfo
*box
,
6877 const struct isds_DbUserInfo
*user
,
6878 const _Bool fee_paid
, const struct isds_approval
*approval
,
6879 struct isds_credentials_delivery
*credentials_delivery
,
6881 isds_error err
= IE_SUCCESS
;
6883 xmlNsPtr isds_ns
= NULL
;
6884 xmlNodePtr request
= NULL
, node
;
6885 xmlDocPtr response
= NULL
;
6889 if (!context
) return IE_INVALID_CONTEXT
;
6890 zfree(context
->long_message
);
6892 if (credentials_delivery
) {
6893 zfree(credentials_delivery
->token
);
6894 zfree(credentials_delivery
->new_user_name
);
6896 if (!box
|| !user
) return IE_INVAL
;
6900 /* Build NewAccessData request */
6901 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6903 isds_log_message(context
,
6904 _("Could build NewAccessData request"));
6907 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6909 isds_log_message(context
, _("Could not create ISDS name space"));
6910 xmlFreeNode(request
);
6913 xmlSetNs(request
, isds_ns
);
6915 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6916 err
= insert_DbOwnerInfo(context
, box
, node
);
6917 if (err
) goto leave
;
6919 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6920 err
= insert_DbUserInfo(context
, user
, 0, node
);
6921 if (err
) goto leave
;
6923 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6925 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6926 if (err
) goto leave
;
6928 err
= insert_GExtApproval(context
, approval
, request
);
6929 if (err
) goto leave
;
6931 /* Send request and check response*/
6932 err
= send_destroy_request_check_response(context
,
6933 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6934 &response
, (xmlChar
**) refnumber
, NULL
);
6935 if (err
) goto leave
;
6938 /* Extract optional token */
6939 err
= extract_credentials_delivery(context
, credentials_delivery
,
6940 response
, "NewAccessData");
6943 xmlFreeDoc(response
);
6944 xmlFreeNode(request
);
6947 isds_log(ILF_ISDS
, ILL_DEBUG
,
6948 _("NewAccessData request processed by server "
6949 "successfully.\n"));
6950 #else /* not HAVE_LIBCURL */
6958 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6959 * code, destroy response and log success.
6960 * @context is ISDS session context.
6961 * @service_name is name of SERVICE_DB_MANIPULATION service
6962 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6963 * members are ignored.
6964 * @user identifies user to remove
6965 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
6966 * @credentials_delivery is NULL if new user's password should be delivered
6967 * off-line to the user. It is valid pointer if user should obtain new
6968 * password on-line on dedicated web server. Then input
6969 * @credentials_delivery.email value is user's e-mail address user must
6970 * provide to dedicated web server together with @credentials_delivery.token.
6971 * The output reallocated token user needs to use to authorize on the web
6972 * server to view his new password. Output reallocated
6973 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6974 * assingned or changed up on this call.
6975 * @approval is optional external approval of box manipulation
6976 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6977 * NULL, if you don't care. */
6978 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6979 struct isds_ctx
*context
, const xmlChar
*service_name
,
6980 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6981 _Bool honor_aifo_ticket
,
6982 struct isds_credentials_delivery
*credentials_delivery
,
6983 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6984 isds_error err
= IE_SUCCESS
;
6986 xmlNsPtr isds_ns
= NULL
;
6987 xmlNodePtr request
= NULL
, node
;
6988 xmlDocPtr response
= NULL
;
6992 if (!context
) return IE_INVALID_CONTEXT
;
6993 zfree(context
->long_message
);
6994 if (credentials_delivery
) {
6995 zfree(credentials_delivery
->token
);
6996 zfree(credentials_delivery
->new_user_name
);
6998 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7003 /* Build NewAccessData or similar request */
7004 request
= xmlNewNode(NULL
, service_name
);
7006 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7007 isds_printf_message(context
, _("Could not build %s request"),
7008 service_name_locale
);
7009 free(service_name_locale
);
7012 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7014 isds_log_message(context
, _("Could not create ISDS name space"));
7015 xmlFreeNode(request
);
7018 xmlSetNs(request
, isds_ns
);
7020 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7021 err
= insert_DbOwnerInfo(context
, box
, node
);
7022 if (err
) goto leave
;
7024 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7025 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7026 if (err
) goto leave
;
7028 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7029 if (err
) goto leave
;
7031 err
= insert_GExtApproval(context
, approval
, request
);
7032 if (err
) goto leave
;
7035 /* Send request and check response*/
7036 err
= send_destroy_request_check_response(context
,
7037 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7040 xmlFreeNode(request
);
7043 /* Pick up credentials_delivery if requested */
7044 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7045 (char *)service_name
);
7048 xmlFreeDoc(response
);
7049 if (request
) xmlFreeNode(request
);
7052 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7053 isds_log(ILF_ISDS
, ILL_DEBUG
,
7054 _("%s request processed by server successfully.\n"),
7055 service_name_locale
);
7056 free(service_name_locale
);
7058 #else /* not HAVE_LIBCURL */
7066 /* Assign new user to given box.
7067 * @context is session context
7068 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7069 * members are ignored.
7070 * @user defines new user to add
7071 * @credentials_delivery is NULL if new user's password should be delivered
7072 * off-line to the user. It is valid pointer if user should obtain new
7073 * password on-line on dedicated web server. Then input
7074 * @credentials_delivery.email value is user's e-mail address user must
7075 * provide to dedicated web server together with @credentials_delivery.token.
7076 * The output reallocated token user needs to use to authorize on the web
7077 * server to view his new password. Output reallocated
7078 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7079 * assingned up on this call.
7080 * @approval is optional external approval of box manipulation
7081 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7082 * NULL, if you don't care.*/
7083 isds_error
isds_add_user(struct isds_ctx
*context
,
7084 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7085 struct isds_credentials_delivery
*credentials_delivery
,
7086 const struct isds_approval
*approval
, char **refnumber
) {
7087 return build_send_manipulationboxuser_request_check_drop_response(context
,
7088 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7089 approval
, (xmlChar
**) refnumber
);
7093 /* Remove user assigned to given box.
7094 * @context is session context
7095 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7096 * members are ignored.
7097 * @user identifies user to remove, aifo_ticket member is ignored
7098 * @approval is optional external approval of box manipulation
7099 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7100 * NULL, if you don't care.*/
7101 isds_error
isds_delete_user(struct isds_ctx
*context
,
7102 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7103 const struct isds_approval
*approval
, char **refnumber
) {
7104 return build_send_manipulationboxuser_request_check_drop_response(context
,
7105 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7106 (xmlChar
**) refnumber
);
7110 /* Get list of boxes in ZIP archive.
7111 * @context is session context
7112 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7113 * System recognizes following values currently: ALL (all boxes), UPG
7114 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7115 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7116 * commercial messages). This argument is a string because specification
7117 * states new values can appear in the future. Not all list types are
7118 * available to all users.
7119 * @buffer is automatically reallocated memory to store the list of boxes. The
7120 * list is zipped CSV file.
7121 * @buffer_length is size of @buffer data in bytes.
7122 * In case of error @buffer will be freed and @buffer_length will be
7124 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7125 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7126 isds_error err
= IE_SUCCESS
;
7128 xmlNsPtr isds_ns
= NULL
;
7129 xmlNodePtr request
= NULL
, node
;
7130 xmlDocPtr response
= NULL
;
7131 xmlXPathContextPtr xpath_ctx
= NULL
;
7132 xmlXPathObjectPtr result
= NULL
;
7133 char *string
= NULL
;
7137 if (!context
) return IE_INVALID_CONTEXT
;
7138 zfree(context
->long_message
);
7139 if (buffer
) zfree(*buffer
);
7140 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7144 /* Check if connection is established
7145 * TODO: This check should be done downstairs. */
7146 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7149 /* Build AuthenticateMessage request */
7150 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7152 isds_log_message(context
,
7153 _("Could not build GetDataBoxList request"));
7156 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7158 isds_log_message(context
, _("Could not create ISDS name space"));
7159 xmlFreeNode(request
);
7162 xmlSetNs(request
, isds_ns
);
7163 INSERT_STRING(request
, "dblType", list_identifier
);
7165 /* Send request to server and process response */
7166 err
= send_destroy_request_check_response(context
,
7167 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7168 &response
, NULL
, NULL
);
7169 if (err
) goto leave
;
7172 /* Extract Base-64 encoded ZIP file */
7173 xpath_ctx
= xmlXPathNewContext(response
);
7178 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7182 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7184 /* Decode non-empty archive */
7185 if (string
&& string
[0] != '\0') {
7186 *buffer_length
= _isds_b64decode(string
, buffer
);
7187 if (*buffer_length
== (size_t) -1) {
7188 isds_printf_message(context
,
7189 _("Error while Base64-decoding box list archive"));
7198 xmlXPathFreeObject(result
);
7199 xmlXPathFreeContext(xpath_ctx
);
7200 xmlFreeDoc(response
);
7201 xmlFreeNode(request
);
7204 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7205 "processed by server successfully.\n"));
7207 #else /* not HAVE_LIBCURL */
7215 /* Find boxes suiting given criteria.
7216 * @criteria is filter. You should fill in at least some members. aifoIsds,
7217 * address->adCode, address->adDistrict members are ignored.
7218 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7219 * possibly empty. Input NULL or valid old structure.
7221 * IE_SUCCESS if search succeeded, @boxes contains useful data
7222 * IE_NOEXIST if no such box exists, @boxes will be NULL
7223 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7224 * contains still valid data
7225 * other code if something bad happens. @boxes will be NULL. */
7226 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7227 const struct isds_DbOwnerInfo
*criteria
,
7228 struct isds_list
**boxes
) {
7229 isds_error err
= IE_SUCCESS
;
7231 _Bool truncated
= 0;
7232 xmlNsPtr isds_ns
= NULL
;
7233 xmlNodePtr request
= NULL
;
7234 xmlDocPtr response
= NULL
;
7235 xmlChar
*code
= NULL
, *message
= NULL
;
7236 xmlNodePtr db_owner_info
;
7237 xmlXPathContextPtr xpath_ctx
= NULL
;
7238 xmlXPathObjectPtr result
= NULL
;
7239 xmlChar
*string
= NULL
;
7243 if (!context
) return IE_INVALID_CONTEXT
;
7244 zfree(context
->long_message
);
7245 if (!boxes
) return IE_INVAL
;
7246 isds_list_free(boxes
);
7253 /* Check if connection is established
7254 * TODO: This check should be done downstairs. */
7255 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7258 /* Build FindDataBox request */
7259 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7261 isds_log_message(context
,
7262 _("Could build FindDataBox request"));
7265 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7267 isds_log_message(context
, _("Could not create ISDS name space"));
7268 xmlFreeNode(request
);
7271 xmlSetNs(request
, isds_ns
);
7272 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7273 if (!db_owner_info
) {
7274 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7275 "FindDataBox element"));
7276 xmlFreeNode(request
);
7280 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7281 if (err
) goto leave
;
7284 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7287 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7289 /* Destroy request */
7290 xmlFreeNode(request
); request
= NULL
;
7293 isds_log(ILF_ISDS
, ILL_DEBUG
,
7294 _("Processing ISDS response on FindDataBox "
7295 "request failed\n"));
7299 /* Check for response status */
7300 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7301 &code
, &message
, NULL
);
7303 isds_log(ILF_ISDS
, ILL_DEBUG
,
7304 _("ISDS response on FindDataBox request is missing status\n"));
7308 /* Request processed, but nothing found */
7309 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7310 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7311 char *code_locale
= _isds_utf82locale((char*)code
);
7312 char *message_locale
= _isds_utf82locale((char*)message
);
7313 isds_log(ILF_ISDS
, ILL_DEBUG
,
7314 _("Server did not found any box on FindDataBox request "
7315 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7316 isds_log_message(context
, message_locale
);
7318 free(message_locale
);
7323 /* Warning, not a error */
7324 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7325 char *code_locale
= _isds_utf82locale((char*)code
);
7326 char *message_locale
= _isds_utf82locale((char*)message
);
7327 isds_log(ILF_ISDS
, ILL_DEBUG
,
7328 _("Server truncated response on FindDataBox request "
7329 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7330 isds_log_message(context
, message_locale
);
7332 free(message_locale
);
7337 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7338 char *code_locale
= _isds_utf82locale((char*)code
);
7339 char *message_locale
= _isds_utf82locale((char*)message
);
7340 isds_log(ILF_ISDS
, ILL_DEBUG
,
7341 _("Server refused FindDataBox request "
7342 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7343 isds_log_message(context
, message_locale
);
7345 free(message_locale
);
7350 xpath_ctx
= xmlXPathNewContext(response
);
7355 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7360 /* Extract boxes if they present */
7361 result
= xmlXPathEvalExpression(BAD_CAST
7362 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7368 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7369 struct isds_list
*item
, *prev_item
= NULL
;
7370 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7371 item
= calloc(1, sizeof(*item
));
7377 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7378 if (i
== 0) *boxes
= item
;
7379 else prev_item
->next
= item
;
7382 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7383 err
= extract_DbOwnerInfo(context
,
7384 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7385 if (err
) goto leave
;
7391 isds_list_free(boxes
);
7393 if (truncated
) err
= IE_2BIG
;
7397 xmlFreeNode(request
);
7398 xmlXPathFreeObject(result
);
7399 xmlXPathFreeContext(xpath_ctx
);
7403 xmlFreeDoc(response
);
7406 isds_log(ILF_ISDS
, ILL_DEBUG
,
7407 _("FindDataBox request processed by server successfully.\n"));
7408 #else /* not HAVE_LIBCURL */
7417 /* Convert a string with match markers into a plain string with list of
7418 * pointers to the matches
7419 * @string is an UTF-8 encoded non-constant string with match markers
7420 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7421 * The markers will be removed from the string.
7422 * @starts is a reallocated list of static pointers into the @string pointing
7423 * to places where match start markers occured.
7424 * @ends is a reallocated list of static pointers into the @string pointing
7425 * to places where match end markers occured.
7426 * @return IE_SUCCESS in case of no failure. */
7427 static isds_error
interpret_matches(xmlChar
*string
,
7428 struct isds_list
**starts
, struct isds_list
**ends
) {
7429 isds_error err
= IE_SUCCESS
;
7430 xmlChar
*pointer
, *destination
, *source
;
7431 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7433 isds_list_free(starts
);
7434 isds_list_free(ends
);
7435 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7436 if (NULL
== string
) return IE_SUCCESS
;
7438 for (pointer
= string
; *pointer
!= '\0';) {
7439 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7440 /* Remove the start marker */
7441 for (source
= pointer
+ 14, destination
= pointer
;
7442 *source
!= '\0'; source
++, destination
++) {
7443 *destination
= *source
;
7445 *destination
= '\0';
7446 /* Append the pointer into the list */
7447 item
= calloc(1, sizeof(*item
));
7452 item
->destructor
= (void (*)(void **))NULL
;
7453 item
->data
= pointer
;
7454 if (NULL
== prev_start
) *starts
= item
;
7455 else prev_start
->next
= item
;
7457 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7458 /* Remove the end marker */
7459 for (source
= pointer
+ 12, destination
= pointer
;
7460 *source
!= '\0'; source
++, destination
++) {
7461 *destination
= *source
;
7463 *destination
= '\0';
7464 /* Append the pointer into the list */
7465 item
= calloc(1, sizeof(*item
));
7470 item
->destructor
= (void (*)(void **))NULL
;
7471 item
->data
= pointer
;
7472 if (NULL
== prev_end
) *ends
= item
;
7473 else prev_end
->next
= item
;
7482 isds_list_free(starts
);
7483 isds_list_free(ends
);
7489 /* Convert isds:dbResult XML tree into structure
7490 * @context is ISDS context.
7491 * @fulltext_result is automatically reallocated found box structure.
7492 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7493 * @collect_matches is true to interpret match markers.
7494 * In case of error @result will be freed. */
7495 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7496 struct isds_fulltext_result
**fulltext_result
,
7497 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7498 isds_error err
= IE_SUCCESS
;
7499 xmlXPathObjectPtr result
= NULL
;
7500 char *string
= NULL
;
7502 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7503 if (NULL
== fulltext_result
) return IE_INVAL
;
7504 isds_fulltext_result_free(fulltext_result
);
7505 if (!xpath_ctx
) return IE_INVAL
;
7508 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7509 if (NULL
== *fulltext_result
) {
7515 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7517 EXTRACT_STRING("isds:dbType", string
);
7518 if (NULL
== string
) {
7520 isds_log_message(context
, _("Empty isds:dbType element"));
7523 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7525 if (err
== IE_ENUM
) {
7527 char *string_locale
= _isds_utf82locale(string
);
7528 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7530 free(string_locale
);
7536 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7537 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7539 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7540 if (err
) goto leave
;
7542 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7543 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7544 (*fulltext_result
)->dbEffectiveOVM
);
7546 EXTRACT_STRING("isds:dbSendOptions", string
);
7547 if (NULL
== string
) {
7549 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7552 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7553 (*fulltext_result
)->active
= 1;
7554 (*fulltext_result
)->public_sending
= 1;
7555 (*fulltext_result
)->commercial_sending
= 0;
7556 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7557 (*fulltext_result
)->active
= 1;
7558 (*fulltext_result
)->public_sending
= 1;
7559 (*fulltext_result
)->commercial_sending
= 1;
7560 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7561 (*fulltext_result
)->active
= 1;
7562 (*fulltext_result
)->public_sending
= 0;
7563 (*fulltext_result
)->commercial_sending
= 1;
7564 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7565 (*fulltext_result
)->active
= 1;
7566 (*fulltext_result
)->public_sending
= 0;
7567 (*fulltext_result
)->commercial_sending
= 0;
7568 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7569 (*fulltext_result
)->active
= 0;
7570 (*fulltext_result
)->public_sending
= 0;
7571 (*fulltext_result
)->commercial_sending
= 0;
7574 char *string_locale
= _isds_utf82locale(string
);
7575 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7577 free(string_locale
);
7582 /* Interpret match marks */
7583 if (collect_matches
) {
7584 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7585 &((*fulltext_result
)->name_match_start
),
7586 &((*fulltext_result
)->name_match_end
));
7587 if (err
) goto leave
;
7588 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7589 &((*fulltext_result
)->address_match_start
),
7590 &((*fulltext_result
)->address_match_end
));
7591 if (err
) goto leave
;
7595 if (err
) isds_fulltext_result_free(fulltext_result
);
7597 xmlXPathFreeObject(result
);
7600 #endif /* HAVE_LIBCURL */
7603 /* Find boxes matching a given full-text criteria.
7604 * @context is a session context
7605 * @query is a non-empty string which consists of words to search
7606 * @target selects box attributes to search for @query words. Pass NULL if you
7608 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7609 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7610 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7611 * which is DBTYPE_SYSTEM.
7612 * @page_size defines count of boxes to constitute a response page. It counts
7613 * from zero. Pass NULL to let server to use a default value (50 now).
7614 * @page_number defines ordinar number of the response page to return. It
7615 * counts from zero. Pass NULL to let server to use a default value (0 now).
7616 * @track_matches points to true for marking @query words found in the box
7617 * attributes. It points to false for not marking. Pass NULL to let the server
7618 * to use default value (false now).
7619 * @total_matching_boxes outputs reallocated number of all boxes matching the
7620 * query. Will be pointer to NULL if server did not provide the value.
7621 * Pass NULL if you don't care.
7622 * @current_page_beginning outputs reallocated ordinar number of the first box
7623 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7624 * server did not provide the value. Pass NULL if you don't care.
7625 * @current_page_size outputs reallocated count of boxes in the this @boxes
7626 * page. It will be pointer to NULL if the server did not provide the value.
7627 * Pass NULL if you don't care.
7628 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7629 * is the last one, false if more boxes match, NULL if the server did not
7630 * provude the value. Pass NULL if you don't care.
7631 * @boxes outputs reallocated list of isds_fulltext_result structures,
7634 * IE_SUCCESS if search succeeded
7635 * IE_2BIG if @page_size is too large
7636 * other code if something bad happens; output arguments will be NULL. */
7637 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7639 const isds_fulltext_target
*target
,
7640 const isds_DbType
*box_type
,
7641 const unsigned long int *page_size
,
7642 const unsigned long int *page_number
,
7643 const _Bool
*track_matches
,
7644 unsigned long int **total_matching_boxes
,
7645 unsigned long int **current_page_beginning
,
7646 unsigned long int **current_page_size
,
7648 struct isds_list
**boxes
) {
7649 isds_error err
= IE_SUCCESS
;
7651 xmlNsPtr isds_ns
= NULL
;
7652 xmlNodePtr request
= NULL
;
7653 xmlDocPtr response
= NULL
;
7655 xmlXPathContextPtr xpath_ctx
= NULL
;
7656 xmlXPathObjectPtr result
= NULL
;
7657 const xmlChar
*static_string
= NULL
;
7658 xmlChar
*string
= NULL
;
7660 const xmlChar
*codes
[] = {
7670 const char *meanings
[] = {
7671 N_("You are not allowed to perform the search"),
7672 N_("The query string is empty"),
7673 N_("Searched box ID is malformed"),
7674 N_("Searched organization ID is malformed"),
7675 N_("Invalid input"),
7676 N_("Requested page size is too large"),
7677 N_("Search engine internal error")
7679 const isds_error errors
[] = {
7688 struct code_map_isds_error map
= {
7690 .meanings
= meanings
,
7696 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7697 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7698 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7699 if (NULL
!= last_page
) zfree(*last_page
);
7700 isds_list_free(boxes
);
7702 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7703 zfree(context
->long_message
);
7705 if (NULL
== boxes
) return IE_INVAL
;
7707 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7708 isds_log_message(context
, _("Query string must be non-empty"));
7713 /* Check if connection is established
7714 * TODO: This check should be done downstairs. */
7715 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7717 /* Build FindDataBox request */
7718 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7719 if (NULL
== request
) {
7720 isds_log_message(context
,
7721 _("Could not build ISDSSearch2 request"));
7724 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7725 if(NULL
== isds_ns
) {
7726 isds_log_message(context
, _("Could not create ISDS name space"));
7727 xmlFreeNode(request
);
7730 xmlSetNs(request
, isds_ns
);
7732 INSERT_STRING(request
, "searchText", query
);
7734 if (NULL
!= target
) {
7735 static_string
= isds_fulltext_target2string(*(target
));
7736 if (NULL
== static_string
) {
7737 isds_printf_message(context
, _("Invalid target value: %d"),
7743 INSERT_STRING(request
, "searchType", static_string
);
7744 static_string
= NULL
;
7746 if (NULL
!= box_type
) {
7747 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7748 if (DBTYPE_SYSTEM
== *box_type
) {
7749 static_string
= BAD_CAST
"ALL";
7750 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
7751 static_string
= BAD_CAST
"OVM_MAIN";
7753 static_string
= isds_DbType2string(*(box_type
));
7754 if (NULL
== static_string
) {
7755 isds_printf_message(context
, _("Invalid box type value: %d"),
7762 INSERT_STRING(request
, "searchScope", static_string
);
7763 static_string
= NULL
;
7765 INSERT_ULONGINT(request
, "page", page_number
, string
);
7766 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7767 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7769 /* Send request and check response */
7770 err
= send_destroy_request_check_response(context
,
7771 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7772 &request
, &response
, NULL
, &map
);
7773 if (err
) goto leave
;
7775 /* Parse response */
7776 xpath_ctx
= xmlXPathNewContext(response
);
7777 if (NULL
== xpath_ctx
) {
7781 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7785 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7791 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7792 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7796 if (result
->nodesetval
->nodeNr
> 1) {
7797 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7801 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7802 xmlXPathFreeObject(result
); result
= NULL
;
7805 /* Extract counters */
7806 if (NULL
!= total_matching_boxes
) {
7807 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7809 if (NULL
!= current_page_size
) {
7810 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7812 if (NULL
!= current_page_beginning
) {
7813 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7815 if (NULL
!= last_page
) {
7816 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7818 xmlXPathFreeObject(result
); result
= NULL
;
7820 /* Extract boxes if they present */
7821 result
= xmlXPathEvalExpression(BAD_CAST
7822 "isds:dbResults/isds:dbResult", xpath_ctx
);
7823 if (NULL
== result
) {
7827 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7828 struct isds_list
*item
, *prev_item
= NULL
;
7829 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7830 item
= calloc(1, sizeof(*item
));
7836 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7837 if (i
== 0) *boxes
= item
;
7838 else prev_item
->next
= item
;
7841 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7842 err
= extract_dbResult(context
,
7843 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7844 (NULL
== track_matches
) ? 0 : *track_matches
);
7845 if (err
) goto leave
;
7851 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7852 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7853 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7854 if (NULL
!= last_page
) zfree(*last_page
);
7855 isds_list_free(boxes
);
7859 xmlFreeNode(request
);
7860 xmlXPathFreeObject(result
);
7861 xmlXPathFreeContext(xpath_ctx
);
7862 xmlFreeDoc(response
);
7865 isds_log(ILF_ISDS
, ILL_DEBUG
,
7866 _("ISDSSearch2 request processed by server successfully.\n"));
7867 #else /* not HAVE_LIBCURL */
7875 /* Get status of a box.
7876 * @context is ISDS session context.
7877 * @box_id is UTF-8 encoded box identifier as zero terminated string
7878 * @box_status is return value of box status.
7880 * IE_SUCCESS if box has been found and its status retrieved
7881 * IE_NOEXIST if box is not known to ISDS server
7882 * or other appropriate error.
7883 * You can use isds_DbState to enumerate box status. However out of enum
7884 * range value can be returned too. This is feature because ISDS
7885 * specification leaves the set of values open.
7886 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7887 * the box has been deleted, but ISDS still lists its former existence. */
7888 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7889 long int *box_status
) {
7890 isds_error err
= IE_SUCCESS
;
7892 xmlNsPtr isds_ns
= NULL
;
7893 xmlNodePtr request
= NULL
, db_id
;
7894 xmlDocPtr response
= NULL
;
7895 xmlXPathContextPtr xpath_ctx
= NULL
;
7896 xmlXPathObjectPtr result
= NULL
;
7897 xmlChar
*string
= NULL
;
7899 const xmlChar
*codes
[] = {
7905 const char *meanings
[] = {
7906 "The box does not exist",
7907 "Box ID is malformed",
7910 const isds_error errors
[] = {
7915 struct code_map_isds_error map
= {
7917 .meanings
= meanings
,
7922 if (!context
) return IE_INVALID_CONTEXT
;
7923 zfree(context
->long_message
);
7924 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7927 /* Check if connection is established
7928 * TODO: This check should be done downstairs. */
7929 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7932 /* Build CheckDataBox request */
7933 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7935 isds_log_message(context
,
7936 _("Could build CheckDataBox request"));
7939 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7941 isds_log_message(context
, _("Could not create ISDS name space"));
7942 xmlFreeNode(request
);
7945 xmlSetNs(request
, isds_ns
);
7946 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7948 isds_log_message(context
, _("Could not add dbID child to "
7949 "CheckDataBox element"));
7950 xmlFreeNode(request
);
7955 /* Send request and check response*/
7956 err
= send_destroy_request_check_response(context
,
7957 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7958 &request
, &response
, NULL
, &map
);
7959 if (err
) goto leave
;
7963 xpath_ctx
= xmlXPathNewContext(response
);
7968 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7972 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7978 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7979 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7983 if (result
->nodesetval
->nodeNr
> 1) {
7984 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7988 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7989 xmlXPathFreeObject(result
); result
= NULL
;
7991 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7996 xmlXPathFreeObject(result
);
7997 xmlXPathFreeContext(xpath_ctx
);
7999 xmlFreeDoc(response
);
8002 isds_log(ILF_ISDS
, ILL_DEBUG
,
8003 _("CheckDataBox request processed by server successfully.\n"));
8004 #else /* not HAVE_LIBCURL */
8012 /* Get list of permissions to send commercial messages.
8013 * @context is ISDS session context.
8014 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8015 * @permissions is a reallocated list of permissions (struct
8016 * isds_commercial_permission*) to send commercial messages from @box_id. The
8017 * order of permissions is significant as the server applies the permissions
8018 * and associated pre-paid credits in the order. Empty list means no
8021 * IE_SUCCESS if the list has been obtained correctly,
8022 * or other appropriate error. */
8023 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8024 const char *box_id
, struct isds_list
**permissions
) {
8025 isds_error err
= IE_SUCCESS
;
8027 xmlDocPtr response
= NULL
;
8028 xmlXPathContextPtr xpath_ctx
= NULL
;
8029 xmlXPathObjectPtr result
= NULL
;
8032 if (!context
) return IE_INVALID_CONTEXT
;
8033 zfree(context
->long_message
);
8034 if (NULL
== permissions
) return IE_INVAL
;
8035 isds_list_free(permissions
);
8036 if (NULL
== box_id
) return IE_INVAL
;
8039 /* Check if connection is established */
8040 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8042 /* Do request and check for success */
8043 err
= build_send_dbid_request_check_response(context
,
8044 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8045 BAD_CAST box_id
, NULL
, &response
, NULL
);
8047 isds_log(ILF_ISDS
, ILL_DEBUG
,
8048 _("PDZInfo request processed by server successfully.\n"));
8052 /* Prepare structure */
8053 xpath_ctx
= xmlXPathNewContext(response
);
8058 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8063 /* Set context node */
8064 result
= xmlXPathEvalExpression(BAD_CAST
8065 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8071 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8072 struct isds_list
*prev_item
= NULL
;
8074 /* Iterate over all permission records */
8075 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8076 struct isds_list
*item
;
8078 /* Prepare structure */
8079 item
= calloc(1, sizeof(*item
));
8084 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8085 if (i
== 0) *permissions
= item
;
8086 else prev_item
->next
= item
;
8090 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8091 err
= extract_DbPDZRecord(context
,
8092 (struct isds_commercial_permission
**) (&item
->data
),
8094 if (err
) goto leave
;
8100 isds_list_free(permissions
);
8103 xmlXPathFreeObject(result
);
8104 xmlXPathFreeContext(xpath_ctx
);
8105 xmlFreeDoc(response
);
8107 #else /* not HAVE_LIBCURL */
8115 /* Get details about credit for sending pre-paid commercial messages.
8116 * @context is ISDS session context.
8117 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8118 * @from_date is first day of credit history to return in @history. Only
8119 * tm_year, tm_mon and tm_mday carry sane value.
8120 * @to_date is last day of credit history to return in @history. Only
8121 * tm_year, tm_mon and tm_mday carry sane value.
8122 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8123 * if you don't care. This and all other credit values are integers in
8124 * hundredths of Czech Crowns.
8125 * @email outputs notification e-mail address where notifications about credit
8126 * are sent. This is automatically reallocated string. Pass NULL if you don't
8127 * care. It can return NULL if no address is defined.
8128 * @history outputs auto-reallocated list of pointers to struct
8129 * isds_credit_event. Events in closed interval @from_time to @to_time are
8130 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8131 * are sorted by time.
8133 * IE_SUCCESS if the credit details have been obtained correctly,
8134 * or other appropriate error. Please note that server allows to retrieve
8135 * only limited history of events. */
8136 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8138 const struct tm
*from_date
, const struct tm
*to_date
,
8139 long int *credit
, char **email
, struct isds_list
**history
) {
8140 isds_error err
= IE_SUCCESS
;
8142 char *box_id_locale
= NULL
;
8143 xmlNodePtr request
= NULL
, node
;
8144 xmlNsPtr isds_ns
= NULL
;
8145 xmlChar
*string
= NULL
;
8147 xmlDocPtr response
= NULL
;
8148 xmlXPathContextPtr xpath_ctx
= NULL
;
8149 xmlXPathObjectPtr result
= NULL
;
8151 const xmlChar
*codes
[] = {
8159 const char *meanings
[] = {
8160 "Insufficient priviledges for the box",
8161 "The box does not exist",
8162 "Date is too long (history is not available after 15 months)",
8163 "Interval is too long (limit is 3 months)",
8166 const isds_error errors
[] = {
8173 struct code_map_isds_error map
= {
8175 .meanings
= meanings
,
8180 if (!context
) return IE_INVALID_CONTEXT
;
8181 zfree(context
->long_message
);
8183 /* Free output argument */
8184 if (NULL
!= credit
) *credit
= 0;
8185 if (NULL
!= email
) zfree(*email
);
8186 isds_list_free(history
);
8188 if (NULL
== box_id
) return IE_INVAL
;
8191 /* Check if connection is established */
8192 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8194 box_id_locale
= _isds_utf82locale((char*)box_id
);
8195 if (NULL
== box_id_locale
) {
8201 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8202 if (NULL
== request
) {
8203 isds_printf_message(context
,
8204 _("Could not build DataBoxCreditInfo request for %s box"),
8209 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8211 isds_log_message(context
, _("Could not create ISDS name space"));
8215 xmlSetNs(request
, isds_ns
);
8217 /* Add mandatory XSD:tIdDbInput child */
8218 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8219 /* Add mandatory dates elements with optional values */
8221 err
= tm2datestring(from_date
, &string
);
8223 isds_log_message(context
,
8224 _("Could not convert `from_date' argument to ISO date "
8228 INSERT_STRING(request
, "ciFromDate", string
);
8231 INSERT_STRING(request
, "ciFromDate", NULL
);
8234 err
= tm2datestring(to_date
, &string
);
8236 isds_log_message(context
,
8237 _("Could not convert `to_date' argument to ISO date "
8241 INSERT_STRING(request
, "ciTodate", string
);
8244 INSERT_STRING(request
, "ciTodate", NULL
);
8247 /* Send request and check response*/
8248 err
= send_destroy_request_check_response(context
,
8249 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8250 &request
, &response
, NULL
, &map
);
8251 if (err
) goto leave
;
8255 /* Set context to the root */
8256 xpath_ctx
= xmlXPathNewContext(response
);
8261 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8265 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8271 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8272 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8276 if (result
->nodesetval
->nodeNr
> 1) {
8277 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8281 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8282 xmlXPathFreeObject(result
); result
= NULL
;
8284 /* Extract common data */
8285 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8286 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8288 /* Extract records */
8289 if (NULL
== history
) goto leave
;
8290 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8296 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8297 struct isds_list
*prev_item
= NULL
;
8299 /* Iterate over all records */
8300 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8301 struct isds_list
*item
;
8303 /* Prepare structure */
8304 item
= calloc(1, sizeof(*item
));
8309 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8310 if (i
== 0) *history
= item
;
8311 else prev_item
->next
= item
;
8315 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8316 err
= extract_CiRecord(context
,
8317 (struct isds_credit_event
**) (&item
->data
),
8319 if (err
) goto leave
;
8325 isds_log(ILF_ISDS
, ILL_DEBUG
,
8326 _("DataBoxCreditInfo request processed by server successfully.\n"));
8329 isds_list_free(history
);
8330 if (NULL
!= email
) zfree(*email
)
8333 free(box_id_locale
);
8334 xmlXPathFreeObject(result
);
8335 xmlXPathFreeContext(xpath_ctx
);
8336 xmlFreeDoc(response
);
8338 #else /* not HAVE_LIBCURL */
8346 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8347 * code, destroy response and log success.
8348 * @context is ISDS session context.
8349 * @service_name is name of SERVICE_DB_MANIPULATION service
8350 * @box_id is UTF-8 encoded box identifier as zero terminated string
8351 * @approval is optional external approval of box manipulation
8352 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8353 * NULL, if you don't care. */
8354 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8355 struct isds_ctx
*context
, const xmlChar
*service_name
,
8356 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8357 xmlChar
**refnumber
) {
8358 isds_error err
= IE_SUCCESS
;
8360 xmlDocPtr response
= NULL
;
8363 if (!context
) return IE_INVALID_CONTEXT
;
8364 zfree(context
->long_message
);
8365 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8368 /* Check if connection is established */
8369 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8371 /* Do request and check for success */
8372 err
= build_send_dbid_request_check_response(context
,
8373 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8374 &response
, refnumber
);
8375 xmlFreeDoc(response
);
8378 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8379 isds_log(ILF_ISDS
, ILL_DEBUG
,
8380 _("%s request processed by server successfully.\n"),
8381 service_name_locale
);
8382 free(service_name_locale
);
8384 #else /* not HAVE_LIBCURL */
8392 /* Switch box into state where box can receive commercial messages (off by
8394 * @context is ISDS session context.
8395 * @box_id is UTF-8 encoded box identifier as zero terminated string
8396 * @allow is true for enable, false for disable commercial messages income
8397 * @approval is optional external approval of box manipulation
8398 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8399 * NULL, if you don't care. */
8400 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8401 const char *box_id
, const _Bool allow
,
8402 const struct isds_approval
*approval
, char **refnumber
) {
8403 return build_send_manipulationdbid_request_check_drop_response(context
,
8404 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8405 BAD_CAST
"ClearOpenAddressing",
8406 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8410 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8411 * message acceptance). This is just a box permission. Sender must apply
8412 * such role by sending each message.
8413 * @context is ISDS session context.
8414 * @box_id is UTF-8 encoded box identifier as zero terminated string
8415 * @allow is true for enable, false for disable OVM role permission
8416 * @approval is optional external approval of box manipulation
8417 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8418 * NULL, if you don't care. */
8419 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8420 const char *box_id
, const _Bool allow
,
8421 const struct isds_approval
*approval
, char **refnumber
) {
8422 return build_send_manipulationdbid_request_check_drop_response(context
,
8423 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8424 BAD_CAST
"ClearEffectiveOVM",
8425 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8429 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8430 * code, destroy response and log success.
8431 * @context is ISDS session context.
8432 * @service_name is name of SERVICE_DB_MANIPULATION service
8433 * @owner is structure describing box. aifoIsds, address->adCode,
8434 * address->adDistrict members are ignored.
8435 * @approval is optional external approval of box manipulation
8436 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8437 * NULL, if you don't care. */
8438 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8439 struct isds_ctx
*context
, const xmlChar
*service_name
,
8440 const struct isds_DbOwnerInfo
*owner
,
8441 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8442 isds_error err
= IE_SUCCESS
;
8444 char *service_name_locale
= NULL
;
8445 xmlNodePtr request
= NULL
, db_owner_info
;
8446 xmlNsPtr isds_ns
= NULL
;
8450 if (!context
) return IE_INVALID_CONTEXT
;
8451 zfree(context
->long_message
);
8452 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8455 service_name_locale
= _isds_utf82locale((char*)service_name
);
8456 if (!service_name_locale
) {
8462 request
= xmlNewNode(NULL
, service_name
);
8464 isds_printf_message(context
,
8465 _("Could not build %s request"), service_name_locale
);
8469 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8471 isds_log_message(context
, _("Could not create ISDS name space"));
8475 xmlSetNs(request
, isds_ns
);
8478 /* Add XSD:tOwnerInfoInput child*/
8479 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8480 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8481 if (err
) goto leave
;
8483 /* Add XSD:gExtApproval*/
8484 err
= insert_GExtApproval(context
, approval
, request
);
8485 if (err
) goto leave
;
8487 /* Send it to server and process response */
8488 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8489 service_name
, &request
, refnumber
);
8492 xmlFreeNode(request
);
8493 free(service_name_locale
);
8494 #else /* not HAVE_LIBCURL */
8502 /* Switch box accessibility state on request of box owner.
8503 * Despite the name, owner must do the request off-line. This function is
8504 * designed for such off-line meeting points (e.g. Czech POINT).
8505 * @context is ISDS session context.
8506 * @box identifies box to switch accessibility state. aifoIsds,
8507 * address->adCode, address->adDistrict members are ignored.
8508 * @allow is true for making accessible, false to disallow access.
8509 * @approval is optional external approval of box manipulation
8510 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8511 * NULL, if you don't care. */
8512 isds_error
isds_switch_box_accessibility_on_owner_request(
8513 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8514 const _Bool allow
, const struct isds_approval
*approval
,
8516 return build_send_manipulationdbowner_request_check_drop_response(context
,
8517 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8518 BAD_CAST
"DisableOwnDataBox",
8519 box
, approval
, (xmlChar
**) refnumber
);
8523 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8525 * @context is ISDS session context.
8526 * @box identifies box to switch accessibility state. aifoIsds,
8527 * address->adCode, address->adDistrict members are ignored.
8528 * @since is date since accessibility has been denied. This can be past too.
8529 * Only tm_year, tm_mon and tm_mday carry sane value.
8530 * @approval is optional external approval of box manipulation
8531 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8532 * NULL, if you don't care. */
8533 isds_error
isds_disable_box_accessibility_externaly(
8534 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8535 const struct tm
*since
, const struct isds_approval
*approval
,
8537 isds_error err
= IE_SUCCESS
;
8539 char *service_name_locale
= NULL
;
8540 xmlNodePtr request
= NULL
, node
;
8541 xmlNsPtr isds_ns
= NULL
;
8542 xmlChar
*string
= NULL
;
8546 if (!context
) return IE_INVALID_CONTEXT
;
8547 zfree(context
->long_message
);
8548 if (!box
|| !since
) return IE_INVAL
;
8552 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8554 isds_printf_message(context
,
8555 _("Could not build %s request"), "DisableDataBoxExternally");
8559 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8561 isds_log_message(context
, _("Could not create ISDS name space"));
8565 xmlSetNs(request
, isds_ns
);
8568 /* Add @box identification */
8569 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8570 err
= insert_DbOwnerInfo(context
, box
, node
);
8571 if (err
) goto leave
;
8573 /* Add @since date */
8574 err
= tm2datestring(since
, &string
);
8576 isds_log_message(context
,
8577 _("Could not convert `since' argument to ISO date string"));
8580 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8584 err
= insert_GExtApproval(context
, approval
, request
);
8585 if (err
) goto leave
;
8587 /* Send it to server and process response */
8588 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8589 BAD_CAST
"DisableDataBoxExternally", &request
,
8590 (xmlChar
**) refnumber
);
8594 xmlFreeNode(request
);
8595 free(service_name_locale
);
8596 #else /* not HAVE_LIBCURL */
8605 /* Insert struct isds_message data (envelope (recipient data optional) and
8606 * documents into XML tree
8607 * @context is session context
8608 * @outgoing_message is libisds structure with message data
8609 * @create_message is XML CreateMessage or CreateMultipleMessage element
8610 * @process_recipient true for recipient data serialization, false for no
8612 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8613 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8614 const _Bool process_recipient
) {
8616 isds_error err
= IE_SUCCESS
;
8617 xmlNodePtr envelope
, dm_files
, node
;
8618 xmlChar
*string
= NULL
;
8620 if (!context
) return IE_INVALID_CONTEXT
;
8621 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8624 /* Build envelope */
8625 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8627 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8628 "%s element"), create_message
->name
);
8632 if (!outgoing_message
->envelope
) {
8633 isds_log_message(context
, _("Outgoing message is missing envelope"));
8638 /* Insert optional message type */
8639 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8641 if (err
) goto leave
;
8643 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8644 outgoing_message
->envelope
->dmSenderOrgUnit
);
8645 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8646 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8648 if (process_recipient
) {
8649 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8650 isds_log_message(context
,
8651 _("Outgoing message is missing recipient box identifier"));
8655 INSERT_STRING(envelope
, "dbIDRecipient",
8656 outgoing_message
->envelope
->dbIDRecipient
);
8658 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8659 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8660 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8661 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8662 INSERT_STRING(envelope
, "dmToHands",
8663 outgoing_message
->envelope
->dmToHands
);
8666 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8668 INSERT_STRING(envelope
, "dmAnnotation",
8669 outgoing_message
->envelope
->dmAnnotation
);
8671 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8672 0, 50, "dmRecipientRefNumber");
8673 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8674 outgoing_message
->envelope
->dmRecipientRefNumber
);
8676 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8677 0, 50, "dmSenderRefNumber");
8678 INSERT_STRING(envelope
, "dmSenderRefNumber",
8679 outgoing_message
->envelope
->dmSenderRefNumber
);
8681 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8682 0, 50, "dmRecipientIdent");
8683 INSERT_STRING(envelope
, "dmRecipientIdent",
8684 outgoing_message
->envelope
->dmRecipientIdent
);
8686 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8687 0, 50, "dmSenderIdent");
8688 INSERT_STRING(envelope
, "dmSenderIdent",
8689 outgoing_message
->envelope
->dmSenderIdent
);
8691 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8692 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8693 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8694 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8695 INSERT_STRING(envelope
, "dmLegalTitleSect",
8696 outgoing_message
->envelope
->dmLegalTitleSect
);
8697 INSERT_STRING(envelope
, "dmLegalTitlePar",
8698 outgoing_message
->envelope
->dmLegalTitlePar
);
8699 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8700 outgoing_message
->envelope
->dmLegalTitlePoint
);
8702 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8703 outgoing_message
->envelope
->dmPersonalDelivery
);
8704 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8705 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8707 /* ???: Should we require value for dbEffectiveOVM sender?
8708 * ISDS has default as true */
8709 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8710 INSERT_BOOLEAN(envelope
, "dmOVM",
8711 outgoing_message
->envelope
->dmPublishOwnID
);
8714 /* Append dmFiles */
8715 if (!outgoing_message
->documents
) {
8716 isds_log_message(context
,
8717 _("Outgoing message is missing list of documents"));
8721 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8723 isds_printf_message(context
, _("Could not add dmFiles child to "
8724 "%s element"), create_message
->name
);
8729 /* Check for document hierarchy */
8730 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8731 if (err
) goto leave
;
8733 /* Process each document */
8734 for (struct isds_list
*item
=
8735 (struct isds_list
*) outgoing_message
->documents
;
8736 item
; item
= item
->next
) {
8738 isds_log_message(context
,
8739 _("List of documents contains empty item"));
8743 /* FIXME: Check for dmFileMetaType and for document references.
8744 * Only first document can be of MAIN type */
8745 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8748 if (err
) goto leave
;
8755 #endif /* HAVE_LIBCURL */
8758 /* Send a message via ISDS to a recipient
8759 * @context is session context
8760 * @outgoing_message is message to send; Some members are mandatory (like
8761 * dbIDRecipient), some are optional and some are irrelevant (especially data
8762 * about sender). Included pointer to isds_list documents must contain at
8763 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8764 * members will be filled with valid data from ISDS. Exact list of write
8765 * members is subject to change. Currently dmID is changed.
8766 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8767 isds_error
isds_send_message(struct isds_ctx
*context
,
8768 struct isds_message
*outgoing_message
) {
8770 isds_error err
= IE_SUCCESS
;
8772 xmlNsPtr isds_ns
= NULL
;
8773 xmlNodePtr request
= NULL
;
8774 xmlDocPtr response
= NULL
;
8775 xmlChar
*code
= NULL
, *message
= NULL
;
8776 xmlXPathContextPtr xpath_ctx
= NULL
;
8777 xmlXPathObjectPtr result
= NULL
;
8778 /*_Bool message_is_complete = 0;*/
8781 if (!context
) return IE_INVALID_CONTEXT
;
8782 zfree(context
->long_message
);
8783 if (!outgoing_message
) return IE_INVAL
;
8786 /* Check if connection is established
8787 * TODO: This check should be done downstairs. */
8788 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8791 /* Build CreateMessage request */
8792 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8794 isds_log_message(context
,
8795 _("Could not build CreateMessage request"));
8798 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8800 isds_log_message(context
, _("Could not create ISDS name space"));
8801 xmlFreeNode(request
);
8804 xmlSetNs(request
, isds_ns
);
8806 /* Append envelope and files */
8807 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8808 if (err
) goto leave
;
8811 /* Signal we can serialize message since now */
8812 /*message_is_complete = 1;*/
8815 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8818 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8820 /* Don't' destroy request, we want to provide it to application later */
8823 isds_log(ILF_ISDS
, ILL_DEBUG
,
8824 _("Processing ISDS response on CreateMessage "
8825 "request failed\n"));
8829 /* Check for response status */
8830 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8831 &code
, &message
, NULL
);
8833 isds_log(ILF_ISDS
, ILL_DEBUG
,
8834 _("ISDS response on CreateMessage request "
8835 "is missing status\n"));
8839 /* Request processed, but refused by server or server failed */
8840 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8841 char *box_id_locale
=
8842 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8843 char *code_locale
= _isds_utf82locale((char*)code
);
8844 char *message_locale
= _isds_utf82locale((char*)message
);
8845 isds_log(ILF_ISDS
, ILL_DEBUG
,
8846 _("Server did not accept message for %s on CreateMessage "
8847 "request (code=%s, message=%s)\n"),
8848 box_id_locale
, code_locale
, message_locale
);
8849 isds_log_message(context
, message_locale
);
8850 free(box_id_locale
);
8852 free(message_locale
);
8859 xpath_ctx
= xmlXPathNewContext(response
);
8864 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8868 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8874 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8875 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8879 if (result
->nodesetval
->nodeNr
> 1) {
8880 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8884 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8885 xmlXPathFreeObject(result
); result
= NULL
;
8887 if (outgoing_message
->envelope
->dmID
) {
8888 free(outgoing_message
->envelope
->dmID
);
8889 outgoing_message
->envelope
->dmID
= NULL
;
8891 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8892 if (!outgoing_message
->envelope
->dmID
) {
8893 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8894 "but did not return assigned message ID\n"));
8898 /* TODO: Serialize message into structure member raw */
8899 /* XXX: Each web service transport message in different format.
8900 * Therefore it's not possible to save them directly.
8901 * To save them, one must figure out common format.
8902 * We can leave it on application, or we can implement the ESS format. */
8903 /*if (message_is_complete) {
8904 if (outgoing_message->envelope->dmID) {
8906 /* Add assigned message ID as first child*/
8907 /*xmlNodePtr dmid_text = xmlNewText(
8908 (xmlChar *) outgoing_message->envelope->dmID);
8909 if (!dmid_text) goto serialization_failed;
8911 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8913 if (!dmid_element) {
8914 xmlFreeNode(dmid_text);
8915 goto serialization_failed;
8918 xmlNodePtr dmid_element_with_text =
8919 xmlAddChild(dmid_element, dmid_text);
8920 if (!dmid_element_with_text) {
8921 xmlFreeNode(dmid_element);
8922 xmlFreeNode(dmid_text);
8923 goto serialization_failed;
8926 node = xmlAddPrevSibling(envelope->childern,
8927 dmid_element_with_text);
8929 xmlFreeNodeList(dmid_element_with_text);
8930 goto serialization_failed;
8934 /* Serialize message with ID into raw */
8935 /*buffer = serialize_element(envelope)*/
8938 serialization_failed:
8943 xmlXPathFreeObject(result
);
8944 xmlXPathFreeContext(xpath_ctx
);
8948 xmlFreeDoc(response
);
8949 xmlFreeNode(request
);
8952 isds_log(ILF_ISDS
, ILL_DEBUG
,
8953 _("CreateMessage request processed by server "
8954 "successfully.\n"));
8955 #else /* not HAVE_LIBCURL */
8963 /* Send a message via ISDS to a multiple recipients
8964 * @context is session context
8965 * @outgoing_message is message to send; Some members are mandatory,
8966 * some are optional and some are irrelevant (especially data
8967 * about sender). Data about recipient will be substituted by ISDS from
8968 * @copies. Included pointer to isds_list documents must
8969 * contain at least one document of FILEMETATYPE_MAIN.
8970 * @copies is list of isds_message_copy structures addressing all desired
8971 * recipients. This is read-write structure, some members will be filled with
8972 * valid data from ISDS (message IDs, error codes, error descriptions).
8974 * ISDS_SUCCESS if all messages have been sent
8975 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8976 * succeeded messages can be identified by copies->data->error),
8977 * or other error code if something other goes wrong. */
8978 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8979 const struct isds_message
*outgoing_message
,
8980 struct isds_list
*copies
) {
8982 isds_error err
= IE_SUCCESS
;
8984 isds_error append_err
;
8985 xmlNsPtr isds_ns
= NULL
;
8986 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8987 struct isds_list
*item
;
8988 struct isds_message_copy
*copy
;
8989 xmlDocPtr response
= NULL
;
8990 xmlChar
*code
= NULL
, *message
= NULL
;
8991 xmlXPathContextPtr xpath_ctx
= NULL
;
8992 xmlXPathObjectPtr result
= NULL
;
8993 xmlChar
*string
= NULL
;
8997 if (!context
) return IE_INVALID_CONTEXT
;
8998 zfree(context
->long_message
);
8999 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9002 /* Check if connection is established
9003 * TODO: This check should be done downstairs. */
9004 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9007 /* Build CreateMultipleMessage request */
9008 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9010 isds_log_message(context
,
9011 _("Could not build CreateMultipleMessage request"));
9014 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9016 isds_log_message(context
, _("Could not create ISDS name space"));
9017 xmlFreeNode(request
);
9020 xmlSetNs(request
, isds_ns
);
9023 /* Build recipients */
9024 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9026 isds_log_message(context
, _("Could not add dmRecipients child to "
9027 "CreateMultipleMessage element"));
9028 xmlFreeNode(request
);
9032 /* Insert each recipient */
9033 for (item
= copies
; item
; item
= item
->next
) {
9034 copy
= (struct isds_message_copy
*) item
->data
;
9036 isds_log_message(context
,
9037 _("`copies' list item contains empty data"));
9042 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9044 isds_log_message(context
, _("Could not add dmRecipient child to "
9045 "dmRecipients element"));
9050 if (!copy
->dbIDRecipient
) {
9051 isds_log_message(context
,
9052 _("Message copy is missing recipient box identifier"));
9056 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9057 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9058 copy
->dmRecipientOrgUnit
);
9059 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9060 copy
->dmRecipientOrgUnitNum
, string
);
9061 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9064 /* Append envelope and files */
9065 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9066 if (err
) goto leave
;
9069 isds_log(ILF_ISDS
, ILL_DEBUG
,
9070 _("Sending CreateMultipleMessage request to ISDS\n"));
9073 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9075 isds_log(ILF_ISDS
, ILL_DEBUG
,
9076 _("Processing ISDS response on CreateMultipleMessage "
9077 "request failed\n"));
9081 /* Check for response status */
9082 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9083 &code
, &message
, NULL
);
9085 isds_log(ILF_ISDS
, ILL_DEBUG
,
9086 _("ISDS response on CreateMultipleMessage request "
9087 "is missing status\n"));
9091 /* Request processed, but some copies failed */
9092 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9093 char *box_id_locale
=
9094 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9095 char *code_locale
= _isds_utf82locale((char*)code
);
9096 char *message_locale
= _isds_utf82locale((char*)message
);
9097 isds_log(ILF_ISDS
, ILL_DEBUG
,
9098 _("Server did accept message for multiple recipients "
9099 "on CreateMultipleMessage request but delivery to "
9100 "some of them failed (code=%s, message=%s)\n"),
9101 box_id_locale
, code_locale
, message_locale
);
9102 isds_log_message(context
, message_locale
);
9103 free(box_id_locale
);
9105 free(message_locale
);
9106 err
= IE_PARTIAL_SUCCESS
;
9109 /* Request refused by server as whole */
9110 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9111 char *box_id_locale
=
9112 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9113 char *code_locale
= _isds_utf82locale((char*)code
);
9114 char *message_locale
= _isds_utf82locale((char*)message
);
9115 isds_log(ILF_ISDS
, ILL_DEBUG
,
9116 _("Server did not accept message for multiple recipients "
9117 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9118 box_id_locale
, code_locale
, message_locale
);
9119 isds_log_message(context
, message_locale
);
9120 free(box_id_locale
);
9122 free(message_locale
);
9129 xpath_ctx
= xmlXPathNewContext(response
);
9134 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9138 result
= xmlXPathEvalExpression(
9139 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9140 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9146 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9147 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9152 /* Extract message ID and delivery status for each copy */
9153 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9154 item
= item
->next
, i
++) {
9155 copy
= (struct isds_message_copy
*) item
->data
;
9156 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9158 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9164 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9165 isds_printf_message(context
, _("ISDS returned unexpected number of "
9166 "message copy delivery states: %d"),
9167 result
->nodesetval
->nodeNr
);
9176 xmlXPathFreeObject(result
);
9177 xmlXPathFreeContext(xpath_ctx
);
9181 xmlFreeDoc(response
);
9182 xmlFreeNode(request
);
9185 isds_log(ILF_ISDS
, ILL_DEBUG
,
9186 _("CreateMultipleMessageResponse request processed by server "
9187 "successfully.\n"));
9188 #else /* not HAVE_LIBCURL */
9196 /* Get list of messages. This is common core for getting sent or received
9198 * Any criterion argument can be NULL, if you don't care about it.
9199 * @context is session context. Must not be NULL.
9200 * @outgoing_direction is true if you want list of outgoing messages,
9201 * it's false if you want incoming messages.
9202 * @from_time is minimal time and date of message sending inclusive.
9203 * @to_time is maximal time and date of message sending inclusive
9204 * @organization_unit_number is number of sender/recipient respectively.
9205 * @status_filter is bit field of isds_message_status values. Use special
9206 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9207 * all values, you can use bit-wise arithmetic if you want.)
9208 * @offset is index of first message we are interested in. First message is 1.
9209 * Set to 0 (or 1) if you don't care.
9210 * @number is maximal length of list you want to get as input value, outputs
9211 * number of messages matching these criteria. Can be NULL if you don't care
9212 * (applies to output value either).
9213 * @messages is automatically reallocated list of isds_message's. Be ware that
9214 * it returns only brief overview (envelope and some other fields) about each
9215 * message, not the complete message. FIXME: Specify exact fields.
9216 * The list is sorted by delivery time in ascending order.
9217 * Use NULL if you don't care about don't need the data (useful if you want to
9218 * know only the @number). If you provide &NULL, list will be allocated on
9219 * heap, if you provide pointer to non-NULL, list will be freed automatically
9220 * at first. Also in case of error the list will be NULLed.
9221 * @return IE_SUCCESS or appropriate error code. */
9222 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9223 _Bool outgoing_direction
,
9224 const struct timeval
*from_time
, const struct timeval
*to_time
,
9225 const long int *organization_unit_number
,
9226 const unsigned int status_filter
,
9227 const unsigned long int offset
, unsigned long int *number
,
9228 struct isds_list
**messages
) {
9230 isds_error err
= IE_SUCCESS
;
9232 xmlNsPtr isds_ns
= NULL
;
9233 xmlNodePtr request
= NULL
, node
;
9234 xmlDocPtr response
= NULL
;
9235 xmlChar
*code
= NULL
, *message
= NULL
;
9236 xmlXPathContextPtr xpath_ctx
= NULL
;
9237 xmlXPathObjectPtr result
= NULL
;
9238 xmlChar
*string
= NULL
;
9242 if (!context
) return IE_INVALID_CONTEXT
;
9243 zfree(context
->long_message
);
9245 /* Free former message list if any */
9246 if (messages
) isds_list_free(messages
);
9249 /* Check if connection is established
9250 * TODO: This check should be done downstairs. */
9251 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9253 /* Build GetListOf*Messages request */
9254 request
= xmlNewNode(NULL
,
9255 (outgoing_direction
) ?
9256 BAD_CAST
"GetListOfSentMessages" :
9257 BAD_CAST
"GetListOfReceivedMessages"
9260 isds_log_message(context
,
9261 (outgoing_direction
) ?
9262 _("Could not build GetListOfSentMessages request") :
9263 _("Could not build GetListOfReceivedMessages request")
9267 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9269 isds_log_message(context
, _("Could not create ISDS name space"));
9270 xmlFreeNode(request
);
9273 xmlSetNs(request
, isds_ns
);
9277 err
= timeval2timestring(from_time
, &string
);
9278 if (err
) goto leave
;
9280 INSERT_STRING(request
, "dmFromTime", string
);
9281 free(string
); string
= NULL
;
9284 err
= timeval2timestring(to_time
, &string
);
9285 if (err
) goto leave
;
9287 INSERT_STRING(request
, "dmToTime", string
);
9288 free(string
); string
= NULL
;
9290 if (outgoing_direction
) {
9291 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9292 organization_unit_number
, string
);
9294 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9295 organization_unit_number
, string
);
9298 if (status_filter
> MESSAGESTATE_ANY
) {
9299 isds_printf_message(context
,
9300 _("Invalid message state filter value: %ld"), status_filter
);
9304 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9307 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9309 INSERT_STRING(request
, "dmOffset", "1");
9312 /* number 0 means no limit */
9313 if (number
&& *number
== 0) {
9314 INSERT_STRING(request
, "dmLimit", NULL
);
9316 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9320 isds_log(ILF_ISDS
, ILL_DEBUG
,
9321 (outgoing_direction
) ?
9322 _("Sending GetListOfSentMessages request to ISDS\n") :
9323 _("Sending GetListOfReceivedMessages request to ISDS\n")
9327 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9328 xmlFreeNode(request
); request
= NULL
;
9331 isds_log(ILF_ISDS
, ILL_DEBUG
,
9332 (outgoing_direction
) ?
9333 _("Processing ISDS response on GetListOfSentMessages "
9334 "request failed\n") :
9335 _("Processing ISDS response on GetListOfReceivedMessages "
9341 /* Check for response status */
9342 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9343 &code
, &message
, NULL
);
9345 isds_log(ILF_ISDS
, ILL_DEBUG
,
9346 (outgoing_direction
) ?
9347 _("ISDS response on GetListOfSentMessages request "
9348 "is missing status\n") :
9349 _("ISDS response on GetListOfReceivedMessages request "
9350 "is missing status\n")
9355 /* Request processed, but nothing found */
9356 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9357 char *code_locale
= _isds_utf82locale((char*)code
);
9358 char *message_locale
= _isds_utf82locale((char*)message
);
9359 isds_log(ILF_ISDS
, ILL_DEBUG
,
9360 (outgoing_direction
) ?
9361 _("Server refused GetListOfSentMessages request "
9362 "(code=%s, message=%s)\n") :
9363 _("Server refused GetListOfReceivedMessages request "
9364 "(code=%s, message=%s)\n"),
9365 code_locale
, message_locale
);
9366 isds_log_message(context
, message_locale
);
9368 free(message_locale
);
9375 xpath_ctx
= xmlXPathNewContext(response
);
9380 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9384 result
= xmlXPathEvalExpression(
9385 (outgoing_direction
) ?
9386 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9387 "isds:dmRecords/isds:dmRecord" :
9388 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9389 "isds:dmRecords/isds:dmRecord",
9396 /* Fill output arguments in */
9397 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9398 struct isds_envelope
*envelope
;
9399 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9401 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9402 /* Create new message */
9403 item
= calloc(1, sizeof(*item
));
9408 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9409 item
->data
= calloc(1, sizeof(struct isds_message
));
9411 isds_list_free(&item
);
9416 /* Extract envelope data */
9417 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9419 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9421 isds_list_free(&item
);
9425 /* Attach extracted envelope */
9426 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9428 /* Append new message into the list */
9430 *messages
= last_item
= item
;
9432 last_item
->next
= item
;
9437 if (number
) *number
= count
;
9441 isds_list_free(messages
);
9445 xmlXPathFreeObject(result
);
9446 xmlXPathFreeContext(xpath_ctx
);
9450 xmlFreeDoc(response
);
9451 xmlFreeNode(request
);
9454 isds_log(ILF_ISDS
, ILL_DEBUG
,
9455 (outgoing_direction
) ?
9456 _("GetListOfSentMessages request processed by server "
9457 "successfully.\n") :
9458 _("GetListOfReceivedMessages request processed by server "
9461 #else /* not HAVE_LIBCURL */
9468 /* Get list of outgoing (already sent) messages.
9469 * Any criterion argument can be NULL, if you don't care about it.
9470 * @context is session context. Must not be NULL.
9471 * @from_time is minimal time and date of message sending inclusive.
9472 * @to_time is maximal time and date of message sending inclusive
9473 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9474 * @status_filter is bit field of isds_message_status values. Use special
9475 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9476 * all values, you can use bit-wise arithmetic if you want.)
9477 * @offset is index of first message we are interested in. First message is 1.
9478 * Set to 0 (or 1) if you don't care.
9479 * @number is maximal length of list you want to get as input value, outputs
9480 * number of messages matching these criteria. Can be NULL if you don't care
9481 * (applies to output value either).
9482 * @messages is automatically reallocated list of isds_message's. Be ware that
9483 * it returns only brief overview (envelope and some other fields) about each
9484 * message, not the complete message. FIXME: Specify exact fields.
9485 * The list is sorted by delivery time in ascending order.
9486 * Use NULL if you don't care about the meta data (useful if you want to know
9487 * only the @number). If you provide &NULL, list will be allocated on heap,
9488 * if you provide pointer to non-NULL, list will be freed automatically at
9489 * first. Also in case of error the list will be NULLed.
9490 * @return IE_SUCCESS or appropriate error code. */
9491 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9492 const struct timeval
*from_time
, const struct timeval
*to_time
,
9493 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9494 const unsigned long int offset
, unsigned long int *number
,
9495 struct isds_list
**messages
) {
9497 return isds_get_list_of_messages(
9499 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9505 /* Get list of incoming (addressed to you) messages.
9506 * Any criterion argument can be NULL, if you don't care about it.
9507 * @context is session context. Must not be NULL.
9508 * @from_time is minimal time and date of message sending inclusive.
9509 * @to_time is maximal time and date of message sending inclusive
9510 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9511 * @status_filter is bit field of isds_message_status values. Use special
9512 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9513 * all values, you can use bit-wise arithmetic if you want.)
9514 * @offset is index of first message we are interested in. First message is 1.
9515 * Set to 0 (or 1) if you don't care.
9516 * @number is maximal length of list you want to get as input value, outputs
9517 * number of messages matching these criteria. Can be NULL if you don't care
9518 * (applies to output value either).
9519 * @messages is automatically reallocated list of isds_message's. Be ware that
9520 * it returns only brief overview (envelope and some other fields) about each
9521 * message, not the complete message. FIXME: Specify exact fields.
9522 * Use NULL if you don't care about the meta data (useful if you want to know
9523 * only the @number). If you provide &NULL, list will be allocated on heap,
9524 * if you provide pointer to non-NULL, list will be freed automatically at
9525 * first. Also in case of error the list will be NULLed.
9526 * @return IE_SUCCESS or appropriate error code. */
9527 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9528 const struct timeval
*from_time
, const struct timeval
*to_time
,
9529 const long int *dmRecipientOrgUnitNum
,
9530 const unsigned int status_filter
,
9531 const unsigned long int offset
, unsigned long int *number
,
9532 struct isds_list
**messages
) {
9534 return isds_get_list_of_messages(
9536 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9542 /* Get list of sent message state changes.
9543 * Any criterion argument can be NULL, if you don't care about it.
9544 * @context is session context. Must not be NULL.
9545 * @from_time is minimal time and date of status changes inclusive
9546 * @to_time is maximal time and date of status changes inclusive
9547 * @changed_states is automatically reallocated list of
9548 * isds_message_status_change's. If you provide &NULL, list will be allocated
9549 * on heap, if you provide pointer to non-NULL, list will be freed
9550 * automatically at first. Also in case of error the list will be NULLed.
9551 * XXX: The list item ordering is not specified.
9552 * XXX: Server provides only `recent' changes.
9553 * @return IE_SUCCESS or appropriate error code. */
9554 isds_error
isds_get_list_of_sent_message_state_changes(
9555 struct isds_ctx
*context
,
9556 const struct timeval
*from_time
, const struct timeval
*to_time
,
9557 struct isds_list
**changed_states
) {
9559 isds_error err
= IE_SUCCESS
;
9561 xmlNsPtr isds_ns
= NULL
;
9562 xmlNodePtr request
= NULL
, node
;
9563 xmlDocPtr response
= NULL
;
9564 xmlXPathContextPtr xpath_ctx
= NULL
;
9565 xmlXPathObjectPtr result
= NULL
;
9566 xmlChar
*string
= NULL
;
9570 if (!context
) return IE_INVALID_CONTEXT
;
9571 zfree(context
->long_message
);
9573 /* Free former message list if any */
9574 isds_list_free(changed_states
);
9577 /* Check if connection is established
9578 * TODO: This check should be done downstairs. */
9579 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9581 /* Build GetMessageStateChanges request */
9582 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9584 isds_log_message(context
,
9585 _("Could not build GetMessageStateChanges request"));
9588 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9590 isds_log_message(context
, _("Could not create ISDS name space"));
9591 xmlFreeNode(request
);
9594 xmlSetNs(request
, isds_ns
);
9598 err
= timeval2timestring(from_time
, &string
);
9599 if (err
) goto leave
;
9601 INSERT_STRING(request
, "dmFromTime", string
);
9605 err
= timeval2timestring(to_time
, &string
);
9606 if (err
) goto leave
;
9608 INSERT_STRING(request
, "dmToTime", string
);
9613 err
= send_destroy_request_check_response(context
,
9614 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9615 &response
, NULL
, NULL
);
9616 if (err
) goto leave
;
9620 xpath_ctx
= xmlXPathNewContext(response
);
9625 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9629 result
= xmlXPathEvalExpression(
9630 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9631 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9637 /* Fill output arguments in */
9638 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9639 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9641 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9642 /* Create new status change */
9643 item
= calloc(1, sizeof(*item
));
9649 (void(*)(void**)) &isds_message_status_change_free
;
9651 /* Extract message status change */
9652 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9653 err
= extract_StateChangesRecord(context
,
9654 (struct isds_message_status_change
**) &item
->data
,
9657 isds_list_free(&item
);
9661 /* Append new message status change into the list */
9662 if (!*changed_states
) {
9663 *changed_states
= last_item
= item
;
9665 last_item
->next
= item
;
9673 isds_list_free(changed_states
);
9677 xmlXPathFreeObject(result
);
9678 xmlXPathFreeContext(xpath_ctx
);
9679 xmlFreeDoc(response
);
9680 xmlFreeNode(request
);
9683 isds_log(ILF_ISDS
, ILL_DEBUG
,
9684 _("GetMessageStateChanges request processed by server "
9685 "successfully.\n"));
9686 #else /* not HAVE_LIBCURL */
9694 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9696 * @context is session context
9697 * @service is ISDS WS service handler
9698 * @service_name is name of SERVICE_DM_OPERATIONS
9699 * @message_id is message ID to send as service argument to ISDS
9700 * @response is reallocated server SOAP body response as XML document
9701 * @raw_response is reallocated bit stream with response body. Use
9702 * NULL if you don't care
9703 * @raw_response_length is size of @raw_response in bytes
9704 * @code is reallocated ISDS status code
9705 * @status_message is reallocated ISDS status message
9706 * @return error coded from lower layer, context message will be set up
9708 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9709 const isds_service service
, const xmlChar
*service_name
,
9710 const char *message_id
,
9711 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9712 xmlChar
**code
, xmlChar
**status_message
) {
9714 isds_error err
= IE_SUCCESS
;
9715 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9716 xmlNodePtr request
= NULL
, node
;
9717 xmlNsPtr isds_ns
= NULL
;
9719 if (!context
) return IE_INVALID_CONTEXT
;
9720 if (!service_name
|| !message_id
) return IE_INVAL
;
9721 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9722 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9724 /* Free output argument */
9725 xmlFreeDoc(*response
); *response
= NULL
;
9726 if (raw_response
) zfree(*raw_response
);
9728 zfree(*status_message
);
9731 /* Check if connection is established
9732 * TODO: This check should be done downstairs. */
9733 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9735 service_name_locale
= _isds_utf82locale((char*)service_name
);
9736 message_id_locale
= _isds_utf82locale(message_id
);
9737 if (!service_name_locale
|| !message_id_locale
) {
9743 request
= xmlNewNode(NULL
, service_name
);
9745 isds_printf_message(context
,
9746 _("Could not build %s request for %s message ID"),
9747 service_name_locale
, message_id_locale
);
9751 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9753 isds_log_message(context
, _("Could not create ISDS name space"));
9757 xmlSetNs(request
, isds_ns
);
9760 /* Add requested ID */
9761 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9762 if (err
) goto leave
;
9763 INSERT_STRING(request
, "dmID", message_id
);
9766 isds_log(ILF_ISDS
, ILL_DEBUG
,
9767 _("Sending %s request for %s message ID to ISDS\n"),
9768 service_name_locale
, message_id_locale
);
9771 err
= _isds(context
, service
, request
, response
,
9772 raw_response
, raw_response_length
);
9773 xmlFreeNode(request
); request
= NULL
;
9776 isds_log(ILF_ISDS
, ILL_DEBUG
,
9777 _("Processing ISDS response on %s request failed\n"),
9778 service_name_locale
);
9782 /* Check for response status */
9783 err
= isds_response_status(context
, service
, *response
,
9784 code
, status_message
, NULL
);
9786 isds_log(ILF_ISDS
, ILL_DEBUG
,
9787 _("ISDS response on %s request is missing status\n"),
9788 service_name_locale
);
9792 /* Request processed, but nothing found */
9793 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9794 char *code_locale
= _isds_utf82locale((char*) *code
);
9795 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9796 isds_log(ILF_ISDS
, ILL_DEBUG
,
9797 _("Server refused %s request for %s message ID "
9798 "(code=%s, message=%s)\n"),
9799 service_name_locale
, message_id_locale
,
9800 code_locale
, status_message_locale
);
9801 isds_log_message(context
, status_message_locale
);
9803 free(status_message_locale
);
9809 free(message_id_locale
);
9810 free(service_name_locale
);
9811 xmlFreeNode(request
);
9816 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9817 * signed data and free ISDS response.
9818 * @context is session context
9819 * @message_id is UTF-8 encoded message ID for logging purpose
9820 * @response is parsed XML document. It will be freed and NULLed in the middle
9821 * of function run to save memory. This is not guaranteed in case of error.
9822 * @request_name is name of ISDS request used to construct response root
9823 * element name and for logging purpose.
9824 * @raw is reallocated output buffer with DER encoded CMS data
9825 * @raw_length is size of @raw buffer in bytes
9826 * @returns standard error codes, in case of error, @raw will be freed and
9827 * NULLed, @response sometimes. */
9828 static isds_error
find_extract_signed_data_free_response(
9829 struct isds_ctx
*context
, const xmlChar
*message_id
,
9830 xmlDocPtr
*response
, const xmlChar
*request_name
,
9831 void **raw
, size_t *raw_length
) {
9833 isds_error err
= IE_SUCCESS
;
9834 char *xpath_expression
= NULL
;
9835 xmlXPathContextPtr xpath_ctx
= NULL
;
9836 xmlXPathObjectPtr result
= NULL
;
9837 char *encoded_structure
= NULL
;
9839 if (!context
) return IE_INVALID_CONTEXT
;
9840 if (!raw
) return IE_INVAL
;
9842 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9845 /* Build XPath expression */
9846 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9847 "Response/isds:dmSignature");
9848 if (!xpath_expression
) return IE_NOMEM
;
9851 xpath_ctx
= xmlXPathNewContext(*response
);
9856 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9860 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9865 /* Empty response */
9866 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9867 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9868 isds_printf_message(context
,
9869 _("Server did not return any signed data for message ID `%s' "
9871 message_id_locale
, request_name
);
9872 free(message_id_locale
);
9876 /* More responses */
9877 if (result
->nodesetval
->nodeNr
> 1) {
9878 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9879 isds_printf_message(context
,
9880 _("Server did return more signed data for message ID `%s' "
9882 message_id_locale
, request_name
);
9883 free(message_id_locale
);
9888 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9890 /* Extract PKCS#7 structure */
9891 EXTRACT_STRING(".", encoded_structure
);
9892 if (!encoded_structure
) {
9893 isds_log_message(context
, _("dmSignature element is empty"));
9896 /* Here we have delivery info as standalone CMS in encoded_structure.
9897 * We don't need any other data, free them: */
9898 xmlXPathFreeObject(result
); result
= NULL
;
9899 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9900 xmlFreeDoc(*response
); *response
= NULL
;
9903 /* Decode PKCS#7 to DER format */
9904 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9905 if (*raw_length
== (size_t) -1) {
9906 isds_log_message(context
,
9907 _("Error while Base64-decoding PKCS#7 structure"));
9918 free(encoded_structure
);
9919 xmlXPathFreeObject(result
);
9920 xmlXPathFreeContext(xpath_ctx
);
9921 free(xpath_expression
);
9925 #endif /* HAVE_LIBCURL */
9928 /* Download incoming message envelope identified by ID.
9929 * @context is session context
9930 * @message_id is message identifier (you can get them from
9931 * isds_get_list_of_received_messages())
9932 * @message is automatically reallocated message retrieved from ISDS.
9933 * It will miss documents per se. Use isds_get_received_message(), if you are
9934 * interested in documents (content) too.
9935 * Returned hash and timestamp require documents to be verifiable. */
9936 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9937 const char *message_id
, struct isds_message
**message
) {
9939 isds_error err
= IE_SUCCESS
;
9941 xmlDocPtr response
= NULL
;
9942 xmlChar
*code
= NULL
, *status_message
= NULL
;
9943 xmlXPathContextPtr xpath_ctx
= NULL
;
9944 xmlXPathObjectPtr result
= NULL
;
9947 if (!context
) return IE_INVALID_CONTEXT
;
9948 zfree(context
->long_message
);
9950 /* Free former message if any */
9951 if (!message
) return IE_INVAL
;
9952 isds_message_free(message
);
9955 /* Do request and check for success */
9956 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9957 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9958 &response
, NULL
, NULL
, &code
, &status_message
);
9959 if (err
) goto leave
;
9962 xpath_ctx
= xmlXPathNewContext(response
);
9967 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9971 result
= xmlXPathEvalExpression(
9972 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9973 "isds:dmReturnedMessageEnvelope",
9979 /* Empty response */
9980 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9981 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9982 isds_printf_message(context
,
9983 _("Server did not return any envelope for ID `%s' "
9984 "on MessageEnvelopeDownload request"), message_id_locale
);
9985 free(message_id_locale
);
9990 if (result
->nodesetval
->nodeNr
> 1) {
9991 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9992 isds_printf_message(context
,
9993 _("Server did return more envelopes for ID `%s' "
9994 "on MessageEnvelopeDownload request"), message_id_locale
);
9995 free(message_id_locale
);
10000 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10002 /* Extract the envelope (= message without documents, hence 0) */
10003 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10004 if (err
) goto leave
;
10006 /* Save XML blob */
10007 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10008 &(*message
)->raw_length
);
10012 isds_message_free(message
);
10015 xmlXPathFreeObject(result
);
10016 xmlXPathFreeContext(xpath_ctx
);
10019 free(status_message
);
10020 if (!*message
|| !(*message
)->xml
) {
10021 xmlFreeDoc(response
);
10025 isds_log(ILF_ISDS
, ILL_DEBUG
,
10026 _("MessageEnvelopeDownload request processed by server "
10029 #else /* not HAVE_LIBCURL */
10036 /* Load delivery info of any format from buffer.
10037 * @context is session context
10038 * @raw_type advertises format of @buffer content. Only delivery info types
10040 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10041 * retrieve such data from message->raw after calling
10042 * isds_get_signed_delivery_info().
10043 * @length is length of buffer in bytes.
10044 * @message is automatically reallocated message parsed from @buffer.
10045 * @strategy selects how buffer will be attached into raw isds_message member.
10047 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10048 const isds_raw_type raw_type
,
10049 const void *buffer
, const size_t length
,
10050 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10052 isds_error err
= IE_SUCCESS
;
10053 message_ns_type message_ns
;
10054 xmlDocPtr message_doc
= NULL
;
10055 xmlXPathContextPtr xpath_ctx
= NULL
;
10056 xmlXPathObjectPtr result
= NULL
;
10057 void *xml_stream
= NULL
;
10058 size_t xml_stream_length
= 0;
10060 if (!context
) return IE_INVALID_CONTEXT
;
10061 zfree(context
->long_message
);
10062 if (!message
) return IE_INVAL
;
10063 isds_message_free(message
);
10064 if (!buffer
) return IE_INVAL
;
10067 /* Select buffer format and extract XML from CMS*/
10068 switch (raw_type
) {
10069 case RAWTYPE_DELIVERYINFO
:
10070 message_ns
= MESSAGE_NS_UNSIGNED
;
10071 xml_stream
= (void *) buffer
;
10072 xml_stream_length
= length
;
10075 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10076 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10077 xml_stream
= (void *) buffer
;
10078 xml_stream_length
= length
;
10081 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10082 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10083 err
= _isds_extract_cms_data(context
, buffer
, length
,
10084 &xml_stream
, &xml_stream_length
);
10085 if (err
) goto leave
;
10089 isds_log_message(context
, _("Bad raw delivery representation type"));
10094 isds_log(ILF_ISDS
, ILL_DEBUG
,
10095 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10096 xml_stream_length
, xml_stream
);
10098 /* Convert delivery info XML stream into XPath context */
10099 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10100 if (!message_doc
) {
10104 xpath_ctx
= xmlXPathNewContext(message_doc
);
10109 /* XXX: Name spaces mangled for signed delivery info:
10110 * http://isds.czechpoint.cz/v20/delivery:
10112 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10114 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10115 * <p:dmID>170272</p:dmID>
10118 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10120 * </q:dmEvents>...</q:dmEvents>
10122 * </q:GetDeliveryInfoResponse>
10124 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10128 result
= xmlXPathEvalExpression(
10129 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10135 /* Empty delivery info */
10136 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10137 isds_printf_message(context
,
10138 _("XML document is not sisds:dmDelivery document"));
10142 /* More delivery info's */
10143 if (result
->nodesetval
->nodeNr
> 1) {
10144 isds_printf_message(context
,
10145 _("XML document has more sisds:dmDelivery elements"));
10149 /* One delivery info */
10150 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10152 /* Extract the envelope (= message without documents, hence 0).
10153 * XXX: extract_TReturnedMessage() can obtain attachments size,
10154 * but delivery info carries none. It's coded as option elements,
10155 * so it should work. */
10156 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10157 if (err
) goto leave
;
10159 /* Extract events */
10160 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10161 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10162 if (err
) { err
= IE_ERROR
; goto leave
; }
10163 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10164 if (err
) goto leave
;
10166 /* Append raw CMS structure into message */
10167 (*message
)->raw_type
= raw_type
;
10168 switch (strategy
) {
10169 case BUFFER_DONT_STORE
:
10172 (*message
)->raw
= malloc(length
);
10173 if (!(*message
)->raw
) {
10177 memcpy((*message
)->raw
, buffer
, length
);
10178 (*message
)->raw_length
= length
;
10181 (*message
)->raw
= (void *) buffer
;
10182 (*message
)->raw_length
= length
;
10191 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10192 isds_message_free(message
);
10195 xmlXPathFreeObject(result
);
10196 xmlXPathFreeContext(xpath_ctx
);
10197 if (!*message
|| !(*message
)->xml
) {
10198 xmlFreeDoc(message_doc
);
10200 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10203 isds_log(ILF_ISDS
, ILL_DEBUG
,
10204 _("Delivery info loaded successfully.\n"));
10209 /* Download signed delivery info-sheet of given message identified by ID.
10210 * @context is session context
10211 * @message_id is message identifier (you can get them from
10212 * isds_get_list_of_{sent,received}_messages())
10213 * @message is automatically reallocated message retrieved from ISDS.
10214 * It will miss documents per se. Use isds_get_signed_received_message(),
10215 * if you are interested in documents (content). OTOH, only this function
10216 * can get list events message has gone through. */
10217 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10218 const char *message_id
, struct isds_message
**message
) {
10220 isds_error err
= IE_SUCCESS
;
10222 xmlDocPtr response
= NULL
;
10223 xmlChar
*code
= NULL
, *status_message
= NULL
;
10225 size_t raw_length
= 0;
10228 if (!context
) return IE_INVALID_CONTEXT
;
10229 zfree(context
->long_message
);
10231 /* Free former message if any */
10232 if (!message
) return IE_INVAL
;
10233 isds_message_free(message
);
10236 /* Do request and check for success */
10237 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10238 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10239 &response
, NULL
, NULL
, &code
, &status_message
);
10240 if (err
) goto leave
;
10242 /* Find signed delivery info, extract it into raw and maybe free
10244 err
= find_extract_signed_data_free_response(context
,
10245 (xmlChar
*)message_id
, &response
,
10246 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10247 if (err
) goto leave
;
10249 /* Parse delivery info */
10250 err
= isds_load_delivery_info(context
,
10251 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10252 message
, BUFFER_MOVE
);
10253 if (err
) goto leave
;
10259 isds_message_free(message
);
10264 free(status_message
);
10265 xmlFreeDoc(response
);
10268 isds_log(ILF_ISDS
, ILL_DEBUG
,
10269 _("GetSignedDeliveryInfo request processed by server "
10272 #else /* not HAVE_LIBCURL */
10279 /* Download delivery info-sheet of given message identified by ID.
10280 * @context is session context
10281 * @message_id is message identifier (you can get them from
10282 * isds_get_list_of_{sent,received}_messages())
10283 * @message is automatically reallocated message retrieved from ISDS.
10284 * It will miss documents per se. Use isds_get_received_message(), if you are
10285 * interested in documents (content). OTOH, only this function can get list
10286 * of events message has gone through. */
10287 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10288 const char *message_id
, struct isds_message
**message
) {
10290 isds_error err
= IE_SUCCESS
;
10292 xmlDocPtr response
= NULL
;
10293 xmlChar
*code
= NULL
, *status_message
= NULL
;
10294 xmlNodePtr delivery_node
= NULL
;
10296 size_t raw_length
= 0;
10299 if (!context
) return IE_INVALID_CONTEXT
;
10300 zfree(context
->long_message
);
10302 /* Free former message if any */
10303 if (!message
) return IE_INVAL
;
10304 isds_message_free(message
);
10307 /* Do request and check for success */
10308 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10309 BAD_CAST
"GetDeliveryInfo", message_id
,
10310 &response
, NULL
, NULL
, &code
, &status_message
);
10311 if (err
) goto leave
;
10314 /* Serialize delivery info */
10315 delivery_node
= xmlDocGetRootElement(response
);
10316 if (!delivery_node
) {
10317 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10318 isds_printf_message(context
,
10319 _("Server did not return any delivery info for ID `%s' "
10320 "on GetDeliveryInfo request"), message_id_locale
);
10321 free(message_id_locale
);
10325 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10326 if (err
) goto leave
;
10328 /* Parse delivery info */
10329 /* TODO: Here we parse the response second time. We could single delivery
10330 * parser from isds_load_delivery_info() to make things faster. */
10331 err
= isds_load_delivery_info(context
,
10332 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10333 message
, BUFFER_MOVE
);
10334 if (err
) goto leave
;
10341 isds_message_free(message
);
10346 free(status_message
);
10347 xmlFreeDoc(response
);
10350 isds_log(ILF_ISDS
, ILL_DEBUG
,
10351 _("GetDeliveryInfo request processed by server "
10354 #else /* not HAVE_LIBCURL */
10361 /* Download incoming message identified by ID.
10362 * @context is session context
10363 * @message_id is message identifier (you can get them from
10364 * isds_get_list_of_received_messages())
10365 * @message is automatically reallocated message retrieved from ISDS */
10366 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10367 const char *message_id
, struct isds_message
**message
) {
10369 isds_error err
= IE_SUCCESS
;
10371 xmlDocPtr response
= NULL
;
10372 void *xml_stream
= NULL
;
10373 size_t xml_stream_length
;
10374 xmlChar
*code
= NULL
, *status_message
= NULL
;
10375 xmlXPathContextPtr xpath_ctx
= NULL
;
10376 xmlXPathObjectPtr result
= NULL
;
10377 char *phys_path
= NULL
;
10378 size_t phys_start
, phys_end
;
10381 if (!context
) return IE_INVALID_CONTEXT
;
10382 zfree(context
->long_message
);
10384 /* Free former message if any */
10385 if (NULL
== message
) return IE_INVAL
;
10386 if (message
) isds_message_free(message
);
10389 /* Do request and check for success */
10390 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10391 BAD_CAST
"MessageDownload", message_id
,
10392 &response
, &xml_stream
, &xml_stream_length
,
10393 &code
, &status_message
);
10394 if (err
) goto leave
;
10397 xpath_ctx
= xmlXPathNewContext(response
);
10402 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10406 result
= xmlXPathEvalExpression(
10407 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10413 /* Empty response */
10414 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10415 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10416 isds_printf_message(context
,
10417 _("Server did not return any message for ID `%s' "
10418 "on MessageDownload request"), message_id_locale
);
10419 free(message_id_locale
);
10423 /* More messages */
10424 if (result
->nodesetval
->nodeNr
> 1) {
10425 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10426 isds_printf_message(context
,
10427 _("Server did return more messages for ID `%s' "
10428 "on MessageDownload request"), message_id_locale
);
10429 free(message_id_locale
);
10434 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10436 /* Extract the message */
10437 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10438 if (err
) goto leave
;
10440 /* Locate raw XML blob */
10441 phys_path
= strdup(
10442 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10443 PHYSXML_ELEMENT_SEPARATOR
10444 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10445 PHYSXML_ELEMENT_SEPARATOR
10446 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10452 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10453 phys_path
, &phys_start
, &phys_end
);
10456 isds_log_message(context
,
10457 _("Substring with isds:MessageDownloadResponse element "
10458 "could not be located in raw SOAP message"));
10461 /* Save XML blob */
10462 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10463 &(*message)->raw_length);*/
10464 /* TODO: Store name space declarations from ancestors */
10465 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10466 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10467 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10468 (*message
)->raw
= malloc((*message
)->raw_length
);
10469 if (!(*message
)->raw
) {
10473 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10478 isds_message_free(message
);
10483 xmlXPathFreeObject(result
);
10484 xmlXPathFreeContext(xpath_ctx
);
10487 free(status_message
);
10489 if (!*message
|| !(*message
)->xml
) {
10490 xmlFreeDoc(response
);
10494 isds_log(ILF_ISDS
, ILL_DEBUG
,
10495 _("MessageDownload request processed by server "
10498 #else /* not HAVE_LIBCURL */
10505 /* Load message of any type from buffer.
10506 * @context is session context
10507 * @raw_type defines content type of @buffer. Only message types are allowed.
10508 * @buffer is message raw representation. Format (CMS, plain signed,
10509 * message direction) is defined in @raw_type. You can retrieve such data
10510 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10511 * @length is length of buffer in bytes.
10512 * @message is automatically reallocated message parsed from @buffer.
10513 * @strategy selects how buffer will be attached into raw isds_message member.
10515 isds_error
isds_load_message(struct isds_ctx
*context
,
10516 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10517 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10519 isds_error err
= IE_SUCCESS
;
10520 void *xml_stream
= NULL
;
10521 size_t xml_stream_length
= 0;
10522 message_ns_type message_ns
;
10523 xmlDocPtr message_doc
= NULL
;
10524 xmlXPathContextPtr xpath_ctx
= NULL
;
10525 xmlXPathObjectPtr result
= NULL
;
10527 if (!context
) return IE_INVALID_CONTEXT
;
10528 zfree(context
->long_message
);
10529 if (!message
) return IE_INVAL
;
10530 isds_message_free(message
);
10531 if (!buffer
) return IE_INVAL
;
10534 /* Select buffer format and extract XML from CMS*/
10535 switch (raw_type
) {
10536 case RAWTYPE_INCOMING_MESSAGE
:
10537 message_ns
= MESSAGE_NS_UNSIGNED
;
10538 xml_stream
= (void *) buffer
;
10539 xml_stream_length
= length
;
10542 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10543 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10544 xml_stream
= (void *) buffer
;
10545 xml_stream_length
= length
;
10548 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10549 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10550 err
= _isds_extract_cms_data(context
, buffer
, length
,
10551 &xml_stream
, &xml_stream_length
);
10552 if (err
) goto leave
;
10555 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10556 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10557 xml_stream
= (void *) buffer
;
10558 xml_stream_length
= length
;
10561 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10562 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10563 err
= _isds_extract_cms_data(context
, buffer
, length
,
10564 &xml_stream
, &xml_stream_length
);
10565 if (err
) goto leave
;
10569 isds_log_message(context
, _("Bad raw message representation type"));
10574 isds_log(ILF_ISDS
, ILL_DEBUG
,
10575 _("Loading message:\n%.*s\nEnd of message\n"),
10576 xml_stream_length
, xml_stream
);
10578 /* Convert messages XML stream into XPath context */
10579 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10580 if (!message_doc
) {
10584 xpath_ctx
= xmlXPathNewContext(message_doc
);
10589 /* XXX: Standard name space for unsigned incoming direction:
10590 * http://isds.czechpoint.cz/v20/
10592 * XXX: Name spaces mangled for signed outgoing direction:
10593 * http://isds.czechpoint.cz/v20/SentMessage:
10595 * <q:MessageDownloadResponse
10596 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10597 * <q:dmReturnedMessage>
10598 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10599 * <p:dmID>151916</p:dmID>
10602 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10604 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10605 * </q:dmReturnedMessage>
10606 * </q:MessageDownloadResponse>
10608 * XXX: Name spaces mangled for signed incoming direction:
10609 * http://isds.czechpoint.cz/v20/message:
10611 * <q:MessageDownloadResponse
10612 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10613 * <q:dmReturnedMessage>
10614 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10615 * <p:dmID>151916</p:dmID>
10618 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10620 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10621 * </q:dmReturnedMessage>
10622 * </q:MessageDownloadResponse>
10624 * Stupidity of ISDS developers is unlimited */
10625 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10629 result
= xmlXPathEvalExpression(
10630 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10636 /* Empty message */
10637 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10638 isds_printf_message(context
,
10639 _("XML document does not contain "
10640 "sisds:dmReturnedMessage element"));
10644 /* More messages */
10645 if (result
->nodesetval
->nodeNr
> 1) {
10646 isds_printf_message(context
,
10647 _("XML document has more sisds:dmReturnedMessage elements"));
10652 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10654 /* Extract the message */
10655 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10656 if (err
) goto leave
;
10658 /* Append raw buffer into message */
10659 (*message
)->raw_type
= raw_type
;
10660 switch (strategy
) {
10661 case BUFFER_DONT_STORE
:
10664 (*message
)->raw
= malloc(length
);
10665 if (!(*message
)->raw
) {
10669 memcpy((*message
)->raw
, buffer
, length
);
10670 (*message
)->raw_length
= length
;
10673 (*message
)->raw
= (void *) buffer
;
10674 (*message
)->raw_length
= length
;
10684 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10685 isds_message_free(message
);
10688 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10689 xmlXPathFreeObject(result
);
10690 xmlXPathFreeContext(xpath_ctx
);
10691 if (!*message
|| !(*message
)->xml
) {
10692 xmlFreeDoc(message_doc
);
10696 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10701 /* Determine type of raw message or delivery info according some heuristics.
10702 * It does not validate the raw blob.
10703 * @context is session context
10704 * @raw_type returns content type of @buffer. Valid only if exit code of this
10705 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10706 * reallocated memory.
10707 * @buffer is message raw representation.
10708 * @length is length of buffer in bytes. */
10709 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10710 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10712 void *xml_stream
= NULL
;
10713 size_t xml_stream_length
= 0;
10714 xmlDocPtr document
= NULL
;
10715 xmlNodePtr root
= NULL
;
10717 if (!context
) return IE_INVALID_CONTEXT
;
10718 zfree(context
->long_message
);
10719 if (length
== 0 || !buffer
) return IE_INVAL
;
10720 if (!raw_type
) return IE_INVAL
;
10723 err
= _isds_extract_cms_data(context
, buffer
, length
,
10724 &xml_stream
, &xml_stream_length
);
10726 xml_stream
= (void *) buffer
;
10727 xml_stream_length
= (size_t) length
;
10732 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10734 isds_printf_message(context
,
10735 _("Could not parse data as XML document"));
10740 /* Get root element */
10741 root
= xmlDocGetRootElement(document
);
10743 isds_printf_message(context
,
10744 _("XML document is missing root element"));
10749 if (!root
->ns
|| !root
->ns
->href
) {
10750 isds_printf_message(context
,
10751 _("Root element does not belong to any name space"));
10756 /* Test name space */
10757 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10758 if (xml_stream
== buffer
)
10759 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10761 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10762 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10763 if (xml_stream
== buffer
)
10764 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10766 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10767 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10768 if (xml_stream
== buffer
)
10769 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10771 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10772 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10773 if (xml_stream
!= buffer
) {
10774 isds_printf_message(context
,
10775 _("Document in ISDS name space is encapsulated into CMS" ));
10777 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10778 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10779 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10780 *raw_type
= RAWTYPE_DELIVERYINFO
;
10782 isds_printf_message(context
,
10783 _("Unknown root element in ISDS name space"));
10787 isds_printf_message(context
,
10788 _("Unknown name space"));
10793 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10794 xmlFreeDoc(document
);
10799 /* Download signed incoming/outgoing message identified by ID.
10800 * @context is session context
10801 * @output is true for outgoing message, false for incoming message
10802 * @message_id is message identifier (you can get them from
10803 * isds_get_list_of_{sent,received}_messages())
10804 * @message is automatically reallocated message retrieved from ISDS. The raw
10805 * member will be filled with PKCS#7 structure in DER format. */
10806 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10807 const _Bool outgoing
, const char *message_id
,
10808 struct isds_message
**message
) {
10810 isds_error err
= IE_SUCCESS
;
10812 xmlDocPtr response
= NULL
;
10813 xmlChar
*code
= NULL
, *status_message
= NULL
;
10814 xmlXPathContextPtr xpath_ctx
= NULL
;
10815 xmlXPathObjectPtr result
= NULL
;
10816 char *encoded_structure
= NULL
;
10818 size_t raw_length
= 0;
10821 if (!context
) return IE_INVALID_CONTEXT
;
10822 zfree(context
->long_message
);
10823 if (!message
) return IE_INVAL
;
10824 isds_message_free(message
);
10827 /* Do request and check for success */
10828 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10829 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10830 BAD_CAST
"SignedMessageDownload",
10831 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10832 if (err
) goto leave
;
10834 /* Find signed message, extract it into raw and maybe free
10836 err
= find_extract_signed_data_free_response(context
,
10837 (xmlChar
*)message_id
, &response
,
10838 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10839 BAD_CAST
"SignedMessageDownload",
10840 &raw
, &raw_length
);
10841 if (err
) goto leave
;
10843 /* Parse message */
10844 err
= isds_load_message(context
,
10845 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10846 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10847 raw
, raw_length
, message
, BUFFER_MOVE
);
10848 if (err
) goto leave
;
10854 isds_message_free(message
);
10857 free(encoded_structure
);
10858 xmlXPathFreeObject(result
);
10859 xmlXPathFreeContext(xpath_ctx
);
10863 free(status_message
);
10864 xmlFreeDoc(response
);
10867 isds_log(ILF_ISDS
, ILL_DEBUG
,
10869 _("SignedSentMessageDownload request processed by server "
10870 "successfully.\n") :
10871 _("SignedMessageDownload request processed by server "
10874 #else /* not HAVE_LIBCURL */
10881 /* Download signed incoming message identified by ID.
10882 * @context is session context
10883 * @message_id is message identifier (you can get them from
10884 * isds_get_list_of_received_messages())
10885 * @message is automatically reallocated message retrieved from ISDS. The raw
10886 * member will be filled with PKCS#7 structure in DER format. */
10887 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10888 const char *message_id
, struct isds_message
**message
) {
10889 return isds_get_signed_message(context
, 0, message_id
, message
);
10893 /* Download signed outgoing message identified by ID.
10894 * @context is session context
10895 * @message_id is message identifier (you can get them from
10896 * isds_get_list_of_sent_messages())
10897 * @message is automatically reallocated message retrieved from ISDS. The raw
10898 * member will be filled with PKCS#7 structure in DER format. */
10899 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10900 const char *message_id
, struct isds_message
**message
) {
10901 return isds_get_signed_message(context
, 1, message_id
, message
);
10905 /* Get type and name of user who sent a message identified by ID.
10906 * @context is session context
10907 * @message_id is message identifier
10908 * @sender_type is pointer to automatically allocated type of sender detected
10909 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10910 * library or to the server, NULL will be returned. Pass NULL if you don't
10912 * @raw_sender_type is automatically reallocated UTF-8 string describing
10913 * sender type or NULL if not known to server. Pass NULL if you don't care.
10914 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10915 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10916 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10917 const char *message_id
, isds_sender_type
**sender_type
,
10918 char **raw_sender_type
, char **sender_name
) {
10919 isds_error err
= IE_SUCCESS
;
10921 xmlDocPtr response
= NULL
;
10922 xmlChar
*code
= NULL
, *status_message
= NULL
;
10923 xmlXPathContextPtr xpath_ctx
= NULL
;
10924 xmlXPathObjectPtr result
= NULL
;
10925 char *type_string
= NULL
;
10928 if (!context
) return IE_INVALID_CONTEXT
;
10929 zfree(context
->long_message
);
10930 if (sender_type
) zfree(*sender_type
);
10931 if (raw_sender_type
) zfree(*raw_sender_type
);
10932 if (sender_name
) zfree(*sender_name
);
10933 if (!message_id
) return IE_INVAL
;
10936 /* Do request and check for success */
10937 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10938 BAD_CAST
"GetMessageAuthor",
10939 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10940 if (err
) goto leave
;
10943 xpath_ctx
= xmlXPathNewContext(response
);
10948 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10952 result
= xmlXPathEvalExpression(
10953 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10958 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10959 isds_log_message(context
,
10960 _("Missing GetMessageAuthorResponse element"));
10964 if (result
->nodesetval
->nodeNr
> 1) {
10965 isds_log_message(context
,
10966 _("Multiple GetMessageAuthorResponse element"));
10970 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10971 xmlXPathFreeObject(result
); result
= NULL
;
10973 /* Fill output arguments in */
10974 EXTRACT_STRING("isds:userType", type_string
);
10975 if (NULL
!= type_string
) {
10976 if (NULL
!= sender_type
) {
10977 *sender_type
= calloc(1, sizeof(**sender_type
));
10978 if (NULL
== *sender_type
) {
10983 err
= string2isds_sender_type((xmlChar
*)type_string
,
10986 zfree(*sender_type
);
10987 if (err
== IE_ENUM
) {
10989 char *type_string_locale
= _isds_utf82locale(type_string
);
10990 isds_log(ILF_ISDS
, ILL_WARNING
,
10991 _("Unknown isds:userType value: %s"),
10992 type_string_locale
);
10993 free(type_string_locale
);
10998 if (NULL
!= sender_name
)
10999 EXTRACT_STRING("isds:authorName", *sender_name
);
11003 if (NULL
!= sender_type
) zfree(*sender_type
);
11004 zfree(type_string
);
11005 if (NULL
!= sender_name
) zfree(*sender_name
);
11007 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11009 xmlXPathFreeObject(result
);
11010 xmlXPathFreeContext(xpath_ctx
);
11013 free(status_message
);
11014 xmlFreeDoc(response
);
11017 isds_log(ILF_ISDS
, ILL_DEBUG
,
11018 _("GetMessageAuthor request processed by server "
11019 "successfully.\n"));
11020 #else /* not HAVE_LIBCURL */
11027 /* Retrieve hash of message identified by ID stored in ISDS.
11028 * @context is session context
11029 * @message_id is message identifier
11030 * @hash is automatically reallocated message hash downloaded from ISDS.
11031 * Message must exist in system and must not be deleted. */
11032 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11033 const char *message_id
, struct isds_hash
**hash
) {
11035 isds_error err
= IE_SUCCESS
;
11037 xmlDocPtr response
= NULL
;
11038 xmlChar
*code
= NULL
, *status_message
= NULL
;
11039 xmlXPathContextPtr xpath_ctx
= NULL
;
11040 xmlXPathObjectPtr result
= NULL
;
11043 if (!context
) return IE_INVALID_CONTEXT
;
11044 zfree(context
->long_message
);
11046 isds_hash_free(hash
);
11049 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11050 BAD_CAST
"VerifyMessage", message_id
,
11051 &response
, NULL
, NULL
, &code
, &status_message
);
11052 if (err
) goto leave
;
11056 xpath_ctx
= xmlXPathNewContext(response
);
11061 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11065 result
= xmlXPathEvalExpression(
11066 BAD_CAST
"/isds:VerifyMessageResponse",
11072 /* Empty response */
11073 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11074 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11075 isds_printf_message(context
,
11076 _("Server did not return any response for ID `%s' "
11077 "on VerifyMessage request"), message_id_locale
);
11078 free(message_id_locale
);
11082 /* More responses */
11083 if (result
->nodesetval
->nodeNr
> 1) {
11084 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11085 isds_printf_message(context
,
11086 _("Server did return more responses for ID `%s' "
11087 "on VerifyMessage request"), message_id_locale
);
11088 free(message_id_locale
);
11093 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11095 /* Extract the hash */
11096 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11100 isds_hash_free(hash
);
11103 xmlXPathFreeObject(result
);
11104 xmlXPathFreeContext(xpath_ctx
);
11107 free(status_message
);
11108 xmlFreeDoc(response
);
11111 isds_log(ILF_ISDS
, ILL_DEBUG
,
11112 _("VerifyMessage request processed by server "
11115 #else /* not HAVE_LIBCURL */
11122 /* Erase message specified by @message_id from long term storage. Other
11123 * message cannot be erased on user request.
11124 * @context is session context
11125 * @message_id is message identifier.
11126 * @incoming is true for incoming message, false for outgoing message.
11128 * IE_SUCCESS if message has ben removed
11129 * IE_INVAL if message does not exist in long term storage or message
11130 * belongs to different box
11131 * TODO: IE_NOEPRM if user has no permission to erase a message */
11132 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11133 const char *message_id
, _Bool incoming
) {
11134 isds_error err
= IE_SUCCESS
;
11136 xmlNodePtr request
= NULL
, node
;
11137 xmlNsPtr isds_ns
= NULL
;
11138 xmlDocPtr response
= NULL
;
11139 xmlChar
*code
= NULL
, *status_message
= NULL
;
11142 if (!context
) return IE_INVALID_CONTEXT
;
11143 zfree(context
->long_message
);
11144 if (NULL
== message_id
) return IE_INVAL
;
11146 /* Check if connection is established
11147 * TODO: This check should be done downstairs. */
11148 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11151 /* Build request */
11152 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11154 isds_log_message(context
,
11155 _("Could build EraseMessage request"));
11158 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11160 isds_log_message(context
, _("Could not create ISDS name space"));
11161 xmlFreeNode(request
);
11164 xmlSetNs(request
, isds_ns
);
11166 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11167 if (err
) goto leave
;
11168 INSERT_STRING(request
, "dmID", message_id
);
11170 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11174 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11175 "message ID %s to ISDS\n"), message_id
);
11176 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11177 xmlFreeNode(request
); request
= NULL
;
11180 isds_log(ILF_ISDS
, ILL_DEBUG
,
11181 _("Processing ISDS response on EraseMessage request "
11186 /* Check for response status */
11187 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11188 &code
, &status_message
, NULL
);
11190 isds_log(ILF_ISDS
, ILL_DEBUG
,
11191 _("ISDS response on EraseMessage request is missing "
11196 /* Check server status code */
11197 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11198 isds_log_message(context
, _("Message to erase belongs to other box"));
11200 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11201 isds_log_message(context
, _("Message to erase is not saved in "
11202 "long term storage or the direction does not match"));
11204 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11205 char *code_locale
= _isds_utf82locale((char*) code
);
11206 char *message_locale
= _isds_utf82locale((char*) status_message
);
11207 isds_log(ILF_ISDS
, ILL_DEBUG
,
11208 _("Server refused EraseMessage request "
11209 "(code=%s, message=%s)\n"),
11210 code_locale
, message_locale
);
11211 isds_log_message(context
, message_locale
);
11213 free(message_locale
);
11220 free(status_message
);
11221 xmlFreeDoc(response
);
11222 xmlFreeNode(request
);
11225 isds_log(ILF_ISDS
, ILL_DEBUG
,
11226 _("EraseMessage request processed by server "
11229 #else /* not HAVE_LIBCURL */
11236 /* Mark message as read. This is a transactional commit function to acknowledge
11237 * to ISDS the message has been downloaded and processed by client properly.
11238 * @context is session context
11239 * @message_id is message identifier. */
11240 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11241 const char *message_id
) {
11243 isds_error err
= IE_SUCCESS
;
11245 xmlDocPtr response
= NULL
;
11246 xmlChar
*code
= NULL
, *status_message
= NULL
;
11249 if (!context
) return IE_INVALID_CONTEXT
;
11250 zfree(context
->long_message
);
11253 /* Do request and check for success */
11254 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11255 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11256 &response
, NULL
, NULL
, &code
, &status_message
);
11259 free(status_message
);
11260 xmlFreeDoc(response
);
11263 isds_log(ILF_ISDS
, ILL_DEBUG
,
11264 _("MarkMessageAsDownloaded request processed by server "
11267 #else /* not HAVE_LIBCURL */
11274 /* Mark message as received by recipient. This is applicable only to
11275 * commercial message. Use envelope->dmType message member to distinguish
11276 * commercial message from government message. Government message is
11277 * received automatically (by law), commercial message on recipient request.
11278 * @context is session context
11279 * @message_id is message identifier. */
11280 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11281 const char *message_id
) {
11283 isds_error err
= IE_SUCCESS
;
11285 xmlDocPtr response
= NULL
;
11286 xmlChar
*code
= NULL
, *status_message
= NULL
;
11289 if (!context
) return IE_INVALID_CONTEXT
;
11290 zfree(context
->long_message
);
11293 /* Do request and check for success */
11294 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11295 BAD_CAST
"ConfirmDelivery", message_id
,
11296 &response
, NULL
, NULL
, &code
, &status_message
);
11299 free(status_message
);
11300 xmlFreeDoc(response
);
11303 isds_log(ILF_ISDS
, ILL_DEBUG
,
11304 _("ConfirmDelivery request processed by server "
11307 #else /* not HAVE_LIBCURL */
11314 /* Send document for authorized conversion into Czech POINT system.
11315 * This is public anonymous service, no log-in necessary. Special context is
11316 * used to reuse keep-a-live HTTPS connection.
11317 * @context is Czech POINT session context. DO NOT use context connected to
11318 * ISDS server. Use new context or context used by this function previously.
11319 * @document is document to convert. Only data, data_length, dmFileDescr and
11320 * is_xml members are significant. Be ware that not all document formats can be
11321 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11322 * @id is reallocated identifier assigned by Czech POINT system to
11323 * your document on submit. Use is to tell it to Czech POINT officer.
11324 * @date is reallocated document submit date (submitted documents
11325 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11327 isds_error
czp_convert_document(struct isds_ctx
*context
,
11328 const struct isds_document
*document
,
11329 char **id
, struct tm
**date
) {
11330 isds_error err
= IE_SUCCESS
;
11332 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11333 xmlNodePtr request
= NULL
, node
;
11334 xmlDocPtr response
= NULL
;
11336 xmlXPathContextPtr xpath_ctx
= NULL
;
11337 xmlXPathObjectPtr result
= NULL
;
11338 long int status
= -1;
11339 long int *status_ptr
= &status
;
11340 char *string
= NULL
;
11344 if (!context
) return IE_INVALID_CONTEXT
;
11345 zfree(context
->long_message
);
11346 if (!document
|| !id
|| !date
) return IE_INVAL
;
11348 if (document
->is_xml
) {
11349 isds_log_message(context
,
11350 _("XML documents cannot be submitted to conversion"));
11354 /* Free output arguments */
11359 /* Store configuration */
11360 context
->type
= CTX_TYPE_CZP
;
11361 free(context
->url
);
11362 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11363 if (!(context
->url
))
11366 /* Prepare CURL handle if not yet connected */
11367 if (!context
->curl
) {
11368 context
->curl
= curl_easy_init();
11369 if (!(context
->curl
))
11373 /* Build conversion request */
11374 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11376 isds_log_message(context
,
11377 _("Could not build Czech POINT conversion request"));
11380 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11382 isds_log_message(context
,
11383 _("Could not create Czech POINT deposit name space"));
11384 xmlFreeNode(request
);
11387 xmlSetNs(request
, deposit_ns
);
11389 /* Insert children. They are in empty namespace! */
11390 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11392 isds_log_message(context
, _("Could not create empty name space"));
11396 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11397 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11398 document
->dmFileDescr
);
11400 /* Document encoded in Base64 */
11401 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11402 document
->data
, document
->data_length
);
11403 if (err
) goto leave
;
11405 isds_log(ILF_ISDS
, ILL_DEBUG
,
11406 _("Submitting document for conversion into Czech POINT deposit"));
11408 /* Send conversion request */
11409 err
= _czp_czpdeposit(context
, request
, &response
);
11410 xmlFreeNode(request
); request
= NULL
;
11413 czp_do_close_connection(context
);
11418 /* Extract response */
11419 xpath_ctx
= xmlXPathNewContext(response
);
11424 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11428 result
= xmlXPathEvalExpression(
11429 BAD_CAST
"/deposit:saveDocumentResponse/return",
11435 /* Empty response */
11436 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11437 isds_printf_message(context
,
11438 _("Missing `return' element in Czech POINT deposit response"));
11442 /* More responses */
11443 if (result
->nodesetval
->nodeNr
> 1) {
11444 isds_printf_message(context
,
11445 _("Multiple `return' element in Czech POINT deposit response"));
11450 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11453 EXTRACT_LONGINT("status", status_ptr
, 1);
11455 EXTRACT_STRING("statusMsg", string
);
11456 char *string_locale
= _isds_utf82locale(string
);
11457 isds_printf_message(context
,
11458 _("Czech POINT deposit refused document for conversion "
11459 "(code=%ld, message=%s)"),
11460 status
, string_locale
);
11461 free(string_locale
);
11466 /* Get document ID */
11467 EXTRACT_STRING("documentID", *id
);
11469 /* Get submit date */
11470 EXTRACT_STRING("dateInserted", string
);
11472 *date
= calloc(1, sizeof(**date
));
11477 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11479 if (err
== IE_NOTSUP
) {
11481 char *string_locale
= _isds_utf82locale(string
);
11482 isds_printf_message(context
,
11483 _("Invalid dateInserted value: %s"), string_locale
);
11484 free(string_locale
);
11492 xmlXPathFreeObject(result
);
11493 xmlXPathFreeContext(xpath_ctx
);
11495 xmlFreeDoc(response
);
11496 xmlFreeNode(request
);
11499 char *id_locale
= _isds_utf82locale((char *) *id
);
11500 isds_log(ILF_ISDS
, ILL_DEBUG
,
11501 _("Document %s has been submitted for conversion "
11502 "to server successfully\n"), id_locale
);
11505 #else /* not HAVE_LIBCURL */
11512 /* Close possibly opened connection to Czech POINT document deposit.
11513 * @context is Czech POINT session context. */
11514 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11515 if (!context
) return IE_INVALID_CONTEXT
;
11516 zfree(context
->long_message
);
11518 return czp_do_close_connection(context
);
11525 /* Send request for new box creation in testing ISDS instance.
11526 * It's not possible to request for a production box currently, as it
11527 * communicates via e-mail.
11528 * XXX: This function does not work either. Server complains about invalid
11530 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11532 * @context is special session context for box creation request. DO NOT use
11533 * standard context as it could reveal your password. Use fresh new context or
11534 * context previously used by this function.
11535 * @box is box description to create including single primary user (in case of
11536 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11537 * ignored. It outputs box ID assigned by ISDS in dbID element.
11538 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11539 * box, or contact address of PFO box owner). The email member is mandatory as
11540 * it will be used to deliver credentials.
11541 * @former_names is former name of box owner. Pass NULL if you don't care.
11542 * @approval is optional external approval of box manipulation
11543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11544 * NULL, if you don't care.*/
11545 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11546 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11547 const char *former_names
, const struct isds_approval
*approval
,
11548 char **refnumber
) {
11549 isds_error err
= IE_SUCCESS
;
11551 xmlNodePtr request
= NULL
;
11552 xmlDocPtr response
= NULL
;
11553 xmlXPathContextPtr xpath_ctx
= NULL
;
11554 xmlXPathObjectPtr result
= NULL
;
11558 if (!context
) return IE_INVALID_CONTEXT
;
11559 zfree(context
->long_message
);
11560 if (!box
) return IE_INVAL
;
11563 if (!box
->email
|| box
->email
[0] == '\0') {
11564 isds_log_message(context
, _("E-mail field is mandatory"));
11568 /* Scratch box ID */
11571 /* Store configuration */
11572 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11573 free(context
->url
);
11574 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11575 if (!(context
->url
))
11578 /* Prepare CURL handle if not yet connected */
11579 if (!context
->curl
) {
11580 context
->curl
= curl_easy_init();
11581 if (!(context
->curl
))
11585 /* Build CreateDataBox request */
11586 err
= build_CreateDBInput_request(context
,
11587 &request
, BAD_CAST
"CreateDataBox",
11588 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11589 if (err
) goto leave
;
11591 /* Send it to server and process response */
11592 err
= send_destroy_request_check_response(context
,
11593 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11594 &response
, (xmlChar
**) refnumber
, NULL
);
11595 if (err
) goto leave
;
11597 /* Extract box ID */
11598 xpath_ctx
= xmlXPathNewContext(response
);
11603 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11607 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11610 xmlXPathFreeObject(result
);
11611 xmlXPathFreeContext(xpath_ctx
);
11612 xmlFreeDoc(response
);
11613 xmlFreeNode(request
);
11616 isds_log(ILF_ISDS
, ILL_DEBUG
,
11617 _("CreateDataBox request processed by server successfully.\n"));
11619 #else /* not HAVE_LIBCURL */
11627 /* Submit CMS signed message to ISDS to verify its originality. This is
11628 * stronger form of isds_verify_message_hash() because ISDS does more checks
11629 * than simple one (potentialy old weak) hash comparison.
11630 * @context is session context
11631 * @message is memory with raw CMS signed message bit stream
11632 * @length is @message size in bytes
11634 * IE_SUCCESS if message originates in ISDS
11635 * IE_NOTEQUAL if message is unknown to ISDS
11636 * other code for other errors */
11637 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11638 const void *message
, size_t length
) {
11639 isds_error err
= IE_SUCCESS
;
11641 xmlNsPtr isds_ns
= NULL
;
11642 xmlNodePtr request
= NULL
;
11643 xmlDocPtr response
= NULL
;
11644 xmlXPathContextPtr xpath_ctx
= NULL
;
11645 xmlXPathObjectPtr result
= NULL
;
11646 _Bool
*authentic
= NULL
;
11649 if (!context
) return IE_INVALID_CONTEXT
;
11650 zfree(context
->long_message
);
11651 if (!message
|| length
== 0) return IE_INVAL
;
11654 /* Check if connection is established
11655 * TODO: This check should be done downstairs. */
11656 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11659 /* Build AuthenticateMessage request */
11660 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11662 isds_log_message(context
,
11663 _("Could not build AuthenticateMessage request"));
11666 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11668 isds_log_message(context
, _("Could not create ISDS name space"));
11669 xmlFreeNode(request
);
11672 xmlSetNs(request
, isds_ns
);
11674 /* Insert Base64 encoded message */
11675 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11677 if (err
) goto leave
;
11679 /* Send request to server and process response */
11680 err
= send_destroy_request_check_response(context
,
11681 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11682 &response
, NULL
, NULL
);
11683 if (err
) goto leave
;
11686 /* ISDS has decided */
11687 xpath_ctx
= xmlXPathNewContext(response
);
11692 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11697 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11700 isds_log_message(context
,
11701 _("Server did not return any response on "
11702 "AuthenticateMessage request"));
11707 isds_log(ILF_ISDS
, ILL_DEBUG
,
11708 _("ISDS authenticated the message successfully\n"));
11710 isds_log_message(context
, _("ISDS does not know the message"));
11717 xmlXPathFreeObject(result
);
11718 xmlXPathFreeContext(xpath_ctx
);
11720 xmlFreeDoc(response
);
11721 xmlFreeNode(request
);
11722 #else /* not HAVE_LIBCURL */
11730 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11731 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11733 * @context is session context
11734 * @input_data is memory with raw CMS signed message or delivery info bit
11735 * stream to re-sign
11736 * @input_length is @input_data size in bytes
11737 * @output_data is pointer to auto-allocated memory where to store re-signed
11738 * input data blob. Caller must free it.
11739 * @output_data is pointer where to store @output_data size in bytes
11740 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11741 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11743 * IE_SUCCESS if CMS blob has been re-signed successfully
11744 * other code for other errors */
11745 isds_error
isds_resign_message(struct isds_ctx
*context
,
11746 const void *input_data
, size_t input_length
,
11747 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11748 isds_error err
= IE_SUCCESS
;
11750 xmlNsPtr isds_ns
= NULL
;
11751 xmlNodePtr request
= NULL
;
11752 xmlDocPtr response
= NULL
;
11753 xmlXPathContextPtr xpath_ctx
= NULL
;
11754 xmlXPathObjectPtr result
= NULL
;
11755 char *string
= NULL
;
11756 const xmlChar
*codes
[] = {
11763 const char *meanings
[] = {
11765 "Message is not original",
11766 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11767 "Time stamp could not been generated in time"
11769 const isds_error errors
[] = {
11775 struct code_map_isds_error map
= {
11777 .meanings
= meanings
,
11782 if (NULL
!= output_data
) *output_data
= NULL
;
11783 if (NULL
!= output_length
) *output_length
= 0;
11784 if (NULL
!= valid_to
) *valid_to
= NULL
;
11786 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11787 zfree(context
->long_message
);
11788 if (NULL
== input_data
|| 0 == input_length
) {
11789 isds_log_message(context
, _("Empty CMS blob on input"));
11792 if (NULL
== output_data
|| NULL
== output_length
) {
11793 isds_log_message(context
,
11794 _("NULL pointer provided for output CMS blob"));
11799 /* Check if connection is established
11800 * TODO: This check should be done downstairs. */
11801 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11804 /* Build Re-signISDSDocument request */
11805 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11807 isds_log_message(context
,
11808 _("Could not build Re-signISDSDocument request"));
11811 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11813 isds_log_message(context
, _("Could not create ISDS name space"));
11814 xmlFreeNode(request
);
11817 xmlSetNs(request
, isds_ns
);
11819 /* Insert Base64 encoded CMS blob */
11820 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11821 input_data
, input_length
);
11822 if (err
) goto leave
;
11824 /* Send request to server and process response */
11825 err
= send_destroy_request_check_response(context
,
11826 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11827 &response
, NULL
, &map
);
11828 if (err
) goto leave
;
11831 /* Extract re-signed data */
11832 xpath_ctx
= xmlXPathNewContext(response
);
11837 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11841 result
= xmlXPathEvalExpression(
11842 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11847 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11848 isds_log_message(context
,
11849 _("Missing Re-signISDSDocumentResponse element"));
11853 if (result
->nodesetval
->nodeNr
> 1) {
11854 isds_log_message(context
,
11855 _("Multiple Re-signISDSDocumentResponse element"));
11859 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11860 xmlXPathFreeObject(result
); result
= NULL
;
11862 EXTRACT_STRING("isds:dmResultDoc", string
);
11863 /* Decode non-empty data */
11864 if (NULL
!= string
&& string
[0] != '\0') {
11865 *output_length
= _isds_b64decode(string
, output_data
);
11866 if (*output_length
== (size_t) -1) {
11867 isds_log_message(context
,
11868 _("Error while Base64-decoding re-signed data"));
11873 isds_log_message(context
, _("Server did not send re-signed data"));
11879 if (NULL
!= valid_to
) {
11880 /* Get time stamp expiration date */
11881 EXTRACT_STRING("isds:dmValidTo", string
);
11882 if (NULL
!= string
) {
11883 *valid_to
= calloc(1, sizeof(**valid_to
));
11888 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11890 if (err
== IE_NOTSUP
) {
11892 char *string_locale
= _isds_utf82locale(string
);
11893 isds_printf_message(context
,
11894 _("Invalid dmValidTo value: %s"), string_locale
);
11895 free(string_locale
);
11905 xmlXPathFreeObject(result
);
11906 xmlXPathFreeContext(xpath_ctx
);
11908 xmlFreeDoc(response
);
11909 xmlFreeNode(request
);
11910 #else /* not HAVE_LIBCURL */
11917 #undef INSERT_ELEMENT
11918 #undef CHECK_FOR_STRING_LENGTH
11919 #undef INSERT_STRING_ATTRIBUTE
11920 #undef INSERT_ULONGINTNOPTR
11921 #undef INSERT_ULONGINT
11922 #undef INSERT_LONGINT
11923 #undef INSERT_BOOLEAN
11924 #undef INSERT_SCALAR_BOOLEAN
11925 #undef INSERT_STRING
11926 #undef INSERT_STRING_WITH_NS
11927 #undef EXTRACT_STRING_ATTRIBUTE
11928 #undef EXTRACT_ULONGINT
11929 #undef EXTRACT_LONGINT
11930 #undef EXTRACT_BOOLEAN
11931 #undef EXTRACT_STRING
11934 /* Compute hash of message from raw representation and store it into envelope.
11935 * Original hash structure will be destroyed in envelope.
11936 * @context is session context
11937 * @message is message carrying raw XML message blob
11938 * @algorithm is desired hash algorithm to use */
11939 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11940 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11941 isds_error err
= IE_SUCCESS
;
11943 void *xml_stream
= NULL
;
11944 size_t xml_stream_length
;
11945 size_t phys_start
, phys_end
;
11946 char *phys_path
= NULL
;
11947 struct isds_hash
*new_hash
= NULL
;
11950 if (!context
) return IE_INVALID_CONTEXT
;
11951 zfree(context
->long_message
);
11952 if (!message
) return IE_INVAL
;
11954 if (!message
->raw
) {
11955 isds_log_message(context
,
11956 _("Message does not carry raw representation"));
11960 switch (message
->raw_type
) {
11961 case RAWTYPE_INCOMING_MESSAGE
:
11963 xml_stream
= message
->raw
;
11964 xml_stream_length
= message
->raw_length
;
11967 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11968 nsuri
= SISDS_INCOMING_NS
;
11969 xml_stream
= message
->raw
;
11970 xml_stream_length
= message
->raw_length
;
11973 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11974 nsuri
= SISDS_INCOMING_NS
;
11975 err
= _isds_extract_cms_data(context
,
11976 message
->raw
, message
->raw_length
,
11977 &xml_stream
, &xml_stream_length
);
11978 if (err
) goto leave
;
11981 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11982 nsuri
= SISDS_OUTGOING_NS
;
11983 xml_stream
= message
->raw
;
11984 xml_stream_length
= message
->raw_length
;
11987 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11988 nsuri
= SISDS_OUTGOING_NS
;
11989 err
= _isds_extract_cms_data(context
,
11990 message
->raw
, message
->raw_length
,
11991 &xml_stream
, &xml_stream_length
);
11992 if (err
) goto leave
;
11996 isds_log_message(context
, _("Bad raw representation type"));
12002 /* XXX: Hash is computed from original string representing isds:dmDm
12003 * subtree. That means no encoding, white space, xmlns attributes changes.
12004 * In other words, input for hash can be invalid XML stream. */
12005 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12006 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12007 PHYSXML_ELEMENT_SEPARATOR
,
12008 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12009 PHYSXML_ELEMENT_SEPARATOR
12010 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12014 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12015 phys_path
, &phys_start
, &phys_end
);
12018 isds_log_message(context
,
12019 _("Substring with isds:dmDM element could not be located "
12020 "in raw message"));
12026 new_hash
= calloc(1, sizeof(*new_hash
));
12031 new_hash
->algorithm
= algorithm
;
12032 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12035 isds_log_message(context
, _("Could not compute message hash"));
12039 /* Save computed hash */
12040 if (!message
->envelope
) {
12041 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12042 if (!message
->envelope
) {
12047 isds_hash_free(&message
->envelope
->hash
);
12048 message
->envelope
->hash
= new_hash
;
12052 isds_hash_free(&new_hash
);
12056 if (xml_stream
!= message
->raw
) free(xml_stream
);
12061 /* Compare two hashes.
12062 * @h1 is first hash
12063 * @h2 is another hash
12065 * IE_SUCCESS if hashes equal
12066 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12067 * IE_ENUM if not comparable, but both structures defined
12068 * IE_INVAL if some of the structures are undefined (NULL)
12069 * IE_ERROR if internal error occurs */
12070 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12071 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12072 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12073 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12074 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12075 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12077 for (size_t i
= 0; i
< h1
->length
; i
++) {
12078 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12079 return IE_NOTEQUAL
;
12085 /* Check message has gone through ISDS by comparing message hash stored in
12086 * ISDS and locally computed hash. You must provide message with valid raw
12087 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12088 * This is convenient wrapper for isds_download_message_hash(),
12089 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12090 * @context is session context
12091 * @message is message with valid raw and envelope member; envelope->hash
12092 * member will be changed during function run. Use envelope on heap only.
12094 * IE_SUCCESS if message originates in ISDS
12095 * IE_NOTEQUAL if message is unknown to ISDS
12096 * other code for other errors */
12097 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12098 struct isds_message
*message
) {
12099 isds_error err
= IE_SUCCESS
;
12100 struct isds_hash
*downloaded_hash
= NULL
;
12102 if (!context
) return IE_INVALID_CONTEXT
;
12103 zfree(context
->long_message
);
12104 if (!message
) return IE_INVAL
;
12106 if (!message
->envelope
) {
12107 isds_log_message(context
,
12108 _("Given message structure is missing envelope"));
12111 if (!message
->raw
) {
12112 isds_log_message(context
,
12113 _("Given message structure is missing raw representation"));
12117 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12119 if (err
) goto leave
;
12121 err
= isds_compute_message_hash(context
, message
,
12122 downloaded_hash
->algorithm
);
12123 if (err
) goto leave
;
12125 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12128 isds_hash_free(&downloaded_hash
);
12133 /* Search for document by document ID in list of documents. IDs are compared
12135 * @documents is list of isds_documents
12136 * @id is document identifier
12137 * @return first matching document or NULL. */
12138 const struct isds_document
*isds_find_document_by_id(
12139 const struct isds_list
*documents
, const char *id
) {
12140 const struct isds_list
*item
;
12141 const struct isds_document
*document
;
12143 for (item
= documents
; item
; item
= item
->next
) {
12144 document
= (struct isds_document
*) item
->data
;
12145 if (!document
) continue;
12147 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12155 /* Normalize @mime_type to be proper MIME type.
12156 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12157 * guess regular MIME type (e.g. "application/pdf").
12158 * @mime_type is UTF-8 encoded MIME type to fix
12159 * @return original @mime_type if no better interpretation exists, or
12160 * constant static UTF-8 encoded string with proper MIME type. */
12161 const char *isds_normalize_mime_type(const char *mime_type
) {
12162 if (!mime_type
) return NULL
;
12164 for (size_t offset
= 0;
12165 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12167 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12168 extension_map_mime
[offset
]))
12169 return (const char *) extension_map_mime
[offset
+ 1];
12176 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12177 struct isds_message **message);
12178 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12179 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12180 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12181 struct isds_address **address);
12183 int isds_message_free(struct isds_message **message);
12184 int isds_address_free(struct isds_address **address);
12188 /* Makes known all relevant namespaces to given XPath context
12189 * @xpath_ctx is XPath context
12190 * @message_ns selects proper message name space. Unsigned and signed
12191 * messages and delivery info's differ in prefix and URI. */
12192 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12193 const message_ns_type message_ns
) {
12194 const xmlChar
*message_namespace
= NULL
;
12196 if (!xpath_ctx
) return IE_ERROR
;
12198 switch(message_ns
) {
12200 message_namespace
= BAD_CAST ISDS1_NS
; break;
12201 case MESSAGE_NS_UNSIGNED
:
12202 message_namespace
= BAD_CAST ISDS_NS
; break;
12203 case MESSAGE_NS_SIGNED_INCOMING
:
12204 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12205 case MESSAGE_NS_SIGNED_OUTGOING
:
12206 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12207 case MESSAGE_NS_SIGNED_DELIVERY
:
12208 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12213 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12215 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12217 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12219 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12221 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12223 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))