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
;
3158 const xmlChar
*type_string
= NULL
;
3160 if (!context
) return IE_INVALID_CONTEXT
;
3161 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3164 /* Build XSD:tDbOwnerInfo */
3165 /* XXX: All the elements except email and telNumber are mandatory. */
3166 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3167 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3170 if (owner
->dbType
) {
3171 type_string
= isds_DbType2string(*(owner
->dbType
));
3173 isds_printf_message(context
, _("Invalid dbType value: %d"),
3179 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3181 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3183 INSERT_STRING(db_owner_info
, "pnFirstName",
3184 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnFirstName
);
3185 INSERT_STRING(db_owner_info
, "pnMiddleName",
3186 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnMiddleName
);
3187 INSERT_STRING(db_owner_info
, "pnLastName",
3188 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnLastName
);
3189 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3190 (NULL
== owner
->personName
) ? NULL
:
3191 owner
->personName
->pnLastNameAtBirth
);
3193 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3195 if (NULL
!= owner
->birthInfo
&& NULL
!= owner
->birthInfo
->biDate
) {
3196 err
= tm2datestring(owner
->birthInfo
->biDate
, &string
);
3197 if (err
) goto leave
;
3199 INSERT_STRING(db_owner_info
, "biDate", string
);
3202 INSERT_STRING(db_owner_info
, "biCity",
3203 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCity
);
3204 INSERT_STRING(db_owner_info
, "biCounty",
3205 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCounty
);
3206 INSERT_STRING(db_owner_info
, "biState",
3207 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biState
);
3209 INSERT_STRING(db_owner_info
, "adCity",
3210 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCity
);
3211 INSERT_STRING(db_owner_info
, "adStreet",
3212 (NULL
== owner
->address
) ? NULL
: owner
->address
->adStreet
);
3213 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3214 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInStreet
);
3215 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3216 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInMunicipality
);
3217 INSERT_STRING(db_owner_info
, "adZipCode",
3218 (NULL
== owner
->address
) ? NULL
: owner
->address
->adZipCode
);
3219 INSERT_STRING(db_owner_info
, "adState",
3220 (NULL
== owner
->address
) ? NULL
: owner
->address
->adState
);
3222 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3223 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3224 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3226 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3227 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3229 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3230 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3232 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3234 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3235 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3236 owner
->dbOpenAddressing
);
3244 /* Convert XSD:tDbUserInfo XML tree into structure
3245 * @context is ISDS context
3246 * @db_user_info is automatically reallocated user info structure
3247 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3248 * In case of error @db_user_info will be freed. */
3249 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3250 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3251 isds_error err
= IE_SUCCESS
;
3252 xmlXPathObjectPtr result
= NULL
;
3253 char *string
= NULL
;
3255 if (!context
) return IE_INVALID_CONTEXT
;
3256 if (!db_user_info
) return IE_INVAL
;
3257 isds_DbUserInfo_free(db_user_info
);
3258 if (!xpath_ctx
) return IE_INVAL
;
3261 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3262 if (!*db_user_info
) {
3267 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info
)->aifo_ticket
, 0);
3269 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3271 EXTRACT_STRING("isds:userType", string
);
3273 (*db_user_info
)->userType
=
3274 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3275 if (!(*db_user_info
)->userType
) {
3279 err
= string2isds_UserType((xmlChar
*)string
,
3280 (*db_user_info
)->userType
);
3282 zfree((*db_user_info
)->userType
);
3283 if (err
== IE_ENUM
) {
3285 char *string_locale
= _isds_utf82locale(string
);
3286 isds_printf_message(context
,
3287 _("Unknown isds:userType value: %s"), string_locale
);
3288 free(string_locale
);
3295 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3297 (*db_user_info
)->personName
=
3298 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3299 if (!(*db_user_info
)->personName
) {
3304 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3306 if (err
) goto leave
;
3308 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3309 if (err
) goto leave
;
3311 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3312 if (err
) goto leave
;
3314 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3315 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3317 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3318 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3319 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3321 /* ???: Default value is "CZ" according specification. Should we provide
3323 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3326 if (err
) isds_DbUserInfo_free(db_user_info
);
3328 xmlXPathFreeObject(result
);
3333 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3334 * @context is session context
3335 * @user is libisds structure with user description
3336 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3337 * @db_user_info is XML element of XSD:tDbUserInfo */
3338 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3339 const struct isds_DbUserInfo
*user
, _Bool honor_aifo_ticket
,
3340 xmlNodePtr db_user_info
) {
3342 isds_error err
= IE_SUCCESS
;
3344 xmlAttrPtr attribute_node
;
3345 xmlChar
*string
= NULL
;
3347 if (!context
) return IE_INVALID_CONTEXT
;
3348 if (!user
|| !db_user_info
) return IE_INVAL
;
3350 /* Build XSD:tDbUserInfo */
3352 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3353 * allow it everywhere. */
3354 if (honor_aifo_ticket
&& user
->aifo_ticket
) {
3355 INSERT_STRING_ATTRIBUTE(db_user_info
, "AIFOTicket", user
->aifo_ticket
);
3358 if (user
->personName
) {
3359 INSERT_STRING(db_user_info
, "pnFirstName",
3360 user
->personName
->pnFirstName
);
3361 INSERT_STRING(db_user_info
, "pnMiddleName",
3362 user
->personName
->pnMiddleName
);
3363 INSERT_STRING(db_user_info
, "pnLastName",
3364 user
->personName
->pnLastName
);
3365 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3366 user
->personName
->pnLastNameAtBirth
);
3368 if (user
->address
) {
3369 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3370 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3371 INSERT_STRING(db_user_info
, "adNumberInStreet",
3372 user
->address
->adNumberInStreet
);
3373 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3374 user
->address
->adNumberInMunicipality
);
3375 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3376 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3379 if (!tm2datestring(user
->biDate
, &string
))
3380 INSERT_STRING(db_user_info
, "biDate", string
);
3383 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3384 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3387 if (user
->userType
) {
3388 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3390 isds_printf_message(context
, _("Invalid userType value: %d"),
3395 INSERT_STRING(db_user_info
, "userType", type_string
);
3398 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3399 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3400 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3401 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3402 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3403 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3404 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3405 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3406 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3414 /* Convert XSD:tPDZRec XML tree into structure
3415 * @context is ISDS context
3416 * @permission is automatically reallocated commercial permission structure
3417 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3418 * In case of error @permission will be freed. */
3419 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3420 struct isds_commercial_permission
**permission
,
3421 xmlXPathContextPtr xpath_ctx
) {
3422 isds_error err
= IE_SUCCESS
;
3423 xmlXPathObjectPtr result
= NULL
;
3424 char *string
= NULL
;
3426 if (!context
) return IE_INVALID_CONTEXT
;
3427 if (!permission
) return IE_INVAL
;
3428 isds_commercial_permission_free(permission
);
3429 if (!xpath_ctx
) return IE_INVAL
;
3432 *permission
= calloc(1, sizeof(**permission
));
3438 EXTRACT_STRING("isds:PDZType", string
);
3440 err
= string2isds_payment_type((xmlChar
*)string
,
3441 &(*permission
)->type
);
3443 if (err
== IE_ENUM
) {
3445 char *string_locale
= _isds_utf82locale(string
);
3446 isds_printf_message(context
,
3447 _("Unknown isds:PDZType value: %s"), string_locale
);
3448 free(string_locale
);
3455 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3456 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3458 EXTRACT_STRING("isds:PDZExpire", string
);
3460 err
= timestring2timeval((xmlChar
*) string
,
3461 &((*permission
)->expiration
));
3463 char *string_locale
= _isds_utf82locale(string
);
3464 if (err
== IE_DATE
) err
= IE_ISDS
;
3465 isds_printf_message(context
,
3466 _("Could not convert PDZExpire as ISO time: %s"),
3468 free(string_locale
);
3474 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3475 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3478 if (err
) isds_commercial_permission_free(permission
);
3480 xmlXPathFreeObject(result
);
3485 /* Convert XSD:tCiRecord XML tree into structure
3486 * @context is ISDS context
3487 * @event is automatically reallocated commercial credit event structure
3488 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3489 * In case of error @event will be freed. */
3490 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3491 struct isds_credit_event
**event
,
3492 xmlXPathContextPtr xpath_ctx
) {
3493 isds_error err
= IE_SUCCESS
;
3494 xmlXPathObjectPtr result
= NULL
;
3495 char *string
= NULL
;
3496 long int *number_ptr
;
3498 if (!context
) return IE_INVALID_CONTEXT
;
3499 if (!event
) return IE_INVAL
;
3500 isds_credit_event_free(event
);
3501 if (!xpath_ctx
) return IE_INVAL
;
3504 *event
= calloc(1, sizeof(**event
));
3510 EXTRACT_STRING("isds:ciEventTime", string
);
3512 err
= timestring2timeval((xmlChar
*) string
,
3515 char *string_locale
= _isds_utf82locale(string
);
3516 if (err
== IE_DATE
) err
= IE_ISDS
;
3517 isds_printf_message(context
,
3518 _("Could not convert ciEventTime as ISO time: %s"),
3520 free(string_locale
);
3526 EXTRACT_STRING("isds:ciEventType", string
);
3528 err
= string2isds_credit_event_type((xmlChar
*)string
,
3531 if (err
== IE_ENUM
) {
3533 char *string_locale
= _isds_utf82locale(string
);
3534 isds_printf_message(context
,
3535 _("Unknown isds:ciEventType value: %s"), string_locale
);
3536 free(string_locale
);
3543 number_ptr
= &((*event
)->credit_change
);
3544 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3545 number_ptr
= &(*event
)->new_credit
;
3546 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3548 switch((*event
)->type
) {
3549 case ISDS_CREDIT_CHARGED
:
3550 EXTRACT_STRING("isds:ciTransID",
3551 (*event
)->details
.charged
.transaction
);
3553 case ISDS_CREDIT_DISCHARGED
:
3554 EXTRACT_STRING("isds:ciTransID",
3555 (*event
)->details
.discharged
.transaction
);
3557 case ISDS_CREDIT_MESSAGE_SENT
:
3558 EXTRACT_STRING("isds:ciRecipientID",
3559 (*event
)->details
.message_sent
.recipient
);
3560 EXTRACT_STRING("isds:ciPDZID",
3561 (*event
)->details
.message_sent
.message_id
);
3563 case ISDS_CREDIT_STORAGE_SET
:
3564 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3565 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3566 EXTRACT_DATE("isds:ciNewFrom",
3567 (*event
)->details
.storage_set
.new_valid_from
);
3568 EXTRACT_DATE("isds:ciNewTo",
3569 (*event
)->details
.storage_set
.new_valid_to
);
3570 EXTRACT_LONGINT("isds:ciOldCapacity",
3571 (*event
)->details
.storage_set
.old_capacity
, 0);
3572 EXTRACT_DATE("isds:ciOldFrom",
3573 (*event
)->details
.storage_set
.old_valid_from
);
3574 EXTRACT_DATE("isds:ciOldTo",
3575 (*event
)->details
.storage_set
.old_valid_to
);
3576 EXTRACT_STRING("isds:ciDoneBy",
3577 (*event
)->details
.storage_set
.initiator
);
3579 case ISDS_CREDIT_EXPIRED
:
3584 if (err
) isds_credit_event_free(event
);
3586 xmlXPathFreeObject(result
);
3591 #endif /* HAVE_LIBCURL */
3594 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3595 * isds_envelope structure. The envelope is automatically allocated but not
3596 * reallocated. The date are just appended into envelope structure.
3597 * @context is ISDS context
3598 * @envelope is automatically allocated message envelope structure
3599 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3600 * In case of error @envelope will be freed. */
3601 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3602 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3603 isds_error err
= IE_SUCCESS
;
3604 xmlXPathObjectPtr result
= NULL
;
3606 if (!context
) return IE_INVALID_CONTEXT
;
3607 if (!envelope
) return IE_INVAL
;
3608 if (!xpath_ctx
) return IE_INVAL
;
3612 /* Allocate envelope */
3613 *envelope
= calloc(1, sizeof(**envelope
));
3619 /* Else free former data */
3620 zfree((*envelope
)->dmSenderOrgUnit
);
3621 zfree((*envelope
)->dmSenderOrgUnitNum
);
3622 zfree((*envelope
)->dbIDRecipient
);
3623 zfree((*envelope
)->dmRecipientOrgUnit
);
3624 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3625 zfree((*envelope
)->dmToHands
);
3626 zfree((*envelope
)->dmAnnotation
);
3627 zfree((*envelope
)->dmRecipientRefNumber
);
3628 zfree((*envelope
)->dmSenderRefNumber
);
3629 zfree((*envelope
)->dmRecipientIdent
);
3630 zfree((*envelope
)->dmSenderIdent
);
3631 zfree((*envelope
)->dmLegalTitleLaw
);
3632 zfree((*envelope
)->dmLegalTitleYear
);
3633 zfree((*envelope
)->dmLegalTitleSect
);
3634 zfree((*envelope
)->dmLegalTitlePar
);
3635 zfree((*envelope
)->dmLegalTitlePoint
);
3636 zfree((*envelope
)->dmPersonalDelivery
);
3637 zfree((*envelope
)->dmAllowSubstDelivery
);
3640 /* Extract envelope elements added by sender or ISDS
3641 * (XSD: gMessageEnvelopeSub type) */
3642 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3643 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3644 (*envelope
)->dmSenderOrgUnitNum
, 0);
3645 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3646 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3647 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3648 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3649 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3650 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3651 EXTRACT_STRING("isds:dmRecipientRefNumber",
3652 (*envelope
)->dmRecipientRefNumber
);
3653 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3654 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3655 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3657 /* Extract envelope elements regarding law reference */
3658 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3659 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3660 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3661 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3662 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3664 /* Extract envelope other elements */
3665 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3666 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3667 (*envelope
)->dmAllowSubstDelivery
);
3670 if (err
) isds_envelope_free(envelope
);
3671 xmlXPathFreeObject(result
);
3677 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3678 * isds_envelope structure. The envelope is automatically allocated but not
3679 * reallocated. The date are just appended into envelope structure.
3680 * @context is ISDS context
3681 * @envelope is automatically allocated message envelope structure
3682 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3683 * In case of error @envelope will be freed. */
3684 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3685 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3686 isds_error err
= IE_SUCCESS
;
3687 xmlXPathObjectPtr result
= NULL
;
3689 if (!context
) return IE_INVALID_CONTEXT
;
3690 if (!envelope
) return IE_INVAL
;
3691 if (!xpath_ctx
) return IE_INVAL
;
3695 /* Allocate envelope */
3696 *envelope
= calloc(1, sizeof(**envelope
));
3702 /* Else free former data */
3703 zfree((*envelope
)->dmID
);
3704 zfree((*envelope
)->dbIDSender
);
3705 zfree((*envelope
)->dmSender
);
3706 zfree((*envelope
)->dmSenderAddress
);
3707 zfree((*envelope
)->dmSenderType
);
3708 zfree((*envelope
)->dmRecipient
);
3709 zfree((*envelope
)->dmRecipientAddress
);
3710 zfree((*envelope
)->dmAmbiguousRecipient
);
3713 /* Extract envelope elements added by ISDS
3714 * (XSD: gMessageEnvelope type) */
3715 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3716 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3717 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3718 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3719 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3720 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3721 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3722 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3723 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3724 (*envelope
)->dmAmbiguousRecipient
);
3726 /* Extract envelope elements added by sender and ISDS
3727 * (XSD: gMessageEnvelope type) */
3728 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3729 if (err
) goto leave
;
3732 if (err
) isds_envelope_free(envelope
);
3733 xmlXPathFreeObject(result
);
3738 /* Convert other envelope elements from XML tree into isds_envelope structure:
3739 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3740 * The envelope is automatically allocated but not reallocated.
3741 * The data are just appended into envelope structure.
3742 * @context is ISDS context
3743 * @envelope is automatically allocated message envelope structure
3744 * @xpath_ctx is XPath context with current node as parent desired elements
3745 * In case of error @envelope will be freed. */
3746 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3747 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3748 isds_error err
= IE_SUCCESS
;
3749 xmlXPathObjectPtr result
= NULL
;
3750 char *string
= NULL
;
3751 unsigned long int *unumber
= NULL
;
3753 if (!context
) return IE_INVALID_CONTEXT
;
3754 if (!envelope
) return IE_INVAL
;
3755 if (!xpath_ctx
) return IE_INVAL
;
3760 *envelope
= calloc(1, sizeof(**envelope
));
3767 zfree((*envelope
)->dmMessageStatus
);
3768 zfree((*envelope
)->dmAttachmentSize
);
3769 zfree((*envelope
)->dmDeliveryTime
);
3770 zfree((*envelope
)->dmAcceptanceTime
);
3774 /* dmMessageStatus element is mandatory */
3775 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3777 isds_log_message(context
,
3778 _("Missing mandatory sisds:dmMessageStatus integer"));
3782 err
= uint2isds_message_status(context
, unumber
,
3783 &((*envelope
)->dmMessageStatus
));
3785 if (err
== IE_ENUM
) err
= IE_ISDS
;
3788 free(unumber
); unumber
= NULL
;
3790 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3793 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3795 err
= timestring2timeval((xmlChar
*) string
,
3796 &((*envelope
)->dmDeliveryTime
));
3798 char *string_locale
= _isds_utf82locale(string
);
3799 if (err
== IE_DATE
) err
= IE_ISDS
;
3800 isds_printf_message(context
,
3801 _("Could not convert dmDeliveryTime as ISO time: %s"),
3803 free(string_locale
);
3809 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3811 err
= timestring2timeval((xmlChar
*) string
,
3812 &((*envelope
)->dmAcceptanceTime
));
3814 char *string_locale
= _isds_utf82locale(string
);
3815 if (err
== IE_DATE
) err
= IE_ISDS
;
3816 isds_printf_message(context
,
3817 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3819 free(string_locale
);
3826 if (err
) isds_envelope_free(envelope
);
3829 xmlXPathFreeObject(result
);
3834 /* Convert message type attribute of current element into isds_envelope
3836 * TODO: This function can be incorporated into append_status_size_times() as
3837 * they are called always together.
3838 * The envelope is automatically allocated but not reallocated.
3839 * The data are just appended into envelope structure.
3840 * @context is ISDS context
3841 * @envelope is automatically allocated message envelope structure
3842 * @xpath_ctx is XPath context with current node as parent of attribute
3843 * carrying message type
3844 * In case of error @envelope will be freed. */
3845 static isds_error
append_message_type(struct isds_ctx
*context
,
3846 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3847 isds_error err
= IE_SUCCESS
;
3849 if (!context
) return IE_INVALID_CONTEXT
;
3850 if (!envelope
) return IE_INVAL
;
3851 if (!xpath_ctx
) return IE_INVAL
;
3856 *envelope
= calloc(1, sizeof(**envelope
));
3863 zfree((*envelope
)->dmType
);
3867 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3869 if (!(*envelope
)->dmType
) {
3870 /* Use default value */
3871 (*envelope
)->dmType
= strdup("V");
3872 if (!(*envelope
)->dmType
) {
3876 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3877 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3878 isds_printf_message(context
,
3879 _("Message type in dmType attribute is not 1 character long: "
3888 if (err
) isds_envelope_free(envelope
);
3894 /* Convert dmType isds_envelope member into XML attribute and append it to
3896 * @context is ISDS context
3897 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3898 * @dm_envelope is XML element the resulting attribute will be appended to.
3899 * @return error code, in case of error context' message is filled. */
3900 static isds_error
insert_message_type(struct isds_ctx
*context
,
3901 const char *type
, xmlNodePtr dm_envelope
) {
3902 isds_error err
= IE_SUCCESS
;
3903 xmlAttrPtr attribute_node
;
3905 if (!context
) return IE_INVALID_CONTEXT
;
3906 if (!dm_envelope
) return IE_INVAL
;
3908 /* Insert optional message type */
3910 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3911 char *type_locale
= _isds_utf82locale(type
);
3912 isds_printf_message(context
,
3913 _("Message type in envelope is not 1 character long: %s"),
3919 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3925 #endif /* HAVE_LIBCURL */
3928 /* Extract message document into reallocated document structure
3929 * @context is ISDS context
3930 * @document is automatically reallocated message documents structure
3931 * @xpath_ctx is XPath context with current node as isds:dmFile
3932 * In case of error @document will be freed. */
3933 static isds_error
extract_document(struct isds_ctx
*context
,
3934 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3935 isds_error err
= IE_SUCCESS
;
3936 xmlXPathObjectPtr result
= NULL
;
3937 xmlNodePtr file_node
;
3938 char *string
= NULL
;
3940 if (!context
) return IE_INVALID_CONTEXT
;
3941 if (!document
) return IE_INVAL
;
3942 isds_document_free(document
);
3943 if (!xpath_ctx
) return IE_INVAL
;
3944 file_node
= xpath_ctx
->node
;
3946 *document
= calloc(1, sizeof(**document
));
3952 /* Extract document meta data */
3953 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3954 if (context
->normalize_mime_type
) {
3955 const char *normalized_type
=
3956 isds_normalize_mime_type((*document
)->dmMimeType
);
3957 if (NULL
!= normalized_type
&&
3958 normalized_type
!= (*document
)->dmMimeType
) {
3959 char *new_type
= strdup(normalized_type
);
3960 if (NULL
== new_type
) {
3961 isds_printf_message(context
,
3962 _("Not enough memory to normalize document MIME type"));
3966 free((*document
)->dmMimeType
);
3967 (*document
)->dmMimeType
= new_type
;
3971 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3972 err
= string2isds_FileMetaType((xmlChar
*)string
,
3973 &((*document
)->dmFileMetaType
));
3975 char *meta_type_locale
= _isds_utf82locale(string
);
3976 isds_printf_message(context
,
3977 _("Document has invalid dmFileMetaType attribute value: %s"),
3979 free(meta_type_locale
);
3985 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3986 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3987 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3988 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3991 /* Extract document data.
3992 * Base64 encoded blob or XML subtree must be presented. */
3994 /* Check for dmEncodedContent */
3995 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
4002 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4003 /* Here we have Base64 blob */
4004 (*document
)->is_xml
= 0;
4006 if (result
->nodesetval
->nodeNr
> 1) {
4007 isds_printf_message(context
,
4008 _("Document has more dmEncodedContent elements"));
4013 xmlXPathFreeObject(result
); result
= NULL
;
4014 EXTRACT_STRING("isds:dmEncodedContent", string
);
4016 /* Decode non-empty document */
4017 if (string
&& string
[0] != '\0') {
4018 (*document
)->data_length
=
4019 _isds_b64decode(string
, &((*document
)->data
));
4020 if ((*document
)->data_length
== (size_t) -1) {
4021 isds_printf_message(context
,
4022 _("Error while Base64-decoding document content"));
4028 /* No Base64 blob, try XML document */
4029 xmlXPathFreeObject(result
); result
= NULL
;
4030 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4037 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4038 /* Here we have XML document */
4039 (*document
)->is_xml
= 1;
4041 if (result
->nodesetval
->nodeNr
> 1) {
4042 isds_printf_message(context
,
4043 _("Document has more dmXMLContent elements"));
4048 /* XXX: We cannot serialize the content simply because:
4049 * - XML document may point out of its scope (e.g. to message
4051 * - isds:dmXMLContent can contain more elements, no element,
4053 * - it's not the XML way
4054 * Thus we provide the only right solution: XML DOM. Let's
4055 * application to cope with this hot potato :) */
4056 (*document
)->xml_node_list
=
4057 result
->nodesetval
->nodeTab
[0]->children
;
4059 /* No base64 blob, nor XML document */
4060 isds_printf_message(context
,
4061 _("Document has no dmEncodedContent, nor dmXMLContent "
4070 if (err
) isds_document_free(document
);
4072 xmlXPathFreeObject(result
);
4073 xpath_ctx
->node
= file_node
;
4079 /* Extract message documents into reallocated list of documents
4080 * @context is ISDS context
4081 * @documents is automatically reallocated message documents list structure
4082 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4083 * In case of error @documents will be freed. */
4084 static isds_error
extract_documents(struct isds_ctx
*context
,
4085 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4086 isds_error err
= IE_SUCCESS
;
4087 xmlXPathObjectPtr result
= NULL
;
4088 xmlNodePtr files_node
;
4089 struct isds_list
*document
, *prev_document
= NULL
;
4091 if (!context
) return IE_INVALID_CONTEXT
;
4092 if (!documents
) return IE_INVAL
;
4093 isds_list_free(documents
);
4094 if (!xpath_ctx
) return IE_INVAL
;
4095 files_node
= xpath_ctx
->node
;
4097 /* Find documents */
4098 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4105 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4106 isds_printf_message(context
,
4107 _("Message does not contain any document"));
4113 /* Iterate over documents */
4114 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4116 /* Allocate and append list item */
4117 document
= calloc(1, sizeof(*document
));
4122 document
->destructor
= (void (*)(void **))isds_document_free
;
4123 if (i
== 0) *documents
= document
;
4124 else prev_document
->next
= document
;
4125 prev_document
= document
;
4127 /* Extract document */
4128 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4129 err
= extract_document(context
,
4130 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4131 if (err
) goto leave
;
4136 if (err
) isds_list_free(documents
);
4137 xmlXPathFreeObject(result
);
4138 xpath_ctx
->node
= files_node
;
4144 /* Convert isds:dmRecord XML tree into structure
4145 * @context is ISDS context
4146 * @envelope is automatically reallocated message envelope structure
4147 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4148 * In case of error @envelope will be freed. */
4149 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4150 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4151 isds_error err
= IE_SUCCESS
;
4152 xmlXPathObjectPtr result
= NULL
;
4154 if (!context
) return IE_INVALID_CONTEXT
;
4155 if (!envelope
) return IE_INVAL
;
4156 isds_envelope_free(envelope
);
4157 if (!xpath_ctx
) return IE_INVAL
;
4160 *envelope
= calloc(1, sizeof(**envelope
));
4167 /* Extract tRecord data */
4168 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4170 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4171 * dmAcceptanceTime. */
4172 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4173 if (err
) goto leave
;
4175 /* Extract envelope elements added by sender and ISDS
4176 * (XSD: gMessageEnvelope type) */
4177 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4178 if (err
) goto leave
;
4180 /* Get message type */
4181 err
= append_message_type(context
, envelope
, xpath_ctx
);
4182 if (err
) goto leave
;
4186 if (err
) isds_envelope_free(envelope
);
4187 xmlXPathFreeObject(result
);
4192 /* Convert XSD:tStateChangesRecord type XML tree into structure
4193 * @context is ISDS context
4194 * @changed_status is automatically reallocated message state change structure
4195 * @xpath_ctx is XPath context with current node as element of
4196 * XSD:tStateChangesRecord type
4197 * In case of error @changed_status will be freed. */
4198 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4199 struct isds_message_status_change
**changed_status
,
4200 xmlXPathContextPtr xpath_ctx
) {
4201 isds_error err
= IE_SUCCESS
;
4202 xmlXPathObjectPtr result
= NULL
;
4203 unsigned long int *unumber
= NULL
;
4204 char *string
= NULL
;
4206 if (!context
) return IE_INVALID_CONTEXT
;
4207 if (!changed_status
) return IE_INVAL
;
4208 isds_message_status_change_free(changed_status
);
4209 if (!xpath_ctx
) return IE_INVAL
;
4212 *changed_status
= calloc(1, sizeof(**changed_status
));
4213 if (!*changed_status
) {
4219 /* Extract tGetStateChangesInput data */
4220 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4222 /* dmEventTime is mandatory */
4223 EXTRACT_STRING("isds:dmEventTime", string
);
4225 err
= timestring2timeval((xmlChar
*) string
,
4226 &((*changed_status
)->time
));
4228 char *string_locale
= _isds_utf82locale(string
);
4229 if (err
== IE_DATE
) err
= IE_ISDS
;
4230 isds_printf_message(context
,
4231 _("Could not convert dmEventTime as ISO time: %s"),
4233 free(string_locale
);
4239 /* dmMessageStatus element is mandatory */
4240 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4242 isds_log_message(context
,
4243 _("Missing mandatory isds:dmMessageStatus integer"));
4247 err
= uint2isds_message_status(context
, unumber
,
4248 &((*changed_status
)->dmMessageStatus
));
4250 if (err
== IE_ENUM
) err
= IE_ISDS
;
4259 if (err
) isds_message_status_change_free(changed_status
);
4260 xmlXPathFreeObject(result
);
4263 #endif /* HAVE_LIBCURL */
4266 /* Find and convert isds:dmHash XML tree into structure
4267 * @context is ISDS context
4268 * @envelope is automatically reallocated message hash structure
4269 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4270 * In case of error @hash will be freed. */
4271 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4272 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4273 isds_error err
= IE_SUCCESS
;
4274 xmlNodePtr old_ctx_node
;
4275 xmlXPathObjectPtr result
= NULL
;
4276 char *string
= NULL
;
4278 if (!context
) return IE_INVALID_CONTEXT
;
4279 if (!hash
) return IE_INVAL
;
4280 isds_hash_free(hash
);
4281 if (!xpath_ctx
) return IE_INVAL
;
4283 old_ctx_node
= xpath_ctx
->node
;
4285 *hash
= calloc(1, sizeof(**hash
));
4292 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4293 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4302 /* Get hash algorithm */
4303 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4304 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4306 if (err
== IE_ENUM
) {
4307 char *string_locale
= _isds_utf82locale(string
);
4308 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4310 free(string_locale
);
4316 /* Get hash value */
4317 EXTRACT_STRING(".", string
);
4319 isds_printf_message(context
,
4320 _("sisds:dmHash element is missing hash value"));
4324 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4325 if ((*hash
)->length
== (size_t) -1) {
4326 isds_printf_message(context
,
4327 _("Error while Base64-decoding hash value"));
4333 if (err
) isds_hash_free(hash
);
4335 xmlXPathFreeObject(result
);
4336 xpath_ctx
->node
= old_ctx_node
;
4341 /* Find and append isds:dmQTimestamp XML tree into envelope.
4342 * Because one service is allowed to miss time-stamp content, and we think
4343 * other could too (flaw in specification), this function is deliberated and
4344 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4345 * @context is ISDS context
4346 * @envelope is automatically allocated envelope structure
4347 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4349 * In case of error @envelope will be freed. */
4350 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4351 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4352 isds_error err
= IE_SUCCESS
;
4353 xmlXPathObjectPtr result
= NULL
;
4354 char *string
= NULL
;
4356 if (!context
) return IE_INVALID_CONTEXT
;
4357 if (!envelope
) return IE_INVAL
;
4359 isds_envelope_free(envelope
);
4364 *envelope
= calloc(1, sizeof(**envelope
));
4370 zfree((*envelope
)->timestamp
);
4371 (*envelope
)->timestamp_length
= 0;
4374 /* Get dmQTimestamp */
4375 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4377 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4380 (*envelope
)->timestamp_length
=
4381 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4382 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4383 isds_printf_message(context
,
4384 _("Error while Base64-decoding time stamp value"));
4390 if (err
) isds_envelope_free(envelope
);
4392 xmlXPathFreeObject(result
);
4397 /* Convert XSD tReturnedMessage XML tree into message structure.
4398 * It does not store serialized XML tree into message->raw.
4399 * It does store (pointer to) parsed XML tree into message->xml if needed.
4400 * @context is ISDS context
4401 * @include_documents Use true if documents must be extracted
4402 * (tReturnedMessage XSD type), use false if documents shall be omitted
4403 * (tReturnedMessageEnvelope).
4404 * @message is automatically reallocated message structure
4405 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4407 * In case of error @message will be freed. */
4408 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4409 const _Bool include_documents
, struct isds_message
**message
,
4410 xmlXPathContextPtr xpath_ctx
) {
4411 isds_error err
= IE_SUCCESS
;
4412 xmlNodePtr message_node
;
4414 if (!context
) return IE_INVALID_CONTEXT
;
4415 if (!message
) return IE_INVAL
;
4416 isds_message_free(message
);
4417 if (!xpath_ctx
) return IE_INVAL
;
4420 *message
= calloc(1, sizeof(**message
));
4426 /* Save message XPATH context node */
4427 message_node
= xpath_ctx
->node
;
4431 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4432 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4433 if (err
) { err
= IE_ERROR
; goto leave
; }
4434 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4435 if (err
) goto leave
;
4437 if (include_documents
) {
4438 struct isds_list
*item
;
4440 /* Extract dmFiles */
4441 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4443 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4444 err
= IE_ISDS
; goto leave
;
4446 if (err
) { err
= IE_ERROR
; goto leave
; }
4447 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4448 if (err
) goto leave
;
4450 /* Store xmlDoc of this message if needed */
4451 /* Only if we got a XML document in all the documents. */
4452 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4453 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4454 (*message
)->xml
= xpath_ctx
->doc
;
4461 /* Restore context to message */
4462 xpath_ctx
->node
= message_node
;
4464 /* Extract dmHash */
4465 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4467 if (err
) goto leave
;
4469 /* Extract dmQTimestamp, */
4470 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4472 if (err
) goto leave
;
4474 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4475 * dmAcceptanceTime. */
4476 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4477 if (err
) goto leave
;
4479 /* Get message type */
4480 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4481 if (err
) goto leave
;
4484 if (err
) isds_message_free(message
);
4489 /* Extract message event into reallocated isds_event structure
4490 * @context is ISDS context
4491 * @event is automatically reallocated message event structure
4492 * @xpath_ctx is XPath context with current node as isds:dmEvent
4493 * In case of error @event will be freed. */
4494 static isds_error
extract_event(struct isds_ctx
*context
,
4495 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4496 isds_error err
= IE_SUCCESS
;
4497 xmlXPathObjectPtr result
= NULL
;
4498 xmlNodePtr event_node
;
4499 char *string
= NULL
;
4501 if (!context
) return IE_INVALID_CONTEXT
;
4502 if (!event
) return IE_INVAL
;
4503 isds_event_free(event
);
4504 if (!xpath_ctx
) return IE_INVAL
;
4505 event_node
= xpath_ctx
->node
;
4507 *event
= calloc(1, sizeof(**event
));
4513 /* Extract event data.
4514 * All elements are optional according XSD. That's funny. */
4515 EXTRACT_STRING("sisds:dmEventTime", string
);
4517 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4519 char *string_locale
= _isds_utf82locale(string
);
4520 if (err
== IE_DATE
) err
= IE_ISDS
;
4521 isds_printf_message(context
,
4522 _("Could not convert dmEventTime as ISO time: %s"),
4524 free(string_locale
);
4530 /* dmEventDescr element has prefix and the rest */
4531 EXTRACT_STRING("sisds:dmEventDescr", string
);
4533 err
= eventstring2event((xmlChar
*) string
, *event
);
4534 if (err
) goto leave
;
4539 if (err
) isds_event_free(event
);
4541 xmlXPathFreeObject(result
);
4542 xpath_ctx
->node
= event_node
;
4547 /* Convert element of XSD tEventsArray type from XML tree into
4548 * isds_list of isds_event's structure. The list is automatically reallocated.
4549 * @context is ISDS context
4550 * @events is automatically reallocated list of event structures
4551 * @xpath_ctx is XPath context with current node as tEventsArray
4552 * In case of error @events will be freed. */
4553 static isds_error
extract_events(struct isds_ctx
*context
,
4554 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4555 isds_error err
= IE_SUCCESS
;
4556 xmlXPathObjectPtr result
= NULL
;
4557 xmlNodePtr events_node
;
4558 struct isds_list
*event
, *prev_event
= NULL
;
4560 if (!context
) return IE_INVALID_CONTEXT
;
4561 if (!events
) return IE_INVAL
;
4562 if (!xpath_ctx
) return IE_INVAL
;
4563 events_node
= xpath_ctx
->node
;
4566 isds_list_free(events
);
4569 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4576 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4577 isds_printf_message(context
,
4578 _("Delivery info does not contain any event"));
4584 /* Iterate over events */
4585 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4587 /* Allocate and append list item */
4588 event
= calloc(1, sizeof(*event
));
4593 event
->destructor
= (void (*)(void **))isds_event_free
;
4594 if (i
== 0) *events
= event
;
4595 else prev_event
->next
= event
;
4599 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4600 err
= extract_event(context
,
4601 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4602 if (err
) goto leave
;
4607 if (err
) isds_list_free(events
);
4608 xmlXPathFreeObject(result
);
4609 xpath_ctx
->node
= events_node
;
4615 /* Insert Base64 encoded data as element with text child.
4616 * @context is session context
4617 * @parent is XML node to append @element with @data as child
4618 * @ns is XML namespace of @element, use NULL to inherit from @parent
4619 * @element is UTF-8 encoded name of new element
4620 * @data is bit stream to encode into @element
4621 * @length is size of @data in bytes
4622 * @return standard error code and fill long error message if needed */
4623 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4624 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4625 const void *data
, size_t length
) {
4626 isds_error err
= IE_SUCCESS
;
4629 if (!context
) return IE_INVALID_CONTEXT
;
4630 if (!data
&& length
> 0) return IE_INVAL
;
4631 if (!parent
|| !element
) return IE_INVAL
;
4633 xmlChar
*base64data
= NULL
;
4634 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4636 isds_printf_message(context
,
4637 ngettext("Not enough memory to encode %zd byte into Base64",
4638 "Not enough memory to encode %zd bytes into Base64",
4644 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4652 /* Convert isds_document structure into XML tree and append to dmFiles node.
4653 * @context is session context
4654 * @document is ISDS document
4655 * @dm_files is XML element the resulting tree will be appended to as a child.
4656 * @return error code, in case of error context' message is filled. */
4657 static isds_error
insert_document(struct isds_ctx
*context
,
4658 struct isds_document
*document
, xmlNodePtr dm_files
) {
4659 isds_error err
= IE_SUCCESS
;
4660 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4661 xmlAttrPtr attribute_node
;
4663 if (!context
) return IE_INVALID_CONTEXT
;
4664 if (!document
|| !dm_files
) return IE_INVAL
;
4666 /* Allocate new dmFile */
4667 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4669 isds_printf_message(context
, _("Could not allocate main dmFile"));
4673 /* Append the new dmFile.
4674 * XXX: Main document must go first */
4675 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4676 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4678 file
= xmlAddChild(dm_files
, new_file
);
4681 xmlFreeNode(new_file
); new_file
= NULL
;
4682 isds_printf_message(context
, _("Could not add dmFile child to "
4683 "%s element"), dm_files
->name
);
4688 /* @dmMimeType is required */
4689 if (!document
->dmMimeType
) {
4690 isds_log_message(context
,
4691 _("Document is missing mandatory MIME type definition"));
4695 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4697 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4699 isds_printf_message(context
,
4700 _("Document has unknown dmFileMetaType: %ld"),
4701 document
->dmFileMetaType
);
4705 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4707 if (document
->dmFileGuid
) {
4708 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4710 if (document
->dmUpFileGuid
) {
4711 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4714 /* @dmFileDescr is required */
4715 if (!document
->dmFileDescr
) {
4716 isds_log_message(context
,
4717 _("Document is missing mandatory description (title)"));
4721 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4723 if (document
->dmFormat
) {
4724 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4728 /* Insert content (body) of the document. */
4729 if (document
->is_xml
) {
4730 /* XML document requested */
4732 /* Allocate new dmXMLContent */
4733 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4735 isds_printf_message(context
,
4736 _("Could not allocate dmXMLContent element"));
4741 node
= xmlAddChild(file
, xmlcontent
);
4743 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4744 isds_printf_message(context
,
4745 _("Could not add dmXMLContent child to %s element"),
4751 /* Copy non-empty node list */
4752 if (document
->xml_node_list
) {
4753 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4754 document
->xml_node_list
);
4756 isds_printf_message(context
,
4757 _("Not enough memory to copy XML document"));
4762 if (!xmlAddChildList(node
, content
)) {
4763 xmlFreeNodeList(content
);
4764 isds_printf_message(context
,
4765 _("Error while adding XML document into dmXMLContent"));
4769 /* XXX: We cannot free the content here because it's part of node's
4770 * document since now. It will be freed with it automatically. */
4773 /* Binary document requested */
4774 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4775 document
->data
, document
->data_length
);
4776 if (err
) goto leave
;
4784 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4785 * The copy must be preallocated, the date are just appended into structure.
4786 * @context is ISDS context
4787 * @copy is message copy structure
4788 * @xpath_ctx is XPath context with current node as tMStatus */
4789 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4790 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4791 isds_error err
= IE_SUCCESS
;
4792 xmlXPathObjectPtr result
= NULL
;
4793 char *code
= NULL
, *message
= NULL
;
4795 if (!context
) return IE_INVALID_CONTEXT
;
4796 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4798 /* Free old values */
4799 zfree(copy
->dmStatus
);
4802 /* Get error specific to this copy */
4803 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4805 isds_log_message(context
,
4806 _("Missing isds:dmStatusCode under "
4807 "XSD:tMStatus type element"));
4812 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4813 /* This copy failed */
4814 copy
->error
= IE_ISDS
;
4815 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4817 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4818 if (!copy
->dmStatus
) {
4819 copy
->dmStatus
= code
;
4823 copy
->dmStatus
= code
;
4827 /* This copy succeeded. In this case only, message ID is valid */
4828 copy
->error
= IE_SUCCESS
;
4830 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4832 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4833 "but did not returned assigned message ID\n"));
4841 xmlXPathFreeObject(result
);
4846 /* Insert struct isds_approval data (box approval) into XML tree
4847 * @context is session context
4848 * @approval is libisds structure with approval description. NULL is
4850 * @parent is XML element to append @approval to */
4851 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4852 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4854 isds_error err
= IE_SUCCESS
;
4857 if (!context
) return IE_INVALID_CONTEXT
;
4858 if (!parent
) return IE_INVAL
;
4860 if (!approval
) return IE_SUCCESS
;
4862 /* Build XSD:gExtApproval */
4863 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4864 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4871 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4873 * @context is session context
4874 * @service_name is name of SERVICE_DB_ACCESS
4875 * @response is reallocated server SOAP body response as XML document
4876 * @raw_response is reallocated bit stream with response body. Use
4877 * NULL if you don't care
4878 * @raw_response_length is size of @raw_response in bytes
4879 * @code is reallocated ISDS status code
4880 * @status_message is reallocated ISDS status message
4881 * @return error coded from lower layer, context message will be set up
4883 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4884 const xmlChar
*service_name
,
4885 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4886 xmlChar
**code
, xmlChar
**status_message
) {
4888 isds_error err
= IE_SUCCESS
;
4889 char *service_name_locale
= NULL
;
4890 xmlNodePtr request
= NULL
, node
;
4891 xmlNsPtr isds_ns
= NULL
;
4893 if (!context
) return IE_INVALID_CONTEXT
;
4894 if (!service_name
) return IE_INVAL
;
4895 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4896 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4898 /* Free output argument */
4899 xmlFreeDoc(*response
); *response
= NULL
;
4900 if (raw_response
) zfree(*raw_response
);
4902 zfree(*status_message
);
4905 /* Check if connection is established
4906 * TODO: This check should be done downstairs. */
4907 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4909 service_name_locale
= _isds_utf82locale((char*)service_name
);
4910 if (!service_name_locale
) {
4916 request
= xmlNewNode(NULL
, service_name
);
4918 isds_printf_message(context
,
4919 _("Could not build %s request"), service_name_locale
);
4923 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4925 isds_log_message(context
, _("Could not create ISDS name space"));
4929 xmlSetNs(request
, isds_ns
);
4932 /* Add XSD:tDummyInput child */
4933 INSERT_STRING(request
, "dbDummy", NULL
);
4936 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4937 service_name_locale
);
4940 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4941 raw_response
, raw_response_length
);
4942 xmlFreeNode(request
); request
= NULL
;
4945 isds_log(ILF_ISDS
, ILL_DEBUG
,
4946 _("Processing ISDS response on %s request failed\n"),
4947 service_name_locale
);
4951 /* Check for response status */
4952 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4953 code
, status_message
, NULL
);
4955 isds_log(ILF_ISDS
, ILL_DEBUG
,
4956 _("ISDS response on %s request is missing status\n"),
4957 service_name_locale
);
4961 /* Request processed, but nothing found */
4962 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4963 char *code_locale
= _isds_utf82locale((char*) *code
);
4964 char *status_message_locale
=
4965 _isds_utf82locale((char*) *status_message
);
4966 isds_log(ILF_ISDS
, ILL_DEBUG
,
4967 _("Server refused %s request (code=%s, message=%s)\n"),
4968 service_name_locale
, code_locale
, status_message_locale
);
4969 isds_log_message(context
, status_message_locale
);
4971 free(status_message_locale
);
4977 free(service_name_locale
);
4978 xmlFreeNode(request
);
4984 /* Get data about logged in user and his box.
4985 * @context is session context
4986 * @db_owner_info is reallocated box owner description. It will be freed on
4988 * @return error code from lower layer, context message will be set up
4990 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4991 struct isds_DbOwnerInfo
**db_owner_info
) {
4992 isds_error err
= IE_SUCCESS
;
4994 xmlDocPtr response
= NULL
;
4995 xmlChar
*code
= NULL
, *message
= NULL
;
4996 xmlXPathContextPtr xpath_ctx
= NULL
;
4997 xmlXPathObjectPtr result
= NULL
;
4998 char *string
= NULL
;
5001 if (!context
) return IE_INVALID_CONTEXT
;
5002 zfree(context
->long_message
);
5003 if (!db_owner_info
) return IE_INVAL
;
5004 isds_DbOwnerInfo_free(db_owner_info
);
5007 /* Check if connection is established */
5008 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5011 /* Do request and check for success */
5012 err
= build_send_check_dbdummy_request(context
,
5013 BAD_CAST
"GetOwnerInfoFromLogin",
5014 &response
, NULL
, NULL
, &code
, &message
);
5015 if (err
) goto leave
;
5019 /* Prepare structure */
5020 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5021 if (!*db_owner_info
) {
5025 xpath_ctx
= xmlXPathNewContext(response
);
5030 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5035 /* Set context node */
5036 result
= xmlXPathEvalExpression(BAD_CAST
5037 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5042 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5043 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5047 if (result
->nodesetval
->nodeNr
> 1) {
5048 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5052 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5053 xmlXPathFreeObject(result
); result
= NULL
;
5056 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5061 isds_DbOwnerInfo_free(db_owner_info
);
5065 xmlXPathFreeObject(result
);
5066 xmlXPathFreeContext(xpath_ctx
);
5070 xmlFreeDoc(response
);
5073 isds_log(ILF_ISDS
, ILL_DEBUG
,
5074 _("GetOwnerInfoFromLogin request processed by server "
5075 "successfully.\n"));
5076 #else /* not HAVE_LIBCURL */
5084 /* Get data about logged in user. */
5085 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5086 struct isds_DbUserInfo
**db_user_info
) {
5087 isds_error err
= IE_SUCCESS
;
5089 xmlDocPtr response
= NULL
;
5090 xmlChar
*code
= NULL
, *message
= NULL
;
5091 xmlXPathContextPtr xpath_ctx
= NULL
;
5092 xmlXPathObjectPtr result
= NULL
;
5095 if (!context
) return IE_INVALID_CONTEXT
;
5096 zfree(context
->long_message
);
5097 if (!db_user_info
) return IE_INVAL
;
5098 isds_DbUserInfo_free(db_user_info
);
5101 /* Check if connection is established */
5102 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5105 /* Do request and check for success */
5106 err
= build_send_check_dbdummy_request(context
,
5107 BAD_CAST
"GetUserInfoFromLogin",
5108 &response
, NULL
, NULL
, &code
, &message
);
5109 if (err
) goto leave
;
5113 /* Prepare structure */
5114 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5115 if (!*db_user_info
) {
5119 xpath_ctx
= xmlXPathNewContext(response
);
5124 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5129 /* Set context node */
5130 result
= xmlXPathEvalExpression(BAD_CAST
5131 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5136 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5137 isds_log_message(context
, _("Missing dbUserInfo element"));
5141 if (result
->nodesetval
->nodeNr
> 1) {
5142 isds_log_message(context
, _("Multiple dbUserInfo element"));
5146 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5147 xmlXPathFreeObject(result
); result
= NULL
;
5150 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5154 isds_DbUserInfo_free(db_user_info
);
5157 xmlXPathFreeObject(result
);
5158 xmlXPathFreeContext(xpath_ctx
);
5162 xmlFreeDoc(response
);
5165 isds_log(ILF_ISDS
, ILL_DEBUG
,
5166 _("GetUserInfoFromLogin request processed by server "
5167 "successfully.\n"));
5168 #else /* not HAVE_LIBCURL */
5176 /* Get expiration time of current password
5177 * @context is session context
5178 * @expiration is automatically reallocated time when password expires. If
5179 * password expiration is disabled, NULL will be returned. In case of error
5180 * it will be nulled too. */
5181 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5182 struct timeval
**expiration
) {
5183 isds_error err
= IE_SUCCESS
;
5185 xmlDocPtr response
= NULL
;
5186 xmlChar
*code
= NULL
, *message
= NULL
;
5187 xmlXPathContextPtr xpath_ctx
= NULL
;
5188 xmlXPathObjectPtr result
= NULL
;
5189 char *string
= NULL
;
5192 if (!context
) return IE_INVALID_CONTEXT
;
5193 zfree(context
->long_message
);
5194 if (!expiration
) return IE_INVAL
;
5198 /* Check if connection is established */
5199 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5202 /* Do request and check for success */
5203 err
= build_send_check_dbdummy_request(context
,
5204 BAD_CAST
"GetPasswordInfo",
5205 &response
, NULL
, NULL
, &code
, &message
);
5206 if (err
) goto leave
;
5210 xpath_ctx
= xmlXPathNewContext(response
);
5215 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5220 /* Set context node */
5221 result
= xmlXPathEvalExpression(BAD_CAST
5222 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5227 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5228 isds_log_message(context
,
5229 _("Missing GetPasswordInfoResponse element"));
5233 if (result
->nodesetval
->nodeNr
> 1) {
5234 isds_log_message(context
,
5235 _("Multiple GetPasswordInfoResponse element"));
5239 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5240 xmlXPathFreeObject(result
); result
= NULL
;
5242 /* Extract expiration date */
5243 EXTRACT_STRING("isds:pswExpDate", string
);
5245 /* And convert it if any returned. Otherwise expiration is disabled. */
5246 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5248 char *string_locale
= _isds_utf82locale(string
);
5249 if (err
== IE_DATE
) err
= IE_ISDS
;
5250 isds_printf_message(context
,
5251 _("Could not convert pswExpDate as ISO time: %s"),
5253 free(string_locale
);
5266 xmlXPathFreeObject(result
);
5267 xmlXPathFreeContext(xpath_ctx
);
5271 xmlFreeDoc(response
);
5274 isds_log(ILF_ISDS
, ILL_DEBUG
,
5275 _("GetPasswordInfo request processed by server "
5276 "successfully.\n"));
5277 #else /* not HAVE_LIBCURL */
5286 /* Request delivering new TOTP code from ISDS through side channel before
5287 * changing password.
5288 * @context is session context
5289 * @password is current password.
5290 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5291 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5292 * function for more details.
5293 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5294 * NULL, if you don't care.
5295 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5297 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5298 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5299 isds_error err
= IE_SUCCESS
;
5300 char *saved_url
= NULL
; /* No copy */
5301 #if HAVE_CURL_REAUTHORIZATION_BUG
5302 CURL
*saved_curl
= NULL
; /* No copy */
5304 xmlNsPtr isds_ns
= NULL
;
5305 xmlNodePtr request
= NULL
;
5306 xmlDocPtr response
= NULL
;
5307 xmlChar
*code
= NULL
, *message
= NULL
;
5308 const xmlChar
*codes
[] = {
5313 const char *meanings
[] = {
5314 N_("Unexpected error"),
5315 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5316 N_("One-time code could not been sent. Try later again.")
5318 const isds_otp_resolution resolutions
[] = {
5319 OTP_RESOLUTION_UNKNOWN
,
5320 OTP_RESOLUTION_TO_FAST
,
5321 OTP_RESOLUTION_TOTP_NOT_SENT
5324 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5325 zfree(context
->long_message
);
5326 if (NULL
== password
) {
5327 isds_log_message(context
,
5328 _("Second argument (password) of isds_change_password() "
5333 /* Check if connection is established
5334 * TODO: This check should be done downstairs. */
5335 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5337 if (!context
->otp
) {
5338 isds_log_message(context
, _("This function requires OTP-authenticated "
5340 return IE_INVALID_CONTEXT
;
5343 isds_log_message(context
, _("If one-time password authentication "
5344 "method is in use, requesting new OTP code requires "
5345 "one-time credentials argument either"));
5348 if (otp
->method
!= OTP_TIME
) {
5349 isds_log_message(context
, _("Requesting new time-based OTP code from "
5350 "server requires one-time password authentication "
5354 if (otp
->otp_code
!= NULL
) {
5355 isds_log_message(context
, _("Requesting new time-based OTP code from "
5356 "server requires undefined OTP code member in "
5357 "one-time credentials argument"));
5363 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5365 isds_log_message(context
, _("Could not build SendSMSCode request"));
5368 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5370 isds_log_message(context
, _("Could not create ISDS name space"));
5371 xmlFreeNode(request
);
5374 xmlSetNs(request
, isds_ns
);
5376 /* Change URL temporarily for sending this request only */
5378 char *new_url
= NULL
;
5379 if ((err
= _isds_build_url_from_context(context
,
5380 "%1$.*2$sasws/changePassword", &new_url
))) {
5383 saved_url
= context
->url
;
5384 context
->url
= new_url
;
5387 /* Store credentials for sending this request only */
5388 context
->otp_credentials
= otp
;
5389 _isds_discard_credentials(context
, 0);
5390 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5392 _isds_discard_credentials(context
, 0);
5395 #if HAVE_CURL_REAUTHORIZATION_BUG
5396 saved_curl
= context
->curl
;
5397 context
->curl
= curl_easy_init();
5398 if (NULL
== context
->curl
) {
5402 if (context
->timeout
) {
5403 err
= isds_set_timeout(context
, context
->timeout
);
5404 if (err
) goto leave
;
5408 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5411 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5413 /* Remove temporal credentials */
5414 _isds_discard_credentials(context
, 0);
5415 /* Detach pointer to OTP credentials from context */
5416 context
->otp_credentials
= NULL
;
5417 /* Keep context->otp true to keep signaling this is OTP session */
5419 /* Destroy request */
5420 xmlFreeNode(request
); request
= NULL
;
5423 isds_log(ILF_ISDS
, ILL_DEBUG
,
5424 _("Processing ISDS response on SendSMSCode request failed\n"));
5428 /* Check for response status */
5429 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5430 &code
, &message
, (xmlChar
**)refnumber
);
5432 isds_log(ILF_ISDS
, ILL_DEBUG
,
5433 _("ISDS response on SendSMSCode request is missing "
5438 /* Check for error */
5439 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5440 char *code_locale
= _isds_utf82locale((char*)code
);
5441 char *message_locale
= _isds_utf82locale((char*)message
);
5443 isds_log(ILF_ISDS
, ILL_DEBUG
,
5444 _("Server refused to send new code on SendSMSCode "
5445 "request (code=%s, message=%s)\n"),
5446 code_locale
, message_locale
);
5448 /* Check for known error codes */
5449 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5450 if (!xmlStrcmp(code
, codes
[i
])) break;
5452 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5453 isds_log_message(context
, _(meanings
[i
]));
5454 /* Mimic otp->resolution according to the code, specification does
5455 * prescribe OTP header to be available. */
5456 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5457 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5458 otp
->resolution
= resolutions
[i
];
5460 isds_log_message(context
, message_locale
);
5463 free(message_locale
);
5469 /* Otherwise new code sent successfully */
5470 /* Mimic otp->resolution according to the code, specification does
5471 * prescribe OTP header to be available. */
5472 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5473 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5476 if (NULL
!= saved_url
) {
5477 /* Revert URL to original one */
5478 zfree(context
->url
);
5479 context
->url
= saved_url
;
5481 #if HAVE_CURL_REAUTHORIZATION_BUG
5482 if (NULL
!= saved_curl
) {
5483 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5484 context
->curl
= saved_curl
;
5490 xmlFreeDoc(response
);
5491 xmlFreeNode(request
);
5494 isds_log(ILF_ISDS
, ILL_DEBUG
,
5495 _("New OTP code has been sent successfully on SendSMSCode "
5501 /* Convert response status code to isds_error code and set long message
5502 * @context is context to save long message to
5503 * @map is mapping from codes to errors and messages. Pass NULL for generic
5505 * @code is status code to translate
5506 * @message is non-localized status message to put into long message in case
5507 * of uknown error. It can be NULL if server did not provide any.
5508 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5509 * invalid invocation. */
5510 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5511 const struct code_map_isds_error
*map
,
5512 const xmlChar
*code
, const xmlChar
*message
) {
5514 isds_log_message(context
,
5515 _("NULL status code passed to statuscode2isds_error()"));
5520 /* Check for known error codes */
5521 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5522 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5523 isds_log_message(context
, _(map
->meanings
[i
]));
5524 return map
->errors
[i
];
5530 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5531 char *message_locale
= _isds_utf82locale((char*)message
);
5532 if (NULL
== message_locale
)
5533 isds_log_message(context
, _("ISDS server returned unknown error"));
5535 isds_log_message(context
, message_locale
);
5536 free(message_locale
);
5545 /* Change user password in ISDS.
5546 * User must supply old password, new password will takes effect after some
5547 * time, current session can continue. Password must fulfill some constraints.
5548 * @context is session context
5549 * @old_password is current password.
5550 * @new_password is requested new password
5551 * @otp auxiliary data required if one-time password authentication is in use,
5552 * defines OTP code (if known) and returns fine grade resolution of OTP
5553 * procedure. Pass NULL, if one-time password authentication is not needed.
5554 * Please note the @otp argument must match OTP method used at log-in time. See
5555 * isds_login() function for more details.
5556 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5557 * NULL, if you don't care.
5558 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5559 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5560 * awaiting OTP code that has been delivered by side channel to the user. */
5561 isds_error
isds_change_password(struct isds_ctx
*context
,
5562 const char *old_password
, const char *new_password
,
5563 struct isds_otp
*otp
, char **refnumber
) {
5564 isds_error err
= IE_SUCCESS
;
5566 char *saved_url
= NULL
; /* No copy */
5567 #if HAVE_CURL_REAUTHORIZATION_BUG
5568 CURL
*saved_curl
= NULL
; /* No copy */
5570 xmlNsPtr isds_ns
= NULL
;
5571 xmlNodePtr request
= NULL
, node
;
5572 xmlDocPtr response
= NULL
;
5573 xmlChar
*code
= NULL
, *message
= NULL
;
5574 const xmlChar
*codes
[] = {
5587 const char *meanings
[] = {
5588 N_("Password length must be between 8 and 32 characters"),
5589 N_("Password cannot be reused"), /* Server does not distinguish 1067
5590 and 1091 on ChangePasswordOTP */
5591 N_("Password contains forbidden character"),
5592 N_("Password must contain at least one upper-case letter, "
5593 "one lower-case, and one digit"),
5594 N_("Password cannot contain sequence of three identical characters"),
5595 N_("Password cannot contain user identifier"),
5596 N_("Password is too simmple"),
5597 N_("Old password is not valid"),
5598 N_("Password cannot be reused"),
5599 N_("Unexpected error"),
5600 N_("LDAP update error")
5604 if (!context
) return IE_INVALID_CONTEXT
;
5605 zfree(context
->long_message
);
5606 if (NULL
!= refnumber
)
5608 if (NULL
== old_password
) {
5609 isds_log_message(context
,
5610 _("Second argument (old password) of isds_change_password() "
5614 if (NULL
== otp
&& NULL
== new_password
) {
5615 isds_log_message(context
,
5616 _("Third argument (new password) of isds_change_password() "
5622 /* Check if connection is established
5623 * TODO: This check should be done downstairs. */
5624 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5626 if (context
->otp
&& NULL
== otp
) {
5627 isds_log_message(context
, _("If one-time password authentication "
5628 "method is in use, changing password requires one-time "
5629 "credentials either"));
5633 /* Build ChangeISDSPassword request */
5634 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5635 BAD_CAST
"ChangePasswordOTP");
5637 isds_log_message(context
, (NULL
== otp
) ?
5638 _("Could not build ChangeISDSPassword request") :
5639 _("Could not build ChangePasswordOTP request"));
5642 isds_ns
= xmlNewNs(request
,
5643 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5646 isds_log_message(context
, _("Could not create ISDS name space"));
5647 xmlFreeNode(request
);
5650 xmlSetNs(request
, isds_ns
);
5652 INSERT_STRING(request
, "dbOldPassword", old_password
);
5653 INSERT_STRING(request
, "dbNewPassword", new_password
);
5656 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5657 switch (otp
->method
) {
5659 isds_log(ILF_SEC
, ILL_INFO
,
5660 _("Selected authentication method: "
5661 "HMAC-based one-time password\n"));
5662 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5665 isds_log(ILF_SEC
, ILL_INFO
,
5666 _("Selected authentication method: "
5667 "Time-based one-time password\n"));
5668 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5669 if (otp
->otp_code
== NULL
) {
5670 isds_log(ILF_SEC
, ILL_INFO
,
5671 _("OTP code has not been provided by "
5672 "application, requesting server for "
5674 err
= _isds_request_totp_code(context
, old_password
, otp
,
5676 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5680 isds_log(ILF_SEC
, ILL_INFO
,
5681 _("OTP code has been provided by "
5682 "application, not requesting server "
5687 isds_log_message(context
,
5688 _("Unknown one-time password authentication "
5689 "method requested by application"));
5694 /* Change URL temporarily for sending this request only */
5696 char *new_url
= NULL
;
5697 if ((err
= _isds_build_url_from_context(context
,
5698 "%1$.*2$sasws/changePassword", &new_url
))) {
5701 saved_url
= context
->url
;
5702 context
->url
= new_url
;
5705 /* Store credentials for sending this request only */
5706 context
->otp_credentials
= otp
;
5707 _isds_discard_credentials(context
, 0);
5708 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5709 old_password
, NULL
))) {
5710 _isds_discard_credentials(context
, 0);
5713 #if HAVE_CURL_REAUTHORIZATION_BUG
5714 saved_curl
= context
->curl
;
5715 context
->curl
= curl_easy_init();
5716 if (NULL
== context
->curl
) {
5720 if (context
->timeout
) {
5721 err
= isds_set_timeout(context
, context
->timeout
);
5722 if (err
) goto leave
;
5727 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5728 _("Sending ChangeISDSPassword request to ISDS\n") :
5729 _("Sending ChangePasswordOTP request to ISDS\n"));
5732 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5733 request
, &response
, NULL
, NULL
);
5736 /* Remove temporal credentials */
5737 _isds_discard_credentials(context
, 0);
5738 /* Detach pointer to OTP credentials from context */
5739 context
->otp_credentials
= NULL
;
5740 /* Keep context->otp true to keep signaling this is OTP session */
5743 /* Destroy request */
5744 xmlFreeNode(request
); request
= NULL
;
5747 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5748 _("Processing ISDS response on ChangeISDSPassword "
5749 "request failed\n") :
5750 _("Processing ISDS response on ChangePasswordOTP "
5751 "request failed\n"));
5755 /* Check for response status */
5756 err
= isds_response_status(context
,
5757 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5758 &code
, &message
, (xmlChar
**)refnumber
);
5760 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5761 _("ISDS response on ChangeISDSPassword request is missing "
5763 _("ISDS response on ChangePasswordOTP request is missing "
5768 /* Check for known error codes */
5769 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5770 if (!xmlStrcmp(code
, codes
[i
])) {
5771 char *code_locale
= _isds_utf82locale((char*)code
);
5772 char *message_locale
= _isds_utf82locale((char*)message
);
5773 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5774 _("Server refused to change password on ChangeISDSPassword "
5775 "request (code=%s, message=%s)\n") :
5776 _("Server refused to change password on ChangePasswordOTP "
5777 "request (code=%s, message=%s)\n"),
5778 code_locale
, message_locale
);
5780 free(message_locale
);
5781 isds_log_message(context
, _(meanings
[i
]));
5788 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5789 char *code_locale
= _isds_utf82locale((char*)code
);
5790 char *message_locale
= _isds_utf82locale((char*)message
);
5791 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5792 _("Server refused to change password on ChangeISDSPassword "
5793 "request (code=%s, message=%s)\n") :
5794 _("Server refused to change password on ChangePasswordOTP "
5795 "request (code=%s, message=%s)\n"),
5796 code_locale
, message_locale
);
5797 isds_log_message(context
, message_locale
);
5799 free(message_locale
);
5804 /* Otherwise password changed successfully */
5807 if (NULL
!= saved_url
) {
5808 /* Revert URL to original one */
5809 zfree(context
->url
);
5810 context
->url
= saved_url
;
5812 #if HAVE_CURL_REAUTHORIZATION_BUG
5813 if (NULL
!= saved_curl
) {
5814 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5815 context
->curl
= saved_curl
;
5821 xmlFreeDoc(response
);
5822 xmlFreeNode(request
);
5825 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5826 _("Password changed successfully on ChangeISDSPassword "
5828 _("Password changed successfully on ChangePasswordOTP "
5830 #else /* not HAVE_LIBCURL */
5839 /* Generic middle part with request sending and response check.
5840 * It sends prepared request and checks for error code.
5841 * @context is ISDS session context.
5842 * @service is ISDS service handler
5843 * @service_name is name in scope of given @service
5844 * @request is XML tree with request. Will be freed to save memory.
5845 * @response is XML document outputting ISDS response.
5846 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5847 * @map is mapping from status code to library error. Pass NULL if no special
5848 * handling is requested.
5849 * NULL, if you don't care. */
5850 static isds_error
send_destroy_request_check_response(
5851 struct isds_ctx
*context
,
5852 const isds_service service
, const xmlChar
*service_name
,
5853 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5854 const struct code_map_isds_error
*map
) {
5855 isds_error err
= IE_SUCCESS
;
5856 char *service_name_locale
= NULL
;
5857 xmlChar
*code
= NULL
, *message
= NULL
;
5860 if (!context
) return IE_INVALID_CONTEXT
;
5861 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5865 /* Check if connection is established
5866 * TODO: This check should be done downstairs. */
5867 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5869 service_name_locale
= _isds_utf82locale((char*) service_name
);
5870 if (!service_name_locale
) {
5875 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5876 service_name_locale
);
5879 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5880 xmlFreeNode(*request
); *request
= NULL
;
5883 isds_log(ILF_ISDS
, ILL_DEBUG
,
5884 _("Processing ISDS response on %s request failed\n"),
5885 service_name_locale
);
5889 /* Check for response status */
5890 err
= isds_response_status(context
, service
, *response
,
5891 &code
, &message
, refnumber
);
5893 isds_log(ILF_ISDS
, ILL_DEBUG
,
5894 _("ISDS response on %s request is missing status\n"),
5895 service_name_locale
);
5899 err
= statuscode2isds_error(context
, map
, code
, message
);
5901 /* Request processed, but server failed */
5902 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5903 char *code_locale
= _isds_utf82locale((char*) code
);
5904 char *message_locale
= _isds_utf82locale((char*) message
);
5905 isds_log(ILF_ISDS
, ILL_DEBUG
,
5906 _("Server refused %s request (code=%s, message=%s)\n"),
5907 service_name_locale
, code_locale
, message_locale
);
5909 free(message_locale
);
5917 if (err
&& *response
) {
5918 xmlFreeDoc(*response
);
5922 xmlFreeNode(*request
);
5925 free(service_name_locale
);
5931 /* Generic bottom half with request sending.
5932 * It sends prepared request, checks for error code, destroys response and
5933 * request and log success or failure.
5934 * @context is ISDS session context.
5935 * @service is ISDS service handler
5936 * @service_name is name in scope of given @service
5937 * @request is XML tree with request. Will be freed to save memory.
5938 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5939 * NULL, if you don't care. */
5940 static isds_error
send_request_check_drop_response(
5941 struct isds_ctx
*context
,
5942 const isds_service service
, const xmlChar
*service_name
,
5943 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5944 isds_error err
= IE_SUCCESS
;
5945 xmlDocPtr response
= NULL
;
5948 if (!context
) return IE_INVALID_CONTEXT
;
5949 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5952 /* Send request and check response*/
5953 err
= send_destroy_request_check_response(context
,
5954 service
, service_name
, request
, &response
, refnumber
, NULL
);
5956 xmlFreeDoc(response
);
5959 xmlFreeNode(*request
);
5964 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5965 isds_log(ILF_ISDS
, ILL_DEBUG
,
5966 _("%s request processed by server successfully.\n"),
5967 service_name_locale
);
5968 free(service_name_locale
);
5975 /* Insert isds_credentials_delivery structure into XML request if not NULL
5976 * @context is session context
5977 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5978 * credentials delivery. The email field is passed.
5979 * @parent is XML element where to insert */
5980 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5981 const struct isds_credentials_delivery
*credentials_delivery
,
5982 xmlNodePtr parent
) {
5983 isds_error err
= IE_SUCCESS
;
5986 if (!context
) return IE_INVALID_CONTEXT
;
5987 if (!parent
) return IE_INVAL
;
5989 if (credentials_delivery
) {
5990 /* Following elements are valid only for services:
5991 * NewAccessData, AddDataBoxUser, CreateDataBox */
5992 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5993 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
6001 /* Extract credentials delivery from ISDS response.
6002 * @context is session context
6003 * @credentials_delivery is pointer to valid structure to fill in returned
6004 * user's password (and new log-in name). If NULL, do not extract the data.
6005 * @response is pointer to XML document with ISDS response
6006 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6007 * @return IE_SUCCESS even if new user name has not been found because it's not
6008 * clear whether it's returned always. */
6009 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
6010 struct isds_credentials_delivery
*credentials_delivery
,
6011 xmlDocPtr response
, const char *request_name
) {
6012 isds_error err
= IE_SUCCESS
;
6013 xmlXPathContextPtr xpath_ctx
= NULL
;
6014 xmlXPathObjectPtr result
= NULL
;
6015 char *xpath_query
= NULL
;
6017 if (!context
) return IE_INVALID_CONTEXT
;
6018 if (credentials_delivery
) {
6019 zfree(credentials_delivery
->token
);
6020 zfree(credentials_delivery
->new_user_name
);
6022 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6025 /* Extract optional token */
6026 if (credentials_delivery
) {
6027 xpath_ctx
= xmlXPathNewContext(response
);
6032 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6037 /* Verify root element */
6038 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6043 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6048 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6049 char *request_name_locale
= _isds_utf82locale(request_name
);
6050 isds_log(ILF_ISDS
, ILL_WARNING
,
6051 _("Wrong element in ISDS response for %s request "
6052 "while extracting credentials delivery details\n"),
6053 request_name_locale
);
6054 free(request_name_locale
);
6058 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6061 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6063 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6065 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6066 if (!credentials_delivery
->token
) {
6067 char *request_name_locale
= _isds_utf82locale(request_name
);
6068 isds_log(ILF_ISDS
, ILL_ERR
,
6069 _("ISDS did not return token on %s request "
6070 "even if requested\n"), request_name_locale
);
6071 free(request_name_locale
);
6078 xmlXPathFreeObject(result
);
6079 xmlXPathFreeContext(xpath_ctx
);
6085 /* Build XSD:tCreateDBInput request type for box creating.
6086 * @context is session context
6087 * @request outputs built XML tree
6088 * @service_name is request name of SERVICE_DB_MANIPULATION service
6089 * @box is box description to create including single primary user (in case of
6090 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6092 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6093 * box, or contact address of PFO box owner)
6094 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6095 * @upper_box_id is optional ID of supper box if currently created box is
6097 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6099 * @credentials_delivery is valid pointer if ISDS should return token that box
6100 * owner can use to obtain his new credentials in on-line way. Then valid email
6101 * member value should be supplied.
6102 * @approval is optional external approval of box manipulation */
6103 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6104 xmlNodePtr
*request
, const xmlChar
*service_name
,
6105 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6106 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6107 const xmlChar
*ceo_label
,
6108 const struct isds_credentials_delivery
*credentials_delivery
,
6109 const struct isds_approval
*approval
) {
6110 isds_error err
= IE_SUCCESS
;
6111 xmlNsPtr isds_ns
= NULL
;
6112 xmlNodePtr node
, dbPrimaryUsers
;
6113 xmlChar
*string
= NULL
;
6114 const struct isds_list
*item
;
6117 if (!context
) return IE_INVALID_CONTEXT
;
6118 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6122 /* Build CreateDataBox-similar request */
6123 *request
= xmlNewNode(NULL
, service_name
);
6125 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6126 isds_printf_message(context
, _("Could build %s request"),
6127 service_name_locale
);
6128 free(service_name_locale
);
6131 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6132 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6134 isds_log_message(context
, _("Could not create ISDS1 name space"));
6135 xmlFreeNode(*request
);
6139 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6141 isds_log_message(context
, _("Could not create ISDS name space"));
6142 xmlFreeNode(*request
);
6146 xmlSetNs(*request
, isds_ns
);
6148 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6149 err
= insert_DbOwnerInfo(context
, box
, node
);
6150 if (err
) goto leave
;
6153 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6154 * verbose documentation allows none dbUserInfo */
6155 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6156 for (item
= users
; item
; item
= item
->next
) {
6158 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6159 err
= insert_DbUserInfo(context
,
6160 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6161 if (err
) goto leave
;
6165 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6166 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6167 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6169 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6170 if (err
) goto leave
;
6172 err
= insert_GExtApproval(context
, approval
, *request
);
6173 if (err
) goto leave
;
6177 xmlFreeNode(*request
);
6183 #endif /* HAVE_LIBCURL */
6187 * @context is session context
6188 * @box is box description to create including single primary user (in case of
6189 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6190 * ignored. It outputs box ID assigned by ISDS in dbID element.
6191 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6192 * box, or contact address of PFO box owner)
6193 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6194 * @upper_box_id is optional ID of supper box if currently created box is
6196 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6197 * @credentials_delivery is NULL if new password should be delivered off-line
6198 * to box owner. It is valid pointer if owner should obtain new password on-line
6199 * on dedicated web server. Then input @credentials_delivery.email value is
6200 * his e-mail address he must provide to dedicated web server together
6201 * with output reallocated @credentials_delivery.token member. Output
6202 * member @credentials_delivery.new_user_name is unused up on this call.
6203 * @approval is optional external approval of box manipulation
6204 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6205 * NULL, if you don't care.*/
6206 isds_error
isds_add_box(struct isds_ctx
*context
,
6207 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6208 const char *former_names
, const char *upper_box_id
,
6209 const char *ceo_label
,
6210 struct isds_credentials_delivery
*credentials_delivery
,
6211 const struct isds_approval
*approval
, char **refnumber
) {
6212 isds_error err
= IE_SUCCESS
;
6214 xmlNodePtr request
= NULL
;
6215 xmlDocPtr response
= NULL
;
6216 xmlXPathContextPtr xpath_ctx
= NULL
;
6217 xmlXPathObjectPtr result
= NULL
;
6221 if (!context
) return IE_INVALID_CONTEXT
;
6222 zfree(context
->long_message
);
6223 if (credentials_delivery
) {
6224 zfree(credentials_delivery
->token
);
6225 zfree(credentials_delivery
->new_user_name
);
6227 if (!box
) return IE_INVAL
;
6230 /* Scratch box ID */
6233 /* Build CreateDataBox request */
6234 err
= build_CreateDBInput_request(context
,
6235 &request
, BAD_CAST
"CreateDataBox",
6236 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6237 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6238 if (err
) goto leave
;
6240 /* Send it to server and process response */
6241 err
= send_destroy_request_check_response(context
,
6242 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6243 &response
, (xmlChar
**) refnumber
, NULL
);
6245 /* Extract box ID */
6246 xpath_ctx
= xmlXPathNewContext(response
);
6251 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6255 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6257 /* Extract optional token */
6258 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6262 xmlXPathFreeObject(result
);
6263 xmlXPathFreeContext(xpath_ctx
);
6264 xmlFreeDoc(response
);
6265 xmlFreeNode(request
);
6268 isds_log(ILF_ISDS
, ILL_DEBUG
,
6269 _("CreateDataBox request processed by server successfully.\n"));
6271 #else /* not HAVE_LIBCURL */
6279 /* Notify ISDS about new PFO entity.
6280 * This function has no real effect.
6281 * @context is session context
6282 * @box is PFO description including single primary user. aifoIsds,
6283 * address->adCode, address->adDistrict members are ignored.
6284 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6285 * @former_names is optional undocumented string. Pass NULL if you don't care.
6286 * @upper_box_id is optional ID of supper box if currently created box is
6288 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6289 * @approval is optional external approval of box manipulation
6290 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6291 * NULL, if you don't care.*/
6292 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6293 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6294 const char *former_names
, const char *upper_box_id
,
6295 const char *ceo_label
, const struct isds_approval
*approval
,
6297 isds_error err
= IE_SUCCESS
;
6299 xmlNodePtr request
= NULL
;
6302 if (!context
) return IE_INVALID_CONTEXT
;
6303 zfree(context
->long_message
);
6304 if (!box
) return IE_INVAL
;
6307 /* Build CreateDataBoxPFOInfo request */
6308 err
= build_CreateDBInput_request(context
,
6309 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6310 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6311 (xmlChar
*) ceo_label
, NULL
, approval
);
6312 if (err
) goto leave
;
6314 /* Send it to server and process response */
6315 err
= send_request_check_drop_response(context
,
6316 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6317 (xmlChar
**) refnumber
);
6318 /* XXX: XML Schema names output dbID element but textual documentation
6319 * states no box identifier is returned. */
6321 xmlFreeNode(request
);
6322 #else /* not HAVE_LIBCURL */
6329 /* Common implementation for removing given box.
6330 * @context is session context
6331 * @service_name is UTF-8 encoded name fo ISDS service
6332 * @box is box description to delete. aifoIsds, address->adCode,
6333 * address->adDistrict members are ignored.
6334 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6335 * carry sane value. If NULL, do not inject this information into request.
6336 * @approval is optional external approval of box manipulation
6337 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6338 * NULL, if you don't care.*/
6339 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6340 const xmlChar
*service_name
,
6341 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6342 const struct isds_approval
*approval
, char **refnumber
) {
6343 isds_error err
= IE_SUCCESS
;
6345 xmlNsPtr isds_ns
= NULL
;
6346 xmlNodePtr request
= NULL
;
6348 xmlChar
*string
= NULL
;
6352 if (!context
) return IE_INVALID_CONTEXT
;
6353 zfree(context
->long_message
);
6354 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6358 /* Build DeleteDataBox(Promptly) request */
6359 request
= xmlNewNode(NULL
, service_name
);
6361 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6362 isds_printf_message(context
,
6363 _("Could build %s request"), service_name_locale
);
6364 free(service_name_locale
);
6367 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6369 isds_log_message(context
, _("Could not create ISDS name space"));
6370 xmlFreeNode(request
);
6373 xmlSetNs(request
, isds_ns
);
6375 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6376 err
= insert_DbOwnerInfo(context
, box
, node
);
6377 if (err
) goto leave
;
6380 err
= tm2datestring(since
, &string
);
6382 isds_log_message(context
,
6383 _("Could not convert `since' argument to ISO date string"));
6386 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6390 err
= insert_GExtApproval(context
, approval
, request
);
6391 if (err
) goto leave
;
6394 /* Send it to server and process response */
6395 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6396 service_name
, &request
, (xmlChar
**) refnumber
);
6399 xmlFreeNode(request
);
6401 #else /* not HAVE_LIBCURL */
6408 /* Remove given box permanently.
6409 * @context is session context
6410 * @box is box description to delete. aifoIsds, address->adCode,
6411 * address->adDistrict members are ignored.
6412 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6414 * @approval is optional external approval of box manipulation
6415 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6416 * NULL, if you don't care.*/
6417 isds_error
isds_delete_box(struct isds_ctx
*context
,
6418 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6419 const struct isds_approval
*approval
, char **refnumber
) {
6420 if (!context
) return IE_INVALID_CONTEXT
;
6421 zfree(context
->long_message
);
6422 if (!box
|| !since
) return IE_INVAL
;
6424 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6425 box
, since
, approval
, refnumber
);
6429 /* Undocumented function.
6430 * @context is session context
6431 * @box is box description to delete. aifoIsds, address->adCode,
6432 * address->adDistrict members are ignored.
6433 * @approval is optional external approval of box manipulation
6434 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6435 * NULL, if you don't care.*/
6436 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6437 const struct isds_DbOwnerInfo
*box
,
6438 const struct isds_approval
*approval
, char **refnumber
) {
6439 if (!context
) return IE_INVALID_CONTEXT
;
6440 zfree(context
->long_message
);
6441 if (!box
) return IE_INVAL
;
6443 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6444 box
, NULL
, approval
, refnumber
);
6448 /* Update data about given box.
6449 * @context is session context
6450 * @old_box current box description. aifoIsds, address->adCode,
6451 * address->adDistrict members are ignored.
6452 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6453 * address->adDistrict members are ignored.
6454 * @approval is optional external approval of box manipulation
6455 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6456 * NULL, if you don't care.*/
6457 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6458 const struct isds_DbOwnerInfo
*old_box
,
6459 const struct isds_DbOwnerInfo
*new_box
,
6460 const struct isds_approval
*approval
, char **refnumber
) {
6461 isds_error err
= IE_SUCCESS
;
6463 xmlNsPtr isds_ns
= NULL
;
6464 xmlNodePtr request
= NULL
;
6469 if (!context
) return IE_INVALID_CONTEXT
;
6470 zfree(context
->long_message
);
6471 if (!old_box
|| !new_box
) return IE_INVAL
;
6475 /* Build UpdateDataBoxDescr request */
6476 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6478 isds_log_message(context
,
6479 _("Could build UpdateDataBoxDescr request"));
6482 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6484 isds_log_message(context
, _("Could not create ISDS name space"));
6485 xmlFreeNode(request
);
6488 xmlSetNs(request
, isds_ns
);
6490 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6491 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6492 if (err
) goto leave
;
6494 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6495 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6496 if (err
) goto leave
;
6498 err
= insert_GExtApproval(context
, approval
, request
);
6499 if (err
) goto leave
;
6502 /* Send it to server and process response */
6503 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6504 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6507 xmlFreeNode(request
);
6508 #else /* not HAVE_LIBCURL */
6517 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6519 * @context is session context
6520 * @service is SOAP service
6521 * @service_name is name of request in @service
6522 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6523 * @box_id is box ID of interest
6524 * @approval is optional external approval of box manipulation
6525 * @response is server SOAP body response as XML document
6526 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6527 * NULL, if you don't care.
6528 * @return error coded from lower layer, context message will be set up
6530 static isds_error
build_send_dbid_request_check_response(
6531 struct isds_ctx
*context
, const isds_service service
,
6532 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6533 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6534 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6536 isds_error err
= IE_SUCCESS
;
6537 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6538 xmlNodePtr request
= NULL
, node
;
6539 xmlNsPtr isds_ns
= NULL
;
6541 if (!context
) return IE_INVALID_CONTEXT
;
6542 if (!service_name
|| !box_id
) return IE_INVAL
;
6543 if (!response
) return IE_INVAL
;
6545 /* Free output argument */
6546 xmlFreeDoc(*response
); *response
= NULL
;
6548 /* Prepare strings */
6549 service_name_locale
= _isds_utf82locale((char*)service_name
);
6550 if (!service_name_locale
) {
6554 box_id_locale
= _isds_utf82locale((char*)box_id
);
6555 if (!box_id_locale
) {
6561 request
= xmlNewNode(NULL
, service_name
);
6563 isds_printf_message(context
,
6564 _("Could not build %s request for %s box"), service_name_locale
,
6569 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6571 isds_log_message(context
, _("Could not create ISDS name space"));
6575 xmlSetNs(request
, isds_ns
);
6577 /* Add XSD:tIdDbInput children */
6578 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6579 INSERT_STRING(request
, box_id_element
, box_id
);
6580 err
= insert_GExtApproval(context
, approval
, request
);
6581 if (err
) goto leave
;
6583 /* Send request and check response*/
6584 err
= send_destroy_request_check_response(context
,
6585 service
, service_name
, &request
, response
, refnumber
, NULL
);
6588 free(service_name_locale
);
6589 free(box_id_locale
);
6590 xmlFreeNode(request
);
6593 #endif /* HAVE_LIBCURL */
6596 /* Get data about all users assigned to given box.
6597 * @context is session context
6599 * @users is automatically reallocated list of struct isds_DbUserInfo */
6600 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6601 struct isds_list
**users
) {
6602 isds_error err
= IE_SUCCESS
;
6604 xmlDocPtr response
= NULL
;
6605 xmlXPathContextPtr xpath_ctx
= NULL
;
6606 xmlXPathObjectPtr result
= NULL
;
6608 struct isds_list
*item
, *prev_item
= NULL
;
6611 if (!context
) return IE_INVALID_CONTEXT
;
6612 zfree(context
->long_message
);
6613 if (!users
|| !box_id
) return IE_INVAL
;
6614 isds_list_free(users
);
6618 /* Do request and check for success */
6619 err
= build_send_dbid_request_check_response(context
,
6620 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6621 BAD_CAST box_id
, NULL
, &response
, NULL
);
6622 if (err
) goto leave
;
6626 /* Prepare structure */
6627 xpath_ctx
= xmlXPathNewContext(response
);
6632 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6637 /* Set context node */
6638 result
= xmlXPathEvalExpression(BAD_CAST
6639 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6645 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6646 /* Iterate over all users */
6647 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6649 /* Prepare structure */
6650 item
= calloc(1, sizeof(*item
));
6655 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6656 if (i
== 0) *users
= item
;
6657 else prev_item
->next
= item
;
6661 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6662 err
= extract_DbUserInfo(context
,
6663 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6664 if (err
) goto leave
;
6670 isds_list_free(users
);
6673 xmlXPathFreeObject(result
);
6674 xmlXPathFreeContext(xpath_ctx
);
6675 xmlFreeDoc(response
);
6678 isds_log(ILF_ISDS
, ILL_DEBUG
,
6679 _("GetDataBoxUsers request processed by server "
6680 "successfully.\n"));
6681 #else /* not HAVE_LIBCURL */
6689 /* Update data about user assigned to given box.
6690 * @context is session context
6691 * @box is box identification. aifoIsds, address->adCode,
6692 * address->adDistrict members are ignored.
6693 * @old_user identifies user to update, aifo_ticket member is ignored
6694 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6695 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6696 * NULL, if you don't care.*/
6697 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6698 const struct isds_DbOwnerInfo
*box
,
6699 const struct isds_DbUserInfo
*old_user
,
6700 const struct isds_DbUserInfo
*new_user
,
6702 isds_error err
= IE_SUCCESS
;
6704 xmlNsPtr isds_ns
= NULL
;
6705 xmlNodePtr request
= NULL
;
6710 if (!context
) return IE_INVALID_CONTEXT
;
6711 zfree(context
->long_message
);
6712 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6716 /* Build UpdateDataBoxUser request */
6717 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6719 isds_log_message(context
,
6720 _("Could build UpdateDataBoxUser request"));
6723 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6725 isds_log_message(context
, _("Could not create ISDS name space"));
6726 xmlFreeNode(request
);
6729 xmlSetNs(request
, isds_ns
);
6731 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6732 err
= insert_DbOwnerInfo(context
, box
, node
);
6733 if (err
) goto leave
;
6735 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6736 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
6737 if (err
) goto leave
;
6739 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6740 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
6741 if (err
) goto leave
;
6743 /* Send it to server and process response */
6744 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6745 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6748 xmlFreeNode(request
);
6749 #else /* not HAVE_LIBCURL */
6757 /* Undocumented function.
6758 * @context is session context
6759 * @box_id is UTF-8 encoded box identifier
6760 * @token is UTF-8 encoded temporary password
6761 * @user_id outputs UTF-8 encoded reallocated user identifier
6762 * @password outpus UTF-8 encoded reallocated user password
6763 * Output arguments will be nulled in case of error */
6764 isds_error
isds_activate(struct isds_ctx
*context
,
6765 const char *box_id
, const char *token
,
6766 char **user_id
, char **password
) {
6767 isds_error err
= IE_SUCCESS
;
6769 xmlNsPtr isds_ns
= NULL
;
6770 xmlNodePtr request
= NULL
, node
;
6771 xmlDocPtr response
= NULL
;
6772 xmlXPathContextPtr xpath_ctx
= NULL
;
6773 xmlXPathObjectPtr result
= NULL
;
6777 if (!context
) return IE_INVALID_CONTEXT
;
6778 zfree(context
->long_message
);
6780 if (user_id
) zfree(*user_id
);
6781 if (password
) zfree(*password
);
6783 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6787 /* Build Activate request */
6788 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6790 isds_log_message(context
, _("Could build Activate request"));
6793 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6795 isds_log_message(context
, _("Could not create ISDS name space"));
6796 xmlFreeNode(request
);
6799 xmlSetNs(request
, isds_ns
);
6801 INSERT_STRING(request
, "dbAccessDataId", token
);
6802 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6803 INSERT_STRING(request
, "dbID", box_id
);
6806 /* Send request and check response*/
6807 err
= send_destroy_request_check_response(context
,
6808 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6809 &response
, NULL
, NULL
);
6810 if (err
) goto leave
;
6814 xpath_ctx
= xmlXPathNewContext(response
);
6819 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6823 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6829 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6830 isds_log_message(context
, _("Missing ActivateResponse element"));
6834 if (result
->nodesetval
->nodeNr
> 1) {
6835 isds_log_message(context
, _("Multiple ActivateResponse element"));
6839 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6840 xmlXPathFreeObject(result
); result
= NULL
;
6842 EXTRACT_STRING("isds:userId", *user_id
);
6844 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6845 "but did not return `userId' element.\n"));
6847 EXTRACT_STRING("isds:password", *password
);
6849 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6850 "but did not return `password' element.\n"));
6853 xmlXPathFreeObject(result
);
6854 xmlXPathFreeContext(xpath_ctx
);
6855 xmlFreeDoc(response
);
6856 xmlFreeNode(request
);
6859 isds_log(ILF_ISDS
, ILL_DEBUG
,
6860 _("Activate request processed by server successfully.\n"));
6861 #else /* not HAVE_LIBCURL */
6869 /* Reset credentials of user assigned to given box.
6870 * @context is session context
6871 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6872 * members are ignored.
6873 * @user identifies user to reset password, aifo_ticket member is ignored
6874 * @fee_paid is true if fee has been paid, false otherwise
6875 * @approval is optional external approval of box manipulation
6876 * @credentials_delivery is NULL if new password should be delivered off-line
6877 * to the user. It is valid pointer if user should obtain new password on-line
6878 * on dedicated web server. Then input @credentials_delivery.email value is
6879 * user's e-mail address user must provide to dedicated web server together
6880 * with @credentials_delivery.token. The output reallocated token user needs
6881 * to use to authorize on the web server to view his new password. Output
6882 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6883 * ISDS changed up on this call. (No reason why server could change the name
6885 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6886 * NULL, if you don't care.*/
6887 isds_error
isds_reset_password(struct isds_ctx
*context
,
6888 const struct isds_DbOwnerInfo
*box
,
6889 const struct isds_DbUserInfo
*user
,
6890 const _Bool fee_paid
, const struct isds_approval
*approval
,
6891 struct isds_credentials_delivery
*credentials_delivery
,
6893 isds_error err
= IE_SUCCESS
;
6895 xmlNsPtr isds_ns
= NULL
;
6896 xmlNodePtr request
= NULL
, node
;
6897 xmlDocPtr response
= NULL
;
6901 if (!context
) return IE_INVALID_CONTEXT
;
6902 zfree(context
->long_message
);
6904 if (credentials_delivery
) {
6905 zfree(credentials_delivery
->token
);
6906 zfree(credentials_delivery
->new_user_name
);
6908 if (!box
|| !user
) return IE_INVAL
;
6912 /* Build NewAccessData request */
6913 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6915 isds_log_message(context
,
6916 _("Could build NewAccessData request"));
6919 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6921 isds_log_message(context
, _("Could not create ISDS name space"));
6922 xmlFreeNode(request
);
6925 xmlSetNs(request
, isds_ns
);
6927 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6928 err
= insert_DbOwnerInfo(context
, box
, node
);
6929 if (err
) goto leave
;
6931 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6932 err
= insert_DbUserInfo(context
, user
, 0, node
);
6933 if (err
) goto leave
;
6935 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6937 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6938 if (err
) goto leave
;
6940 err
= insert_GExtApproval(context
, approval
, request
);
6941 if (err
) goto leave
;
6943 /* Send request and check response*/
6944 err
= send_destroy_request_check_response(context
,
6945 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6946 &response
, (xmlChar
**) refnumber
, NULL
);
6947 if (err
) goto leave
;
6950 /* Extract optional token */
6951 err
= extract_credentials_delivery(context
, credentials_delivery
,
6952 response
, "NewAccessData");
6955 xmlFreeDoc(response
);
6956 xmlFreeNode(request
);
6959 isds_log(ILF_ISDS
, ILL_DEBUG
,
6960 _("NewAccessData request processed by server "
6961 "successfully.\n"));
6962 #else /* not HAVE_LIBCURL */
6970 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6971 * code, destroy response and log success.
6972 * @context is ISDS session context.
6973 * @service_name is name of SERVICE_DB_MANIPULATION service
6974 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6975 * members are ignored.
6976 * @user identifies user to remove
6977 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
6978 * @credentials_delivery is NULL if new user's password should be delivered
6979 * off-line to the user. It is valid pointer if user should obtain new
6980 * password on-line on dedicated web server. Then input
6981 * @credentials_delivery.email value is user's e-mail address user must
6982 * provide to dedicated web server together with @credentials_delivery.token.
6983 * The output reallocated token user needs to use to authorize on the web
6984 * server to view his new password. Output reallocated
6985 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6986 * assingned or changed up on this call.
6987 * @approval is optional external approval of box manipulation
6988 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6989 * NULL, if you don't care. */
6990 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6991 struct isds_ctx
*context
, const xmlChar
*service_name
,
6992 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6993 _Bool honor_aifo_ticket
,
6994 struct isds_credentials_delivery
*credentials_delivery
,
6995 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6996 isds_error err
= IE_SUCCESS
;
6998 xmlNsPtr isds_ns
= NULL
;
6999 xmlNodePtr request
= NULL
, node
;
7000 xmlDocPtr response
= NULL
;
7004 if (!context
) return IE_INVALID_CONTEXT
;
7005 zfree(context
->long_message
);
7006 if (credentials_delivery
) {
7007 zfree(credentials_delivery
->token
);
7008 zfree(credentials_delivery
->new_user_name
);
7010 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7015 /* Build NewAccessData or similar request */
7016 request
= xmlNewNode(NULL
, service_name
);
7018 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7019 isds_printf_message(context
, _("Could not build %s request"),
7020 service_name_locale
);
7021 free(service_name_locale
);
7024 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7026 isds_log_message(context
, _("Could not create ISDS name space"));
7027 xmlFreeNode(request
);
7030 xmlSetNs(request
, isds_ns
);
7032 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7033 err
= insert_DbOwnerInfo(context
, box
, node
);
7034 if (err
) goto leave
;
7036 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7037 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7038 if (err
) goto leave
;
7040 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7041 if (err
) goto leave
;
7043 err
= insert_GExtApproval(context
, approval
, request
);
7044 if (err
) goto leave
;
7047 /* Send request and check response*/
7048 err
= send_destroy_request_check_response(context
,
7049 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7052 xmlFreeNode(request
);
7055 /* Pick up credentials_delivery if requested */
7056 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7057 (char *)service_name
);
7060 xmlFreeDoc(response
);
7061 if (request
) xmlFreeNode(request
);
7064 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7065 isds_log(ILF_ISDS
, ILL_DEBUG
,
7066 _("%s request processed by server successfully.\n"),
7067 service_name_locale
);
7068 free(service_name_locale
);
7070 #else /* not HAVE_LIBCURL */
7078 /* Assign new user to given box.
7079 * @context is session context
7080 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7081 * members are ignored.
7082 * @user defines new user to add
7083 * @credentials_delivery is NULL if new user's password should be delivered
7084 * off-line to the user. It is valid pointer if user should obtain new
7085 * password on-line on dedicated web server. Then input
7086 * @credentials_delivery.email value is user's e-mail address user must
7087 * provide to dedicated web server together with @credentials_delivery.token.
7088 * The output reallocated token user needs to use to authorize on the web
7089 * server to view his new password. Output reallocated
7090 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7091 * assingned up on this call.
7092 * @approval is optional external approval of box manipulation
7093 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7094 * NULL, if you don't care.*/
7095 isds_error
isds_add_user(struct isds_ctx
*context
,
7096 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7097 struct isds_credentials_delivery
*credentials_delivery
,
7098 const struct isds_approval
*approval
, char **refnumber
) {
7099 return build_send_manipulationboxuser_request_check_drop_response(context
,
7100 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7101 approval
, (xmlChar
**) refnumber
);
7105 /* Remove user assigned to given box.
7106 * @context is session context
7107 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7108 * members are ignored.
7109 * @user identifies user to remove, aifo_ticket member is ignored
7110 * @approval is optional external approval of box manipulation
7111 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7112 * NULL, if you don't care.*/
7113 isds_error
isds_delete_user(struct isds_ctx
*context
,
7114 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7115 const struct isds_approval
*approval
, char **refnumber
) {
7116 return build_send_manipulationboxuser_request_check_drop_response(context
,
7117 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7118 (xmlChar
**) refnumber
);
7122 /* Get list of boxes in ZIP archive.
7123 * @context is session context
7124 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7125 * System recognizes following values currently: ALL (all boxes), UPG
7126 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7127 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7128 * commercial messages). This argument is a string because specification
7129 * states new values can appear in the future. Not all list types are
7130 * available to all users.
7131 * @buffer is automatically reallocated memory to store the list of boxes. The
7132 * list is zipped CSV file.
7133 * @buffer_length is size of @buffer data in bytes.
7134 * In case of error @buffer will be freed and @buffer_length will be
7136 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7137 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7138 isds_error err
= IE_SUCCESS
;
7140 xmlNsPtr isds_ns
= NULL
;
7141 xmlNodePtr request
= NULL
, node
;
7142 xmlDocPtr response
= NULL
;
7143 xmlXPathContextPtr xpath_ctx
= NULL
;
7144 xmlXPathObjectPtr result
= NULL
;
7145 char *string
= NULL
;
7149 if (!context
) return IE_INVALID_CONTEXT
;
7150 zfree(context
->long_message
);
7151 if (buffer
) zfree(*buffer
);
7152 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7156 /* Check if connection is established
7157 * TODO: This check should be done downstairs. */
7158 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7161 /* Build AuthenticateMessage request */
7162 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7164 isds_log_message(context
,
7165 _("Could not build GetDataBoxList request"));
7168 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7170 isds_log_message(context
, _("Could not create ISDS name space"));
7171 xmlFreeNode(request
);
7174 xmlSetNs(request
, isds_ns
);
7175 INSERT_STRING(request
, "dblType", list_identifier
);
7177 /* Send request to server and process response */
7178 err
= send_destroy_request_check_response(context
,
7179 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7180 &response
, NULL
, NULL
);
7181 if (err
) goto leave
;
7184 /* Extract Base-64 encoded ZIP file */
7185 xpath_ctx
= xmlXPathNewContext(response
);
7190 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7194 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7196 /* Decode non-empty archive */
7197 if (string
&& string
[0] != '\0') {
7198 *buffer_length
= _isds_b64decode(string
, buffer
);
7199 if (*buffer_length
== (size_t) -1) {
7200 isds_printf_message(context
,
7201 _("Error while Base64-decoding box list archive"));
7210 xmlXPathFreeObject(result
);
7211 xmlXPathFreeContext(xpath_ctx
);
7212 xmlFreeDoc(response
);
7213 xmlFreeNode(request
);
7216 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7217 "processed by server successfully.\n"));
7219 #else /* not HAVE_LIBCURL */
7227 /* Find boxes suiting given criteria.
7228 * @criteria is filter. You should fill in at least some members. aifoIsds,
7229 * address->adCode, address->adDistrict members are ignored.
7230 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7231 * possibly empty. Input NULL or valid old structure.
7233 * IE_SUCCESS if search succeeded, @boxes contains useful data
7234 * IE_NOEXIST if no such box exists, @boxes will be NULL
7235 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7236 * contains still valid data
7237 * other code if something bad happens. @boxes will be NULL. */
7238 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7239 const struct isds_DbOwnerInfo
*criteria
,
7240 struct isds_list
**boxes
) {
7241 isds_error err
= IE_SUCCESS
;
7243 _Bool truncated
= 0;
7244 xmlNsPtr isds_ns
= NULL
;
7245 xmlNodePtr request
= NULL
;
7246 xmlDocPtr response
= NULL
;
7247 xmlChar
*code
= NULL
, *message
= NULL
;
7248 xmlNodePtr db_owner_info
;
7249 xmlXPathContextPtr xpath_ctx
= NULL
;
7250 xmlXPathObjectPtr result
= NULL
;
7251 xmlChar
*string
= NULL
;
7255 if (!context
) return IE_INVALID_CONTEXT
;
7256 zfree(context
->long_message
);
7257 if (!boxes
) return IE_INVAL
;
7258 isds_list_free(boxes
);
7265 /* Check if connection is established
7266 * TODO: This check should be done downstairs. */
7267 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7270 /* Build FindDataBox request */
7271 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7273 isds_log_message(context
,
7274 _("Could build FindDataBox request"));
7277 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7279 isds_log_message(context
, _("Could not create ISDS name space"));
7280 xmlFreeNode(request
);
7283 xmlSetNs(request
, isds_ns
);
7284 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7285 if (!db_owner_info
) {
7286 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7287 "FindDataBox element"));
7288 xmlFreeNode(request
);
7292 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7293 if (err
) goto leave
;
7296 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7299 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7301 /* Destroy request */
7302 xmlFreeNode(request
); request
= NULL
;
7305 isds_log(ILF_ISDS
, ILL_DEBUG
,
7306 _("Processing ISDS response on FindDataBox "
7307 "request failed\n"));
7311 /* Check for response status */
7312 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7313 &code
, &message
, NULL
);
7315 isds_log(ILF_ISDS
, ILL_DEBUG
,
7316 _("ISDS response on FindDataBox request is missing status\n"));
7320 /* Request processed, but nothing found */
7321 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7322 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7323 char *code_locale
= _isds_utf82locale((char*)code
);
7324 char *message_locale
= _isds_utf82locale((char*)message
);
7325 isds_log(ILF_ISDS
, ILL_DEBUG
,
7326 _("Server did not found any box on FindDataBox request "
7327 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7328 isds_log_message(context
, message_locale
);
7330 free(message_locale
);
7335 /* Warning, not a error */
7336 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7337 char *code_locale
= _isds_utf82locale((char*)code
);
7338 char *message_locale
= _isds_utf82locale((char*)message
);
7339 isds_log(ILF_ISDS
, ILL_DEBUG
,
7340 _("Server truncated response on FindDataBox request "
7341 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7342 isds_log_message(context
, message_locale
);
7344 free(message_locale
);
7349 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7350 char *code_locale
= _isds_utf82locale((char*)code
);
7351 char *message_locale
= _isds_utf82locale((char*)message
);
7352 isds_log(ILF_ISDS
, ILL_DEBUG
,
7353 _("Server refused FindDataBox request "
7354 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7355 isds_log_message(context
, message_locale
);
7357 free(message_locale
);
7362 xpath_ctx
= xmlXPathNewContext(response
);
7367 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7372 /* Extract boxes if they present */
7373 result
= xmlXPathEvalExpression(BAD_CAST
7374 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7380 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7381 struct isds_list
*item
, *prev_item
= NULL
;
7382 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7383 item
= calloc(1, sizeof(*item
));
7389 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7390 if (i
== 0) *boxes
= item
;
7391 else prev_item
->next
= item
;
7394 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7395 err
= extract_DbOwnerInfo(context
,
7396 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7397 if (err
) goto leave
;
7403 isds_list_free(boxes
);
7405 if (truncated
) err
= IE_2BIG
;
7409 xmlFreeNode(request
);
7410 xmlXPathFreeObject(result
);
7411 xmlXPathFreeContext(xpath_ctx
);
7415 xmlFreeDoc(response
);
7418 isds_log(ILF_ISDS
, ILL_DEBUG
,
7419 _("FindDataBox request processed by server successfully.\n"));
7420 #else /* not HAVE_LIBCURL */
7429 /* Convert a string with match markers into a plain string with list of
7430 * pointers to the matches
7431 * @string is an UTF-8 encoded non-constant string with match markers
7432 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7433 * The markers will be removed from the string.
7434 * @starts is a reallocated list of static pointers into the @string pointing
7435 * to places where match start markers occured.
7436 * @ends is a reallocated list of static pointers into the @string pointing
7437 * to places where match end markers occured.
7438 * @return IE_SUCCESS in case of no failure. */
7439 static isds_error
interpret_matches(xmlChar
*string
,
7440 struct isds_list
**starts
, struct isds_list
**ends
) {
7441 isds_error err
= IE_SUCCESS
;
7442 xmlChar
*pointer
, *destination
, *source
;
7443 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7445 isds_list_free(starts
);
7446 isds_list_free(ends
);
7447 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7448 if (NULL
== string
) return IE_SUCCESS
;
7450 for (pointer
= string
; *pointer
!= '\0';) {
7451 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7452 /* Remove the start marker */
7453 for (source
= pointer
+ 14, destination
= pointer
;
7454 *source
!= '\0'; source
++, destination
++) {
7455 *destination
= *source
;
7457 *destination
= '\0';
7458 /* Append the pointer into the list */
7459 item
= calloc(1, sizeof(*item
));
7464 item
->destructor
= (void (*)(void **))NULL
;
7465 item
->data
= pointer
;
7466 if (NULL
== prev_start
) *starts
= item
;
7467 else prev_start
->next
= item
;
7469 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7470 /* Remove the end marker */
7471 for (source
= pointer
+ 12, destination
= pointer
;
7472 *source
!= '\0'; source
++, destination
++) {
7473 *destination
= *source
;
7475 *destination
= '\0';
7476 /* Append the pointer into the list */
7477 item
= calloc(1, sizeof(*item
));
7482 item
->destructor
= (void (*)(void **))NULL
;
7483 item
->data
= pointer
;
7484 if (NULL
== prev_end
) *ends
= item
;
7485 else prev_end
->next
= item
;
7494 isds_list_free(starts
);
7495 isds_list_free(ends
);
7501 /* Convert isds:dbResult XML tree into structure
7502 * @context is ISDS context.
7503 * @fulltext_result is automatically reallocated found box structure.
7504 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7505 * @collect_matches is true to interpret match markers.
7506 * In case of error @result will be freed. */
7507 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7508 struct isds_fulltext_result
**fulltext_result
,
7509 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7510 isds_error err
= IE_SUCCESS
;
7511 xmlXPathObjectPtr result
= NULL
;
7512 char *string
= NULL
;
7514 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7515 if (NULL
== fulltext_result
) return IE_INVAL
;
7516 isds_fulltext_result_free(fulltext_result
);
7517 if (!xpath_ctx
) return IE_INVAL
;
7520 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7521 if (NULL
== *fulltext_result
) {
7527 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7529 EXTRACT_STRING("isds:dbType", string
);
7530 if (NULL
== string
) {
7532 isds_log_message(context
, _("Empty isds:dbType element"));
7535 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7537 if (err
== IE_ENUM
) {
7539 char *string_locale
= _isds_utf82locale(string
);
7540 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7542 free(string_locale
);
7548 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7549 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7551 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7552 if (err
) goto leave
;
7554 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7555 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7556 (*fulltext_result
)->dbEffectiveOVM
);
7558 EXTRACT_STRING("isds:dbSendOptions", string
);
7559 if (NULL
== string
) {
7561 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7564 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7565 (*fulltext_result
)->active
= 1;
7566 (*fulltext_result
)->public_sending
= 1;
7567 (*fulltext_result
)->commercial_sending
= 0;
7568 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7569 (*fulltext_result
)->active
= 1;
7570 (*fulltext_result
)->public_sending
= 1;
7571 (*fulltext_result
)->commercial_sending
= 1;
7572 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7573 (*fulltext_result
)->active
= 1;
7574 (*fulltext_result
)->public_sending
= 0;
7575 (*fulltext_result
)->commercial_sending
= 1;
7576 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7577 (*fulltext_result
)->active
= 1;
7578 (*fulltext_result
)->public_sending
= 0;
7579 (*fulltext_result
)->commercial_sending
= 0;
7580 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7581 (*fulltext_result
)->active
= 0;
7582 (*fulltext_result
)->public_sending
= 0;
7583 (*fulltext_result
)->commercial_sending
= 0;
7586 char *string_locale
= _isds_utf82locale(string
);
7587 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7589 free(string_locale
);
7594 /* Interpret match marks */
7595 if (collect_matches
) {
7596 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7597 &((*fulltext_result
)->name_match_start
),
7598 &((*fulltext_result
)->name_match_end
));
7599 if (err
) goto leave
;
7600 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7601 &((*fulltext_result
)->address_match_start
),
7602 &((*fulltext_result
)->address_match_end
));
7603 if (err
) goto leave
;
7607 if (err
) isds_fulltext_result_free(fulltext_result
);
7609 xmlXPathFreeObject(result
);
7612 #endif /* HAVE_LIBCURL */
7615 /* Find boxes matching a given full-text criteria.
7616 * @context is a session context
7617 * @query is a non-empty string which consists of words to search
7618 * @target selects box attributes to search for @query words. Pass NULL if you
7620 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7621 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7622 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7623 * which is DBTYPE_SYSTEM.
7624 * @page_size defines count of boxes to constitute a response page. It counts
7625 * from zero. Pass NULL to let server to use a default value (50 now).
7626 * @page_number defines ordinar number of the response page to return. It
7627 * counts from zero. Pass NULL to let server to use a default value (0 now).
7628 * @track_matches points to true for marking @query words found in the box
7629 * attributes. It points to false for not marking. Pass NULL to let the server
7630 * to use default value (false now).
7631 * @total_matching_boxes outputs reallocated number of all boxes matching the
7632 * query. Will be pointer to NULL if server did not provide the value.
7633 * Pass NULL if you don't care.
7634 * @current_page_beginning outputs reallocated ordinar number of the first box
7635 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7636 * server did not provide the value. Pass NULL if you don't care.
7637 * @current_page_size outputs reallocated count of boxes in the this @boxes
7638 * page. It will be pointer to NULL if the server did not provide the value.
7639 * Pass NULL if you don't care.
7640 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7641 * is the last one, false if more boxes match, NULL if the server did not
7642 * provude the value. Pass NULL if you don't care.
7643 * @boxes outputs reallocated list of isds_fulltext_result structures,
7646 * IE_SUCCESS if search succeeded
7647 * IE_2BIG if @page_size is too large
7648 * other code if something bad happens; output arguments will be NULL. */
7649 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7651 const isds_fulltext_target
*target
,
7652 const isds_DbType
*box_type
,
7653 const unsigned long int *page_size
,
7654 const unsigned long int *page_number
,
7655 const _Bool
*track_matches
,
7656 unsigned long int **total_matching_boxes
,
7657 unsigned long int **current_page_beginning
,
7658 unsigned long int **current_page_size
,
7660 struct isds_list
**boxes
) {
7661 isds_error err
= IE_SUCCESS
;
7663 xmlNsPtr isds_ns
= NULL
;
7664 xmlNodePtr request
= NULL
;
7665 xmlDocPtr response
= NULL
;
7667 xmlXPathContextPtr xpath_ctx
= NULL
;
7668 xmlXPathObjectPtr result
= NULL
;
7669 const xmlChar
*static_string
= NULL
;
7670 xmlChar
*string
= NULL
;
7672 const xmlChar
*codes
[] = {
7682 const char *meanings
[] = {
7683 N_("You are not allowed to perform the search"),
7684 N_("The query string is empty"),
7685 N_("Searched box ID is malformed"),
7686 N_("Searched organization ID is malformed"),
7687 N_("Invalid input"),
7688 N_("Requested page size is too large"),
7689 N_("Search engine internal error")
7691 const isds_error errors
[] = {
7700 struct code_map_isds_error map
= {
7702 .meanings
= meanings
,
7708 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7709 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7710 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7711 if (NULL
!= last_page
) zfree(*last_page
);
7712 isds_list_free(boxes
);
7714 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7715 zfree(context
->long_message
);
7717 if (NULL
== boxes
) return IE_INVAL
;
7719 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7720 isds_log_message(context
, _("Query string must be non-empty"));
7725 /* Check if connection is established
7726 * TODO: This check should be done downstairs. */
7727 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7729 /* Build FindDataBox request */
7730 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7731 if (NULL
== request
) {
7732 isds_log_message(context
,
7733 _("Could not build ISDSSearch2 request"));
7736 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7737 if(NULL
== isds_ns
) {
7738 isds_log_message(context
, _("Could not create ISDS name space"));
7739 xmlFreeNode(request
);
7742 xmlSetNs(request
, isds_ns
);
7744 INSERT_STRING(request
, "searchText", query
);
7746 if (NULL
!= target
) {
7747 static_string
= isds_fulltext_target2string(*(target
));
7748 if (NULL
== static_string
) {
7749 isds_printf_message(context
, _("Invalid target value: %d"),
7755 INSERT_STRING(request
, "searchType", static_string
);
7756 static_string
= NULL
;
7758 if (NULL
!= box_type
) {
7759 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7760 if (DBTYPE_SYSTEM
== *box_type
) {
7761 static_string
= BAD_CAST
"ALL";
7762 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
7763 static_string
= BAD_CAST
"OVM_MAIN";
7765 static_string
= isds_DbType2string(*(box_type
));
7766 if (NULL
== static_string
) {
7767 isds_printf_message(context
, _("Invalid box type value: %d"),
7774 INSERT_STRING(request
, "searchScope", static_string
);
7775 static_string
= NULL
;
7777 INSERT_ULONGINT(request
, "page", page_number
, string
);
7778 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7779 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7781 /* Send request and check response */
7782 err
= send_destroy_request_check_response(context
,
7783 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7784 &request
, &response
, NULL
, &map
);
7785 if (err
) goto leave
;
7787 /* Parse response */
7788 xpath_ctx
= xmlXPathNewContext(response
);
7789 if (NULL
== xpath_ctx
) {
7793 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7797 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7803 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7804 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7808 if (result
->nodesetval
->nodeNr
> 1) {
7809 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7813 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7814 xmlXPathFreeObject(result
); result
= NULL
;
7817 /* Extract counters */
7818 if (NULL
!= total_matching_boxes
) {
7819 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7821 if (NULL
!= current_page_size
) {
7822 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7824 if (NULL
!= current_page_beginning
) {
7825 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7827 if (NULL
!= last_page
) {
7828 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7830 xmlXPathFreeObject(result
); result
= NULL
;
7832 /* Extract boxes if they present */
7833 result
= xmlXPathEvalExpression(BAD_CAST
7834 "isds:dbResults/isds:dbResult", xpath_ctx
);
7835 if (NULL
== result
) {
7839 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7840 struct isds_list
*item
, *prev_item
= NULL
;
7841 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7842 item
= calloc(1, sizeof(*item
));
7848 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7849 if (i
== 0) *boxes
= item
;
7850 else prev_item
->next
= item
;
7853 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7854 err
= extract_dbResult(context
,
7855 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7856 (NULL
== track_matches
) ? 0 : *track_matches
);
7857 if (err
) goto leave
;
7863 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7864 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7865 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7866 if (NULL
!= last_page
) zfree(*last_page
);
7867 isds_list_free(boxes
);
7871 xmlFreeNode(request
);
7872 xmlXPathFreeObject(result
);
7873 xmlXPathFreeContext(xpath_ctx
);
7874 xmlFreeDoc(response
);
7877 isds_log(ILF_ISDS
, ILL_DEBUG
,
7878 _("ISDSSearch2 request processed by server successfully.\n"));
7879 #else /* not HAVE_LIBCURL */
7887 /* Get status of a box.
7888 * @context is ISDS session context.
7889 * @box_id is UTF-8 encoded box identifier as zero terminated string
7890 * @box_status is return value of box status.
7892 * IE_SUCCESS if box has been found and its status retrieved
7893 * IE_NOEXIST if box is not known to ISDS server
7894 * or other appropriate error.
7895 * You can use isds_DbState to enumerate box status. However out of enum
7896 * range value can be returned too. This is feature because ISDS
7897 * specification leaves the set of values open.
7898 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7899 * the box has been deleted, but ISDS still lists its former existence. */
7900 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7901 long int *box_status
) {
7902 isds_error err
= IE_SUCCESS
;
7904 xmlNsPtr isds_ns
= NULL
;
7905 xmlNodePtr request
= NULL
, db_id
;
7906 xmlDocPtr response
= NULL
;
7907 xmlXPathContextPtr xpath_ctx
= NULL
;
7908 xmlXPathObjectPtr result
= NULL
;
7909 xmlChar
*string
= NULL
;
7911 const xmlChar
*codes
[] = {
7917 const char *meanings
[] = {
7918 "The box does not exist",
7919 "Box ID is malformed",
7922 const isds_error errors
[] = {
7927 struct code_map_isds_error map
= {
7929 .meanings
= meanings
,
7934 if (!context
) return IE_INVALID_CONTEXT
;
7935 zfree(context
->long_message
);
7936 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7939 /* Check if connection is established
7940 * TODO: This check should be done downstairs. */
7941 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7944 /* Build CheckDataBox request */
7945 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7947 isds_log_message(context
,
7948 _("Could build CheckDataBox request"));
7951 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7953 isds_log_message(context
, _("Could not create ISDS name space"));
7954 xmlFreeNode(request
);
7957 xmlSetNs(request
, isds_ns
);
7958 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7960 isds_log_message(context
, _("Could not add dbID child to "
7961 "CheckDataBox element"));
7962 xmlFreeNode(request
);
7967 /* Send request and check response*/
7968 err
= send_destroy_request_check_response(context
,
7969 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7970 &request
, &response
, NULL
, &map
);
7971 if (err
) goto leave
;
7975 xpath_ctx
= xmlXPathNewContext(response
);
7980 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7984 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7990 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7991 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7995 if (result
->nodesetval
->nodeNr
> 1) {
7996 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
8000 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8001 xmlXPathFreeObject(result
); result
= NULL
;
8003 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
8008 xmlXPathFreeObject(result
);
8009 xmlXPathFreeContext(xpath_ctx
);
8011 xmlFreeDoc(response
);
8014 isds_log(ILF_ISDS
, ILL_DEBUG
,
8015 _("CheckDataBox request processed by server successfully.\n"));
8016 #else /* not HAVE_LIBCURL */
8024 /* Get list of permissions to send commercial messages.
8025 * @context is ISDS session context.
8026 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8027 * @permissions is a reallocated list of permissions (struct
8028 * isds_commercial_permission*) to send commercial messages from @box_id. The
8029 * order of permissions is significant as the server applies the permissions
8030 * and associated pre-paid credits in the order. Empty list means no
8033 * IE_SUCCESS if the list has been obtained correctly,
8034 * or other appropriate error. */
8035 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8036 const char *box_id
, struct isds_list
**permissions
) {
8037 isds_error err
= IE_SUCCESS
;
8039 xmlDocPtr response
= NULL
;
8040 xmlXPathContextPtr xpath_ctx
= NULL
;
8041 xmlXPathObjectPtr result
= NULL
;
8044 if (!context
) return IE_INVALID_CONTEXT
;
8045 zfree(context
->long_message
);
8046 if (NULL
== permissions
) return IE_INVAL
;
8047 isds_list_free(permissions
);
8048 if (NULL
== box_id
) return IE_INVAL
;
8051 /* Check if connection is established */
8052 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8054 /* Do request and check for success */
8055 err
= build_send_dbid_request_check_response(context
,
8056 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8057 BAD_CAST box_id
, NULL
, &response
, NULL
);
8059 isds_log(ILF_ISDS
, ILL_DEBUG
,
8060 _("PDZInfo request processed by server successfully.\n"));
8064 /* Prepare structure */
8065 xpath_ctx
= xmlXPathNewContext(response
);
8070 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8075 /* Set context node */
8076 result
= xmlXPathEvalExpression(BAD_CAST
8077 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8083 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8084 struct isds_list
*prev_item
= NULL
;
8086 /* Iterate over all permission records */
8087 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8088 struct isds_list
*item
;
8090 /* Prepare structure */
8091 item
= calloc(1, sizeof(*item
));
8096 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8097 if (i
== 0) *permissions
= item
;
8098 else prev_item
->next
= item
;
8102 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8103 err
= extract_DbPDZRecord(context
,
8104 (struct isds_commercial_permission
**) (&item
->data
),
8106 if (err
) goto leave
;
8112 isds_list_free(permissions
);
8115 xmlXPathFreeObject(result
);
8116 xmlXPathFreeContext(xpath_ctx
);
8117 xmlFreeDoc(response
);
8119 #else /* not HAVE_LIBCURL */
8127 /* Get details about credit for sending pre-paid commercial messages.
8128 * @context is ISDS session context.
8129 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8130 * @from_date is first day of credit history to return in @history. Only
8131 * tm_year, tm_mon and tm_mday carry sane value.
8132 * @to_date is last day of credit history to return in @history. Only
8133 * tm_year, tm_mon and tm_mday carry sane value.
8134 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8135 * if you don't care. This and all other credit values are integers in
8136 * hundredths of Czech Crowns.
8137 * @email outputs notification e-mail address where notifications about credit
8138 * are sent. This is automatically reallocated string. Pass NULL if you don't
8139 * care. It can return NULL if no address is defined.
8140 * @history outputs auto-reallocated list of pointers to struct
8141 * isds_credit_event. Events in closed interval @from_time to @to_time are
8142 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8143 * are sorted by time.
8145 * IE_SUCCESS if the credit details have been obtained correctly,
8146 * or other appropriate error. Please note that server allows to retrieve
8147 * only limited history of events. */
8148 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8150 const struct tm
*from_date
, const struct tm
*to_date
,
8151 long int *credit
, char **email
, struct isds_list
**history
) {
8152 isds_error err
= IE_SUCCESS
;
8154 char *box_id_locale
= NULL
;
8155 xmlNodePtr request
= NULL
, node
;
8156 xmlNsPtr isds_ns
= NULL
;
8157 xmlChar
*string
= NULL
;
8159 xmlDocPtr response
= NULL
;
8160 xmlXPathContextPtr xpath_ctx
= NULL
;
8161 xmlXPathObjectPtr result
= NULL
;
8163 const xmlChar
*codes
[] = {
8171 const char *meanings
[] = {
8172 "Insufficient priviledges for the box",
8173 "The box does not exist",
8174 "Date is too long (history is not available after 15 months)",
8175 "Interval is too long (limit is 3 months)",
8178 const isds_error errors
[] = {
8185 struct code_map_isds_error map
= {
8187 .meanings
= meanings
,
8192 if (!context
) return IE_INVALID_CONTEXT
;
8193 zfree(context
->long_message
);
8195 /* Free output argument */
8196 if (NULL
!= credit
) *credit
= 0;
8197 if (NULL
!= email
) zfree(*email
);
8198 isds_list_free(history
);
8200 if (NULL
== box_id
) return IE_INVAL
;
8203 /* Check if connection is established */
8204 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8206 box_id_locale
= _isds_utf82locale((char*)box_id
);
8207 if (NULL
== box_id_locale
) {
8213 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8214 if (NULL
== request
) {
8215 isds_printf_message(context
,
8216 _("Could not build DataBoxCreditInfo request for %s box"),
8221 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8223 isds_log_message(context
, _("Could not create ISDS name space"));
8227 xmlSetNs(request
, isds_ns
);
8229 /* Add mandatory XSD:tIdDbInput child */
8230 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8231 /* Add mandatory dates elements with optional values */
8233 err
= tm2datestring(from_date
, &string
);
8235 isds_log_message(context
,
8236 _("Could not convert `from_date' argument to ISO date "
8240 INSERT_STRING(request
, "ciFromDate", string
);
8243 INSERT_STRING(request
, "ciFromDate", NULL
);
8246 err
= tm2datestring(to_date
, &string
);
8248 isds_log_message(context
,
8249 _("Could not convert `to_date' argument to ISO date "
8253 INSERT_STRING(request
, "ciTodate", string
);
8256 INSERT_STRING(request
, "ciTodate", NULL
);
8259 /* Send request and check response*/
8260 err
= send_destroy_request_check_response(context
,
8261 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8262 &request
, &response
, NULL
, &map
);
8263 if (err
) goto leave
;
8267 /* Set context to the root */
8268 xpath_ctx
= xmlXPathNewContext(response
);
8273 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8277 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8283 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8284 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8288 if (result
->nodesetval
->nodeNr
> 1) {
8289 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8293 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8294 xmlXPathFreeObject(result
); result
= NULL
;
8296 /* Extract common data */
8297 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8298 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8300 /* Extract records */
8301 if (NULL
== history
) goto leave
;
8302 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8308 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8309 struct isds_list
*prev_item
= NULL
;
8311 /* Iterate over all records */
8312 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8313 struct isds_list
*item
;
8315 /* Prepare structure */
8316 item
= calloc(1, sizeof(*item
));
8321 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8322 if (i
== 0) *history
= item
;
8323 else prev_item
->next
= item
;
8327 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8328 err
= extract_CiRecord(context
,
8329 (struct isds_credit_event
**) (&item
->data
),
8331 if (err
) goto leave
;
8337 isds_log(ILF_ISDS
, ILL_DEBUG
,
8338 _("DataBoxCreditInfo request processed by server successfully.\n"));
8341 isds_list_free(history
);
8342 if (NULL
!= email
) zfree(*email
)
8345 free(box_id_locale
);
8346 xmlXPathFreeObject(result
);
8347 xmlXPathFreeContext(xpath_ctx
);
8348 xmlFreeDoc(response
);
8350 #else /* not HAVE_LIBCURL */
8358 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8359 * code, destroy response and log success.
8360 * @context is ISDS session context.
8361 * @service_name is name of SERVICE_DB_MANIPULATION service
8362 * @box_id is UTF-8 encoded box identifier as zero terminated string
8363 * @approval is optional external approval of box manipulation
8364 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8365 * NULL, if you don't care. */
8366 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8367 struct isds_ctx
*context
, const xmlChar
*service_name
,
8368 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8369 xmlChar
**refnumber
) {
8370 isds_error err
= IE_SUCCESS
;
8372 xmlDocPtr response
= NULL
;
8375 if (!context
) return IE_INVALID_CONTEXT
;
8376 zfree(context
->long_message
);
8377 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8380 /* Check if connection is established */
8381 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8383 /* Do request and check for success */
8384 err
= build_send_dbid_request_check_response(context
,
8385 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8386 &response
, refnumber
);
8387 xmlFreeDoc(response
);
8390 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8391 isds_log(ILF_ISDS
, ILL_DEBUG
,
8392 _("%s request processed by server successfully.\n"),
8393 service_name_locale
);
8394 free(service_name_locale
);
8396 #else /* not HAVE_LIBCURL */
8404 /* Switch box into state where box can receive commercial messages (off by
8406 * @context is ISDS session context.
8407 * @box_id is UTF-8 encoded box identifier as zero terminated string
8408 * @allow is true for enable, false for disable commercial messages income
8409 * @approval is optional external approval of box manipulation
8410 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8411 * NULL, if you don't care. */
8412 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8413 const char *box_id
, const _Bool allow
,
8414 const struct isds_approval
*approval
, char **refnumber
) {
8415 return build_send_manipulationdbid_request_check_drop_response(context
,
8416 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8417 BAD_CAST
"ClearOpenAddressing",
8418 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8422 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8423 * message acceptance). This is just a box permission. Sender must apply
8424 * such role by sending each message.
8425 * @context is ISDS session context.
8426 * @box_id is UTF-8 encoded box identifier as zero terminated string
8427 * @allow is true for enable, false for disable OVM role permission
8428 * @approval is optional external approval of box manipulation
8429 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8430 * NULL, if you don't care. */
8431 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8432 const char *box_id
, const _Bool allow
,
8433 const struct isds_approval
*approval
, char **refnumber
) {
8434 return build_send_manipulationdbid_request_check_drop_response(context
,
8435 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8436 BAD_CAST
"ClearEffectiveOVM",
8437 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8441 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8442 * code, destroy response and log success.
8443 * @context is ISDS session context.
8444 * @service_name is name of SERVICE_DB_MANIPULATION service
8445 * @owner is structure describing box. aifoIsds, address->adCode,
8446 * address->adDistrict members are ignored.
8447 * @approval is optional external approval of box manipulation
8448 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8449 * NULL, if you don't care. */
8450 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8451 struct isds_ctx
*context
, const xmlChar
*service_name
,
8452 const struct isds_DbOwnerInfo
*owner
,
8453 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8454 isds_error err
= IE_SUCCESS
;
8456 char *service_name_locale
= NULL
;
8457 xmlNodePtr request
= NULL
, db_owner_info
;
8458 xmlNsPtr isds_ns
= NULL
;
8462 if (!context
) return IE_INVALID_CONTEXT
;
8463 zfree(context
->long_message
);
8464 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8467 service_name_locale
= _isds_utf82locale((char*)service_name
);
8468 if (!service_name_locale
) {
8474 request
= xmlNewNode(NULL
, service_name
);
8476 isds_printf_message(context
,
8477 _("Could not build %s request"), service_name_locale
);
8481 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8483 isds_log_message(context
, _("Could not create ISDS name space"));
8487 xmlSetNs(request
, isds_ns
);
8490 /* Add XSD:tOwnerInfoInput child*/
8491 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8492 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
8493 if (err
) goto leave
;
8495 /* Add XSD:gExtApproval*/
8496 err
= insert_GExtApproval(context
, approval
, request
);
8497 if (err
) goto leave
;
8499 /* Send it to server and process response */
8500 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8501 service_name
, &request
, refnumber
);
8504 xmlFreeNode(request
);
8505 free(service_name_locale
);
8506 #else /* not HAVE_LIBCURL */
8514 /* Switch box accessibility state on request of box owner.
8515 * Despite the name, owner must do the request off-line. This function is
8516 * designed for such off-line meeting points (e.g. Czech POINT).
8517 * @context is ISDS session context.
8518 * @box identifies box to switch accessibility state. aifoIsds,
8519 * address->adCode, address->adDistrict members are ignored.
8520 * @allow is true for making accessible, false to disallow access.
8521 * @approval is optional external approval of box manipulation
8522 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8523 * NULL, if you don't care. */
8524 isds_error
isds_switch_box_accessibility_on_owner_request(
8525 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8526 const _Bool allow
, const struct isds_approval
*approval
,
8528 return build_send_manipulationdbowner_request_check_drop_response(context
,
8529 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8530 BAD_CAST
"DisableOwnDataBox",
8531 box
, approval
, (xmlChar
**) refnumber
);
8535 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8537 * @context is ISDS session context.
8538 * @box identifies box to switch accessibility state. aifoIsds,
8539 * address->adCode, address->adDistrict members are ignored.
8540 * @since is date since accessibility has been denied. This can be past too.
8541 * Only tm_year, tm_mon and tm_mday carry sane value.
8542 * @approval is optional external approval of box manipulation
8543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8544 * NULL, if you don't care. */
8545 isds_error
isds_disable_box_accessibility_externaly(
8546 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8547 const struct tm
*since
, const struct isds_approval
*approval
,
8549 isds_error err
= IE_SUCCESS
;
8551 char *service_name_locale
= NULL
;
8552 xmlNodePtr request
= NULL
, node
;
8553 xmlNsPtr isds_ns
= NULL
;
8554 xmlChar
*string
= NULL
;
8558 if (!context
) return IE_INVALID_CONTEXT
;
8559 zfree(context
->long_message
);
8560 if (!box
|| !since
) return IE_INVAL
;
8564 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8566 isds_printf_message(context
,
8567 _("Could not build %s request"), "DisableDataBoxExternally");
8571 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8573 isds_log_message(context
, _("Could not create ISDS name space"));
8577 xmlSetNs(request
, isds_ns
);
8580 /* Add @box identification */
8581 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8582 err
= insert_DbOwnerInfo(context
, box
, node
);
8583 if (err
) goto leave
;
8585 /* Add @since date */
8586 err
= tm2datestring(since
, &string
);
8588 isds_log_message(context
,
8589 _("Could not convert `since' argument to ISO date string"));
8592 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8596 err
= insert_GExtApproval(context
, approval
, request
);
8597 if (err
) goto leave
;
8599 /* Send it to server and process response */
8600 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8601 BAD_CAST
"DisableDataBoxExternally", &request
,
8602 (xmlChar
**) refnumber
);
8606 xmlFreeNode(request
);
8607 free(service_name_locale
);
8608 #else /* not HAVE_LIBCURL */
8617 /* Insert struct isds_message data (envelope (recipient data optional) and
8618 * documents into XML tree
8619 * @context is session context
8620 * @outgoing_message is libisds structure with message data
8621 * @create_message is XML CreateMessage or CreateMultipleMessage element
8622 * @process_recipient true for recipient data serialization, false for no
8624 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8625 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8626 const _Bool process_recipient
) {
8628 isds_error err
= IE_SUCCESS
;
8629 xmlNodePtr envelope
, dm_files
, node
;
8630 xmlChar
*string
= NULL
;
8632 if (!context
) return IE_INVALID_CONTEXT
;
8633 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8636 /* Build envelope */
8637 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8639 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8640 "%s element"), create_message
->name
);
8644 if (!outgoing_message
->envelope
) {
8645 isds_log_message(context
, _("Outgoing message is missing envelope"));
8650 /* Insert optional message type */
8651 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8653 if (err
) goto leave
;
8655 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8656 outgoing_message
->envelope
->dmSenderOrgUnit
);
8657 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8658 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8660 if (process_recipient
) {
8661 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8662 isds_log_message(context
,
8663 _("Outgoing message is missing recipient box identifier"));
8667 INSERT_STRING(envelope
, "dbIDRecipient",
8668 outgoing_message
->envelope
->dbIDRecipient
);
8670 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8671 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8672 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8673 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8674 INSERT_STRING(envelope
, "dmToHands",
8675 outgoing_message
->envelope
->dmToHands
);
8678 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8680 INSERT_STRING(envelope
, "dmAnnotation",
8681 outgoing_message
->envelope
->dmAnnotation
);
8683 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8684 0, 50, "dmRecipientRefNumber");
8685 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8686 outgoing_message
->envelope
->dmRecipientRefNumber
);
8688 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8689 0, 50, "dmSenderRefNumber");
8690 INSERT_STRING(envelope
, "dmSenderRefNumber",
8691 outgoing_message
->envelope
->dmSenderRefNumber
);
8693 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8694 0, 50, "dmRecipientIdent");
8695 INSERT_STRING(envelope
, "dmRecipientIdent",
8696 outgoing_message
->envelope
->dmRecipientIdent
);
8698 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8699 0, 50, "dmSenderIdent");
8700 INSERT_STRING(envelope
, "dmSenderIdent",
8701 outgoing_message
->envelope
->dmSenderIdent
);
8703 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8704 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8705 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8706 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8707 INSERT_STRING(envelope
, "dmLegalTitleSect",
8708 outgoing_message
->envelope
->dmLegalTitleSect
);
8709 INSERT_STRING(envelope
, "dmLegalTitlePar",
8710 outgoing_message
->envelope
->dmLegalTitlePar
);
8711 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8712 outgoing_message
->envelope
->dmLegalTitlePoint
);
8714 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8715 outgoing_message
->envelope
->dmPersonalDelivery
);
8716 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8717 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8719 /* ???: Should we require value for dbEffectiveOVM sender?
8720 * ISDS has default as true */
8721 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8722 INSERT_BOOLEAN(envelope
, "dmOVM",
8723 outgoing_message
->envelope
->dmPublishOwnID
);
8726 /* Append dmFiles */
8727 if (!outgoing_message
->documents
) {
8728 isds_log_message(context
,
8729 _("Outgoing message is missing list of documents"));
8733 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8735 isds_printf_message(context
, _("Could not add dmFiles child to "
8736 "%s element"), create_message
->name
);
8741 /* Check for document hierarchy */
8742 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8743 if (err
) goto leave
;
8745 /* Process each document */
8746 for (struct isds_list
*item
=
8747 (struct isds_list
*) outgoing_message
->documents
;
8748 item
; item
= item
->next
) {
8750 isds_log_message(context
,
8751 _("List of documents contains empty item"));
8755 /* FIXME: Check for dmFileMetaType and for document references.
8756 * Only first document can be of MAIN type */
8757 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8760 if (err
) goto leave
;
8767 #endif /* HAVE_LIBCURL */
8770 /* Send a message via ISDS to a recipient
8771 * @context is session context
8772 * @outgoing_message is message to send; Some members are mandatory (like
8773 * dbIDRecipient), some are optional and some are irrelevant (especially data
8774 * about sender). Included pointer to isds_list documents must contain at
8775 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8776 * members will be filled with valid data from ISDS. Exact list of write
8777 * members is subject to change. Currently dmID is changed.
8778 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8779 isds_error
isds_send_message(struct isds_ctx
*context
,
8780 struct isds_message
*outgoing_message
) {
8782 isds_error err
= IE_SUCCESS
;
8784 xmlNsPtr isds_ns
= NULL
;
8785 xmlNodePtr request
= NULL
;
8786 xmlDocPtr response
= NULL
;
8787 xmlChar
*code
= NULL
, *message
= NULL
;
8788 xmlXPathContextPtr xpath_ctx
= NULL
;
8789 xmlXPathObjectPtr result
= NULL
;
8790 /*_Bool message_is_complete = 0;*/
8793 if (!context
) return IE_INVALID_CONTEXT
;
8794 zfree(context
->long_message
);
8795 if (!outgoing_message
) return IE_INVAL
;
8798 /* Check if connection is established
8799 * TODO: This check should be done downstairs. */
8800 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8803 /* Build CreateMessage request */
8804 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8806 isds_log_message(context
,
8807 _("Could not build CreateMessage request"));
8810 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8812 isds_log_message(context
, _("Could not create ISDS name space"));
8813 xmlFreeNode(request
);
8816 xmlSetNs(request
, isds_ns
);
8818 /* Append envelope and files */
8819 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8820 if (err
) goto leave
;
8823 /* Signal we can serialize message since now */
8824 /*message_is_complete = 1;*/
8827 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8830 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8832 /* Don't' destroy request, we want to provide it to application later */
8835 isds_log(ILF_ISDS
, ILL_DEBUG
,
8836 _("Processing ISDS response on CreateMessage "
8837 "request failed\n"));
8841 /* Check for response status */
8842 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8843 &code
, &message
, NULL
);
8845 isds_log(ILF_ISDS
, ILL_DEBUG
,
8846 _("ISDS response on CreateMessage request "
8847 "is missing status\n"));
8851 /* Request processed, but refused by server or server failed */
8852 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8853 char *box_id_locale
=
8854 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8855 char *code_locale
= _isds_utf82locale((char*)code
);
8856 char *message_locale
= _isds_utf82locale((char*)message
);
8857 isds_log(ILF_ISDS
, ILL_DEBUG
,
8858 _("Server did not accept message for %s on CreateMessage "
8859 "request (code=%s, message=%s)\n"),
8860 box_id_locale
, code_locale
, message_locale
);
8861 isds_log_message(context
, message_locale
);
8862 free(box_id_locale
);
8864 free(message_locale
);
8871 xpath_ctx
= xmlXPathNewContext(response
);
8876 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8880 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8886 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8887 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8891 if (result
->nodesetval
->nodeNr
> 1) {
8892 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8896 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8897 xmlXPathFreeObject(result
); result
= NULL
;
8899 if (outgoing_message
->envelope
->dmID
) {
8900 free(outgoing_message
->envelope
->dmID
);
8901 outgoing_message
->envelope
->dmID
= NULL
;
8903 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8904 if (!outgoing_message
->envelope
->dmID
) {
8905 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8906 "but did not return assigned message ID\n"));
8910 /* TODO: Serialize message into structure member raw */
8911 /* XXX: Each web service transport message in different format.
8912 * Therefore it's not possible to save them directly.
8913 * To save them, one must figure out common format.
8914 * We can leave it on application, or we can implement the ESS format. */
8915 /*if (message_is_complete) {
8916 if (outgoing_message->envelope->dmID) {
8918 /* Add assigned message ID as first child*/
8919 /*xmlNodePtr dmid_text = xmlNewText(
8920 (xmlChar *) outgoing_message->envelope->dmID);
8921 if (!dmid_text) goto serialization_failed;
8923 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8925 if (!dmid_element) {
8926 xmlFreeNode(dmid_text);
8927 goto serialization_failed;
8930 xmlNodePtr dmid_element_with_text =
8931 xmlAddChild(dmid_element, dmid_text);
8932 if (!dmid_element_with_text) {
8933 xmlFreeNode(dmid_element);
8934 xmlFreeNode(dmid_text);
8935 goto serialization_failed;
8938 node = xmlAddPrevSibling(envelope->childern,
8939 dmid_element_with_text);
8941 xmlFreeNodeList(dmid_element_with_text);
8942 goto serialization_failed;
8946 /* Serialize message with ID into raw */
8947 /*buffer = serialize_element(envelope)*/
8950 serialization_failed:
8955 xmlXPathFreeObject(result
);
8956 xmlXPathFreeContext(xpath_ctx
);
8960 xmlFreeDoc(response
);
8961 xmlFreeNode(request
);
8964 isds_log(ILF_ISDS
, ILL_DEBUG
,
8965 _("CreateMessage request processed by server "
8966 "successfully.\n"));
8967 #else /* not HAVE_LIBCURL */
8975 /* Send a message via ISDS to a multiple recipients
8976 * @context is session context
8977 * @outgoing_message is message to send; Some members are mandatory,
8978 * some are optional and some are irrelevant (especially data
8979 * about sender). Data about recipient will be substituted by ISDS from
8980 * @copies. Included pointer to isds_list documents must
8981 * contain at least one document of FILEMETATYPE_MAIN.
8982 * @copies is list of isds_message_copy structures addressing all desired
8983 * recipients. This is read-write structure, some members will be filled with
8984 * valid data from ISDS (message IDs, error codes, error descriptions).
8986 * ISDS_SUCCESS if all messages have been sent
8987 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8988 * succeeded messages can be identified by copies->data->error),
8989 * or other error code if something other goes wrong. */
8990 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8991 const struct isds_message
*outgoing_message
,
8992 struct isds_list
*copies
) {
8994 isds_error err
= IE_SUCCESS
;
8996 isds_error append_err
;
8997 xmlNsPtr isds_ns
= NULL
;
8998 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8999 struct isds_list
*item
;
9000 struct isds_message_copy
*copy
;
9001 xmlDocPtr response
= NULL
;
9002 xmlChar
*code
= NULL
, *message
= NULL
;
9003 xmlXPathContextPtr xpath_ctx
= NULL
;
9004 xmlXPathObjectPtr result
= NULL
;
9005 xmlChar
*string
= NULL
;
9009 if (!context
) return IE_INVALID_CONTEXT
;
9010 zfree(context
->long_message
);
9011 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9014 /* Check if connection is established
9015 * TODO: This check should be done downstairs. */
9016 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9019 /* Build CreateMultipleMessage request */
9020 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9022 isds_log_message(context
,
9023 _("Could not build CreateMultipleMessage request"));
9026 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9028 isds_log_message(context
, _("Could not create ISDS name space"));
9029 xmlFreeNode(request
);
9032 xmlSetNs(request
, isds_ns
);
9035 /* Build recipients */
9036 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9038 isds_log_message(context
, _("Could not add dmRecipients child to "
9039 "CreateMultipleMessage element"));
9040 xmlFreeNode(request
);
9044 /* Insert each recipient */
9045 for (item
= copies
; item
; item
= item
->next
) {
9046 copy
= (struct isds_message_copy
*) item
->data
;
9048 isds_log_message(context
,
9049 _("`copies' list item contains empty data"));
9054 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9056 isds_log_message(context
, _("Could not add dmRecipient child to "
9057 "dmRecipients element"));
9062 if (!copy
->dbIDRecipient
) {
9063 isds_log_message(context
,
9064 _("Message copy is missing recipient box identifier"));
9068 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9069 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9070 copy
->dmRecipientOrgUnit
);
9071 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9072 copy
->dmRecipientOrgUnitNum
, string
);
9073 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9076 /* Append envelope and files */
9077 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9078 if (err
) goto leave
;
9081 isds_log(ILF_ISDS
, ILL_DEBUG
,
9082 _("Sending CreateMultipleMessage request to ISDS\n"));
9085 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9087 isds_log(ILF_ISDS
, ILL_DEBUG
,
9088 _("Processing ISDS response on CreateMultipleMessage "
9089 "request failed\n"));
9093 /* Check for response status */
9094 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9095 &code
, &message
, NULL
);
9097 isds_log(ILF_ISDS
, ILL_DEBUG
,
9098 _("ISDS response on CreateMultipleMessage request "
9099 "is missing status\n"));
9103 /* Request processed, but some copies failed */
9104 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9105 char *box_id_locale
=
9106 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9107 char *code_locale
= _isds_utf82locale((char*)code
);
9108 char *message_locale
= _isds_utf82locale((char*)message
);
9109 isds_log(ILF_ISDS
, ILL_DEBUG
,
9110 _("Server did accept message for multiple recipients "
9111 "on CreateMultipleMessage request but delivery to "
9112 "some of them failed (code=%s, message=%s)\n"),
9113 box_id_locale
, code_locale
, message_locale
);
9114 isds_log_message(context
, message_locale
);
9115 free(box_id_locale
);
9117 free(message_locale
);
9118 err
= IE_PARTIAL_SUCCESS
;
9121 /* Request refused by server as whole */
9122 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9123 char *box_id_locale
=
9124 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9125 char *code_locale
= _isds_utf82locale((char*)code
);
9126 char *message_locale
= _isds_utf82locale((char*)message
);
9127 isds_log(ILF_ISDS
, ILL_DEBUG
,
9128 _("Server did not accept message for multiple recipients "
9129 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9130 box_id_locale
, code_locale
, message_locale
);
9131 isds_log_message(context
, message_locale
);
9132 free(box_id_locale
);
9134 free(message_locale
);
9141 xpath_ctx
= xmlXPathNewContext(response
);
9146 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9150 result
= xmlXPathEvalExpression(
9151 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9152 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9158 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9159 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9164 /* Extract message ID and delivery status for each copy */
9165 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9166 item
= item
->next
, i
++) {
9167 copy
= (struct isds_message_copy
*) item
->data
;
9168 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9170 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9176 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9177 isds_printf_message(context
, _("ISDS returned unexpected number of "
9178 "message copy delivery states: %d"),
9179 result
->nodesetval
->nodeNr
);
9188 xmlXPathFreeObject(result
);
9189 xmlXPathFreeContext(xpath_ctx
);
9193 xmlFreeDoc(response
);
9194 xmlFreeNode(request
);
9197 isds_log(ILF_ISDS
, ILL_DEBUG
,
9198 _("CreateMultipleMessageResponse request processed by server "
9199 "successfully.\n"));
9200 #else /* not HAVE_LIBCURL */
9208 /* Get list of messages. This is common core for getting sent or received
9210 * Any criterion argument can be NULL, if you don't care about it.
9211 * @context is session context. Must not be NULL.
9212 * @outgoing_direction is true if you want list of outgoing messages,
9213 * it's false if you want incoming messages.
9214 * @from_time is minimal time and date of message sending inclusive.
9215 * @to_time is maximal time and date of message sending inclusive
9216 * @organization_unit_number is number of sender/recipient respectively.
9217 * @status_filter is bit field of isds_message_status values. Use special
9218 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9219 * all values, you can use bit-wise arithmetic if you want.)
9220 * @offset is index of first message we are interested in. First message is 1.
9221 * Set to 0 (or 1) if you don't care.
9222 * @number is maximal length of list you want to get as input value, outputs
9223 * number of messages matching these criteria. Can be NULL if you don't care
9224 * (applies to output value either).
9225 * @messages is automatically reallocated list of isds_message's. Be ware that
9226 * it returns only brief overview (envelope and some other fields) about each
9227 * message, not the complete message. FIXME: Specify exact fields.
9228 * The list is sorted by delivery time in ascending order.
9229 * Use NULL if you don't care about don't need the data (useful if you want to
9230 * know only the @number). If you provide &NULL, list will be allocated on
9231 * heap, if you provide pointer to non-NULL, list will be freed automatically
9232 * at first. Also in case of error the list will be NULLed.
9233 * @return IE_SUCCESS or appropriate error code. */
9234 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9235 _Bool outgoing_direction
,
9236 const struct timeval
*from_time
, const struct timeval
*to_time
,
9237 const long int *organization_unit_number
,
9238 const unsigned int status_filter
,
9239 const unsigned long int offset
, unsigned long int *number
,
9240 struct isds_list
**messages
) {
9242 isds_error err
= IE_SUCCESS
;
9244 xmlNsPtr isds_ns
= NULL
;
9245 xmlNodePtr request
= NULL
, node
;
9246 xmlDocPtr response
= NULL
;
9247 xmlChar
*code
= NULL
, *message
= NULL
;
9248 xmlXPathContextPtr xpath_ctx
= NULL
;
9249 xmlXPathObjectPtr result
= NULL
;
9250 xmlChar
*string
= NULL
;
9254 if (!context
) return IE_INVALID_CONTEXT
;
9255 zfree(context
->long_message
);
9257 /* Free former message list if any */
9258 if (messages
) isds_list_free(messages
);
9261 /* Check if connection is established
9262 * TODO: This check should be done downstairs. */
9263 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9265 /* Build GetListOf*Messages request */
9266 request
= xmlNewNode(NULL
,
9267 (outgoing_direction
) ?
9268 BAD_CAST
"GetListOfSentMessages" :
9269 BAD_CAST
"GetListOfReceivedMessages"
9272 isds_log_message(context
,
9273 (outgoing_direction
) ?
9274 _("Could not build GetListOfSentMessages request") :
9275 _("Could not build GetListOfReceivedMessages request")
9279 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9281 isds_log_message(context
, _("Could not create ISDS name space"));
9282 xmlFreeNode(request
);
9285 xmlSetNs(request
, isds_ns
);
9289 err
= timeval2timestring(from_time
, &string
);
9290 if (err
) goto leave
;
9292 INSERT_STRING(request
, "dmFromTime", string
);
9293 free(string
); string
= NULL
;
9296 err
= timeval2timestring(to_time
, &string
);
9297 if (err
) goto leave
;
9299 INSERT_STRING(request
, "dmToTime", string
);
9300 free(string
); string
= NULL
;
9302 if (outgoing_direction
) {
9303 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9304 organization_unit_number
, string
);
9306 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9307 organization_unit_number
, string
);
9310 if (status_filter
> MESSAGESTATE_ANY
) {
9311 isds_printf_message(context
,
9312 _("Invalid message state filter value: %ld"), status_filter
);
9316 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9319 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9321 INSERT_STRING(request
, "dmOffset", "1");
9324 /* number 0 means no limit */
9325 if (number
&& *number
== 0) {
9326 INSERT_STRING(request
, "dmLimit", NULL
);
9328 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9332 isds_log(ILF_ISDS
, ILL_DEBUG
,
9333 (outgoing_direction
) ?
9334 _("Sending GetListOfSentMessages request to ISDS\n") :
9335 _("Sending GetListOfReceivedMessages request to ISDS\n")
9339 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9340 xmlFreeNode(request
); request
= NULL
;
9343 isds_log(ILF_ISDS
, ILL_DEBUG
,
9344 (outgoing_direction
) ?
9345 _("Processing ISDS response on GetListOfSentMessages "
9346 "request failed\n") :
9347 _("Processing ISDS response on GetListOfReceivedMessages "
9353 /* Check for response status */
9354 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9355 &code
, &message
, NULL
);
9357 isds_log(ILF_ISDS
, ILL_DEBUG
,
9358 (outgoing_direction
) ?
9359 _("ISDS response on GetListOfSentMessages request "
9360 "is missing status\n") :
9361 _("ISDS response on GetListOfReceivedMessages request "
9362 "is missing status\n")
9367 /* Request processed, but nothing found */
9368 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9369 char *code_locale
= _isds_utf82locale((char*)code
);
9370 char *message_locale
= _isds_utf82locale((char*)message
);
9371 isds_log(ILF_ISDS
, ILL_DEBUG
,
9372 (outgoing_direction
) ?
9373 _("Server refused GetListOfSentMessages request "
9374 "(code=%s, message=%s)\n") :
9375 _("Server refused GetListOfReceivedMessages request "
9376 "(code=%s, message=%s)\n"),
9377 code_locale
, message_locale
);
9378 isds_log_message(context
, message_locale
);
9380 free(message_locale
);
9387 xpath_ctx
= xmlXPathNewContext(response
);
9392 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9396 result
= xmlXPathEvalExpression(
9397 (outgoing_direction
) ?
9398 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9399 "isds:dmRecords/isds:dmRecord" :
9400 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9401 "isds:dmRecords/isds:dmRecord",
9408 /* Fill output arguments in */
9409 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9410 struct isds_envelope
*envelope
;
9411 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9413 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9414 /* Create new message */
9415 item
= calloc(1, sizeof(*item
));
9420 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9421 item
->data
= calloc(1, sizeof(struct isds_message
));
9423 isds_list_free(&item
);
9428 /* Extract envelope data */
9429 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9431 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9433 isds_list_free(&item
);
9437 /* Attach extracted envelope */
9438 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9440 /* Append new message into the list */
9442 *messages
= last_item
= item
;
9444 last_item
->next
= item
;
9449 if (number
) *number
= count
;
9453 isds_list_free(messages
);
9457 xmlXPathFreeObject(result
);
9458 xmlXPathFreeContext(xpath_ctx
);
9462 xmlFreeDoc(response
);
9463 xmlFreeNode(request
);
9466 isds_log(ILF_ISDS
, ILL_DEBUG
,
9467 (outgoing_direction
) ?
9468 _("GetListOfSentMessages request processed by server "
9469 "successfully.\n") :
9470 _("GetListOfReceivedMessages request processed by server "
9473 #else /* not HAVE_LIBCURL */
9480 /* Get list of outgoing (already sent) messages.
9481 * Any criterion argument can be NULL, if you don't care about it.
9482 * @context is session context. Must not be NULL.
9483 * @from_time is minimal time and date of message sending inclusive.
9484 * @to_time is maximal time and date of message sending inclusive
9485 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9486 * @status_filter is bit field of isds_message_status values. Use special
9487 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9488 * all values, you can use bit-wise arithmetic if you want.)
9489 * @offset is index of first message we are interested in. First message is 1.
9490 * Set to 0 (or 1) if you don't care.
9491 * @number is maximal length of list you want to get as input value, outputs
9492 * number of messages matching these criteria. Can be NULL if you don't care
9493 * (applies to output value either).
9494 * @messages is automatically reallocated list of isds_message's. Be ware that
9495 * it returns only brief overview (envelope and some other fields) about each
9496 * message, not the complete message. FIXME: Specify exact fields.
9497 * The list is sorted by delivery time in ascending order.
9498 * Use NULL if you don't care about the meta data (useful if you want to know
9499 * only the @number). If you provide &NULL, list will be allocated on heap,
9500 * if you provide pointer to non-NULL, list will be freed automatically at
9501 * first. Also in case of error the list will be NULLed.
9502 * @return IE_SUCCESS or appropriate error code. */
9503 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9504 const struct timeval
*from_time
, const struct timeval
*to_time
,
9505 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9506 const unsigned long int offset
, unsigned long int *number
,
9507 struct isds_list
**messages
) {
9509 return isds_get_list_of_messages(
9511 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9517 /* Get list of incoming (addressed to you) messages.
9518 * Any criterion argument can be NULL, if you don't care about it.
9519 * @context is session context. Must not be NULL.
9520 * @from_time is minimal time and date of message sending inclusive.
9521 * @to_time is maximal time and date of message sending inclusive
9522 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9523 * @status_filter is bit field of isds_message_status values. Use special
9524 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9525 * all values, you can use bit-wise arithmetic if you want.)
9526 * @offset is index of first message we are interested in. First message is 1.
9527 * Set to 0 (or 1) if you don't care.
9528 * @number is maximal length of list you want to get as input value, outputs
9529 * number of messages matching these criteria. Can be NULL if you don't care
9530 * (applies to output value either).
9531 * @messages is automatically reallocated list of isds_message's. Be ware that
9532 * it returns only brief overview (envelope and some other fields) about each
9533 * message, not the complete message. FIXME: Specify exact fields.
9534 * Use NULL if you don't care about the meta data (useful if you want to know
9535 * only the @number). If you provide &NULL, list will be allocated on heap,
9536 * if you provide pointer to non-NULL, list will be freed automatically at
9537 * first. Also in case of error the list will be NULLed.
9538 * @return IE_SUCCESS or appropriate error code. */
9539 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9540 const struct timeval
*from_time
, const struct timeval
*to_time
,
9541 const long int *dmRecipientOrgUnitNum
,
9542 const unsigned int status_filter
,
9543 const unsigned long int offset
, unsigned long int *number
,
9544 struct isds_list
**messages
) {
9546 return isds_get_list_of_messages(
9548 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9554 /* Get list of sent message state changes.
9555 * Any criterion argument can be NULL, if you don't care about it.
9556 * @context is session context. Must not be NULL.
9557 * @from_time is minimal time and date of status changes inclusive
9558 * @to_time is maximal time and date of status changes inclusive
9559 * @changed_states is automatically reallocated list of
9560 * isds_message_status_change's. If you provide &NULL, list will be allocated
9561 * on heap, if you provide pointer to non-NULL, list will be freed
9562 * automatically at first. Also in case of error the list will be NULLed.
9563 * XXX: The list item ordering is not specified.
9564 * XXX: Server provides only `recent' changes.
9565 * @return IE_SUCCESS or appropriate error code. */
9566 isds_error
isds_get_list_of_sent_message_state_changes(
9567 struct isds_ctx
*context
,
9568 const struct timeval
*from_time
, const struct timeval
*to_time
,
9569 struct isds_list
**changed_states
) {
9571 isds_error err
= IE_SUCCESS
;
9573 xmlNsPtr isds_ns
= NULL
;
9574 xmlNodePtr request
= NULL
, node
;
9575 xmlDocPtr response
= NULL
;
9576 xmlXPathContextPtr xpath_ctx
= NULL
;
9577 xmlXPathObjectPtr result
= NULL
;
9578 xmlChar
*string
= NULL
;
9582 if (!context
) return IE_INVALID_CONTEXT
;
9583 zfree(context
->long_message
);
9585 /* Free former message list if any */
9586 isds_list_free(changed_states
);
9589 /* Check if connection is established
9590 * TODO: This check should be done downstairs. */
9591 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9593 /* Build GetMessageStateChanges request */
9594 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9596 isds_log_message(context
,
9597 _("Could not build GetMessageStateChanges request"));
9600 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9602 isds_log_message(context
, _("Could not create ISDS name space"));
9603 xmlFreeNode(request
);
9606 xmlSetNs(request
, isds_ns
);
9610 err
= timeval2timestring(from_time
, &string
);
9611 if (err
) goto leave
;
9613 INSERT_STRING(request
, "dmFromTime", string
);
9617 err
= timeval2timestring(to_time
, &string
);
9618 if (err
) goto leave
;
9620 INSERT_STRING(request
, "dmToTime", string
);
9625 err
= send_destroy_request_check_response(context
,
9626 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9627 &response
, NULL
, NULL
);
9628 if (err
) goto leave
;
9632 xpath_ctx
= xmlXPathNewContext(response
);
9637 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9641 result
= xmlXPathEvalExpression(
9642 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9643 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9649 /* Fill output arguments in */
9650 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9651 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9653 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9654 /* Create new status change */
9655 item
= calloc(1, sizeof(*item
));
9661 (void(*)(void**)) &isds_message_status_change_free
;
9663 /* Extract message status change */
9664 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9665 err
= extract_StateChangesRecord(context
,
9666 (struct isds_message_status_change
**) &item
->data
,
9669 isds_list_free(&item
);
9673 /* Append new message status change into the list */
9674 if (!*changed_states
) {
9675 *changed_states
= last_item
= item
;
9677 last_item
->next
= item
;
9685 isds_list_free(changed_states
);
9689 xmlXPathFreeObject(result
);
9690 xmlXPathFreeContext(xpath_ctx
);
9691 xmlFreeDoc(response
);
9692 xmlFreeNode(request
);
9695 isds_log(ILF_ISDS
, ILL_DEBUG
,
9696 _("GetMessageStateChanges request processed by server "
9697 "successfully.\n"));
9698 #else /* not HAVE_LIBCURL */
9706 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9708 * @context is session context
9709 * @service is ISDS WS service handler
9710 * @service_name is name of SERVICE_DM_OPERATIONS
9711 * @message_id is message ID to send as service argument to ISDS
9712 * @response is reallocated server SOAP body response as XML document
9713 * @raw_response is reallocated bit stream with response body. Use
9714 * NULL if you don't care
9715 * @raw_response_length is size of @raw_response in bytes
9716 * @code is reallocated ISDS status code
9717 * @status_message is reallocated ISDS status message
9718 * @return error coded from lower layer, context message will be set up
9720 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9721 const isds_service service
, const xmlChar
*service_name
,
9722 const char *message_id
,
9723 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9724 xmlChar
**code
, xmlChar
**status_message
) {
9726 isds_error err
= IE_SUCCESS
;
9727 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9728 xmlNodePtr request
= NULL
, node
;
9729 xmlNsPtr isds_ns
= NULL
;
9731 if (!context
) return IE_INVALID_CONTEXT
;
9732 if (!service_name
|| !message_id
) return IE_INVAL
;
9733 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9734 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9736 /* Free output argument */
9737 xmlFreeDoc(*response
); *response
= NULL
;
9738 if (raw_response
) zfree(*raw_response
);
9740 zfree(*status_message
);
9743 /* Check if connection is established
9744 * TODO: This check should be done downstairs. */
9745 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9747 service_name_locale
= _isds_utf82locale((char*)service_name
);
9748 message_id_locale
= _isds_utf82locale(message_id
);
9749 if (!service_name_locale
|| !message_id_locale
) {
9755 request
= xmlNewNode(NULL
, service_name
);
9757 isds_printf_message(context
,
9758 _("Could not build %s request for %s message ID"),
9759 service_name_locale
, message_id_locale
);
9763 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9765 isds_log_message(context
, _("Could not create ISDS name space"));
9769 xmlSetNs(request
, isds_ns
);
9772 /* Add requested ID */
9773 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9774 if (err
) goto leave
;
9775 INSERT_STRING(request
, "dmID", message_id
);
9778 isds_log(ILF_ISDS
, ILL_DEBUG
,
9779 _("Sending %s request for %s message ID to ISDS\n"),
9780 service_name_locale
, message_id_locale
);
9783 err
= _isds(context
, service
, request
, response
,
9784 raw_response
, raw_response_length
);
9785 xmlFreeNode(request
); request
= NULL
;
9788 isds_log(ILF_ISDS
, ILL_DEBUG
,
9789 _("Processing ISDS response on %s request failed\n"),
9790 service_name_locale
);
9794 /* Check for response status */
9795 err
= isds_response_status(context
, service
, *response
,
9796 code
, status_message
, NULL
);
9798 isds_log(ILF_ISDS
, ILL_DEBUG
,
9799 _("ISDS response on %s request is missing status\n"),
9800 service_name_locale
);
9804 /* Request processed, but nothing found */
9805 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9806 char *code_locale
= _isds_utf82locale((char*) *code
);
9807 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9808 isds_log(ILF_ISDS
, ILL_DEBUG
,
9809 _("Server refused %s request for %s message ID "
9810 "(code=%s, message=%s)\n"),
9811 service_name_locale
, message_id_locale
,
9812 code_locale
, status_message_locale
);
9813 isds_log_message(context
, status_message_locale
);
9815 free(status_message_locale
);
9821 free(message_id_locale
);
9822 free(service_name_locale
);
9823 xmlFreeNode(request
);
9828 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9829 * signed data and free ISDS response.
9830 * @context is session context
9831 * @message_id is UTF-8 encoded message ID for logging purpose
9832 * @response is parsed XML document. It will be freed and NULLed in the middle
9833 * of function run to save memory. This is not guaranteed in case of error.
9834 * @request_name is name of ISDS request used to construct response root
9835 * element name and for logging purpose.
9836 * @raw is reallocated output buffer with DER encoded CMS data
9837 * @raw_length is size of @raw buffer in bytes
9838 * @returns standard error codes, in case of error, @raw will be freed and
9839 * NULLed, @response sometimes. */
9840 static isds_error
find_extract_signed_data_free_response(
9841 struct isds_ctx
*context
, const xmlChar
*message_id
,
9842 xmlDocPtr
*response
, const xmlChar
*request_name
,
9843 void **raw
, size_t *raw_length
) {
9845 isds_error err
= IE_SUCCESS
;
9846 char *xpath_expression
= NULL
;
9847 xmlXPathContextPtr xpath_ctx
= NULL
;
9848 xmlXPathObjectPtr result
= NULL
;
9849 char *encoded_structure
= NULL
;
9851 if (!context
) return IE_INVALID_CONTEXT
;
9852 if (!raw
) return IE_INVAL
;
9854 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9857 /* Build XPath expression */
9858 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9859 "Response/isds:dmSignature");
9860 if (!xpath_expression
) return IE_NOMEM
;
9863 xpath_ctx
= xmlXPathNewContext(*response
);
9868 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9872 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9877 /* Empty response */
9878 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9879 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9880 isds_printf_message(context
,
9881 _("Server did not return any signed data for message ID `%s' "
9883 message_id_locale
, request_name
);
9884 free(message_id_locale
);
9888 /* More responses */
9889 if (result
->nodesetval
->nodeNr
> 1) {
9890 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9891 isds_printf_message(context
,
9892 _("Server did return more signed data for message ID `%s' "
9894 message_id_locale
, request_name
);
9895 free(message_id_locale
);
9900 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9902 /* Extract PKCS#7 structure */
9903 EXTRACT_STRING(".", encoded_structure
);
9904 if (!encoded_structure
) {
9905 isds_log_message(context
, _("dmSignature element is empty"));
9908 /* Here we have delivery info as standalone CMS in encoded_structure.
9909 * We don't need any other data, free them: */
9910 xmlXPathFreeObject(result
); result
= NULL
;
9911 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9912 xmlFreeDoc(*response
); *response
= NULL
;
9915 /* Decode PKCS#7 to DER format */
9916 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9917 if (*raw_length
== (size_t) -1) {
9918 isds_log_message(context
,
9919 _("Error while Base64-decoding PKCS#7 structure"));
9930 free(encoded_structure
);
9931 xmlXPathFreeObject(result
);
9932 xmlXPathFreeContext(xpath_ctx
);
9933 free(xpath_expression
);
9937 #endif /* HAVE_LIBCURL */
9940 /* Download incoming message envelope identified by ID.
9941 * @context is session context
9942 * @message_id is message identifier (you can get them from
9943 * isds_get_list_of_received_messages())
9944 * @message is automatically reallocated message retrieved from ISDS.
9945 * It will miss documents per se. Use isds_get_received_message(), if you are
9946 * interested in documents (content) too.
9947 * Returned hash and timestamp require documents to be verifiable. */
9948 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9949 const char *message_id
, struct isds_message
**message
) {
9951 isds_error err
= IE_SUCCESS
;
9953 xmlDocPtr response
= NULL
;
9954 xmlChar
*code
= NULL
, *status_message
= NULL
;
9955 xmlXPathContextPtr xpath_ctx
= NULL
;
9956 xmlXPathObjectPtr result
= NULL
;
9959 if (!context
) return IE_INVALID_CONTEXT
;
9960 zfree(context
->long_message
);
9962 /* Free former message if any */
9963 if (!message
) return IE_INVAL
;
9964 isds_message_free(message
);
9967 /* Do request and check for success */
9968 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9969 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9970 &response
, NULL
, NULL
, &code
, &status_message
);
9971 if (err
) goto leave
;
9974 xpath_ctx
= xmlXPathNewContext(response
);
9979 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9983 result
= xmlXPathEvalExpression(
9984 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9985 "isds:dmReturnedMessageEnvelope",
9991 /* Empty response */
9992 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9993 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9994 isds_printf_message(context
,
9995 _("Server did not return any envelope for ID `%s' "
9996 "on MessageEnvelopeDownload request"), message_id_locale
);
9997 free(message_id_locale
);
10001 /* More envelops */
10002 if (result
->nodesetval
->nodeNr
> 1) {
10003 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10004 isds_printf_message(context
,
10005 _("Server did return more envelopes for ID `%s' "
10006 "on MessageEnvelopeDownload request"), message_id_locale
);
10007 free(message_id_locale
);
10012 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10014 /* Extract the envelope (= message without documents, hence 0) */
10015 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10016 if (err
) goto leave
;
10018 /* Save XML blob */
10019 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10020 &(*message
)->raw_length
);
10024 isds_message_free(message
);
10027 xmlXPathFreeObject(result
);
10028 xmlXPathFreeContext(xpath_ctx
);
10031 free(status_message
);
10032 if (!*message
|| !(*message
)->xml
) {
10033 xmlFreeDoc(response
);
10037 isds_log(ILF_ISDS
, ILL_DEBUG
,
10038 _("MessageEnvelopeDownload request processed by server "
10041 #else /* not HAVE_LIBCURL */
10048 /* Load delivery info of any format from buffer.
10049 * @context is session context
10050 * @raw_type advertises format of @buffer content. Only delivery info types
10052 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10053 * retrieve such data from message->raw after calling
10054 * isds_get_signed_delivery_info().
10055 * @length is length of buffer in bytes.
10056 * @message is automatically reallocated message parsed from @buffer.
10057 * @strategy selects how buffer will be attached into raw isds_message member.
10059 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10060 const isds_raw_type raw_type
,
10061 const void *buffer
, const size_t length
,
10062 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10064 isds_error err
= IE_SUCCESS
;
10065 message_ns_type message_ns
;
10066 xmlDocPtr message_doc
= NULL
;
10067 xmlXPathContextPtr xpath_ctx
= NULL
;
10068 xmlXPathObjectPtr result
= NULL
;
10069 void *xml_stream
= NULL
;
10070 size_t xml_stream_length
= 0;
10072 if (!context
) return IE_INVALID_CONTEXT
;
10073 zfree(context
->long_message
);
10074 if (!message
) return IE_INVAL
;
10075 isds_message_free(message
);
10076 if (!buffer
) return IE_INVAL
;
10079 /* Select buffer format and extract XML from CMS*/
10080 switch (raw_type
) {
10081 case RAWTYPE_DELIVERYINFO
:
10082 message_ns
= MESSAGE_NS_UNSIGNED
;
10083 xml_stream
= (void *) buffer
;
10084 xml_stream_length
= length
;
10087 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10088 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10089 xml_stream
= (void *) buffer
;
10090 xml_stream_length
= length
;
10093 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10094 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10095 err
= _isds_extract_cms_data(context
, buffer
, length
,
10096 &xml_stream
, &xml_stream_length
);
10097 if (err
) goto leave
;
10101 isds_log_message(context
, _("Bad raw delivery representation type"));
10106 isds_log(ILF_ISDS
, ILL_DEBUG
,
10107 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10108 xml_stream_length
, xml_stream
);
10110 /* Convert delivery info XML stream into XPath context */
10111 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10112 if (!message_doc
) {
10116 xpath_ctx
= xmlXPathNewContext(message_doc
);
10121 /* XXX: Name spaces mangled for signed delivery info:
10122 * http://isds.czechpoint.cz/v20/delivery:
10124 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10126 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10127 * <p:dmID>170272</p:dmID>
10130 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10132 * </q:dmEvents>...</q:dmEvents>
10134 * </q:GetDeliveryInfoResponse>
10136 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10140 result
= xmlXPathEvalExpression(
10141 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10147 /* Empty delivery info */
10148 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10149 isds_printf_message(context
,
10150 _("XML document is not sisds:dmDelivery document"));
10154 /* More delivery info's */
10155 if (result
->nodesetval
->nodeNr
> 1) {
10156 isds_printf_message(context
,
10157 _("XML document has more sisds:dmDelivery elements"));
10161 /* One delivery info */
10162 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10164 /* Extract the envelope (= message without documents, hence 0).
10165 * XXX: extract_TReturnedMessage() can obtain attachments size,
10166 * but delivery info carries none. It's coded as option elements,
10167 * so it should work. */
10168 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10169 if (err
) goto leave
;
10171 /* Extract events */
10172 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10173 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10174 if (err
) { err
= IE_ERROR
; goto leave
; }
10175 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10176 if (err
) goto leave
;
10178 /* Append raw CMS structure into message */
10179 (*message
)->raw_type
= raw_type
;
10180 switch (strategy
) {
10181 case BUFFER_DONT_STORE
:
10184 (*message
)->raw
= malloc(length
);
10185 if (!(*message
)->raw
) {
10189 memcpy((*message
)->raw
, buffer
, length
);
10190 (*message
)->raw_length
= length
;
10193 (*message
)->raw
= (void *) buffer
;
10194 (*message
)->raw_length
= length
;
10203 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10204 isds_message_free(message
);
10207 xmlXPathFreeObject(result
);
10208 xmlXPathFreeContext(xpath_ctx
);
10209 if (!*message
|| !(*message
)->xml
) {
10210 xmlFreeDoc(message_doc
);
10212 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10215 isds_log(ILF_ISDS
, ILL_DEBUG
,
10216 _("Delivery info loaded successfully.\n"));
10221 /* Download signed delivery info-sheet of given message identified by ID.
10222 * @context is session context
10223 * @message_id is message identifier (you can get them from
10224 * isds_get_list_of_{sent,received}_messages())
10225 * @message is automatically reallocated message retrieved from ISDS.
10226 * It will miss documents per se. Use isds_get_signed_received_message(),
10227 * if you are interested in documents (content). OTOH, only this function
10228 * can get list events message has gone through. */
10229 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10230 const char *message_id
, struct isds_message
**message
) {
10232 isds_error err
= IE_SUCCESS
;
10234 xmlDocPtr response
= NULL
;
10235 xmlChar
*code
= NULL
, *status_message
= NULL
;
10237 size_t raw_length
= 0;
10240 if (!context
) return IE_INVALID_CONTEXT
;
10241 zfree(context
->long_message
);
10243 /* Free former message if any */
10244 if (!message
) return IE_INVAL
;
10245 isds_message_free(message
);
10248 /* Do request and check for success */
10249 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10250 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10251 &response
, NULL
, NULL
, &code
, &status_message
);
10252 if (err
) goto leave
;
10254 /* Find signed delivery info, extract it into raw and maybe free
10256 err
= find_extract_signed_data_free_response(context
,
10257 (xmlChar
*)message_id
, &response
,
10258 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10259 if (err
) goto leave
;
10261 /* Parse delivery info */
10262 err
= isds_load_delivery_info(context
,
10263 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10264 message
, BUFFER_MOVE
);
10265 if (err
) goto leave
;
10271 isds_message_free(message
);
10276 free(status_message
);
10277 xmlFreeDoc(response
);
10280 isds_log(ILF_ISDS
, ILL_DEBUG
,
10281 _("GetSignedDeliveryInfo request processed by server "
10284 #else /* not HAVE_LIBCURL */
10291 /* Download delivery info-sheet of given message identified by ID.
10292 * @context is session context
10293 * @message_id is message identifier (you can get them from
10294 * isds_get_list_of_{sent,received}_messages())
10295 * @message is automatically reallocated message retrieved from ISDS.
10296 * It will miss documents per se. Use isds_get_received_message(), if you are
10297 * interested in documents (content). OTOH, only this function can get list
10298 * of events message has gone through. */
10299 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10300 const char *message_id
, struct isds_message
**message
) {
10302 isds_error err
= IE_SUCCESS
;
10304 xmlDocPtr response
= NULL
;
10305 xmlChar
*code
= NULL
, *status_message
= NULL
;
10306 xmlNodePtr delivery_node
= NULL
;
10308 size_t raw_length
= 0;
10311 if (!context
) return IE_INVALID_CONTEXT
;
10312 zfree(context
->long_message
);
10314 /* Free former message if any */
10315 if (!message
) return IE_INVAL
;
10316 isds_message_free(message
);
10319 /* Do request and check for success */
10320 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10321 BAD_CAST
"GetDeliveryInfo", message_id
,
10322 &response
, NULL
, NULL
, &code
, &status_message
);
10323 if (err
) goto leave
;
10326 /* Serialize delivery info */
10327 delivery_node
= xmlDocGetRootElement(response
);
10328 if (!delivery_node
) {
10329 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10330 isds_printf_message(context
,
10331 _("Server did not return any delivery info for ID `%s' "
10332 "on GetDeliveryInfo request"), message_id_locale
);
10333 free(message_id_locale
);
10337 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10338 if (err
) goto leave
;
10340 /* Parse delivery info */
10341 /* TODO: Here we parse the response second time. We could single delivery
10342 * parser from isds_load_delivery_info() to make things faster. */
10343 err
= isds_load_delivery_info(context
,
10344 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10345 message
, BUFFER_MOVE
);
10346 if (err
) goto leave
;
10353 isds_message_free(message
);
10358 free(status_message
);
10359 xmlFreeDoc(response
);
10362 isds_log(ILF_ISDS
, ILL_DEBUG
,
10363 _("GetDeliveryInfo request processed by server "
10366 #else /* not HAVE_LIBCURL */
10373 /* Download incoming message identified by ID.
10374 * @context is session context
10375 * @message_id is message identifier (you can get them from
10376 * isds_get_list_of_received_messages())
10377 * @message is automatically reallocated message retrieved from ISDS */
10378 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10379 const char *message_id
, struct isds_message
**message
) {
10381 isds_error err
= IE_SUCCESS
;
10383 xmlDocPtr response
= NULL
;
10384 void *xml_stream
= NULL
;
10385 size_t xml_stream_length
;
10386 xmlChar
*code
= NULL
, *status_message
= NULL
;
10387 xmlXPathContextPtr xpath_ctx
= NULL
;
10388 xmlXPathObjectPtr result
= NULL
;
10389 char *phys_path
= NULL
;
10390 size_t phys_start
, phys_end
;
10393 if (!context
) return IE_INVALID_CONTEXT
;
10394 zfree(context
->long_message
);
10396 /* Free former message if any */
10397 if (NULL
== message
) return IE_INVAL
;
10398 if (message
) isds_message_free(message
);
10401 /* Do request and check for success */
10402 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10403 BAD_CAST
"MessageDownload", message_id
,
10404 &response
, &xml_stream
, &xml_stream_length
,
10405 &code
, &status_message
);
10406 if (err
) goto leave
;
10409 xpath_ctx
= xmlXPathNewContext(response
);
10414 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10418 result
= xmlXPathEvalExpression(
10419 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10425 /* Empty response */
10426 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10427 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10428 isds_printf_message(context
,
10429 _("Server did not return any message for ID `%s' "
10430 "on MessageDownload request"), message_id_locale
);
10431 free(message_id_locale
);
10435 /* More messages */
10436 if (result
->nodesetval
->nodeNr
> 1) {
10437 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10438 isds_printf_message(context
,
10439 _("Server did return more messages for ID `%s' "
10440 "on MessageDownload request"), message_id_locale
);
10441 free(message_id_locale
);
10446 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10448 /* Extract the message */
10449 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10450 if (err
) goto leave
;
10452 /* Locate raw XML blob */
10453 phys_path
= strdup(
10454 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10455 PHYSXML_ELEMENT_SEPARATOR
10456 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10457 PHYSXML_ELEMENT_SEPARATOR
10458 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10464 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10465 phys_path
, &phys_start
, &phys_end
);
10468 isds_log_message(context
,
10469 _("Substring with isds:MessageDownloadResponse element "
10470 "could not be located in raw SOAP message"));
10473 /* Save XML blob */
10474 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10475 &(*message)->raw_length);*/
10476 /* TODO: Store name space declarations from ancestors */
10477 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10478 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10479 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10480 (*message
)->raw
= malloc((*message
)->raw_length
);
10481 if (!(*message
)->raw
) {
10485 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10490 isds_message_free(message
);
10495 xmlXPathFreeObject(result
);
10496 xmlXPathFreeContext(xpath_ctx
);
10499 free(status_message
);
10501 if (!*message
|| !(*message
)->xml
) {
10502 xmlFreeDoc(response
);
10506 isds_log(ILF_ISDS
, ILL_DEBUG
,
10507 _("MessageDownload request processed by server "
10510 #else /* not HAVE_LIBCURL */
10517 /* Load message of any type from buffer.
10518 * @context is session context
10519 * @raw_type defines content type of @buffer. Only message types are allowed.
10520 * @buffer is message raw representation. Format (CMS, plain signed,
10521 * message direction) is defined in @raw_type. You can retrieve such data
10522 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10523 * @length is length of buffer in bytes.
10524 * @message is automatically reallocated message parsed from @buffer.
10525 * @strategy selects how buffer will be attached into raw isds_message member.
10527 isds_error
isds_load_message(struct isds_ctx
*context
,
10528 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10529 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10531 isds_error err
= IE_SUCCESS
;
10532 void *xml_stream
= NULL
;
10533 size_t xml_stream_length
= 0;
10534 message_ns_type message_ns
;
10535 xmlDocPtr message_doc
= NULL
;
10536 xmlXPathContextPtr xpath_ctx
= NULL
;
10537 xmlXPathObjectPtr result
= NULL
;
10539 if (!context
) return IE_INVALID_CONTEXT
;
10540 zfree(context
->long_message
);
10541 if (!message
) return IE_INVAL
;
10542 isds_message_free(message
);
10543 if (!buffer
) return IE_INVAL
;
10546 /* Select buffer format and extract XML from CMS*/
10547 switch (raw_type
) {
10548 case RAWTYPE_INCOMING_MESSAGE
:
10549 message_ns
= MESSAGE_NS_UNSIGNED
;
10550 xml_stream
= (void *) buffer
;
10551 xml_stream_length
= length
;
10554 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10555 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10556 xml_stream
= (void *) buffer
;
10557 xml_stream_length
= length
;
10560 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10561 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10562 err
= _isds_extract_cms_data(context
, buffer
, length
,
10563 &xml_stream
, &xml_stream_length
);
10564 if (err
) goto leave
;
10567 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10568 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10569 xml_stream
= (void *) buffer
;
10570 xml_stream_length
= length
;
10573 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10574 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10575 err
= _isds_extract_cms_data(context
, buffer
, length
,
10576 &xml_stream
, &xml_stream_length
);
10577 if (err
) goto leave
;
10581 isds_log_message(context
, _("Bad raw message representation type"));
10586 isds_log(ILF_ISDS
, ILL_DEBUG
,
10587 _("Loading message:\n%.*s\nEnd of message\n"),
10588 xml_stream_length
, xml_stream
);
10590 /* Convert messages XML stream into XPath context */
10591 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10592 if (!message_doc
) {
10596 xpath_ctx
= xmlXPathNewContext(message_doc
);
10601 /* XXX: Standard name space for unsigned incoming direction:
10602 * http://isds.czechpoint.cz/v20/
10604 * XXX: Name spaces mangled for signed outgoing direction:
10605 * http://isds.czechpoint.cz/v20/SentMessage:
10607 * <q:MessageDownloadResponse
10608 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10609 * <q:dmReturnedMessage>
10610 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10611 * <p:dmID>151916</p:dmID>
10614 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10616 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10617 * </q:dmReturnedMessage>
10618 * </q:MessageDownloadResponse>
10620 * XXX: Name spaces mangled for signed incoming direction:
10621 * http://isds.czechpoint.cz/v20/message:
10623 * <q:MessageDownloadResponse
10624 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10625 * <q:dmReturnedMessage>
10626 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10627 * <p:dmID>151916</p:dmID>
10630 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10632 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10633 * </q:dmReturnedMessage>
10634 * </q:MessageDownloadResponse>
10636 * Stupidity of ISDS developers is unlimited */
10637 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10641 result
= xmlXPathEvalExpression(
10642 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10648 /* Empty message */
10649 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10650 isds_printf_message(context
,
10651 _("XML document does not contain "
10652 "sisds:dmReturnedMessage element"));
10656 /* More messages */
10657 if (result
->nodesetval
->nodeNr
> 1) {
10658 isds_printf_message(context
,
10659 _("XML document has more sisds:dmReturnedMessage elements"));
10664 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10666 /* Extract the message */
10667 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10668 if (err
) goto leave
;
10670 /* Append raw buffer into message */
10671 (*message
)->raw_type
= raw_type
;
10672 switch (strategy
) {
10673 case BUFFER_DONT_STORE
:
10676 (*message
)->raw
= malloc(length
);
10677 if (!(*message
)->raw
) {
10681 memcpy((*message
)->raw
, buffer
, length
);
10682 (*message
)->raw_length
= length
;
10685 (*message
)->raw
= (void *) buffer
;
10686 (*message
)->raw_length
= length
;
10696 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10697 isds_message_free(message
);
10700 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10701 xmlXPathFreeObject(result
);
10702 xmlXPathFreeContext(xpath_ctx
);
10703 if (!*message
|| !(*message
)->xml
) {
10704 xmlFreeDoc(message_doc
);
10708 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10713 /* Determine type of raw message or delivery info according some heuristics.
10714 * It does not validate the raw blob.
10715 * @context is session context
10716 * @raw_type returns content type of @buffer. Valid only if exit code of this
10717 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10718 * reallocated memory.
10719 * @buffer is message raw representation.
10720 * @length is length of buffer in bytes. */
10721 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10722 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10724 void *xml_stream
= NULL
;
10725 size_t xml_stream_length
= 0;
10726 xmlDocPtr document
= NULL
;
10727 xmlNodePtr root
= NULL
;
10729 if (!context
) return IE_INVALID_CONTEXT
;
10730 zfree(context
->long_message
);
10731 if (length
== 0 || !buffer
) return IE_INVAL
;
10732 if (!raw_type
) return IE_INVAL
;
10735 err
= _isds_extract_cms_data(context
, buffer
, length
,
10736 &xml_stream
, &xml_stream_length
);
10738 xml_stream
= (void *) buffer
;
10739 xml_stream_length
= (size_t) length
;
10744 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10746 isds_printf_message(context
,
10747 _("Could not parse data as XML document"));
10752 /* Get root element */
10753 root
= xmlDocGetRootElement(document
);
10755 isds_printf_message(context
,
10756 _("XML document is missing root element"));
10761 if (!root
->ns
|| !root
->ns
->href
) {
10762 isds_printf_message(context
,
10763 _("Root element does not belong to any name space"));
10768 /* Test name space */
10769 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10770 if (xml_stream
== buffer
)
10771 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10773 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10774 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10775 if (xml_stream
== buffer
)
10776 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10778 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10779 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10780 if (xml_stream
== buffer
)
10781 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10783 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10784 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10785 if (xml_stream
!= buffer
) {
10786 isds_printf_message(context
,
10787 _("Document in ISDS name space is encapsulated into CMS" ));
10789 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10790 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10791 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10792 *raw_type
= RAWTYPE_DELIVERYINFO
;
10794 isds_printf_message(context
,
10795 _("Unknown root element in ISDS name space"));
10799 isds_printf_message(context
,
10800 _("Unknown name space"));
10805 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10806 xmlFreeDoc(document
);
10811 /* Download signed incoming/outgoing message identified by ID.
10812 * @context is session context
10813 * @output is true for outgoing message, false for incoming message
10814 * @message_id is message identifier (you can get them from
10815 * isds_get_list_of_{sent,received}_messages())
10816 * @message is automatically reallocated message retrieved from ISDS. The raw
10817 * member will be filled with PKCS#7 structure in DER format. */
10818 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10819 const _Bool outgoing
, const char *message_id
,
10820 struct isds_message
**message
) {
10822 isds_error err
= IE_SUCCESS
;
10824 xmlDocPtr response
= NULL
;
10825 xmlChar
*code
= NULL
, *status_message
= NULL
;
10826 xmlXPathContextPtr xpath_ctx
= NULL
;
10827 xmlXPathObjectPtr result
= NULL
;
10828 char *encoded_structure
= NULL
;
10830 size_t raw_length
= 0;
10833 if (!context
) return IE_INVALID_CONTEXT
;
10834 zfree(context
->long_message
);
10835 if (!message
) return IE_INVAL
;
10836 isds_message_free(message
);
10839 /* Do request and check for success */
10840 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10841 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10842 BAD_CAST
"SignedMessageDownload",
10843 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10844 if (err
) goto leave
;
10846 /* Find signed message, extract it into raw and maybe free
10848 err
= find_extract_signed_data_free_response(context
,
10849 (xmlChar
*)message_id
, &response
,
10850 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10851 BAD_CAST
"SignedMessageDownload",
10852 &raw
, &raw_length
);
10853 if (err
) goto leave
;
10855 /* Parse message */
10856 err
= isds_load_message(context
,
10857 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10858 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10859 raw
, raw_length
, message
, BUFFER_MOVE
);
10860 if (err
) goto leave
;
10866 isds_message_free(message
);
10869 free(encoded_structure
);
10870 xmlXPathFreeObject(result
);
10871 xmlXPathFreeContext(xpath_ctx
);
10875 free(status_message
);
10876 xmlFreeDoc(response
);
10879 isds_log(ILF_ISDS
, ILL_DEBUG
,
10881 _("SignedSentMessageDownload request processed by server "
10882 "successfully.\n") :
10883 _("SignedMessageDownload request processed by server "
10886 #else /* not HAVE_LIBCURL */
10893 /* Download signed incoming 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_received_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_received_message(struct isds_ctx
*context
,
10900 const char *message_id
, struct isds_message
**message
) {
10901 return isds_get_signed_message(context
, 0, message_id
, message
);
10905 /* Download signed outgoing message identified by ID.
10906 * @context is session context
10907 * @message_id is message identifier (you can get them from
10908 * isds_get_list_of_sent_messages())
10909 * @message is automatically reallocated message retrieved from ISDS. The raw
10910 * member will be filled with PKCS#7 structure in DER format. */
10911 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10912 const char *message_id
, struct isds_message
**message
) {
10913 return isds_get_signed_message(context
, 1, message_id
, message
);
10917 /* Get type and name of user who sent a message identified by ID.
10918 * @context is session context
10919 * @message_id is message identifier
10920 * @sender_type is pointer to automatically allocated type of sender detected
10921 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10922 * library or to the server, NULL will be returned. Pass NULL if you don't
10924 * @raw_sender_type is automatically reallocated UTF-8 string describing
10925 * sender type or NULL if not known to server. Pass NULL if you don't care.
10926 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10927 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10928 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10929 const char *message_id
, isds_sender_type
**sender_type
,
10930 char **raw_sender_type
, char **sender_name
) {
10931 isds_error err
= IE_SUCCESS
;
10933 xmlDocPtr response
= NULL
;
10934 xmlChar
*code
= NULL
, *status_message
= NULL
;
10935 xmlXPathContextPtr xpath_ctx
= NULL
;
10936 xmlXPathObjectPtr result
= NULL
;
10937 char *type_string
= NULL
;
10940 if (!context
) return IE_INVALID_CONTEXT
;
10941 zfree(context
->long_message
);
10942 if (sender_type
) zfree(*sender_type
);
10943 if (raw_sender_type
) zfree(*raw_sender_type
);
10944 if (sender_name
) zfree(*sender_name
);
10945 if (!message_id
) return IE_INVAL
;
10948 /* Do request and check for success */
10949 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10950 BAD_CAST
"GetMessageAuthor",
10951 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10952 if (err
) goto leave
;
10955 xpath_ctx
= xmlXPathNewContext(response
);
10960 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10964 result
= xmlXPathEvalExpression(
10965 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10970 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10971 isds_log_message(context
,
10972 _("Missing GetMessageAuthorResponse element"));
10976 if (result
->nodesetval
->nodeNr
> 1) {
10977 isds_log_message(context
,
10978 _("Multiple GetMessageAuthorResponse element"));
10982 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10983 xmlXPathFreeObject(result
); result
= NULL
;
10985 /* Fill output arguments in */
10986 EXTRACT_STRING("isds:userType", type_string
);
10987 if (NULL
!= type_string
) {
10988 if (NULL
!= sender_type
) {
10989 *sender_type
= calloc(1, sizeof(**sender_type
));
10990 if (NULL
== *sender_type
) {
10995 err
= string2isds_sender_type((xmlChar
*)type_string
,
10998 zfree(*sender_type
);
10999 if (err
== IE_ENUM
) {
11001 char *type_string_locale
= _isds_utf82locale(type_string
);
11002 isds_log(ILF_ISDS
, ILL_WARNING
,
11003 _("Unknown isds:userType value: %s"),
11004 type_string_locale
);
11005 free(type_string_locale
);
11010 if (NULL
!= sender_name
)
11011 EXTRACT_STRING("isds:authorName", *sender_name
);
11015 if (NULL
!= sender_type
) zfree(*sender_type
);
11016 zfree(type_string
);
11017 if (NULL
!= sender_name
) zfree(*sender_name
);
11019 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11021 xmlXPathFreeObject(result
);
11022 xmlXPathFreeContext(xpath_ctx
);
11025 free(status_message
);
11026 xmlFreeDoc(response
);
11029 isds_log(ILF_ISDS
, ILL_DEBUG
,
11030 _("GetMessageAuthor request processed by server "
11031 "successfully.\n"));
11032 #else /* not HAVE_LIBCURL */
11039 /* Retrieve hash of message identified by ID stored in ISDS.
11040 * @context is session context
11041 * @message_id is message identifier
11042 * @hash is automatically reallocated message hash downloaded from ISDS.
11043 * Message must exist in system and must not be deleted. */
11044 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11045 const char *message_id
, struct isds_hash
**hash
) {
11047 isds_error err
= IE_SUCCESS
;
11049 xmlDocPtr response
= NULL
;
11050 xmlChar
*code
= NULL
, *status_message
= NULL
;
11051 xmlXPathContextPtr xpath_ctx
= NULL
;
11052 xmlXPathObjectPtr result
= NULL
;
11055 if (!context
) return IE_INVALID_CONTEXT
;
11056 zfree(context
->long_message
);
11058 isds_hash_free(hash
);
11061 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11062 BAD_CAST
"VerifyMessage", message_id
,
11063 &response
, NULL
, NULL
, &code
, &status_message
);
11064 if (err
) goto leave
;
11068 xpath_ctx
= xmlXPathNewContext(response
);
11073 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11077 result
= xmlXPathEvalExpression(
11078 BAD_CAST
"/isds:VerifyMessageResponse",
11084 /* Empty response */
11085 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11086 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11087 isds_printf_message(context
,
11088 _("Server did not return any response for ID `%s' "
11089 "on VerifyMessage request"), message_id_locale
);
11090 free(message_id_locale
);
11094 /* More responses */
11095 if (result
->nodesetval
->nodeNr
> 1) {
11096 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11097 isds_printf_message(context
,
11098 _("Server did return more responses for ID `%s' "
11099 "on VerifyMessage request"), message_id_locale
);
11100 free(message_id_locale
);
11105 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11107 /* Extract the hash */
11108 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11112 isds_hash_free(hash
);
11115 xmlXPathFreeObject(result
);
11116 xmlXPathFreeContext(xpath_ctx
);
11119 free(status_message
);
11120 xmlFreeDoc(response
);
11123 isds_log(ILF_ISDS
, ILL_DEBUG
,
11124 _("VerifyMessage request processed by server "
11127 #else /* not HAVE_LIBCURL */
11134 /* Erase message specified by @message_id from long term storage. Other
11135 * message cannot be erased on user request.
11136 * @context is session context
11137 * @message_id is message identifier.
11138 * @incoming is true for incoming message, false for outgoing message.
11140 * IE_SUCCESS if message has ben removed
11141 * IE_INVAL if message does not exist in long term storage or message
11142 * belongs to different box
11143 * TODO: IE_NOEPRM if user has no permission to erase a message */
11144 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11145 const char *message_id
, _Bool incoming
) {
11146 isds_error err
= IE_SUCCESS
;
11148 xmlNodePtr request
= NULL
, node
;
11149 xmlNsPtr isds_ns
= NULL
;
11150 xmlDocPtr response
= NULL
;
11151 xmlChar
*code
= NULL
, *status_message
= NULL
;
11154 if (!context
) return IE_INVALID_CONTEXT
;
11155 zfree(context
->long_message
);
11156 if (NULL
== message_id
) return IE_INVAL
;
11158 /* Check if connection is established
11159 * TODO: This check should be done downstairs. */
11160 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11163 /* Build request */
11164 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11166 isds_log_message(context
,
11167 _("Could build EraseMessage request"));
11170 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11172 isds_log_message(context
, _("Could not create ISDS name space"));
11173 xmlFreeNode(request
);
11176 xmlSetNs(request
, isds_ns
);
11178 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11179 if (err
) goto leave
;
11180 INSERT_STRING(request
, "dmID", message_id
);
11182 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11186 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11187 "message ID %s to ISDS\n"), message_id
);
11188 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11189 xmlFreeNode(request
); request
= NULL
;
11192 isds_log(ILF_ISDS
, ILL_DEBUG
,
11193 _("Processing ISDS response on EraseMessage request "
11198 /* Check for response status */
11199 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11200 &code
, &status_message
, NULL
);
11202 isds_log(ILF_ISDS
, ILL_DEBUG
,
11203 _("ISDS response on EraseMessage request is missing "
11208 /* Check server status code */
11209 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11210 isds_log_message(context
, _("Message to erase belongs to other box"));
11212 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11213 isds_log_message(context
, _("Message to erase is not saved in "
11214 "long term storage or the direction does not match"));
11216 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11217 char *code_locale
= _isds_utf82locale((char*) code
);
11218 char *message_locale
= _isds_utf82locale((char*) status_message
);
11219 isds_log(ILF_ISDS
, ILL_DEBUG
,
11220 _("Server refused EraseMessage request "
11221 "(code=%s, message=%s)\n"),
11222 code_locale
, message_locale
);
11223 isds_log_message(context
, message_locale
);
11225 free(message_locale
);
11232 free(status_message
);
11233 xmlFreeDoc(response
);
11234 xmlFreeNode(request
);
11237 isds_log(ILF_ISDS
, ILL_DEBUG
,
11238 _("EraseMessage request processed by server "
11241 #else /* not HAVE_LIBCURL */
11248 /* Mark message as read. This is a transactional commit function to acknowledge
11249 * to ISDS the message has been downloaded and processed by client properly.
11250 * @context is session context
11251 * @message_id is message identifier. */
11252 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11253 const char *message_id
) {
11255 isds_error err
= IE_SUCCESS
;
11257 xmlDocPtr response
= NULL
;
11258 xmlChar
*code
= NULL
, *status_message
= NULL
;
11261 if (!context
) return IE_INVALID_CONTEXT
;
11262 zfree(context
->long_message
);
11265 /* Do request and check for success */
11266 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11267 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11268 &response
, NULL
, NULL
, &code
, &status_message
);
11271 free(status_message
);
11272 xmlFreeDoc(response
);
11275 isds_log(ILF_ISDS
, ILL_DEBUG
,
11276 _("MarkMessageAsDownloaded request processed by server "
11279 #else /* not HAVE_LIBCURL */
11286 /* Mark message as received by recipient. This is applicable only to
11287 * commercial message. Use envelope->dmType message member to distinguish
11288 * commercial message from government message. Government message is
11289 * received automatically (by law), commercial message on recipient request.
11290 * @context is session context
11291 * @message_id is message identifier. */
11292 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11293 const char *message_id
) {
11295 isds_error err
= IE_SUCCESS
;
11297 xmlDocPtr response
= NULL
;
11298 xmlChar
*code
= NULL
, *status_message
= NULL
;
11301 if (!context
) return IE_INVALID_CONTEXT
;
11302 zfree(context
->long_message
);
11305 /* Do request and check for success */
11306 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11307 BAD_CAST
"ConfirmDelivery", message_id
,
11308 &response
, NULL
, NULL
, &code
, &status_message
);
11311 free(status_message
);
11312 xmlFreeDoc(response
);
11315 isds_log(ILF_ISDS
, ILL_DEBUG
,
11316 _("ConfirmDelivery request processed by server "
11319 #else /* not HAVE_LIBCURL */
11326 /* Send document for authorized conversion into Czech POINT system.
11327 * This is public anonymous service, no log-in necessary. Special context is
11328 * used to reuse keep-a-live HTTPS connection.
11329 * @context is Czech POINT session context. DO NOT use context connected to
11330 * ISDS server. Use new context or context used by this function previously.
11331 * @document is document to convert. Only data, data_length, dmFileDescr and
11332 * is_xml members are significant. Be ware that not all document formats can be
11333 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11334 * @id is reallocated identifier assigned by Czech POINT system to
11335 * your document on submit. Use is to tell it to Czech POINT officer.
11336 * @date is reallocated document submit date (submitted documents
11337 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11339 isds_error
czp_convert_document(struct isds_ctx
*context
,
11340 const struct isds_document
*document
,
11341 char **id
, struct tm
**date
) {
11342 isds_error err
= IE_SUCCESS
;
11344 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11345 xmlNodePtr request
= NULL
, node
;
11346 xmlDocPtr response
= NULL
;
11348 xmlXPathContextPtr xpath_ctx
= NULL
;
11349 xmlXPathObjectPtr result
= NULL
;
11350 long int status
= -1;
11351 long int *status_ptr
= &status
;
11352 char *string
= NULL
;
11356 if (!context
) return IE_INVALID_CONTEXT
;
11357 zfree(context
->long_message
);
11358 if (!document
|| !id
|| !date
) return IE_INVAL
;
11360 if (document
->is_xml
) {
11361 isds_log_message(context
,
11362 _("XML documents cannot be submitted to conversion"));
11366 /* Free output arguments */
11371 /* Store configuration */
11372 context
->type
= CTX_TYPE_CZP
;
11373 free(context
->url
);
11374 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11375 if (!(context
->url
))
11378 /* Prepare CURL handle if not yet connected */
11379 if (!context
->curl
) {
11380 context
->curl
= curl_easy_init();
11381 if (!(context
->curl
))
11385 /* Build conversion request */
11386 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11388 isds_log_message(context
,
11389 _("Could not build Czech POINT conversion request"));
11392 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11394 isds_log_message(context
,
11395 _("Could not create Czech POINT deposit name space"));
11396 xmlFreeNode(request
);
11399 xmlSetNs(request
, deposit_ns
);
11401 /* Insert children. They are in empty namespace! */
11402 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11404 isds_log_message(context
, _("Could not create empty name space"));
11408 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11409 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11410 document
->dmFileDescr
);
11412 /* Document encoded in Base64 */
11413 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11414 document
->data
, document
->data_length
);
11415 if (err
) goto leave
;
11417 isds_log(ILF_ISDS
, ILL_DEBUG
,
11418 _("Submitting document for conversion into Czech POINT deposit"));
11420 /* Send conversion request */
11421 err
= _czp_czpdeposit(context
, request
, &response
);
11422 xmlFreeNode(request
); request
= NULL
;
11425 czp_do_close_connection(context
);
11430 /* Extract response */
11431 xpath_ctx
= xmlXPathNewContext(response
);
11436 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11440 result
= xmlXPathEvalExpression(
11441 BAD_CAST
"/deposit:saveDocumentResponse/return",
11447 /* Empty response */
11448 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11449 isds_printf_message(context
,
11450 _("Missing `return' element in Czech POINT deposit response"));
11454 /* More responses */
11455 if (result
->nodesetval
->nodeNr
> 1) {
11456 isds_printf_message(context
,
11457 _("Multiple `return' element in Czech POINT deposit response"));
11462 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11465 EXTRACT_LONGINT("status", status_ptr
, 1);
11467 EXTRACT_STRING("statusMsg", string
);
11468 char *string_locale
= _isds_utf82locale(string
);
11469 isds_printf_message(context
,
11470 _("Czech POINT deposit refused document for conversion "
11471 "(code=%ld, message=%s)"),
11472 status
, string_locale
);
11473 free(string_locale
);
11478 /* Get document ID */
11479 EXTRACT_STRING("documentID", *id
);
11481 /* Get submit date */
11482 EXTRACT_STRING("dateInserted", string
);
11484 *date
= calloc(1, sizeof(**date
));
11489 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11491 if (err
== IE_NOTSUP
) {
11493 char *string_locale
= _isds_utf82locale(string
);
11494 isds_printf_message(context
,
11495 _("Invalid dateInserted value: %s"), string_locale
);
11496 free(string_locale
);
11504 xmlXPathFreeObject(result
);
11505 xmlXPathFreeContext(xpath_ctx
);
11507 xmlFreeDoc(response
);
11508 xmlFreeNode(request
);
11511 char *id_locale
= _isds_utf82locale((char *) *id
);
11512 isds_log(ILF_ISDS
, ILL_DEBUG
,
11513 _("Document %s has been submitted for conversion "
11514 "to server successfully\n"), id_locale
);
11517 #else /* not HAVE_LIBCURL */
11524 /* Close possibly opened connection to Czech POINT document deposit.
11525 * @context is Czech POINT session context. */
11526 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11527 if (!context
) return IE_INVALID_CONTEXT
;
11528 zfree(context
->long_message
);
11530 return czp_do_close_connection(context
);
11537 /* Send request for new box creation in testing ISDS instance.
11538 * It's not possible to request for a production box currently, as it
11539 * communicates via e-mail.
11540 * XXX: This function does not work either. Server complains about invalid
11542 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11544 * @context is special session context for box creation request. DO NOT use
11545 * standard context as it could reveal your password. Use fresh new context or
11546 * context previously used by this function.
11547 * @box is box description to create including single primary user (in case of
11548 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11549 * ignored. It outputs box ID assigned by ISDS in dbID element.
11550 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11551 * box, or contact address of PFO box owner). The email member is mandatory as
11552 * it will be used to deliver credentials.
11553 * @former_names is former name of box owner. Pass NULL if you don't care.
11554 * @approval is optional external approval of box manipulation
11555 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11556 * NULL, if you don't care.*/
11557 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11558 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11559 const char *former_names
, const struct isds_approval
*approval
,
11560 char **refnumber
) {
11561 isds_error err
= IE_SUCCESS
;
11563 xmlNodePtr request
= NULL
;
11564 xmlDocPtr response
= NULL
;
11565 xmlXPathContextPtr xpath_ctx
= NULL
;
11566 xmlXPathObjectPtr result
= NULL
;
11570 if (!context
) return IE_INVALID_CONTEXT
;
11571 zfree(context
->long_message
);
11572 if (!box
) return IE_INVAL
;
11575 if (!box
->email
|| box
->email
[0] == '\0') {
11576 isds_log_message(context
, _("E-mail field is mandatory"));
11580 /* Scratch box ID */
11583 /* Store configuration */
11584 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11585 free(context
->url
);
11586 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11587 if (!(context
->url
))
11590 /* Prepare CURL handle if not yet connected */
11591 if (!context
->curl
) {
11592 context
->curl
= curl_easy_init();
11593 if (!(context
->curl
))
11597 /* Build CreateDataBox request */
11598 err
= build_CreateDBInput_request(context
,
11599 &request
, BAD_CAST
"CreateDataBox",
11600 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11601 if (err
) goto leave
;
11603 /* Send it to server and process response */
11604 err
= send_destroy_request_check_response(context
,
11605 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11606 &response
, (xmlChar
**) refnumber
, NULL
);
11607 if (err
) goto leave
;
11609 /* Extract box ID */
11610 xpath_ctx
= xmlXPathNewContext(response
);
11615 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11619 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11622 xmlXPathFreeObject(result
);
11623 xmlXPathFreeContext(xpath_ctx
);
11624 xmlFreeDoc(response
);
11625 xmlFreeNode(request
);
11628 isds_log(ILF_ISDS
, ILL_DEBUG
,
11629 _("CreateDataBox request processed by server successfully.\n"));
11631 #else /* not HAVE_LIBCURL */
11639 /* Submit CMS signed message to ISDS to verify its originality. This is
11640 * stronger form of isds_verify_message_hash() because ISDS does more checks
11641 * than simple one (potentialy old weak) hash comparison.
11642 * @context is session context
11643 * @message is memory with raw CMS signed message bit stream
11644 * @length is @message size in bytes
11646 * IE_SUCCESS if message originates in ISDS
11647 * IE_NOTEQUAL if message is unknown to ISDS
11648 * other code for other errors */
11649 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11650 const void *message
, size_t length
) {
11651 isds_error err
= IE_SUCCESS
;
11653 xmlNsPtr isds_ns
= NULL
;
11654 xmlNodePtr request
= NULL
;
11655 xmlDocPtr response
= NULL
;
11656 xmlXPathContextPtr xpath_ctx
= NULL
;
11657 xmlXPathObjectPtr result
= NULL
;
11658 _Bool
*authentic
= NULL
;
11661 if (!context
) return IE_INVALID_CONTEXT
;
11662 zfree(context
->long_message
);
11663 if (!message
|| length
== 0) return IE_INVAL
;
11666 /* Check if connection is established
11667 * TODO: This check should be done downstairs. */
11668 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11671 /* Build AuthenticateMessage request */
11672 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11674 isds_log_message(context
,
11675 _("Could not build AuthenticateMessage request"));
11678 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11680 isds_log_message(context
, _("Could not create ISDS name space"));
11681 xmlFreeNode(request
);
11684 xmlSetNs(request
, isds_ns
);
11686 /* Insert Base64 encoded message */
11687 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11689 if (err
) goto leave
;
11691 /* Send request to server and process response */
11692 err
= send_destroy_request_check_response(context
,
11693 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11694 &response
, NULL
, NULL
);
11695 if (err
) goto leave
;
11698 /* ISDS has decided */
11699 xpath_ctx
= xmlXPathNewContext(response
);
11704 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11709 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11712 isds_log_message(context
,
11713 _("Server did not return any response on "
11714 "AuthenticateMessage request"));
11719 isds_log(ILF_ISDS
, ILL_DEBUG
,
11720 _("ISDS authenticated the message successfully\n"));
11722 isds_log_message(context
, _("ISDS does not know the message"));
11729 xmlXPathFreeObject(result
);
11730 xmlXPathFreeContext(xpath_ctx
);
11732 xmlFreeDoc(response
);
11733 xmlFreeNode(request
);
11734 #else /* not HAVE_LIBCURL */
11742 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11743 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11745 * @context is session context
11746 * @input_data is memory with raw CMS signed message or delivery info bit
11747 * stream to re-sign
11748 * @input_length is @input_data size in bytes
11749 * @output_data is pointer to auto-allocated memory where to store re-signed
11750 * input data blob. Caller must free it.
11751 * @output_data is pointer where to store @output_data size in bytes
11752 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11753 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11755 * IE_SUCCESS if CMS blob has been re-signed successfully
11756 * other code for other errors */
11757 isds_error
isds_resign_message(struct isds_ctx
*context
,
11758 const void *input_data
, size_t input_length
,
11759 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11760 isds_error err
= IE_SUCCESS
;
11762 xmlNsPtr isds_ns
= NULL
;
11763 xmlNodePtr request
= NULL
;
11764 xmlDocPtr response
= NULL
;
11765 xmlXPathContextPtr xpath_ctx
= NULL
;
11766 xmlXPathObjectPtr result
= NULL
;
11767 char *string
= NULL
;
11768 const xmlChar
*codes
[] = {
11775 const char *meanings
[] = {
11777 "Message is not original",
11778 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11779 "Time stamp could not been generated in time"
11781 const isds_error errors
[] = {
11787 struct code_map_isds_error map
= {
11789 .meanings
= meanings
,
11794 if (NULL
!= output_data
) *output_data
= NULL
;
11795 if (NULL
!= output_length
) *output_length
= 0;
11796 if (NULL
!= valid_to
) *valid_to
= NULL
;
11798 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11799 zfree(context
->long_message
);
11800 if (NULL
== input_data
|| 0 == input_length
) {
11801 isds_log_message(context
, _("Empty CMS blob on input"));
11804 if (NULL
== output_data
|| NULL
== output_length
) {
11805 isds_log_message(context
,
11806 _("NULL pointer provided for output CMS blob"));
11811 /* Check if connection is established
11812 * TODO: This check should be done downstairs. */
11813 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11816 /* Build Re-signISDSDocument request */
11817 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11819 isds_log_message(context
,
11820 _("Could not build Re-signISDSDocument request"));
11823 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11825 isds_log_message(context
, _("Could not create ISDS name space"));
11826 xmlFreeNode(request
);
11829 xmlSetNs(request
, isds_ns
);
11831 /* Insert Base64 encoded CMS blob */
11832 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11833 input_data
, input_length
);
11834 if (err
) goto leave
;
11836 /* Send request to server and process response */
11837 err
= send_destroy_request_check_response(context
,
11838 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11839 &response
, NULL
, &map
);
11840 if (err
) goto leave
;
11843 /* Extract re-signed data */
11844 xpath_ctx
= xmlXPathNewContext(response
);
11849 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11853 result
= xmlXPathEvalExpression(
11854 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11859 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11860 isds_log_message(context
,
11861 _("Missing Re-signISDSDocumentResponse element"));
11865 if (result
->nodesetval
->nodeNr
> 1) {
11866 isds_log_message(context
,
11867 _("Multiple Re-signISDSDocumentResponse element"));
11871 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11872 xmlXPathFreeObject(result
); result
= NULL
;
11874 EXTRACT_STRING("isds:dmResultDoc", string
);
11875 /* Decode non-empty data */
11876 if (NULL
!= string
&& string
[0] != '\0') {
11877 *output_length
= _isds_b64decode(string
, output_data
);
11878 if (*output_length
== (size_t) -1) {
11879 isds_log_message(context
,
11880 _("Error while Base64-decoding re-signed data"));
11885 isds_log_message(context
, _("Server did not send re-signed data"));
11891 if (NULL
!= valid_to
) {
11892 /* Get time stamp expiration date */
11893 EXTRACT_STRING("isds:dmValidTo", string
);
11894 if (NULL
!= string
) {
11895 *valid_to
= calloc(1, sizeof(**valid_to
));
11900 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11902 if (err
== IE_NOTSUP
) {
11904 char *string_locale
= _isds_utf82locale(string
);
11905 isds_printf_message(context
,
11906 _("Invalid dmValidTo value: %s"), string_locale
);
11907 free(string_locale
);
11917 xmlXPathFreeObject(result
);
11918 xmlXPathFreeContext(xpath_ctx
);
11920 xmlFreeDoc(response
);
11921 xmlFreeNode(request
);
11922 #else /* not HAVE_LIBCURL */
11929 #undef INSERT_ELEMENT
11930 #undef CHECK_FOR_STRING_LENGTH
11931 #undef INSERT_STRING_ATTRIBUTE
11932 #undef INSERT_ULONGINTNOPTR
11933 #undef INSERT_ULONGINT
11934 #undef INSERT_LONGINT
11935 #undef INSERT_BOOLEAN
11936 #undef INSERT_SCALAR_BOOLEAN
11937 #undef INSERT_STRING
11938 #undef INSERT_STRING_WITH_NS
11939 #undef EXTRACT_STRING_ATTRIBUTE
11940 #undef EXTRACT_ULONGINT
11941 #undef EXTRACT_LONGINT
11942 #undef EXTRACT_BOOLEAN
11943 #undef EXTRACT_STRING
11946 /* Compute hash of message from raw representation and store it into envelope.
11947 * Original hash structure will be destroyed in envelope.
11948 * @context is session context
11949 * @message is message carrying raw XML message blob
11950 * @algorithm is desired hash algorithm to use */
11951 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11952 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11953 isds_error err
= IE_SUCCESS
;
11955 void *xml_stream
= NULL
;
11956 size_t xml_stream_length
;
11957 size_t phys_start
, phys_end
;
11958 char *phys_path
= NULL
;
11959 struct isds_hash
*new_hash
= NULL
;
11962 if (!context
) return IE_INVALID_CONTEXT
;
11963 zfree(context
->long_message
);
11964 if (!message
) return IE_INVAL
;
11966 if (!message
->raw
) {
11967 isds_log_message(context
,
11968 _("Message does not carry raw representation"));
11972 switch (message
->raw_type
) {
11973 case RAWTYPE_INCOMING_MESSAGE
:
11975 xml_stream
= message
->raw
;
11976 xml_stream_length
= message
->raw_length
;
11979 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11980 nsuri
= SISDS_INCOMING_NS
;
11981 xml_stream
= message
->raw
;
11982 xml_stream_length
= message
->raw_length
;
11985 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11986 nsuri
= SISDS_INCOMING_NS
;
11987 err
= _isds_extract_cms_data(context
,
11988 message
->raw
, message
->raw_length
,
11989 &xml_stream
, &xml_stream_length
);
11990 if (err
) goto leave
;
11993 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11994 nsuri
= SISDS_OUTGOING_NS
;
11995 xml_stream
= message
->raw
;
11996 xml_stream_length
= message
->raw_length
;
11999 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
12000 nsuri
= SISDS_OUTGOING_NS
;
12001 err
= _isds_extract_cms_data(context
,
12002 message
->raw
, message
->raw_length
,
12003 &xml_stream
, &xml_stream_length
);
12004 if (err
) goto leave
;
12008 isds_log_message(context
, _("Bad raw representation type"));
12014 /* XXX: Hash is computed from original string representing isds:dmDm
12015 * subtree. That means no encoding, white space, xmlns attributes changes.
12016 * In other words, input for hash can be invalid XML stream. */
12017 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12018 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12019 PHYSXML_ELEMENT_SEPARATOR
,
12020 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12021 PHYSXML_ELEMENT_SEPARATOR
12022 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12026 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12027 phys_path
, &phys_start
, &phys_end
);
12030 isds_log_message(context
,
12031 _("Substring with isds:dmDM element could not be located "
12032 "in raw message"));
12038 new_hash
= calloc(1, sizeof(*new_hash
));
12043 new_hash
->algorithm
= algorithm
;
12044 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12047 isds_log_message(context
, _("Could not compute message hash"));
12051 /* Save computed hash */
12052 if (!message
->envelope
) {
12053 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12054 if (!message
->envelope
) {
12059 isds_hash_free(&message
->envelope
->hash
);
12060 message
->envelope
->hash
= new_hash
;
12064 isds_hash_free(&new_hash
);
12068 if (xml_stream
!= message
->raw
) free(xml_stream
);
12073 /* Compare two hashes.
12074 * @h1 is first hash
12075 * @h2 is another hash
12077 * IE_SUCCESS if hashes equal
12078 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12079 * IE_ENUM if not comparable, but both structures defined
12080 * IE_INVAL if some of the structures are undefined (NULL)
12081 * IE_ERROR if internal error occurs */
12082 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12083 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12084 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12085 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12086 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12087 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12089 for (size_t i
= 0; i
< h1
->length
; i
++) {
12090 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12091 return IE_NOTEQUAL
;
12097 /* Check message has gone through ISDS by comparing message hash stored in
12098 * ISDS and locally computed hash. You must provide message with valid raw
12099 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12100 * This is convenient wrapper for isds_download_message_hash(),
12101 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12102 * @context is session context
12103 * @message is message with valid raw and envelope member; envelope->hash
12104 * member will be changed during function run. Use envelope on heap only.
12106 * IE_SUCCESS if message originates in ISDS
12107 * IE_NOTEQUAL if message is unknown to ISDS
12108 * other code for other errors */
12109 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12110 struct isds_message
*message
) {
12111 isds_error err
= IE_SUCCESS
;
12112 struct isds_hash
*downloaded_hash
= NULL
;
12114 if (!context
) return IE_INVALID_CONTEXT
;
12115 zfree(context
->long_message
);
12116 if (!message
) return IE_INVAL
;
12118 if (!message
->envelope
) {
12119 isds_log_message(context
,
12120 _("Given message structure is missing envelope"));
12123 if (!message
->raw
) {
12124 isds_log_message(context
,
12125 _("Given message structure is missing raw representation"));
12129 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12131 if (err
) goto leave
;
12133 err
= isds_compute_message_hash(context
, message
,
12134 downloaded_hash
->algorithm
);
12135 if (err
) goto leave
;
12137 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12140 isds_hash_free(&downloaded_hash
);
12145 /* Search for document by document ID in list of documents. IDs are compared
12147 * @documents is list of isds_documents
12148 * @id is document identifier
12149 * @return first matching document or NULL. */
12150 const struct isds_document
*isds_find_document_by_id(
12151 const struct isds_list
*documents
, const char *id
) {
12152 const struct isds_list
*item
;
12153 const struct isds_document
*document
;
12155 for (item
= documents
; item
; item
= item
->next
) {
12156 document
= (struct isds_document
*) item
->data
;
12157 if (!document
) continue;
12159 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12167 /* Normalize @mime_type to be proper MIME type.
12168 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12169 * guess regular MIME type (e.g. "application/pdf").
12170 * @mime_type is UTF-8 encoded MIME type to fix
12171 * @return original @mime_type if no better interpretation exists, or
12172 * constant static UTF-8 encoded string with proper MIME type. */
12173 const char *isds_normalize_mime_type(const char *mime_type
) {
12174 if (!mime_type
) return NULL
;
12176 for (size_t offset
= 0;
12177 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12179 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12180 extension_map_mime
[offset
]))
12181 return (const char *) extension_map_mime
[offset
+ 1];
12188 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12189 struct isds_message **message);
12190 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12191 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12192 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12193 struct isds_address **address);
12195 int isds_message_free(struct isds_message **message);
12196 int isds_address_free(struct isds_address **address);
12200 /* Makes known all relevant namespaces to given XPath context
12201 * @xpath_ctx is XPath context
12202 * @message_ns selects proper message name space. Unsigned and signed
12203 * messages and delivery info's differ in prefix and URI. */
12204 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12205 const message_ns_type message_ns
) {
12206 const xmlChar
*message_namespace
= NULL
;
12208 if (!xpath_ctx
) return IE_ERROR
;
12210 switch(message_ns
) {
12212 message_namespace
= BAD_CAST ISDS1_NS
; break;
12213 case MESSAGE_NS_UNSIGNED
:
12214 message_namespace
= BAD_CAST ISDS_NS
; break;
12215 case MESSAGE_NS_SIGNED_INCOMING
:
12216 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12217 case MESSAGE_NS_SIGNED_OUTGOING
:
12218 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12219 case MESSAGE_NS_SIGNED_DELIVERY
:
12220 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12225 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12227 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12229 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12231 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12233 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12235 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))