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 extended with relevant
2960 * tdbPersonalOwnerinfo members in current node into structure
2961 * @context is ISDS context
2962 * @address is automatically reallocated address structure. If no member
2963 * value is found, will be freed.
2964 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2966 * In case of error @address will be freed. */
2967 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2968 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2969 isds_error err
= IE_SUCCESS
;
2970 xmlXPathObjectPtr result
= NULL
;
2972 if (!context
) return IE_INVALID_CONTEXT
;
2973 if (!address
) return IE_INVAL
;
2974 isds_Address_free(address
);
2975 if (!xpath_ctx
) return IE_INVAL
;
2978 *address
= calloc(1, sizeof(**address
));
2984 EXTRACT_LONGINT("isds:adCode", (*address
)->adCode
, 0);
2985 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2986 EXTRACT_STRING("isds:adDistrict", (*address
)->adDistrict
);
2987 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2988 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2989 EXTRACT_STRING("isds:adNumberInMunicipality",
2990 (*address
)->adNumberInMunicipality
);
2991 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2992 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2994 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2995 !(*address
)->adNumberInStreet
&&
2996 !(*address
)->adNumberInMunicipality
&&
2997 !(*address
)->adZipCode
&& !(*address
)->adState
)
2998 isds_Address_free(address
);
3001 if (err
) isds_Address_free(address
);
3002 xmlXPathFreeObject(result
);
3007 /* Find and convert isds:biDate element in current node into structure
3008 * @context is ISDS context
3009 * @biDate is automatically reallocated birth date structure. If no member
3010 * value is found, will be freed.
3011 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3013 * In case of error @biDate will be freed. */
3014 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3015 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3016 isds_error err
= IE_SUCCESS
;
3017 xmlXPathObjectPtr result
= NULL
;
3018 char *string
= NULL
;
3020 if (!context
) return IE_INVALID_CONTEXT
;
3021 if (!biDate
) return IE_INVAL
;
3023 if (!xpath_ctx
) return IE_INVAL
;
3025 EXTRACT_STRING("isds:biDate", string
);
3027 *biDate
= calloc(1, sizeof(**biDate
));
3032 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3034 if (err
== IE_NOTSUP
) {
3036 char *string_locale
= _isds_utf82locale(string
);
3037 isds_printf_message(context
,
3038 _("Invalid isds:biDate value: %s"), string_locale
);
3039 free(string_locale
);
3046 if (err
) zfree(*biDate
);
3048 xmlXPathFreeObject(result
);
3053 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3054 * @context is ISDS context
3055 * @db_owner_info is automatically reallocated box owner info structure
3056 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3057 * XSD:tdbPersonalOwenerInfo element
3058 * In case of error @db_owner_info will be freed. */
3059 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3060 struct isds_DbOwnerInfo
**db_owner_info
,
3061 xmlXPathContextPtr xpath_ctx
) {
3062 isds_error err
= IE_SUCCESS
;
3063 xmlXPathObjectPtr result
= NULL
;
3064 char *string
= NULL
;
3066 if (!context
) return IE_INVALID_CONTEXT
;
3067 if (!db_owner_info
) return IE_INVAL
;
3068 isds_DbOwnerInfo_free(db_owner_info
);
3069 if (!xpath_ctx
) return IE_INVAL
;
3072 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3073 if (!*db_owner_info
) {
3078 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3080 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info
)->aifoIsds
);
3082 EXTRACT_STRING("isds:dbType", string
);
3084 (*db_owner_info
)->dbType
=
3085 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3086 if (!(*db_owner_info
)->dbType
) {
3090 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3092 zfree((*db_owner_info
)->dbType
);
3093 if (err
== IE_ENUM
) {
3095 char *string_locale
= _isds_utf82locale(string
);
3096 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3098 free(string_locale
);
3105 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3107 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3109 if (err
) goto leave
;
3111 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3113 (*db_owner_info
)->birthInfo
=
3114 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3115 if (!(*db_owner_info
)->birthInfo
) {
3119 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3121 if (err
) goto leave
;
3122 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3123 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3124 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3125 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3126 !(*db_owner_info
)->birthInfo
->biCity
&&
3127 !(*db_owner_info
)->birthInfo
->biCounty
&&
3128 !(*db_owner_info
)->birthInfo
->biState
)
3129 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3131 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3132 if (err
) goto leave
;
3134 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3135 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3136 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3137 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3138 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3140 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3142 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3143 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3144 (*db_owner_info
)->dbOpenAddressing
);
3147 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3149 xmlXPathFreeObject(result
);
3154 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3155 * @context is session context
3156 * @owner is libisds structure with box description.
3157 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3158 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3159 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3160 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3162 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3163 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3164 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3165 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3167 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3168 const struct isds_DbOwnerInfo
*owner
, _Bool pfo_subtype
,
3169 xmlNodePtr db_owner_info
) {
3171 isds_error err
= IE_SUCCESS
;
3173 xmlChar
*string
= NULL
;
3174 const xmlChar
*type_string
= NULL
;
3176 if (!context
) return IE_INVALID_CONTEXT
;
3177 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3180 /* XXX: All the elements except email and telNumber are mandatory. */
3181 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3182 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3185 INSERT_BOOLEAN(db_owner_info
, "aifoIsds", owner
->aifoIsds
);
3190 if (owner
->dbType
) {
3191 type_string
= isds_DbType2string(*(owner
->dbType
));
3193 isds_printf_message(context
, _("Invalid dbType value: %d"),
3199 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3201 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3204 INSERT_STRING(db_owner_info
, "pnFirstName",
3205 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnFirstName
);
3206 INSERT_STRING(db_owner_info
, "pnMiddleName",
3207 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnMiddleName
);
3208 INSERT_STRING(db_owner_info
, "pnLastName",
3209 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnLastName
);
3211 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3212 (NULL
== owner
->personName
) ? NULL
:
3213 owner
->personName
->pnLastNameAtBirth
);
3215 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3218 if (NULL
!= owner
->birthInfo
&& NULL
!= owner
->birthInfo
->biDate
) {
3219 err
= tm2datestring(owner
->birthInfo
->biDate
, &string
);
3220 if (err
) goto leave
;
3222 INSERT_STRING(db_owner_info
, "biDate", string
);
3225 INSERT_STRING(db_owner_info
, "biCity",
3226 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCity
);
3227 INSERT_STRING(db_owner_info
, "biCounty",
3228 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCounty
);
3229 INSERT_STRING(db_owner_info
, "biState",
3230 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biState
);
3233 INSERT_LONGINT(db_owner_info
, "adCode",
3234 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCode
,
3237 INSERT_STRING(db_owner_info
, "adCity",
3238 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCity
);
3240 INSERT_STRING(db_owner_info
, "adDistrict",
3241 (NULL
== owner
->address
) ? NULL
: owner
->address
->adDistrict
);
3243 INSERT_STRING(db_owner_info
, "adStreet",
3244 (NULL
== owner
->address
) ? NULL
: owner
->address
->adStreet
);
3245 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3246 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInStreet
);
3247 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3248 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInMunicipality
);
3249 INSERT_STRING(db_owner_info
, "adZipCode",
3250 (NULL
== owner
->address
) ? NULL
: owner
->address
->adZipCode
);
3251 INSERT_STRING(db_owner_info
, "adState",
3252 (NULL
== owner
->address
) ? NULL
: owner
->address
->adState
);
3254 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3257 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3258 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3260 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3261 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3263 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3264 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3266 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3268 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3269 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3270 owner
->dbOpenAddressing
);
3279 /* Convert XSD:tDbUserInfo XML tree into structure
3280 * @context is ISDS context
3281 * @db_user_info is automatically reallocated user info structure
3282 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3283 * In case of error @db_user_info will be freed. */
3284 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3285 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3286 isds_error err
= IE_SUCCESS
;
3287 xmlXPathObjectPtr result
= NULL
;
3288 char *string
= NULL
;
3290 if (!context
) return IE_INVALID_CONTEXT
;
3291 if (!db_user_info
) return IE_INVAL
;
3292 isds_DbUserInfo_free(db_user_info
);
3293 if (!xpath_ctx
) return IE_INVAL
;
3296 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3297 if (!*db_user_info
) {
3302 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info
)->aifo_ticket
, 0);
3304 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3306 EXTRACT_STRING("isds:userType", string
);
3308 (*db_user_info
)->userType
=
3309 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3310 if (!(*db_user_info
)->userType
) {
3314 err
= string2isds_UserType((xmlChar
*)string
,
3315 (*db_user_info
)->userType
);
3317 zfree((*db_user_info
)->userType
);
3318 if (err
== IE_ENUM
) {
3320 char *string_locale
= _isds_utf82locale(string
);
3321 isds_printf_message(context
,
3322 _("Unknown isds:userType value: %s"), string_locale
);
3323 free(string_locale
);
3330 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3332 (*db_user_info
)->personName
=
3333 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3334 if (!(*db_user_info
)->personName
) {
3339 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3341 if (err
) goto leave
;
3343 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3344 if (err
) goto leave
;
3346 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3347 if (err
) goto leave
;
3349 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3350 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3352 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3353 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3354 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3356 /* ???: Default value is "CZ" according specification. Should we provide
3358 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3361 if (err
) isds_DbUserInfo_free(db_user_info
);
3363 xmlXPathFreeObject(result
);
3368 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3369 * @context is session context
3370 * @user is libisds structure with user description
3371 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3372 * @db_user_info is XML element of XSD:tDbUserInfo */
3373 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3374 const struct isds_DbUserInfo
*user
, _Bool honor_aifo_ticket
,
3375 xmlNodePtr db_user_info
) {
3377 isds_error err
= IE_SUCCESS
;
3379 xmlAttrPtr attribute_node
;
3380 xmlChar
*string
= NULL
;
3382 if (!context
) return IE_INVALID_CONTEXT
;
3383 if (!user
|| !db_user_info
) return IE_INVAL
;
3385 /* Build XSD:tDbUserInfo */
3387 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3388 * allow it everywhere. */
3389 if (honor_aifo_ticket
&& user
->aifo_ticket
) {
3390 INSERT_STRING_ATTRIBUTE(db_user_info
, "AIFOTicket", user
->aifo_ticket
);
3393 if (user
->personName
) {
3394 INSERT_STRING(db_user_info
, "pnFirstName",
3395 user
->personName
->pnFirstName
);
3396 INSERT_STRING(db_user_info
, "pnMiddleName",
3397 user
->personName
->pnMiddleName
);
3398 INSERT_STRING(db_user_info
, "pnLastName",
3399 user
->personName
->pnLastName
);
3400 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3401 user
->personName
->pnLastNameAtBirth
);
3403 if (user
->address
) {
3404 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3405 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3406 INSERT_STRING(db_user_info
, "adNumberInStreet",
3407 user
->address
->adNumberInStreet
);
3408 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3409 user
->address
->adNumberInMunicipality
);
3410 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3411 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3414 if (!tm2datestring(user
->biDate
, &string
))
3415 INSERT_STRING(db_user_info
, "biDate", string
);
3418 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3419 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3422 if (user
->userType
) {
3423 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3425 isds_printf_message(context
, _("Invalid userType value: %d"),
3430 INSERT_STRING(db_user_info
, "userType", type_string
);
3433 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3434 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3435 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3436 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3437 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3438 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3439 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3440 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3441 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3449 /* Convert XSD:tPDZRec XML tree into structure
3450 * @context is ISDS context
3451 * @permission is automatically reallocated commercial permission structure
3452 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3453 * In case of error @permission will be freed. */
3454 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3455 struct isds_commercial_permission
**permission
,
3456 xmlXPathContextPtr xpath_ctx
) {
3457 isds_error err
= IE_SUCCESS
;
3458 xmlXPathObjectPtr result
= NULL
;
3459 char *string
= NULL
;
3461 if (!context
) return IE_INVALID_CONTEXT
;
3462 if (!permission
) return IE_INVAL
;
3463 isds_commercial_permission_free(permission
);
3464 if (!xpath_ctx
) return IE_INVAL
;
3467 *permission
= calloc(1, sizeof(**permission
));
3473 EXTRACT_STRING("isds:PDZType", string
);
3475 err
= string2isds_payment_type((xmlChar
*)string
,
3476 &(*permission
)->type
);
3478 if (err
== IE_ENUM
) {
3480 char *string_locale
= _isds_utf82locale(string
);
3481 isds_printf_message(context
,
3482 _("Unknown isds:PDZType value: %s"), string_locale
);
3483 free(string_locale
);
3490 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3491 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3493 EXTRACT_STRING("isds:PDZExpire", string
);
3495 err
= timestring2timeval((xmlChar
*) string
,
3496 &((*permission
)->expiration
));
3498 char *string_locale
= _isds_utf82locale(string
);
3499 if (err
== IE_DATE
) err
= IE_ISDS
;
3500 isds_printf_message(context
,
3501 _("Could not convert PDZExpire as ISO time: %s"),
3503 free(string_locale
);
3509 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3510 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3513 if (err
) isds_commercial_permission_free(permission
);
3515 xmlXPathFreeObject(result
);
3520 /* Convert XSD:tCiRecord XML tree into structure
3521 * @context is ISDS context
3522 * @event is automatically reallocated commercial credit event structure
3523 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3524 * In case of error @event will be freed. */
3525 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3526 struct isds_credit_event
**event
,
3527 xmlXPathContextPtr xpath_ctx
) {
3528 isds_error err
= IE_SUCCESS
;
3529 xmlXPathObjectPtr result
= NULL
;
3530 char *string
= NULL
;
3531 long int *number_ptr
;
3533 if (!context
) return IE_INVALID_CONTEXT
;
3534 if (!event
) return IE_INVAL
;
3535 isds_credit_event_free(event
);
3536 if (!xpath_ctx
) return IE_INVAL
;
3539 *event
= calloc(1, sizeof(**event
));
3545 EXTRACT_STRING("isds:ciEventTime", string
);
3547 err
= timestring2timeval((xmlChar
*) string
,
3550 char *string_locale
= _isds_utf82locale(string
);
3551 if (err
== IE_DATE
) err
= IE_ISDS
;
3552 isds_printf_message(context
,
3553 _("Could not convert ciEventTime as ISO time: %s"),
3555 free(string_locale
);
3561 EXTRACT_STRING("isds:ciEventType", string
);
3563 err
= string2isds_credit_event_type((xmlChar
*)string
,
3566 if (err
== IE_ENUM
) {
3568 char *string_locale
= _isds_utf82locale(string
);
3569 isds_printf_message(context
,
3570 _("Unknown isds:ciEventType value: %s"), string_locale
);
3571 free(string_locale
);
3578 number_ptr
= &((*event
)->credit_change
);
3579 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3580 number_ptr
= &(*event
)->new_credit
;
3581 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3583 switch((*event
)->type
) {
3584 case ISDS_CREDIT_CHARGED
:
3585 EXTRACT_STRING("isds:ciTransID",
3586 (*event
)->details
.charged
.transaction
);
3588 case ISDS_CREDIT_DISCHARGED
:
3589 EXTRACT_STRING("isds:ciTransID",
3590 (*event
)->details
.discharged
.transaction
);
3592 case ISDS_CREDIT_MESSAGE_SENT
:
3593 EXTRACT_STRING("isds:ciRecipientID",
3594 (*event
)->details
.message_sent
.recipient
);
3595 EXTRACT_STRING("isds:ciPDZID",
3596 (*event
)->details
.message_sent
.message_id
);
3598 case ISDS_CREDIT_STORAGE_SET
:
3599 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3600 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3601 EXTRACT_DATE("isds:ciNewFrom",
3602 (*event
)->details
.storage_set
.new_valid_from
);
3603 EXTRACT_DATE("isds:ciNewTo",
3604 (*event
)->details
.storage_set
.new_valid_to
);
3605 EXTRACT_LONGINT("isds:ciOldCapacity",
3606 (*event
)->details
.storage_set
.old_capacity
, 0);
3607 EXTRACT_DATE("isds:ciOldFrom",
3608 (*event
)->details
.storage_set
.old_valid_from
);
3609 EXTRACT_DATE("isds:ciOldTo",
3610 (*event
)->details
.storage_set
.old_valid_to
);
3611 EXTRACT_STRING("isds:ciDoneBy",
3612 (*event
)->details
.storage_set
.initiator
);
3614 case ISDS_CREDIT_EXPIRED
:
3619 if (err
) isds_credit_event_free(event
);
3621 xmlXPathFreeObject(result
);
3626 #endif /* HAVE_LIBCURL */
3629 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3630 * isds_envelope structure. The envelope is automatically allocated but not
3631 * reallocated. The date are just appended into envelope structure.
3632 * @context is ISDS context
3633 * @envelope is automatically allocated message envelope structure
3634 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3635 * In case of error @envelope will be freed. */
3636 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3637 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3638 isds_error err
= IE_SUCCESS
;
3639 xmlXPathObjectPtr result
= NULL
;
3641 if (!context
) return IE_INVALID_CONTEXT
;
3642 if (!envelope
) return IE_INVAL
;
3643 if (!xpath_ctx
) return IE_INVAL
;
3647 /* Allocate envelope */
3648 *envelope
= calloc(1, sizeof(**envelope
));
3654 /* Else free former data */
3655 zfree((*envelope
)->dmSenderOrgUnit
);
3656 zfree((*envelope
)->dmSenderOrgUnitNum
);
3657 zfree((*envelope
)->dbIDRecipient
);
3658 zfree((*envelope
)->dmRecipientOrgUnit
);
3659 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3660 zfree((*envelope
)->dmToHands
);
3661 zfree((*envelope
)->dmAnnotation
);
3662 zfree((*envelope
)->dmRecipientRefNumber
);
3663 zfree((*envelope
)->dmSenderRefNumber
);
3664 zfree((*envelope
)->dmRecipientIdent
);
3665 zfree((*envelope
)->dmSenderIdent
);
3666 zfree((*envelope
)->dmLegalTitleLaw
);
3667 zfree((*envelope
)->dmLegalTitleYear
);
3668 zfree((*envelope
)->dmLegalTitleSect
);
3669 zfree((*envelope
)->dmLegalTitlePar
);
3670 zfree((*envelope
)->dmLegalTitlePoint
);
3671 zfree((*envelope
)->dmPersonalDelivery
);
3672 zfree((*envelope
)->dmAllowSubstDelivery
);
3675 /* Extract envelope elements added by sender or ISDS
3676 * (XSD: gMessageEnvelopeSub type) */
3677 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3678 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3679 (*envelope
)->dmSenderOrgUnitNum
, 0);
3680 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3681 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3682 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3683 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3684 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3685 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3686 EXTRACT_STRING("isds:dmRecipientRefNumber",
3687 (*envelope
)->dmRecipientRefNumber
);
3688 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3689 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3690 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3692 /* Extract envelope elements regarding law reference */
3693 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3694 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3695 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3696 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3697 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3699 /* Extract envelope other elements */
3700 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3701 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3702 (*envelope
)->dmAllowSubstDelivery
);
3705 if (err
) isds_envelope_free(envelope
);
3706 xmlXPathFreeObject(result
);
3712 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3713 * isds_envelope structure. The envelope is automatically allocated but not
3714 * reallocated. The date are just appended into envelope structure.
3715 * @context is ISDS context
3716 * @envelope is automatically allocated message envelope structure
3717 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3718 * In case of error @envelope will be freed. */
3719 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3720 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3721 isds_error err
= IE_SUCCESS
;
3722 xmlXPathObjectPtr result
= NULL
;
3724 if (!context
) return IE_INVALID_CONTEXT
;
3725 if (!envelope
) return IE_INVAL
;
3726 if (!xpath_ctx
) return IE_INVAL
;
3730 /* Allocate envelope */
3731 *envelope
= calloc(1, sizeof(**envelope
));
3737 /* Else free former data */
3738 zfree((*envelope
)->dmID
);
3739 zfree((*envelope
)->dbIDSender
);
3740 zfree((*envelope
)->dmSender
);
3741 zfree((*envelope
)->dmSenderAddress
);
3742 zfree((*envelope
)->dmSenderType
);
3743 zfree((*envelope
)->dmRecipient
);
3744 zfree((*envelope
)->dmRecipientAddress
);
3745 zfree((*envelope
)->dmAmbiguousRecipient
);
3748 /* Extract envelope elements added by ISDS
3749 * (XSD: gMessageEnvelope type) */
3750 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3751 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3752 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3753 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3754 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3755 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3756 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3757 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3758 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3759 (*envelope
)->dmAmbiguousRecipient
);
3761 /* Extract envelope elements added by sender and ISDS
3762 * (XSD: gMessageEnvelope type) */
3763 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3764 if (err
) goto leave
;
3767 if (err
) isds_envelope_free(envelope
);
3768 xmlXPathFreeObject(result
);
3773 /* Convert other envelope elements from XML tree into isds_envelope structure:
3774 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3775 * The envelope is automatically allocated but not reallocated.
3776 * The data are just appended into envelope structure.
3777 * @context is ISDS context
3778 * @envelope is automatically allocated message envelope structure
3779 * @xpath_ctx is XPath context with current node as parent desired elements
3780 * In case of error @envelope will be freed. */
3781 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3782 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3783 isds_error err
= IE_SUCCESS
;
3784 xmlXPathObjectPtr result
= NULL
;
3785 char *string
= NULL
;
3786 unsigned long int *unumber
= NULL
;
3788 if (!context
) return IE_INVALID_CONTEXT
;
3789 if (!envelope
) return IE_INVAL
;
3790 if (!xpath_ctx
) return IE_INVAL
;
3795 *envelope
= calloc(1, sizeof(**envelope
));
3802 zfree((*envelope
)->dmMessageStatus
);
3803 zfree((*envelope
)->dmAttachmentSize
);
3804 zfree((*envelope
)->dmDeliveryTime
);
3805 zfree((*envelope
)->dmAcceptanceTime
);
3809 /* dmMessageStatus element is mandatory */
3810 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3812 isds_log_message(context
,
3813 _("Missing mandatory sisds:dmMessageStatus integer"));
3817 err
= uint2isds_message_status(context
, unumber
,
3818 &((*envelope
)->dmMessageStatus
));
3820 if (err
== IE_ENUM
) err
= IE_ISDS
;
3823 free(unumber
); unumber
= NULL
;
3825 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3828 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3830 err
= timestring2timeval((xmlChar
*) string
,
3831 &((*envelope
)->dmDeliveryTime
));
3833 char *string_locale
= _isds_utf82locale(string
);
3834 if (err
== IE_DATE
) err
= IE_ISDS
;
3835 isds_printf_message(context
,
3836 _("Could not convert dmDeliveryTime as ISO time: %s"),
3838 free(string_locale
);
3844 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3846 err
= timestring2timeval((xmlChar
*) string
,
3847 &((*envelope
)->dmAcceptanceTime
));
3849 char *string_locale
= _isds_utf82locale(string
);
3850 if (err
== IE_DATE
) err
= IE_ISDS
;
3851 isds_printf_message(context
,
3852 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3854 free(string_locale
);
3861 if (err
) isds_envelope_free(envelope
);
3864 xmlXPathFreeObject(result
);
3869 /* Convert message type attribute of current element into isds_envelope
3871 * TODO: This function can be incorporated into append_status_size_times() as
3872 * they are called always together.
3873 * The envelope is automatically allocated but not reallocated.
3874 * The data are just appended into envelope structure.
3875 * @context is ISDS context
3876 * @envelope is automatically allocated message envelope structure
3877 * @xpath_ctx is XPath context with current node as parent of attribute
3878 * carrying message type
3879 * In case of error @envelope will be freed. */
3880 static isds_error
append_message_type(struct isds_ctx
*context
,
3881 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3882 isds_error err
= IE_SUCCESS
;
3884 if (!context
) return IE_INVALID_CONTEXT
;
3885 if (!envelope
) return IE_INVAL
;
3886 if (!xpath_ctx
) return IE_INVAL
;
3891 *envelope
= calloc(1, sizeof(**envelope
));
3898 zfree((*envelope
)->dmType
);
3902 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3904 if (!(*envelope
)->dmType
) {
3905 /* Use default value */
3906 (*envelope
)->dmType
= strdup("V");
3907 if (!(*envelope
)->dmType
) {
3911 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3912 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3913 isds_printf_message(context
,
3914 _("Message type in dmType attribute is not 1 character long: "
3923 if (err
) isds_envelope_free(envelope
);
3929 /* Convert dmType isds_envelope member into XML attribute and append it to
3931 * @context is ISDS context
3932 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3933 * @dm_envelope is XML element the resulting attribute will be appended to.
3934 * @return error code, in case of error context' message is filled. */
3935 static isds_error
insert_message_type(struct isds_ctx
*context
,
3936 const char *type
, xmlNodePtr dm_envelope
) {
3937 isds_error err
= IE_SUCCESS
;
3938 xmlAttrPtr attribute_node
;
3940 if (!context
) return IE_INVALID_CONTEXT
;
3941 if (!dm_envelope
) return IE_INVAL
;
3943 /* Insert optional message type */
3945 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3946 char *type_locale
= _isds_utf82locale(type
);
3947 isds_printf_message(context
,
3948 _("Message type in envelope is not 1 character long: %s"),
3954 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3960 #endif /* HAVE_LIBCURL */
3963 /* Extract message document into reallocated document structure
3964 * @context is ISDS context
3965 * @document is automatically reallocated message documents structure
3966 * @xpath_ctx is XPath context with current node as isds:dmFile
3967 * In case of error @document will be freed. */
3968 static isds_error
extract_document(struct isds_ctx
*context
,
3969 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3970 isds_error err
= IE_SUCCESS
;
3971 xmlXPathObjectPtr result
= NULL
;
3972 xmlNodePtr file_node
;
3973 char *string
= NULL
;
3975 if (!context
) return IE_INVALID_CONTEXT
;
3976 if (!document
) return IE_INVAL
;
3977 isds_document_free(document
);
3978 if (!xpath_ctx
) return IE_INVAL
;
3979 file_node
= xpath_ctx
->node
;
3981 *document
= calloc(1, sizeof(**document
));
3987 /* Extract document meta data */
3988 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3989 if (context
->normalize_mime_type
) {
3990 const char *normalized_type
=
3991 isds_normalize_mime_type((*document
)->dmMimeType
);
3992 if (NULL
!= normalized_type
&&
3993 normalized_type
!= (*document
)->dmMimeType
) {
3994 char *new_type
= strdup(normalized_type
);
3995 if (NULL
== new_type
) {
3996 isds_printf_message(context
,
3997 _("Not enough memory to normalize document MIME type"));
4001 free((*document
)->dmMimeType
);
4002 (*document
)->dmMimeType
= new_type
;
4006 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
4007 err
= string2isds_FileMetaType((xmlChar
*)string
,
4008 &((*document
)->dmFileMetaType
));
4010 char *meta_type_locale
= _isds_utf82locale(string
);
4011 isds_printf_message(context
,
4012 _("Document has invalid dmFileMetaType attribute value: %s"),
4014 free(meta_type_locale
);
4020 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
4021 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
4022 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
4023 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
4026 /* Extract document data.
4027 * Base64 encoded blob or XML subtree must be presented. */
4029 /* Check for dmEncodedContent */
4030 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
4037 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4038 /* Here we have Base64 blob */
4039 (*document
)->is_xml
= 0;
4041 if (result
->nodesetval
->nodeNr
> 1) {
4042 isds_printf_message(context
,
4043 _("Document has more dmEncodedContent elements"));
4048 xmlXPathFreeObject(result
); result
= NULL
;
4049 EXTRACT_STRING("isds:dmEncodedContent", string
);
4051 /* Decode non-empty document */
4052 if (string
&& string
[0] != '\0') {
4053 (*document
)->data_length
=
4054 _isds_b64decode(string
, &((*document
)->data
));
4055 if ((*document
)->data_length
== (size_t) -1) {
4056 isds_printf_message(context
,
4057 _("Error while Base64-decoding document content"));
4063 /* No Base64 blob, try XML document */
4064 xmlXPathFreeObject(result
); result
= NULL
;
4065 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4072 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4073 /* Here we have XML document */
4074 (*document
)->is_xml
= 1;
4076 if (result
->nodesetval
->nodeNr
> 1) {
4077 isds_printf_message(context
,
4078 _("Document has more dmXMLContent elements"));
4083 /* XXX: We cannot serialize the content simply because:
4084 * - XML document may point out of its scope (e.g. to message
4086 * - isds:dmXMLContent can contain more elements, no element,
4088 * - it's not the XML way
4089 * Thus we provide the only right solution: XML DOM. Let's
4090 * application to cope with this hot potato :) */
4091 (*document
)->xml_node_list
=
4092 result
->nodesetval
->nodeTab
[0]->children
;
4094 /* No base64 blob, nor XML document */
4095 isds_printf_message(context
,
4096 _("Document has no dmEncodedContent, nor dmXMLContent "
4105 if (err
) isds_document_free(document
);
4107 xmlXPathFreeObject(result
);
4108 xpath_ctx
->node
= file_node
;
4114 /* Extract message documents into reallocated list of documents
4115 * @context is ISDS context
4116 * @documents is automatically reallocated message documents list structure
4117 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4118 * In case of error @documents will be freed. */
4119 static isds_error
extract_documents(struct isds_ctx
*context
,
4120 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4121 isds_error err
= IE_SUCCESS
;
4122 xmlXPathObjectPtr result
= NULL
;
4123 xmlNodePtr files_node
;
4124 struct isds_list
*document
, *prev_document
= NULL
;
4126 if (!context
) return IE_INVALID_CONTEXT
;
4127 if (!documents
) return IE_INVAL
;
4128 isds_list_free(documents
);
4129 if (!xpath_ctx
) return IE_INVAL
;
4130 files_node
= xpath_ctx
->node
;
4132 /* Find documents */
4133 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4140 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4141 isds_printf_message(context
,
4142 _("Message does not contain any document"));
4148 /* Iterate over documents */
4149 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4151 /* Allocate and append list item */
4152 document
= calloc(1, sizeof(*document
));
4157 document
->destructor
= (void (*)(void **))isds_document_free
;
4158 if (i
== 0) *documents
= document
;
4159 else prev_document
->next
= document
;
4160 prev_document
= document
;
4162 /* Extract document */
4163 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4164 err
= extract_document(context
,
4165 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4166 if (err
) goto leave
;
4171 if (err
) isds_list_free(documents
);
4172 xmlXPathFreeObject(result
);
4173 xpath_ctx
->node
= files_node
;
4179 /* Convert isds:dmRecord XML tree into structure
4180 * @context is ISDS context
4181 * @envelope is automatically reallocated message envelope structure
4182 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4183 * In case of error @envelope will be freed. */
4184 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4185 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4186 isds_error err
= IE_SUCCESS
;
4187 xmlXPathObjectPtr result
= NULL
;
4189 if (!context
) return IE_INVALID_CONTEXT
;
4190 if (!envelope
) return IE_INVAL
;
4191 isds_envelope_free(envelope
);
4192 if (!xpath_ctx
) return IE_INVAL
;
4195 *envelope
= calloc(1, sizeof(**envelope
));
4202 /* Extract tRecord data */
4203 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4205 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4206 * dmAcceptanceTime. */
4207 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4208 if (err
) goto leave
;
4210 /* Extract envelope elements added by sender and ISDS
4211 * (XSD: gMessageEnvelope type) */
4212 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4213 if (err
) goto leave
;
4215 /* Get message type */
4216 err
= append_message_type(context
, envelope
, xpath_ctx
);
4217 if (err
) goto leave
;
4221 if (err
) isds_envelope_free(envelope
);
4222 xmlXPathFreeObject(result
);
4227 /* Convert XSD:tStateChangesRecord type XML tree into structure
4228 * @context is ISDS context
4229 * @changed_status is automatically reallocated message state change structure
4230 * @xpath_ctx is XPath context with current node as element of
4231 * XSD:tStateChangesRecord type
4232 * In case of error @changed_status will be freed. */
4233 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4234 struct isds_message_status_change
**changed_status
,
4235 xmlXPathContextPtr xpath_ctx
) {
4236 isds_error err
= IE_SUCCESS
;
4237 xmlXPathObjectPtr result
= NULL
;
4238 unsigned long int *unumber
= NULL
;
4239 char *string
= NULL
;
4241 if (!context
) return IE_INVALID_CONTEXT
;
4242 if (!changed_status
) return IE_INVAL
;
4243 isds_message_status_change_free(changed_status
);
4244 if (!xpath_ctx
) return IE_INVAL
;
4247 *changed_status
= calloc(1, sizeof(**changed_status
));
4248 if (!*changed_status
) {
4254 /* Extract tGetStateChangesInput data */
4255 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4257 /* dmEventTime is mandatory */
4258 EXTRACT_STRING("isds:dmEventTime", string
);
4260 err
= timestring2timeval((xmlChar
*) string
,
4261 &((*changed_status
)->time
));
4263 char *string_locale
= _isds_utf82locale(string
);
4264 if (err
== IE_DATE
) err
= IE_ISDS
;
4265 isds_printf_message(context
,
4266 _("Could not convert dmEventTime as ISO time: %s"),
4268 free(string_locale
);
4274 /* dmMessageStatus element is mandatory */
4275 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4277 isds_log_message(context
,
4278 _("Missing mandatory isds:dmMessageStatus integer"));
4282 err
= uint2isds_message_status(context
, unumber
,
4283 &((*changed_status
)->dmMessageStatus
));
4285 if (err
== IE_ENUM
) err
= IE_ISDS
;
4294 if (err
) isds_message_status_change_free(changed_status
);
4295 xmlXPathFreeObject(result
);
4298 #endif /* HAVE_LIBCURL */
4301 /* Find and convert isds:dmHash XML tree into structure
4302 * @context is ISDS context
4303 * @envelope is automatically reallocated message hash structure
4304 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4305 * In case of error @hash will be freed. */
4306 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4307 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4308 isds_error err
= IE_SUCCESS
;
4309 xmlNodePtr old_ctx_node
;
4310 xmlXPathObjectPtr result
= NULL
;
4311 char *string
= NULL
;
4313 if (!context
) return IE_INVALID_CONTEXT
;
4314 if (!hash
) return IE_INVAL
;
4315 isds_hash_free(hash
);
4316 if (!xpath_ctx
) return IE_INVAL
;
4318 old_ctx_node
= xpath_ctx
->node
;
4320 *hash
= calloc(1, sizeof(**hash
));
4327 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4328 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4337 /* Get hash algorithm */
4338 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4339 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4341 if (err
== IE_ENUM
) {
4342 char *string_locale
= _isds_utf82locale(string
);
4343 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4345 free(string_locale
);
4351 /* Get hash value */
4352 EXTRACT_STRING(".", string
);
4354 isds_printf_message(context
,
4355 _("sisds:dmHash element is missing hash value"));
4359 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4360 if ((*hash
)->length
== (size_t) -1) {
4361 isds_printf_message(context
,
4362 _("Error while Base64-decoding hash value"));
4368 if (err
) isds_hash_free(hash
);
4370 xmlXPathFreeObject(result
);
4371 xpath_ctx
->node
= old_ctx_node
;
4376 /* Find and append isds:dmQTimestamp XML tree into envelope.
4377 * Because one service is allowed to miss time-stamp content, and we think
4378 * other could too (flaw in specification), this function is deliberated and
4379 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4380 * @context is ISDS context
4381 * @envelope is automatically allocated envelope structure
4382 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4384 * In case of error @envelope will be freed. */
4385 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4386 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4387 isds_error err
= IE_SUCCESS
;
4388 xmlXPathObjectPtr result
= NULL
;
4389 char *string
= NULL
;
4391 if (!context
) return IE_INVALID_CONTEXT
;
4392 if (!envelope
) return IE_INVAL
;
4394 isds_envelope_free(envelope
);
4399 *envelope
= calloc(1, sizeof(**envelope
));
4405 zfree((*envelope
)->timestamp
);
4406 (*envelope
)->timestamp_length
= 0;
4409 /* Get dmQTimestamp */
4410 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4412 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4415 (*envelope
)->timestamp_length
=
4416 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4417 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4418 isds_printf_message(context
,
4419 _("Error while Base64-decoding time stamp value"));
4425 if (err
) isds_envelope_free(envelope
);
4427 xmlXPathFreeObject(result
);
4432 /* Convert XSD tReturnedMessage XML tree into message structure.
4433 * It does not store serialized XML tree into message->raw.
4434 * It does store (pointer to) parsed XML tree into message->xml if needed.
4435 * @context is ISDS context
4436 * @include_documents Use true if documents must be extracted
4437 * (tReturnedMessage XSD type), use false if documents shall be omitted
4438 * (tReturnedMessageEnvelope).
4439 * @message is automatically reallocated message structure
4440 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4442 * In case of error @message will be freed. */
4443 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4444 const _Bool include_documents
, struct isds_message
**message
,
4445 xmlXPathContextPtr xpath_ctx
) {
4446 isds_error err
= IE_SUCCESS
;
4447 xmlNodePtr message_node
;
4449 if (!context
) return IE_INVALID_CONTEXT
;
4450 if (!message
) return IE_INVAL
;
4451 isds_message_free(message
);
4452 if (!xpath_ctx
) return IE_INVAL
;
4455 *message
= calloc(1, sizeof(**message
));
4461 /* Save message XPATH context node */
4462 message_node
= xpath_ctx
->node
;
4466 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4467 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4468 if (err
) { err
= IE_ERROR
; goto leave
; }
4469 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4470 if (err
) goto leave
;
4472 if (include_documents
) {
4473 struct isds_list
*item
;
4475 /* Extract dmFiles */
4476 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4478 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4479 err
= IE_ISDS
; goto leave
;
4481 if (err
) { err
= IE_ERROR
; goto leave
; }
4482 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4483 if (err
) goto leave
;
4485 /* Store xmlDoc of this message if needed */
4486 /* Only if we got a XML document in all the documents. */
4487 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4488 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4489 (*message
)->xml
= xpath_ctx
->doc
;
4496 /* Restore context to message */
4497 xpath_ctx
->node
= message_node
;
4499 /* Extract dmHash */
4500 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4502 if (err
) goto leave
;
4504 /* Extract dmQTimestamp, */
4505 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4507 if (err
) goto leave
;
4509 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4510 * dmAcceptanceTime. */
4511 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4512 if (err
) goto leave
;
4514 /* Get message type */
4515 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4516 if (err
) goto leave
;
4519 if (err
) isds_message_free(message
);
4524 /* Extract message event into reallocated isds_event structure
4525 * @context is ISDS context
4526 * @event is automatically reallocated message event structure
4527 * @xpath_ctx is XPath context with current node as isds:dmEvent
4528 * In case of error @event will be freed. */
4529 static isds_error
extract_event(struct isds_ctx
*context
,
4530 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4531 isds_error err
= IE_SUCCESS
;
4532 xmlXPathObjectPtr result
= NULL
;
4533 xmlNodePtr event_node
;
4534 char *string
= NULL
;
4536 if (!context
) return IE_INVALID_CONTEXT
;
4537 if (!event
) return IE_INVAL
;
4538 isds_event_free(event
);
4539 if (!xpath_ctx
) return IE_INVAL
;
4540 event_node
= xpath_ctx
->node
;
4542 *event
= calloc(1, sizeof(**event
));
4548 /* Extract event data.
4549 * All elements are optional according XSD. That's funny. */
4550 EXTRACT_STRING("sisds:dmEventTime", string
);
4552 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4554 char *string_locale
= _isds_utf82locale(string
);
4555 if (err
== IE_DATE
) err
= IE_ISDS
;
4556 isds_printf_message(context
,
4557 _("Could not convert dmEventTime as ISO time: %s"),
4559 free(string_locale
);
4565 /* dmEventDescr element has prefix and the rest */
4566 EXTRACT_STRING("sisds:dmEventDescr", string
);
4568 err
= eventstring2event((xmlChar
*) string
, *event
);
4569 if (err
) goto leave
;
4574 if (err
) isds_event_free(event
);
4576 xmlXPathFreeObject(result
);
4577 xpath_ctx
->node
= event_node
;
4582 /* Convert element of XSD tEventsArray type from XML tree into
4583 * isds_list of isds_event's structure. The list is automatically reallocated.
4584 * @context is ISDS context
4585 * @events is automatically reallocated list of event structures
4586 * @xpath_ctx is XPath context with current node as tEventsArray
4587 * In case of error @events will be freed. */
4588 static isds_error
extract_events(struct isds_ctx
*context
,
4589 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4590 isds_error err
= IE_SUCCESS
;
4591 xmlXPathObjectPtr result
= NULL
;
4592 xmlNodePtr events_node
;
4593 struct isds_list
*event
, *prev_event
= NULL
;
4595 if (!context
) return IE_INVALID_CONTEXT
;
4596 if (!events
) return IE_INVAL
;
4597 if (!xpath_ctx
) return IE_INVAL
;
4598 events_node
= xpath_ctx
->node
;
4601 isds_list_free(events
);
4604 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4611 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4612 isds_printf_message(context
,
4613 _("Delivery info does not contain any event"));
4619 /* Iterate over events */
4620 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4622 /* Allocate and append list item */
4623 event
= calloc(1, sizeof(*event
));
4628 event
->destructor
= (void (*)(void **))isds_event_free
;
4629 if (i
== 0) *events
= event
;
4630 else prev_event
->next
= event
;
4634 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4635 err
= extract_event(context
,
4636 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4637 if (err
) goto leave
;
4642 if (err
) isds_list_free(events
);
4643 xmlXPathFreeObject(result
);
4644 xpath_ctx
->node
= events_node
;
4650 /* Insert Base64 encoded data as element with text child.
4651 * @context is session context
4652 * @parent is XML node to append @element with @data as child
4653 * @ns is XML namespace of @element, use NULL to inherit from @parent
4654 * @element is UTF-8 encoded name of new element
4655 * @data is bit stream to encode into @element
4656 * @length is size of @data in bytes
4657 * @return standard error code and fill long error message if needed */
4658 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4659 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4660 const void *data
, size_t length
) {
4661 isds_error err
= IE_SUCCESS
;
4664 if (!context
) return IE_INVALID_CONTEXT
;
4665 if (!data
&& length
> 0) return IE_INVAL
;
4666 if (!parent
|| !element
) return IE_INVAL
;
4668 xmlChar
*base64data
= NULL
;
4669 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4671 isds_printf_message(context
,
4672 ngettext("Not enough memory to encode %zd byte into Base64",
4673 "Not enough memory to encode %zd bytes into Base64",
4679 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4687 /* Convert isds_document structure into XML tree and append to dmFiles node.
4688 * @context is session context
4689 * @document is ISDS document
4690 * @dm_files is XML element the resulting tree will be appended to as a child.
4691 * @return error code, in case of error context' message is filled. */
4692 static isds_error
insert_document(struct isds_ctx
*context
,
4693 struct isds_document
*document
, xmlNodePtr dm_files
) {
4694 isds_error err
= IE_SUCCESS
;
4695 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4696 xmlAttrPtr attribute_node
;
4698 if (!context
) return IE_INVALID_CONTEXT
;
4699 if (!document
|| !dm_files
) return IE_INVAL
;
4701 /* Allocate new dmFile */
4702 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4704 isds_printf_message(context
, _("Could not allocate main dmFile"));
4708 /* Append the new dmFile.
4709 * XXX: Main document must go first */
4710 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4711 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4713 file
= xmlAddChild(dm_files
, new_file
);
4716 xmlFreeNode(new_file
); new_file
= NULL
;
4717 isds_printf_message(context
, _("Could not add dmFile child to "
4718 "%s element"), dm_files
->name
);
4723 /* @dmMimeType is required */
4724 if (!document
->dmMimeType
) {
4725 isds_log_message(context
,
4726 _("Document is missing mandatory MIME type definition"));
4730 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4732 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4734 isds_printf_message(context
,
4735 _("Document has unknown dmFileMetaType: %ld"),
4736 document
->dmFileMetaType
);
4740 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4742 if (document
->dmFileGuid
) {
4743 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4745 if (document
->dmUpFileGuid
) {
4746 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4749 /* @dmFileDescr is required */
4750 if (!document
->dmFileDescr
) {
4751 isds_log_message(context
,
4752 _("Document is missing mandatory description (title)"));
4756 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4758 if (document
->dmFormat
) {
4759 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4763 /* Insert content (body) of the document. */
4764 if (document
->is_xml
) {
4765 /* XML document requested */
4767 /* Allocate new dmXMLContent */
4768 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4770 isds_printf_message(context
,
4771 _("Could not allocate dmXMLContent element"));
4776 node
= xmlAddChild(file
, xmlcontent
);
4778 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4779 isds_printf_message(context
,
4780 _("Could not add dmXMLContent child to %s element"),
4786 /* Copy non-empty node list */
4787 if (document
->xml_node_list
) {
4788 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4789 document
->xml_node_list
);
4791 isds_printf_message(context
,
4792 _("Not enough memory to copy XML document"));
4797 if (!xmlAddChildList(node
, content
)) {
4798 xmlFreeNodeList(content
);
4799 isds_printf_message(context
,
4800 _("Error while adding XML document into dmXMLContent"));
4804 /* XXX: We cannot free the content here because it's part of node's
4805 * document since now. It will be freed with it automatically. */
4808 /* Binary document requested */
4809 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4810 document
->data
, document
->data_length
);
4811 if (err
) goto leave
;
4819 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4820 * The copy must be preallocated, the date are just appended into structure.
4821 * @context is ISDS context
4822 * @copy is message copy structure
4823 * @xpath_ctx is XPath context with current node as tMStatus */
4824 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4825 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4826 isds_error err
= IE_SUCCESS
;
4827 xmlXPathObjectPtr result
= NULL
;
4828 char *code
= NULL
, *message
= NULL
;
4830 if (!context
) return IE_INVALID_CONTEXT
;
4831 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4833 /* Free old values */
4834 zfree(copy
->dmStatus
);
4837 /* Get error specific to this copy */
4838 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4840 isds_log_message(context
,
4841 _("Missing isds:dmStatusCode under "
4842 "XSD:tMStatus type element"));
4847 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4848 /* This copy failed */
4849 copy
->error
= IE_ISDS
;
4850 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4852 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4853 if (!copy
->dmStatus
) {
4854 copy
->dmStatus
= code
;
4858 copy
->dmStatus
= code
;
4862 /* This copy succeeded. In this case only, message ID is valid */
4863 copy
->error
= IE_SUCCESS
;
4865 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4867 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4868 "but did not returned assigned message ID\n"));
4876 xmlXPathFreeObject(result
);
4881 /* Insert struct isds_approval data (box approval) into XML tree
4882 * @context is session context
4883 * @approval is libisds structure with approval description. NULL is
4885 * @parent is XML element to append @approval to */
4886 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4887 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4889 isds_error err
= IE_SUCCESS
;
4892 if (!context
) return IE_INVALID_CONTEXT
;
4893 if (!parent
) return IE_INVAL
;
4895 if (!approval
) return IE_SUCCESS
;
4897 /* Build XSD:gExtApproval */
4898 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4899 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4906 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4908 * @context is session context
4909 * @service_name is name of SERVICE_DB_ACCESS
4910 * @response is reallocated server SOAP body response as XML document
4911 * @raw_response is reallocated bit stream with response body. Use
4912 * NULL if you don't care
4913 * @raw_response_length is size of @raw_response in bytes
4914 * @code is reallocated ISDS status code
4915 * @status_message is reallocated ISDS status message
4916 * @return error coded from lower layer, context message will be set up
4918 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4919 const xmlChar
*service_name
,
4920 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4921 xmlChar
**code
, xmlChar
**status_message
) {
4923 isds_error err
= IE_SUCCESS
;
4924 char *service_name_locale
= NULL
;
4925 xmlNodePtr request
= NULL
, node
;
4926 xmlNsPtr isds_ns
= NULL
;
4928 if (!context
) return IE_INVALID_CONTEXT
;
4929 if (!service_name
) return IE_INVAL
;
4930 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4931 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4933 /* Free output argument */
4934 xmlFreeDoc(*response
); *response
= NULL
;
4935 if (raw_response
) zfree(*raw_response
);
4937 zfree(*status_message
);
4940 /* Check if connection is established
4941 * TODO: This check should be done downstairs. */
4942 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4944 service_name_locale
= _isds_utf82locale((char*)service_name
);
4945 if (!service_name_locale
) {
4951 request
= xmlNewNode(NULL
, service_name
);
4953 isds_printf_message(context
,
4954 _("Could not build %s request"), service_name_locale
);
4958 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4960 isds_log_message(context
, _("Could not create ISDS name space"));
4964 xmlSetNs(request
, isds_ns
);
4967 /* Add XSD:tDummyInput child */
4968 INSERT_STRING(request
, "dbDummy", NULL
);
4971 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4972 service_name_locale
);
4975 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4976 raw_response
, raw_response_length
);
4977 xmlFreeNode(request
); request
= NULL
;
4980 isds_log(ILF_ISDS
, ILL_DEBUG
,
4981 _("Processing ISDS response on %s request failed\n"),
4982 service_name_locale
);
4986 /* Check for response status */
4987 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4988 code
, status_message
, NULL
);
4990 isds_log(ILF_ISDS
, ILL_DEBUG
,
4991 _("ISDS response on %s request is missing status\n"),
4992 service_name_locale
);
4996 /* Request processed, but nothing found */
4997 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4998 char *code_locale
= _isds_utf82locale((char*) *code
);
4999 char *status_message_locale
=
5000 _isds_utf82locale((char*) *status_message
);
5001 isds_log(ILF_ISDS
, ILL_DEBUG
,
5002 _("Server refused %s request (code=%s, message=%s)\n"),
5003 service_name_locale
, code_locale
, status_message_locale
);
5004 isds_log_message(context
, status_message_locale
);
5006 free(status_message_locale
);
5012 free(service_name_locale
);
5013 xmlFreeNode(request
);
5019 /* Get data about logged in user and his box.
5020 * @context is session context
5021 * @db_owner_info is reallocated box owner description. It will be freed on
5023 * @return error code from lower layer, context message will be set up
5025 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
5026 struct isds_DbOwnerInfo
**db_owner_info
) {
5027 isds_error err
= IE_SUCCESS
;
5029 xmlDocPtr response
= NULL
;
5030 xmlChar
*code
= NULL
, *message
= NULL
;
5031 xmlXPathContextPtr xpath_ctx
= NULL
;
5032 xmlXPathObjectPtr result
= NULL
;
5033 char *string
= NULL
;
5036 if (!context
) return IE_INVALID_CONTEXT
;
5037 zfree(context
->long_message
);
5038 if (!db_owner_info
) return IE_INVAL
;
5039 isds_DbOwnerInfo_free(db_owner_info
);
5042 /* Check if connection is established */
5043 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5046 /* Do request and check for success */
5047 err
= build_send_check_dbdummy_request(context
,
5048 BAD_CAST
"GetOwnerInfoFromLogin",
5049 &response
, NULL
, NULL
, &code
, &message
);
5050 if (err
) goto leave
;
5054 /* Prepare structure */
5055 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5056 if (!*db_owner_info
) {
5060 xpath_ctx
= xmlXPathNewContext(response
);
5065 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5070 /* Set context node */
5071 result
= xmlXPathEvalExpression(BAD_CAST
5072 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5077 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5078 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5082 if (result
->nodesetval
->nodeNr
> 1) {
5083 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5087 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5088 xmlXPathFreeObject(result
); result
= NULL
;
5091 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5096 isds_DbOwnerInfo_free(db_owner_info
);
5100 xmlXPathFreeObject(result
);
5101 xmlXPathFreeContext(xpath_ctx
);
5105 xmlFreeDoc(response
);
5108 isds_log(ILF_ISDS
, ILL_DEBUG
,
5109 _("GetOwnerInfoFromLogin request processed by server "
5110 "successfully.\n"));
5111 #else /* not HAVE_LIBCURL */
5119 /* Get data about logged in user. */
5120 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5121 struct isds_DbUserInfo
**db_user_info
) {
5122 isds_error err
= IE_SUCCESS
;
5124 xmlDocPtr response
= NULL
;
5125 xmlChar
*code
= NULL
, *message
= NULL
;
5126 xmlXPathContextPtr xpath_ctx
= NULL
;
5127 xmlXPathObjectPtr result
= NULL
;
5130 if (!context
) return IE_INVALID_CONTEXT
;
5131 zfree(context
->long_message
);
5132 if (!db_user_info
) return IE_INVAL
;
5133 isds_DbUserInfo_free(db_user_info
);
5136 /* Check if connection is established */
5137 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5140 /* Do request and check for success */
5141 err
= build_send_check_dbdummy_request(context
,
5142 BAD_CAST
"GetUserInfoFromLogin",
5143 &response
, NULL
, NULL
, &code
, &message
);
5144 if (err
) goto leave
;
5148 /* Prepare structure */
5149 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5150 if (!*db_user_info
) {
5154 xpath_ctx
= xmlXPathNewContext(response
);
5159 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5164 /* Set context node */
5165 result
= xmlXPathEvalExpression(BAD_CAST
5166 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5171 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5172 isds_log_message(context
, _("Missing dbUserInfo element"));
5176 if (result
->nodesetval
->nodeNr
> 1) {
5177 isds_log_message(context
, _("Multiple dbUserInfo element"));
5181 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5182 xmlXPathFreeObject(result
); result
= NULL
;
5185 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5189 isds_DbUserInfo_free(db_user_info
);
5192 xmlXPathFreeObject(result
);
5193 xmlXPathFreeContext(xpath_ctx
);
5197 xmlFreeDoc(response
);
5200 isds_log(ILF_ISDS
, ILL_DEBUG
,
5201 _("GetUserInfoFromLogin request processed by server "
5202 "successfully.\n"));
5203 #else /* not HAVE_LIBCURL */
5211 /* Get expiration time of current password
5212 * @context is session context
5213 * @expiration is automatically reallocated time when password expires. If
5214 * password expiration is disabled, NULL will be returned. In case of error
5215 * it will be nulled too. */
5216 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5217 struct timeval
**expiration
) {
5218 isds_error err
= IE_SUCCESS
;
5220 xmlDocPtr response
= NULL
;
5221 xmlChar
*code
= NULL
, *message
= NULL
;
5222 xmlXPathContextPtr xpath_ctx
= NULL
;
5223 xmlXPathObjectPtr result
= NULL
;
5224 char *string
= NULL
;
5227 if (!context
) return IE_INVALID_CONTEXT
;
5228 zfree(context
->long_message
);
5229 if (!expiration
) return IE_INVAL
;
5233 /* Check if connection is established */
5234 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5237 /* Do request and check for success */
5238 err
= build_send_check_dbdummy_request(context
,
5239 BAD_CAST
"GetPasswordInfo",
5240 &response
, NULL
, NULL
, &code
, &message
);
5241 if (err
) goto leave
;
5245 xpath_ctx
= xmlXPathNewContext(response
);
5250 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5255 /* Set context node */
5256 result
= xmlXPathEvalExpression(BAD_CAST
5257 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5262 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5263 isds_log_message(context
,
5264 _("Missing GetPasswordInfoResponse element"));
5268 if (result
->nodesetval
->nodeNr
> 1) {
5269 isds_log_message(context
,
5270 _("Multiple GetPasswordInfoResponse element"));
5274 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5275 xmlXPathFreeObject(result
); result
= NULL
;
5277 /* Extract expiration date */
5278 EXTRACT_STRING("isds:pswExpDate", string
);
5280 /* And convert it if any returned. Otherwise expiration is disabled. */
5281 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5283 char *string_locale
= _isds_utf82locale(string
);
5284 if (err
== IE_DATE
) err
= IE_ISDS
;
5285 isds_printf_message(context
,
5286 _("Could not convert pswExpDate as ISO time: %s"),
5288 free(string_locale
);
5301 xmlXPathFreeObject(result
);
5302 xmlXPathFreeContext(xpath_ctx
);
5306 xmlFreeDoc(response
);
5309 isds_log(ILF_ISDS
, ILL_DEBUG
,
5310 _("GetPasswordInfo request processed by server "
5311 "successfully.\n"));
5312 #else /* not HAVE_LIBCURL */
5321 /* Request delivering new TOTP code from ISDS through side channel before
5322 * changing password.
5323 * @context is session context
5324 * @password is current password.
5325 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5326 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5327 * function for more details.
5328 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5329 * NULL, if you don't care.
5330 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5332 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5333 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5334 isds_error err
= IE_SUCCESS
;
5335 char *saved_url
= NULL
; /* No copy */
5336 #if HAVE_CURL_REAUTHORIZATION_BUG
5337 CURL
*saved_curl
= NULL
; /* No copy */
5339 xmlNsPtr isds_ns
= NULL
;
5340 xmlNodePtr request
= NULL
;
5341 xmlDocPtr response
= NULL
;
5342 xmlChar
*code
= NULL
, *message
= NULL
;
5343 const xmlChar
*codes
[] = {
5348 const char *meanings
[] = {
5349 N_("Unexpected error"),
5350 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5351 N_("One-time code could not been sent. Try later again.")
5353 const isds_otp_resolution resolutions
[] = {
5354 OTP_RESOLUTION_UNKNOWN
,
5355 OTP_RESOLUTION_TO_FAST
,
5356 OTP_RESOLUTION_TOTP_NOT_SENT
5359 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5360 zfree(context
->long_message
);
5361 if (NULL
== password
) {
5362 isds_log_message(context
,
5363 _("Second argument (password) of isds_change_password() "
5368 /* Check if connection is established
5369 * TODO: This check should be done downstairs. */
5370 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5372 if (!context
->otp
) {
5373 isds_log_message(context
, _("This function requires OTP-authenticated "
5375 return IE_INVALID_CONTEXT
;
5378 isds_log_message(context
, _("If one-time password authentication "
5379 "method is in use, requesting new OTP code requires "
5380 "one-time credentials argument either"));
5383 if (otp
->method
!= OTP_TIME
) {
5384 isds_log_message(context
, _("Requesting new time-based OTP code from "
5385 "server requires one-time password authentication "
5389 if (otp
->otp_code
!= NULL
) {
5390 isds_log_message(context
, _("Requesting new time-based OTP code from "
5391 "server requires undefined OTP code member in "
5392 "one-time credentials argument"));
5398 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5400 isds_log_message(context
, _("Could not build SendSMSCode request"));
5403 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5405 isds_log_message(context
, _("Could not create ISDS name space"));
5406 xmlFreeNode(request
);
5409 xmlSetNs(request
, isds_ns
);
5411 /* Change URL temporarily for sending this request only */
5413 char *new_url
= NULL
;
5414 if ((err
= _isds_build_url_from_context(context
,
5415 "%1$.*2$sasws/changePassword", &new_url
))) {
5418 saved_url
= context
->url
;
5419 context
->url
= new_url
;
5422 /* Store credentials for sending this request only */
5423 context
->otp_credentials
= otp
;
5424 _isds_discard_credentials(context
, 0);
5425 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5427 _isds_discard_credentials(context
, 0);
5430 #if HAVE_CURL_REAUTHORIZATION_BUG
5431 saved_curl
= context
->curl
;
5432 context
->curl
= curl_easy_init();
5433 if (NULL
== context
->curl
) {
5437 if (context
->timeout
) {
5438 err
= isds_set_timeout(context
, context
->timeout
);
5439 if (err
) goto leave
;
5443 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5446 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5448 /* Remove temporal credentials */
5449 _isds_discard_credentials(context
, 0);
5450 /* Detach pointer to OTP credentials from context */
5451 context
->otp_credentials
= NULL
;
5452 /* Keep context->otp true to keep signaling this is OTP session */
5454 /* Destroy request */
5455 xmlFreeNode(request
); request
= NULL
;
5458 isds_log(ILF_ISDS
, ILL_DEBUG
,
5459 _("Processing ISDS response on SendSMSCode request failed\n"));
5463 /* Check for response status */
5464 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5465 &code
, &message
, (xmlChar
**)refnumber
);
5467 isds_log(ILF_ISDS
, ILL_DEBUG
,
5468 _("ISDS response on SendSMSCode request is missing "
5473 /* Check for error */
5474 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5475 char *code_locale
= _isds_utf82locale((char*)code
);
5476 char *message_locale
= _isds_utf82locale((char*)message
);
5478 isds_log(ILF_ISDS
, ILL_DEBUG
,
5479 _("Server refused to send new code on SendSMSCode "
5480 "request (code=%s, message=%s)\n"),
5481 code_locale
, message_locale
);
5483 /* Check for known error codes */
5484 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5485 if (!xmlStrcmp(code
, codes
[i
])) break;
5487 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5488 isds_log_message(context
, _(meanings
[i
]));
5489 /* Mimic otp->resolution according to the code, specification does
5490 * prescribe OTP header to be available. */
5491 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5492 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5493 otp
->resolution
= resolutions
[i
];
5495 isds_log_message(context
, message_locale
);
5498 free(message_locale
);
5504 /* Otherwise new code sent successfully */
5505 /* Mimic otp->resolution according to the code, specification does
5506 * prescribe OTP header to be available. */
5507 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5508 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5511 if (NULL
!= saved_url
) {
5512 /* Revert URL to original one */
5513 zfree(context
->url
);
5514 context
->url
= saved_url
;
5516 #if HAVE_CURL_REAUTHORIZATION_BUG
5517 if (NULL
!= saved_curl
) {
5518 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5519 context
->curl
= saved_curl
;
5525 xmlFreeDoc(response
);
5526 xmlFreeNode(request
);
5529 isds_log(ILF_ISDS
, ILL_DEBUG
,
5530 _("New OTP code has been sent successfully on SendSMSCode "
5536 /* Convert response status code to isds_error code and set long message
5537 * @context is context to save long message to
5538 * @map is mapping from codes to errors and messages. Pass NULL for generic
5540 * @code is status code to translate
5541 * @message is non-localized status message to put into long message in case
5542 * of uknown error. It can be NULL if server did not provide any.
5543 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5544 * invalid invocation. */
5545 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5546 const struct code_map_isds_error
*map
,
5547 const xmlChar
*code
, const xmlChar
*message
) {
5549 isds_log_message(context
,
5550 _("NULL status code passed to statuscode2isds_error()"));
5555 /* Check for known error codes */
5556 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5557 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5558 isds_log_message(context
, _(map
->meanings
[i
]));
5559 return map
->errors
[i
];
5565 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5566 char *message_locale
= _isds_utf82locale((char*)message
);
5567 if (NULL
== message_locale
)
5568 isds_log_message(context
, _("ISDS server returned unknown error"));
5570 isds_log_message(context
, message_locale
);
5571 free(message_locale
);
5580 /* Change user password in ISDS.
5581 * User must supply old password, new password will takes effect after some
5582 * time, current session can continue. Password must fulfill some constraints.
5583 * @context is session context
5584 * @old_password is current password.
5585 * @new_password is requested new password
5586 * @otp auxiliary data required if one-time password authentication is in use,
5587 * defines OTP code (if known) and returns fine grade resolution of OTP
5588 * procedure. Pass NULL, if one-time password authentication is not needed.
5589 * Please note the @otp argument must match OTP method used at log-in time. See
5590 * isds_login() function for more details.
5591 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5592 * NULL, if you don't care.
5593 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5594 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5595 * awaiting OTP code that has been delivered by side channel to the user. */
5596 isds_error
isds_change_password(struct isds_ctx
*context
,
5597 const char *old_password
, const char *new_password
,
5598 struct isds_otp
*otp
, char **refnumber
) {
5599 isds_error err
= IE_SUCCESS
;
5601 char *saved_url
= NULL
; /* No copy */
5602 #if HAVE_CURL_REAUTHORIZATION_BUG
5603 CURL
*saved_curl
= NULL
; /* No copy */
5605 xmlNsPtr isds_ns
= NULL
;
5606 xmlNodePtr request
= NULL
, node
;
5607 xmlDocPtr response
= NULL
;
5608 xmlChar
*code
= NULL
, *message
= NULL
;
5609 const xmlChar
*codes
[] = {
5622 const char *meanings
[] = {
5623 N_("Password length must be between 8 and 32 characters"),
5624 N_("Password cannot be reused"), /* Server does not distinguish 1067
5625 and 1091 on ChangePasswordOTP */
5626 N_("Password contains forbidden character"),
5627 N_("Password must contain at least one upper-case letter, "
5628 "one lower-case, and one digit"),
5629 N_("Password cannot contain sequence of three identical characters"),
5630 N_("Password cannot contain user identifier"),
5631 N_("Password is too simmple"),
5632 N_("Old password is not valid"),
5633 N_("Password cannot be reused"),
5634 N_("Unexpected error"),
5635 N_("LDAP update error")
5639 if (!context
) return IE_INVALID_CONTEXT
;
5640 zfree(context
->long_message
);
5641 if (NULL
!= refnumber
)
5643 if (NULL
== old_password
) {
5644 isds_log_message(context
,
5645 _("Second argument (old password) of isds_change_password() "
5649 if (NULL
== otp
&& NULL
== new_password
) {
5650 isds_log_message(context
,
5651 _("Third argument (new password) of isds_change_password() "
5657 /* Check if connection is established
5658 * TODO: This check should be done downstairs. */
5659 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5661 if (context
->otp
&& NULL
== otp
) {
5662 isds_log_message(context
, _("If one-time password authentication "
5663 "method is in use, changing password requires one-time "
5664 "credentials either"));
5668 /* Build ChangeISDSPassword request */
5669 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5670 BAD_CAST
"ChangePasswordOTP");
5672 isds_log_message(context
, (NULL
== otp
) ?
5673 _("Could not build ChangeISDSPassword request") :
5674 _("Could not build ChangePasswordOTP request"));
5677 isds_ns
= xmlNewNs(request
,
5678 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5681 isds_log_message(context
, _("Could not create ISDS name space"));
5682 xmlFreeNode(request
);
5685 xmlSetNs(request
, isds_ns
);
5687 INSERT_STRING(request
, "dbOldPassword", old_password
);
5688 INSERT_STRING(request
, "dbNewPassword", new_password
);
5691 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5692 switch (otp
->method
) {
5694 isds_log(ILF_SEC
, ILL_INFO
,
5695 _("Selected authentication method: "
5696 "HMAC-based one-time password\n"));
5697 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5700 isds_log(ILF_SEC
, ILL_INFO
,
5701 _("Selected authentication method: "
5702 "Time-based one-time password\n"));
5703 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5704 if (otp
->otp_code
== NULL
) {
5705 isds_log(ILF_SEC
, ILL_INFO
,
5706 _("OTP code has not been provided by "
5707 "application, requesting server for "
5709 err
= _isds_request_totp_code(context
, old_password
, otp
,
5711 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5715 isds_log(ILF_SEC
, ILL_INFO
,
5716 _("OTP code has been provided by "
5717 "application, not requesting server "
5722 isds_log_message(context
,
5723 _("Unknown one-time password authentication "
5724 "method requested by application"));
5729 /* Change URL temporarily for sending this request only */
5731 char *new_url
= NULL
;
5732 if ((err
= _isds_build_url_from_context(context
,
5733 "%1$.*2$sasws/changePassword", &new_url
))) {
5736 saved_url
= context
->url
;
5737 context
->url
= new_url
;
5740 /* Store credentials for sending this request only */
5741 context
->otp_credentials
= otp
;
5742 _isds_discard_credentials(context
, 0);
5743 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5744 old_password
, NULL
))) {
5745 _isds_discard_credentials(context
, 0);
5748 #if HAVE_CURL_REAUTHORIZATION_BUG
5749 saved_curl
= context
->curl
;
5750 context
->curl
= curl_easy_init();
5751 if (NULL
== context
->curl
) {
5755 if (context
->timeout
) {
5756 err
= isds_set_timeout(context
, context
->timeout
);
5757 if (err
) goto leave
;
5762 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5763 _("Sending ChangeISDSPassword request to ISDS\n") :
5764 _("Sending ChangePasswordOTP request to ISDS\n"));
5767 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5768 request
, &response
, NULL
, NULL
);
5771 /* Remove temporal credentials */
5772 _isds_discard_credentials(context
, 0);
5773 /* Detach pointer to OTP credentials from context */
5774 context
->otp_credentials
= NULL
;
5775 /* Keep context->otp true to keep signaling this is OTP session */
5778 /* Destroy request */
5779 xmlFreeNode(request
); request
= NULL
;
5782 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5783 _("Processing ISDS response on ChangeISDSPassword "
5784 "request failed\n") :
5785 _("Processing ISDS response on ChangePasswordOTP "
5786 "request failed\n"));
5790 /* Check for response status */
5791 err
= isds_response_status(context
,
5792 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5793 &code
, &message
, (xmlChar
**)refnumber
);
5795 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5796 _("ISDS response on ChangeISDSPassword request is missing "
5798 _("ISDS response on ChangePasswordOTP request is missing "
5803 /* Check for known error codes */
5804 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5805 if (!xmlStrcmp(code
, codes
[i
])) {
5806 char *code_locale
= _isds_utf82locale((char*)code
);
5807 char *message_locale
= _isds_utf82locale((char*)message
);
5808 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5809 _("Server refused to change password on ChangeISDSPassword "
5810 "request (code=%s, message=%s)\n") :
5811 _("Server refused to change password on ChangePasswordOTP "
5812 "request (code=%s, message=%s)\n"),
5813 code_locale
, message_locale
);
5815 free(message_locale
);
5816 isds_log_message(context
, _(meanings
[i
]));
5823 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5824 char *code_locale
= _isds_utf82locale((char*)code
);
5825 char *message_locale
= _isds_utf82locale((char*)message
);
5826 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5827 _("Server refused to change password on ChangeISDSPassword "
5828 "request (code=%s, message=%s)\n") :
5829 _("Server refused to change password on ChangePasswordOTP "
5830 "request (code=%s, message=%s)\n"),
5831 code_locale
, message_locale
);
5832 isds_log_message(context
, message_locale
);
5834 free(message_locale
);
5839 /* Otherwise password changed successfully */
5842 if (NULL
!= saved_url
) {
5843 /* Revert URL to original one */
5844 zfree(context
->url
);
5845 context
->url
= saved_url
;
5847 #if HAVE_CURL_REAUTHORIZATION_BUG
5848 if (NULL
!= saved_curl
) {
5849 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5850 context
->curl
= saved_curl
;
5856 xmlFreeDoc(response
);
5857 xmlFreeNode(request
);
5860 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5861 _("Password changed successfully on ChangeISDSPassword "
5863 _("Password changed successfully on ChangePasswordOTP "
5865 #else /* not HAVE_LIBCURL */
5874 /* Generic middle part with request sending and response check.
5875 * It sends prepared request and checks for error code.
5876 * @context is ISDS session context.
5877 * @service is ISDS service handler
5878 * @service_name is name in scope of given @service
5879 * @request is XML tree with request. Will be freed to save memory.
5880 * @response is XML document outputting ISDS response.
5881 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5882 * @map is mapping from status code to library error. Pass NULL if no special
5883 * handling is requested.
5884 * NULL, if you don't care. */
5885 static isds_error
send_destroy_request_check_response(
5886 struct isds_ctx
*context
,
5887 const isds_service service
, const xmlChar
*service_name
,
5888 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5889 const struct code_map_isds_error
*map
) {
5890 isds_error err
= IE_SUCCESS
;
5891 char *service_name_locale
= NULL
;
5892 xmlChar
*code
= NULL
, *message
= NULL
;
5895 if (!context
) return IE_INVALID_CONTEXT
;
5896 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5900 /* Check if connection is established
5901 * TODO: This check should be done downstairs. */
5902 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5904 service_name_locale
= _isds_utf82locale((char*) service_name
);
5905 if (!service_name_locale
) {
5910 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5911 service_name_locale
);
5914 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5915 xmlFreeNode(*request
); *request
= NULL
;
5918 isds_log(ILF_ISDS
, ILL_DEBUG
,
5919 _("Processing ISDS response on %s request failed\n"),
5920 service_name_locale
);
5924 /* Check for response status */
5925 err
= isds_response_status(context
, service
, *response
,
5926 &code
, &message
, refnumber
);
5928 isds_log(ILF_ISDS
, ILL_DEBUG
,
5929 _("ISDS response on %s request is missing status\n"),
5930 service_name_locale
);
5934 err
= statuscode2isds_error(context
, map
, code
, message
);
5936 /* Request processed, but server failed */
5937 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5938 char *code_locale
= _isds_utf82locale((char*) code
);
5939 char *message_locale
= _isds_utf82locale((char*) message
);
5940 isds_log(ILF_ISDS
, ILL_DEBUG
,
5941 _("Server refused %s request (code=%s, message=%s)\n"),
5942 service_name_locale
, code_locale
, message_locale
);
5944 free(message_locale
);
5952 if (err
&& *response
) {
5953 xmlFreeDoc(*response
);
5957 xmlFreeNode(*request
);
5960 free(service_name_locale
);
5966 /* Generic bottom half with request sending.
5967 * It sends prepared request, checks for error code, destroys response and
5968 * request and log success or failure.
5969 * @context is ISDS session context.
5970 * @service is ISDS service handler
5971 * @service_name is name in scope of given @service
5972 * @request is XML tree with request. Will be freed to save memory.
5973 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5974 * NULL, if you don't care. */
5975 static isds_error
send_request_check_drop_response(
5976 struct isds_ctx
*context
,
5977 const isds_service service
, const xmlChar
*service_name
,
5978 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5979 isds_error err
= IE_SUCCESS
;
5980 xmlDocPtr response
= NULL
;
5983 if (!context
) return IE_INVALID_CONTEXT
;
5984 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5987 /* Send request and check response*/
5988 err
= send_destroy_request_check_response(context
,
5989 service
, service_name
, request
, &response
, refnumber
, NULL
);
5991 xmlFreeDoc(response
);
5994 xmlFreeNode(*request
);
5999 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6000 isds_log(ILF_ISDS
, ILL_DEBUG
,
6001 _("%s request processed by server successfully.\n"),
6002 service_name_locale
);
6003 free(service_name_locale
);
6010 /* Insert isds_credentials_delivery structure into XML request if not NULL
6011 * @context is session context
6012 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6013 * credentials delivery. The email field is passed.
6014 * @parent is XML element where to insert */
6015 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
6016 const struct isds_credentials_delivery
*credentials_delivery
,
6017 xmlNodePtr parent
) {
6018 isds_error err
= IE_SUCCESS
;
6021 if (!context
) return IE_INVALID_CONTEXT
;
6022 if (!parent
) return IE_INVAL
;
6024 if (credentials_delivery
) {
6025 /* Following elements are valid only for services:
6026 * NewAccessData, AddDataBoxUser, CreateDataBox */
6027 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
6028 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
6036 /* Extract credentials delivery from ISDS response.
6037 * @context is session context
6038 * @credentials_delivery is pointer to valid structure to fill in returned
6039 * user's password (and new log-in name). If NULL, do not extract the data.
6040 * @response is pointer to XML document with ISDS response
6041 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6042 * @return IE_SUCCESS even if new user name has not been found because it's not
6043 * clear whether it's returned always. */
6044 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
6045 struct isds_credentials_delivery
*credentials_delivery
,
6046 xmlDocPtr response
, const char *request_name
) {
6047 isds_error err
= IE_SUCCESS
;
6048 xmlXPathContextPtr xpath_ctx
= NULL
;
6049 xmlXPathObjectPtr result
= NULL
;
6050 char *xpath_query
= NULL
;
6052 if (!context
) return IE_INVALID_CONTEXT
;
6053 if (credentials_delivery
) {
6054 zfree(credentials_delivery
->token
);
6055 zfree(credentials_delivery
->new_user_name
);
6057 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6060 /* Extract optional token */
6061 if (credentials_delivery
) {
6062 xpath_ctx
= xmlXPathNewContext(response
);
6067 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6072 /* Verify root element */
6073 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6078 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6083 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6084 char *request_name_locale
= _isds_utf82locale(request_name
);
6085 isds_log(ILF_ISDS
, ILL_WARNING
,
6086 _("Wrong element in ISDS response for %s request "
6087 "while extracting credentials delivery details\n"),
6088 request_name_locale
);
6089 free(request_name_locale
);
6093 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6096 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6098 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6100 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6101 if (!credentials_delivery
->token
) {
6102 char *request_name_locale
= _isds_utf82locale(request_name
);
6103 isds_log(ILF_ISDS
, ILL_ERR
,
6104 _("ISDS did not return token on %s request "
6105 "even if requested\n"), request_name_locale
);
6106 free(request_name_locale
);
6113 xmlXPathFreeObject(result
);
6114 xmlXPathFreeContext(xpath_ctx
);
6120 /* Build XSD:tCreateDBInput request type for box creating.
6121 * @context is session context
6122 * @request outputs built XML tree
6123 * @service_name is request name of SERVICE_DB_MANIPULATION service
6124 * @box is box description to create including single primary user (in case of
6125 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6127 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6128 * box, or contact address of PFO box owner)
6129 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6130 * @upper_box_id is optional ID of supper box if currently created box is
6132 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6134 * @credentials_delivery is valid pointer if ISDS should return token that box
6135 * owner can use to obtain his new credentials in on-line way. Then valid email
6136 * member value should be supplied.
6137 * @approval is optional external approval of box manipulation */
6138 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6139 xmlNodePtr
*request
, const xmlChar
*service_name
,
6140 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6141 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6142 const xmlChar
*ceo_label
,
6143 const struct isds_credentials_delivery
*credentials_delivery
,
6144 const struct isds_approval
*approval
) {
6145 isds_error err
= IE_SUCCESS
;
6146 xmlNsPtr isds_ns
= NULL
;
6147 xmlNodePtr node
, dbPrimaryUsers
;
6148 xmlChar
*string
= NULL
;
6149 const struct isds_list
*item
;
6152 if (!context
) return IE_INVALID_CONTEXT
;
6153 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6157 /* Build CreateDataBox-similar request */
6158 *request
= xmlNewNode(NULL
, service_name
);
6160 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6161 isds_printf_message(context
, _("Could build %s request"),
6162 service_name_locale
);
6163 free(service_name_locale
);
6166 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6167 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6169 isds_log_message(context
, _("Could not create ISDS1 name space"));
6170 xmlFreeNode(*request
);
6174 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6176 isds_log_message(context
, _("Could not create ISDS name space"));
6177 xmlFreeNode(*request
);
6181 xmlSetNs(*request
, isds_ns
);
6183 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6184 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6185 if (err
) goto leave
;
6188 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6189 * verbose documentation allows none dbUserInfo */
6190 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6191 for (item
= users
; item
; item
= item
->next
) {
6193 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6194 err
= insert_DbUserInfo(context
,
6195 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6196 if (err
) goto leave
;
6200 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6201 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6202 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6204 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6205 if (err
) goto leave
;
6207 err
= insert_GExtApproval(context
, approval
, *request
);
6208 if (err
) goto leave
;
6212 xmlFreeNode(*request
);
6218 #endif /* HAVE_LIBCURL */
6222 * @context is session context
6223 * @box is box description to create including single primary user (in case of
6224 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6225 * ignored. It outputs box ID assigned by ISDS in dbID element.
6226 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6227 * box, or contact address of PFO box owner)
6228 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6229 * @upper_box_id is optional ID of supper box if currently created box is
6231 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6232 * @credentials_delivery is NULL if new password should be delivered off-line
6233 * to box owner. It is valid pointer if owner should obtain new password on-line
6234 * on dedicated web server. Then input @credentials_delivery.email value is
6235 * his e-mail address he must provide to dedicated web server together
6236 * with output reallocated @credentials_delivery.token member. Output
6237 * member @credentials_delivery.new_user_name is unused up on this call.
6238 * @approval is optional external approval of box manipulation
6239 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6240 * NULL, if you don't care.*/
6241 isds_error
isds_add_box(struct isds_ctx
*context
,
6242 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6243 const char *former_names
, const char *upper_box_id
,
6244 const char *ceo_label
,
6245 struct isds_credentials_delivery
*credentials_delivery
,
6246 const struct isds_approval
*approval
, char **refnumber
) {
6247 isds_error err
= IE_SUCCESS
;
6249 xmlNodePtr request
= NULL
;
6250 xmlDocPtr response
= NULL
;
6251 xmlXPathContextPtr xpath_ctx
= NULL
;
6252 xmlXPathObjectPtr result
= NULL
;
6256 if (!context
) return IE_INVALID_CONTEXT
;
6257 zfree(context
->long_message
);
6258 if (credentials_delivery
) {
6259 zfree(credentials_delivery
->token
);
6260 zfree(credentials_delivery
->new_user_name
);
6262 if (!box
) return IE_INVAL
;
6265 /* Scratch box ID */
6268 /* Build CreateDataBox request */
6269 err
= build_CreateDBInput_request(context
,
6270 &request
, BAD_CAST
"CreateDataBox",
6271 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6272 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6273 if (err
) goto leave
;
6275 /* Send it to server and process response */
6276 err
= send_destroy_request_check_response(context
,
6277 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6278 &response
, (xmlChar
**) refnumber
, NULL
);
6280 /* Extract box ID */
6281 xpath_ctx
= xmlXPathNewContext(response
);
6286 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6290 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6292 /* Extract optional token */
6293 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6297 xmlXPathFreeObject(result
);
6298 xmlXPathFreeContext(xpath_ctx
);
6299 xmlFreeDoc(response
);
6300 xmlFreeNode(request
);
6303 isds_log(ILF_ISDS
, ILL_DEBUG
,
6304 _("CreateDataBox request processed by server successfully.\n"));
6306 #else /* not HAVE_LIBCURL */
6314 /* Notify ISDS about new PFO entity.
6315 * This function has no real effect.
6316 * @context is session context
6317 * @box is PFO description including single primary user. aifoIsds,
6318 * address->adCode, address->adDistrict members are ignored.
6319 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6320 * @former_names is optional undocumented string. Pass NULL if you don't care.
6321 * @upper_box_id is optional ID of supper box if currently created box is
6323 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6324 * @approval is optional external approval of box manipulation
6325 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6326 * NULL, if you don't care.*/
6327 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6328 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6329 const char *former_names
, const char *upper_box_id
,
6330 const char *ceo_label
, const struct isds_approval
*approval
,
6332 isds_error err
= IE_SUCCESS
;
6334 xmlNodePtr request
= NULL
;
6337 if (!context
) return IE_INVALID_CONTEXT
;
6338 zfree(context
->long_message
);
6339 if (!box
) return IE_INVAL
;
6342 /* Build CreateDataBoxPFOInfo request */
6343 err
= build_CreateDBInput_request(context
,
6344 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6345 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6346 (xmlChar
*) ceo_label
, NULL
, approval
);
6347 if (err
) goto leave
;
6349 /* Send it to server and process response */
6350 err
= send_request_check_drop_response(context
,
6351 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6352 (xmlChar
**) refnumber
);
6353 /* XXX: XML Schema names output dbID element but textual documentation
6354 * states no box identifier is returned. */
6356 xmlFreeNode(request
);
6357 #else /* not HAVE_LIBCURL */
6364 /* Common implementation for removing given box.
6365 * @context is session context
6366 * @service_name is UTF-8 encoded name fo ISDS service
6367 * @box is box description to delete. aifoIsds, address->adCode,
6368 * address->adDistrict members are ignored.
6369 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6370 * carry sane value. If NULL, do not inject this information into request.
6371 * @approval is optional external approval of box manipulation
6372 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6373 * NULL, if you don't care.*/
6374 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6375 const xmlChar
*service_name
,
6376 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6377 const struct isds_approval
*approval
, char **refnumber
) {
6378 isds_error err
= IE_SUCCESS
;
6380 xmlNsPtr isds_ns
= NULL
;
6381 xmlNodePtr request
= NULL
;
6383 xmlChar
*string
= NULL
;
6387 if (!context
) return IE_INVALID_CONTEXT
;
6388 zfree(context
->long_message
);
6389 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6393 /* Build DeleteDataBox(Promptly) request */
6394 request
= xmlNewNode(NULL
, service_name
);
6396 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6397 isds_printf_message(context
,
6398 _("Could build %s request"), service_name_locale
);
6399 free(service_name_locale
);
6402 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6404 isds_log_message(context
, _("Could not create ISDS name space"));
6405 xmlFreeNode(request
);
6408 xmlSetNs(request
, isds_ns
);
6410 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6411 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6412 if (err
) goto leave
;
6415 err
= tm2datestring(since
, &string
);
6417 isds_log_message(context
,
6418 _("Could not convert `since' argument to ISO date string"));
6421 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6425 err
= insert_GExtApproval(context
, approval
, request
);
6426 if (err
) goto leave
;
6429 /* Send it to server and process response */
6430 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6431 service_name
, &request
, (xmlChar
**) refnumber
);
6434 xmlFreeNode(request
);
6436 #else /* not HAVE_LIBCURL */
6443 /* Remove given box permanently.
6444 * @context is session context
6445 * @box is box description to delete. aifoIsds, address->adCode,
6446 * address->adDistrict members are ignored.
6447 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6449 * @approval is optional external approval of box manipulation
6450 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6451 * NULL, if you don't care.*/
6452 isds_error
isds_delete_box(struct isds_ctx
*context
,
6453 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6454 const struct isds_approval
*approval
, char **refnumber
) {
6455 if (!context
) return IE_INVALID_CONTEXT
;
6456 zfree(context
->long_message
);
6457 if (!box
|| !since
) return IE_INVAL
;
6459 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6460 box
, since
, approval
, refnumber
);
6464 /* Undocumented function.
6465 * @context is session context
6466 * @box is box description to delete. aifoIsds, address->adCode,
6467 * address->adDistrict members are ignored.
6468 * @approval is optional external approval of box manipulation
6469 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6470 * NULL, if you don't care.*/
6471 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6472 const struct isds_DbOwnerInfo
*box
,
6473 const struct isds_approval
*approval
, char **refnumber
) {
6474 if (!context
) return IE_INVALID_CONTEXT
;
6475 zfree(context
->long_message
);
6476 if (!box
) return IE_INVAL
;
6478 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6479 box
, NULL
, approval
, refnumber
);
6483 /* Update data about given box.
6484 * @context is session context
6485 * @old_box current box description. aifoIsds, address->adCode,
6486 * address->adDistrict members are ignored.
6487 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6488 * address->adDistrict members are ignored.
6489 * @approval is optional external approval of box manipulation
6490 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6491 * NULL, if you don't care.*/
6492 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6493 const struct isds_DbOwnerInfo
*old_box
,
6494 const struct isds_DbOwnerInfo
*new_box
,
6495 const struct isds_approval
*approval
, char **refnumber
) {
6496 isds_error err
= IE_SUCCESS
;
6498 xmlNsPtr isds_ns
= NULL
;
6499 xmlNodePtr request
= NULL
;
6504 if (!context
) return IE_INVALID_CONTEXT
;
6505 zfree(context
->long_message
);
6506 if (!old_box
|| !new_box
) return IE_INVAL
;
6510 /* Build UpdateDataBoxDescr request */
6511 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6513 isds_log_message(context
,
6514 _("Could build UpdateDataBoxDescr request"));
6517 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6519 isds_log_message(context
, _("Could not create ISDS name space"));
6520 xmlFreeNode(request
);
6523 xmlSetNs(request
, isds_ns
);
6525 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6526 err
= insert_DbOwnerInfo(context
, old_box
, 0, node
);
6527 if (err
) goto leave
;
6529 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6530 err
= insert_DbOwnerInfo(context
, new_box
, 0, node
);
6531 if (err
) goto leave
;
6533 err
= insert_GExtApproval(context
, approval
, request
);
6534 if (err
) goto leave
;
6537 /* Send it to server and process response */
6538 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6539 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6542 xmlFreeNode(request
);
6543 #else /* not HAVE_LIBCURL */
6552 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6554 * @context is session context
6555 * @service is SOAP service
6556 * @service_name is name of request in @service
6557 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6558 * @box_id is box ID of interest
6559 * @approval is optional external approval of box manipulation
6560 * @response is server SOAP body response as XML document
6561 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6562 * NULL, if you don't care.
6563 * @return error coded from lower layer, context message will be set up
6565 static isds_error
build_send_dbid_request_check_response(
6566 struct isds_ctx
*context
, const isds_service service
,
6567 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6568 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6569 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6571 isds_error err
= IE_SUCCESS
;
6572 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6573 xmlNodePtr request
= NULL
, node
;
6574 xmlNsPtr isds_ns
= NULL
;
6576 if (!context
) return IE_INVALID_CONTEXT
;
6577 if (!service_name
|| !box_id
) return IE_INVAL
;
6578 if (!response
) return IE_INVAL
;
6580 /* Free output argument */
6581 xmlFreeDoc(*response
); *response
= NULL
;
6583 /* Prepare strings */
6584 service_name_locale
= _isds_utf82locale((char*)service_name
);
6585 if (!service_name_locale
) {
6589 box_id_locale
= _isds_utf82locale((char*)box_id
);
6590 if (!box_id_locale
) {
6596 request
= xmlNewNode(NULL
, service_name
);
6598 isds_printf_message(context
,
6599 _("Could not build %s request for %s box"), service_name_locale
,
6604 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6606 isds_log_message(context
, _("Could not create ISDS name space"));
6610 xmlSetNs(request
, isds_ns
);
6612 /* Add XSD:tIdDbInput children */
6613 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6614 INSERT_STRING(request
, box_id_element
, box_id
);
6615 err
= insert_GExtApproval(context
, approval
, request
);
6616 if (err
) goto leave
;
6618 /* Send request and check response*/
6619 err
= send_destroy_request_check_response(context
,
6620 service
, service_name
, &request
, response
, refnumber
, NULL
);
6623 free(service_name_locale
);
6624 free(box_id_locale
);
6625 xmlFreeNode(request
);
6628 #endif /* HAVE_LIBCURL */
6631 /* Get data about all users assigned to given box.
6632 * @context is session context
6634 * @users is automatically reallocated list of struct isds_DbUserInfo */
6635 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6636 struct isds_list
**users
) {
6637 isds_error err
= IE_SUCCESS
;
6639 xmlDocPtr response
= NULL
;
6640 xmlXPathContextPtr xpath_ctx
= NULL
;
6641 xmlXPathObjectPtr result
= NULL
;
6643 struct isds_list
*item
, *prev_item
= NULL
;
6646 if (!context
) return IE_INVALID_CONTEXT
;
6647 zfree(context
->long_message
);
6648 if (!users
|| !box_id
) return IE_INVAL
;
6649 isds_list_free(users
);
6653 /* Do request and check for success */
6654 err
= build_send_dbid_request_check_response(context
,
6655 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6656 BAD_CAST box_id
, NULL
, &response
, NULL
);
6657 if (err
) goto leave
;
6661 /* Prepare structure */
6662 xpath_ctx
= xmlXPathNewContext(response
);
6667 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6672 /* Set context node */
6673 result
= xmlXPathEvalExpression(BAD_CAST
6674 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6680 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6681 /* Iterate over all users */
6682 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6684 /* Prepare structure */
6685 item
= calloc(1, sizeof(*item
));
6690 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6691 if (i
== 0) *users
= item
;
6692 else prev_item
->next
= item
;
6696 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6697 err
= extract_DbUserInfo(context
,
6698 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6699 if (err
) goto leave
;
6705 isds_list_free(users
);
6708 xmlXPathFreeObject(result
);
6709 xmlXPathFreeContext(xpath_ctx
);
6710 xmlFreeDoc(response
);
6713 isds_log(ILF_ISDS
, ILL_DEBUG
,
6714 _("GetDataBoxUsers request processed by server "
6715 "successfully.\n"));
6716 #else /* not HAVE_LIBCURL */
6724 /* Update data about user assigned to given box.
6725 * @context is session context
6726 * @box is box identification. aifoIsds, address->adCode,
6727 * address->adDistrict members are ignored.
6728 * @old_user identifies user to update, aifo_ticket member is ignored
6729 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6730 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6731 * NULL, if you don't care.*/
6732 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6733 const struct isds_DbOwnerInfo
*box
,
6734 const struct isds_DbUserInfo
*old_user
,
6735 const struct isds_DbUserInfo
*new_user
,
6737 isds_error err
= IE_SUCCESS
;
6739 xmlNsPtr isds_ns
= NULL
;
6740 xmlNodePtr request
= NULL
;
6745 if (!context
) return IE_INVALID_CONTEXT
;
6746 zfree(context
->long_message
);
6747 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6751 /* Build UpdateDataBoxUser request */
6752 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6754 isds_log_message(context
,
6755 _("Could build UpdateDataBoxUser request"));
6758 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6760 isds_log_message(context
, _("Could not create ISDS name space"));
6761 xmlFreeNode(request
);
6764 xmlSetNs(request
, isds_ns
);
6766 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6767 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6768 if (err
) goto leave
;
6770 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6771 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
6772 if (err
) goto leave
;
6774 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6775 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
6776 if (err
) goto leave
;
6778 /* Send it to server and process response */
6779 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6780 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6783 xmlFreeNode(request
);
6784 #else /* not HAVE_LIBCURL */
6792 /* Undocumented function.
6793 * @context is session context
6794 * @box_id is UTF-8 encoded box identifier
6795 * @token is UTF-8 encoded temporary password
6796 * @user_id outputs UTF-8 encoded reallocated user identifier
6797 * @password outpus UTF-8 encoded reallocated user password
6798 * Output arguments will be nulled in case of error */
6799 isds_error
isds_activate(struct isds_ctx
*context
,
6800 const char *box_id
, const char *token
,
6801 char **user_id
, char **password
) {
6802 isds_error err
= IE_SUCCESS
;
6804 xmlNsPtr isds_ns
= NULL
;
6805 xmlNodePtr request
= NULL
, node
;
6806 xmlDocPtr response
= NULL
;
6807 xmlXPathContextPtr xpath_ctx
= NULL
;
6808 xmlXPathObjectPtr result
= NULL
;
6812 if (!context
) return IE_INVALID_CONTEXT
;
6813 zfree(context
->long_message
);
6815 if (user_id
) zfree(*user_id
);
6816 if (password
) zfree(*password
);
6818 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6822 /* Build Activate request */
6823 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6825 isds_log_message(context
, _("Could build Activate request"));
6828 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6830 isds_log_message(context
, _("Could not create ISDS name space"));
6831 xmlFreeNode(request
);
6834 xmlSetNs(request
, isds_ns
);
6836 INSERT_STRING(request
, "dbAccessDataId", token
);
6837 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6838 INSERT_STRING(request
, "dbID", box_id
);
6841 /* Send request and check response*/
6842 err
= send_destroy_request_check_response(context
,
6843 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6844 &response
, NULL
, NULL
);
6845 if (err
) goto leave
;
6849 xpath_ctx
= xmlXPathNewContext(response
);
6854 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6858 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6864 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6865 isds_log_message(context
, _("Missing ActivateResponse element"));
6869 if (result
->nodesetval
->nodeNr
> 1) {
6870 isds_log_message(context
, _("Multiple ActivateResponse element"));
6874 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6875 xmlXPathFreeObject(result
); result
= NULL
;
6877 EXTRACT_STRING("isds:userId", *user_id
);
6879 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6880 "but did not return `userId' element.\n"));
6882 EXTRACT_STRING("isds:password", *password
);
6884 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6885 "but did not return `password' element.\n"));
6888 xmlXPathFreeObject(result
);
6889 xmlXPathFreeContext(xpath_ctx
);
6890 xmlFreeDoc(response
);
6891 xmlFreeNode(request
);
6894 isds_log(ILF_ISDS
, ILL_DEBUG
,
6895 _("Activate request processed by server successfully.\n"));
6896 #else /* not HAVE_LIBCURL */
6904 /* Reset credentials of user assigned to given box.
6905 * @context is session context
6906 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6907 * members are ignored.
6908 * @user identifies user to reset password, aifo_ticket member is ignored
6909 * @fee_paid is true if fee has been paid, false otherwise
6910 * @approval is optional external approval of box manipulation
6911 * @credentials_delivery is NULL if new password should be delivered off-line
6912 * to the user. It is valid pointer if user should obtain new password on-line
6913 * on dedicated web server. Then input @credentials_delivery.email value is
6914 * user's e-mail address user must provide to dedicated web server together
6915 * with @credentials_delivery.token. The output reallocated token user needs
6916 * to use to authorize on the web server to view his new password. Output
6917 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6918 * ISDS changed up on this call. (No reason why server could change the name
6920 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6921 * NULL, if you don't care.*/
6922 isds_error
isds_reset_password(struct isds_ctx
*context
,
6923 const struct isds_DbOwnerInfo
*box
,
6924 const struct isds_DbUserInfo
*user
,
6925 const _Bool fee_paid
, const struct isds_approval
*approval
,
6926 struct isds_credentials_delivery
*credentials_delivery
,
6928 isds_error err
= IE_SUCCESS
;
6930 xmlNsPtr isds_ns
= NULL
;
6931 xmlNodePtr request
= NULL
, node
;
6932 xmlDocPtr response
= NULL
;
6936 if (!context
) return IE_INVALID_CONTEXT
;
6937 zfree(context
->long_message
);
6939 if (credentials_delivery
) {
6940 zfree(credentials_delivery
->token
);
6941 zfree(credentials_delivery
->new_user_name
);
6943 if (!box
|| !user
) return IE_INVAL
;
6947 /* Build NewAccessData request */
6948 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6950 isds_log_message(context
,
6951 _("Could build NewAccessData request"));
6954 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6956 isds_log_message(context
, _("Could not create ISDS name space"));
6957 xmlFreeNode(request
);
6960 xmlSetNs(request
, isds_ns
);
6962 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6963 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6964 if (err
) goto leave
;
6966 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6967 err
= insert_DbUserInfo(context
, user
, 0, node
);
6968 if (err
) goto leave
;
6970 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6972 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6973 if (err
) goto leave
;
6975 err
= insert_GExtApproval(context
, approval
, request
);
6976 if (err
) goto leave
;
6978 /* Send request and check response*/
6979 err
= send_destroy_request_check_response(context
,
6980 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6981 &response
, (xmlChar
**) refnumber
, NULL
);
6982 if (err
) goto leave
;
6985 /* Extract optional token */
6986 err
= extract_credentials_delivery(context
, credentials_delivery
,
6987 response
, "NewAccessData");
6990 xmlFreeDoc(response
);
6991 xmlFreeNode(request
);
6994 isds_log(ILF_ISDS
, ILL_DEBUG
,
6995 _("NewAccessData request processed by server "
6996 "successfully.\n"));
6997 #else /* not HAVE_LIBCURL */
7005 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7006 * code, destroy response and log success.
7007 * @context is ISDS session context.
7008 * @service_name is name of SERVICE_DB_MANIPULATION service
7009 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7010 * members are ignored.
7011 * @user identifies user to remove
7012 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7013 * @credentials_delivery is NULL if new user's password should be delivered
7014 * off-line to the user. It is valid pointer if user should obtain new
7015 * password on-line on dedicated web server. Then input
7016 * @credentials_delivery.email value is user's e-mail address user must
7017 * provide to dedicated web server together with @credentials_delivery.token.
7018 * The output reallocated token user needs to use to authorize on the web
7019 * server to view his new password. Output reallocated
7020 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7021 * assingned or changed up on this call.
7022 * @approval is optional external approval of box manipulation
7023 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7024 * NULL, if you don't care. */
7025 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
7026 struct isds_ctx
*context
, const xmlChar
*service_name
,
7027 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7028 _Bool honor_aifo_ticket
,
7029 struct isds_credentials_delivery
*credentials_delivery
,
7030 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7031 isds_error err
= IE_SUCCESS
;
7033 xmlNsPtr isds_ns
= NULL
;
7034 xmlNodePtr request
= NULL
, node
;
7035 xmlDocPtr response
= NULL
;
7039 if (!context
) return IE_INVALID_CONTEXT
;
7040 zfree(context
->long_message
);
7041 if (credentials_delivery
) {
7042 zfree(credentials_delivery
->token
);
7043 zfree(credentials_delivery
->new_user_name
);
7045 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7050 /* Build NewAccessData or similar request */
7051 request
= xmlNewNode(NULL
, service_name
);
7053 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7054 isds_printf_message(context
, _("Could not build %s request"),
7055 service_name_locale
);
7056 free(service_name_locale
);
7059 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7061 isds_log_message(context
, _("Could not create ISDS name space"));
7062 xmlFreeNode(request
);
7065 xmlSetNs(request
, isds_ns
);
7067 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7068 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7069 if (err
) goto leave
;
7071 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7072 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7073 if (err
) goto leave
;
7075 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7076 if (err
) goto leave
;
7078 err
= insert_GExtApproval(context
, approval
, request
);
7079 if (err
) goto leave
;
7082 /* Send request and check response*/
7083 err
= send_destroy_request_check_response(context
,
7084 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7087 xmlFreeNode(request
);
7090 /* Pick up credentials_delivery if requested */
7091 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7092 (char *)service_name
);
7095 xmlFreeDoc(response
);
7096 if (request
) xmlFreeNode(request
);
7099 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7100 isds_log(ILF_ISDS
, ILL_DEBUG
,
7101 _("%s request processed by server successfully.\n"),
7102 service_name_locale
);
7103 free(service_name_locale
);
7105 #else /* not HAVE_LIBCURL */
7113 /* Assign new user to given box.
7114 * @context is session context
7115 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7116 * members are ignored.
7117 * @user defines new user to add
7118 * @credentials_delivery is NULL if new user's password should be delivered
7119 * off-line to the user. It is valid pointer if user should obtain new
7120 * password on-line on dedicated web server. Then input
7121 * @credentials_delivery.email value is user's e-mail address user must
7122 * provide to dedicated web server together with @credentials_delivery.token.
7123 * The output reallocated token user needs to use to authorize on the web
7124 * server to view his new password. Output reallocated
7125 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7126 * assingned up on this call.
7127 * @approval is optional external approval of box manipulation
7128 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7129 * NULL, if you don't care.*/
7130 isds_error
isds_add_user(struct isds_ctx
*context
,
7131 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7132 struct isds_credentials_delivery
*credentials_delivery
,
7133 const struct isds_approval
*approval
, char **refnumber
) {
7134 return build_send_manipulationboxuser_request_check_drop_response(context
,
7135 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7136 approval
, (xmlChar
**) refnumber
);
7140 /* Remove user assigned to given box.
7141 * @context is session context
7142 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7143 * members are ignored.
7144 * @user identifies user to remove, aifo_ticket member is ignored
7145 * @approval is optional external approval of box manipulation
7146 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7147 * NULL, if you don't care.*/
7148 isds_error
isds_delete_user(struct isds_ctx
*context
,
7149 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7150 const struct isds_approval
*approval
, char **refnumber
) {
7151 return build_send_manipulationboxuser_request_check_drop_response(context
,
7152 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7153 (xmlChar
**) refnumber
);
7157 /* Get list of boxes in ZIP archive.
7158 * @context is session context
7159 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7160 * System recognizes following values currently: ALL (all boxes), UPG
7161 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7162 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7163 * commercial messages). This argument is a string because specification
7164 * states new values can appear in the future. Not all list types are
7165 * available to all users.
7166 * @buffer is automatically reallocated memory to store the list of boxes. The
7167 * list is zipped CSV file.
7168 * @buffer_length is size of @buffer data in bytes.
7169 * In case of error @buffer will be freed and @buffer_length will be
7171 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7172 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7173 isds_error err
= IE_SUCCESS
;
7175 xmlNsPtr isds_ns
= NULL
;
7176 xmlNodePtr request
= NULL
, node
;
7177 xmlDocPtr response
= NULL
;
7178 xmlXPathContextPtr xpath_ctx
= NULL
;
7179 xmlXPathObjectPtr result
= NULL
;
7180 char *string
= NULL
;
7184 if (!context
) return IE_INVALID_CONTEXT
;
7185 zfree(context
->long_message
);
7186 if (buffer
) zfree(*buffer
);
7187 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7191 /* Check if connection is established
7192 * TODO: This check should be done downstairs. */
7193 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7196 /* Build AuthenticateMessage request */
7197 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7199 isds_log_message(context
,
7200 _("Could not build GetDataBoxList request"));
7203 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7205 isds_log_message(context
, _("Could not create ISDS name space"));
7206 xmlFreeNode(request
);
7209 xmlSetNs(request
, isds_ns
);
7210 INSERT_STRING(request
, "dblType", list_identifier
);
7212 /* Send request to server and process response */
7213 err
= send_destroy_request_check_response(context
,
7214 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7215 &response
, NULL
, NULL
);
7216 if (err
) goto leave
;
7219 /* Extract Base-64 encoded ZIP file */
7220 xpath_ctx
= xmlXPathNewContext(response
);
7225 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7229 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7231 /* Decode non-empty archive */
7232 if (string
&& string
[0] != '\0') {
7233 *buffer_length
= _isds_b64decode(string
, buffer
);
7234 if (*buffer_length
== (size_t) -1) {
7235 isds_printf_message(context
,
7236 _("Error while Base64-decoding box list archive"));
7245 xmlXPathFreeObject(result
);
7246 xmlXPathFreeContext(xpath_ctx
);
7247 xmlFreeDoc(response
);
7248 xmlFreeNode(request
);
7251 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7252 "processed by server successfully.\n"));
7254 #else /* not HAVE_LIBCURL */
7262 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7263 * send it, check for error code, extract list of results, destroy response
7265 * @context is ISDS session context.
7266 * @service_name is name of SERVICE_DB_SEARCH service
7267 * @pfo_service is false if tDbOwnerInfo request should be built from
7268 * @criteria and corresponding result extracted. It is true if
7269 * tDbPersonalOwnerInfoRequest request should be built. The request and
7270 * response differ subset of significant isds_DbOwnerInfo structure members.
7271 * @criteria is filter. You should fill in at least some members.
7272 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7273 * members will be ignored. If @pfo_service is true, dbType, ic,
7274 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7275 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7277 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7278 * possibly empty. Input NULL or valid old structure. The same memebers as
7279 * in described for @criteria argument will be NULL according to @pfo_service
7282 * IE_SUCCESS if search succeeded, @boxes contains useful data
7283 * IE_NOEXIST if no such box exists, @boxes will be NULL
7284 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7285 * contains still valid data
7286 * other code if something bad happens. @boxes will be NULL. */
7287 static isds_error
build_send_findbox_request_check_parse_drop_response(
7288 struct isds_ctx
*context
, const xmlChar
*service_name
,
7289 _Bool pfo_service
, const struct isds_DbOwnerInfo
*criteria
,
7290 struct isds_list
**boxes
) {
7291 isds_error err
= IE_SUCCESS
;
7293 char *service_name_locale
= NULL
;
7294 _Bool truncated
= 0;
7295 xmlNsPtr isds_ns
= NULL
;
7296 xmlNodePtr request
= NULL
;
7297 xmlDocPtr response
= NULL
;
7298 xmlChar
*code
= NULL
, *message
= NULL
;
7299 xmlNodePtr db_owner_info
;
7300 xmlXPathContextPtr xpath_ctx
= NULL
;
7301 xmlXPathObjectPtr result
= NULL
;
7302 xmlChar
*string
= NULL
;
7306 if (!context
) return IE_INVALID_CONTEXT
;
7307 zfree(context
->long_message
);
7308 if (!boxes
) return IE_INVAL
;
7309 isds_list_free(boxes
);
7316 /* Check if connection is established
7317 * TODO: This check should be done downstairs. */
7318 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7319 service_name_locale
= _isds_utf82locale((char *) service_name
);
7322 request
= xmlNewNode(NULL
, service_name
);
7324 isds_printf_message(context
, _("Could not build %s request"),
7325 service_name_locale
);
7326 free(service_name_locale
);
7329 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7331 isds_log_message(context
, _("Could not create ISDS name space"));
7332 free(service_name_locale
);
7333 xmlFreeNode(request
);
7336 xmlSetNs(request
, isds_ns
);
7337 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7338 if (!db_owner_info
) {
7339 isds_printf_message(context
,
7340 _("Could not add dbOwnerInfo child to %s element"),
7341 service_name_locale
);
7342 free(service_name_locale
);
7343 xmlFreeNode(request
);
7347 err
= insert_DbOwnerInfo(context
, criteria
, pfo_service
, db_owner_info
);
7348 if (err
) goto leave
;
7352 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
7353 service_name_locale
);
7354 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7356 /* Destroy request */
7357 xmlFreeNode(request
); request
= NULL
;
7360 isds_log(ILF_ISDS
, ILL_DEBUG
,
7361 _("Processing ISDS response on %s request failed\n"),
7362 service_name_locale
);
7366 /* Check for response status */
7367 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7368 &code
, &message
, NULL
);
7370 isds_log(ILF_ISDS
, ILL_DEBUG
,
7371 _("ISDS response on %s request is missing status\n"),
7372 service_name_locale
);
7376 /* Request processed, but nothing found */
7377 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7378 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7379 char *code_locale
= _isds_utf82locale((char*)code
);
7380 char *message_locale
= _isds_utf82locale((char*)message
);
7381 isds_log(ILF_ISDS
, ILL_DEBUG
,
7382 _("Server did not find any box on %s request "
7383 "(code=%s, message=%s)\n"), service_name_locale
,
7384 code_locale
, message_locale
);
7385 isds_log_message(context
, message_locale
);
7387 free(message_locale
);
7392 /* Warning, not an error */
7393 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7394 char *code_locale
= _isds_utf82locale((char*)code
);
7395 char *message_locale
= _isds_utf82locale((char*)message
);
7396 isds_log(ILF_ISDS
, ILL_DEBUG
,
7397 _("Server truncated response on %s request "
7398 "(code=%s, message=%s)\n"), service_name_locale
,
7399 code_locale
, message_locale
);
7400 isds_log_message(context
, message_locale
);
7402 free(message_locale
);
7407 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7408 char *code_locale
= _isds_utf82locale((char*)code
);
7409 char *message_locale
= _isds_utf82locale((char*)message
);
7410 isds_log(ILF_ISDS
, ILL_DEBUG
,
7411 _("Server refused %s request (code=%s, message=%s)\n"),
7412 service_name_locale
, code_locale
, message_locale
);
7413 isds_log_message(context
, message_locale
);
7415 free(message_locale
);
7420 xpath_ctx
= xmlXPathNewContext(response
);
7425 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7430 /* Extract boxes if they present */
7431 if (-1 == isds_asprintf((char **)&string
,
7432 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7437 result
= xmlXPathEvalExpression(string
, xpath_ctx
);
7443 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7444 struct isds_list
*item
, *prev_item
= NULL
;
7445 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7446 item
= calloc(1, sizeof(*item
));
7452 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7453 if (i
== 0) *boxes
= item
;
7454 else prev_item
->next
= item
;
7457 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7458 err
= extract_DbOwnerInfo(context
,
7459 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7460 if (err
) goto leave
;
7466 isds_list_free(boxes
);
7468 if (truncated
) err
= IE_2BIG
;
7472 xmlFreeNode(request
);
7473 xmlXPathFreeObject(result
);
7474 xmlXPathFreeContext(xpath_ctx
);
7478 xmlFreeDoc(response
);
7481 isds_log(ILF_ISDS
, ILL_DEBUG
,
7482 _("%s request processed by server successfully.\n"),
7483 service_name_locale
);
7484 free(service_name_locale
);
7485 #else /* not HAVE_LIBCURL */
7493 /* Find boxes suiting given criteria.
7494 * @criteria is filter. You should fill in at least some members. aifoIsds,
7495 * address->adCode, address->adDistrict members are ignored.
7496 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7497 * possibly empty. Input NULL or valid old structure.
7499 * IE_SUCCESS if search succeeded, @boxes contains useful data
7500 * IE_NOEXIST if no such box exists, @boxes will be NULL
7501 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7502 * contains still valid data
7503 * other code if something bad happens. @boxes will be NULL. */
7504 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7505 const struct isds_DbOwnerInfo
*criteria
,
7506 struct isds_list
**boxes
) {
7507 return build_send_findbox_request_check_parse_drop_response(context
,
7508 BAD_CAST
"FindDataBox", 0, criteria
, boxes
);
7512 /* Find accessible FO-type boxes suiting given criteria.
7513 * @criteria is filter. You should fill in at least some members. dbType, ic,
7514 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7515 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7516 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7517 * possibly empty. Input NULL or valid old structure.
7519 * IE_SUCCESS if search succeeded, @boxes contains useful data
7520 * IE_NOEXIST if no such box exists, @boxes will be NULL
7521 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7522 * contains still valid data
7523 * other code if something bad happens. @boxes will be NULL. */
7524 isds_error
isds_FindPersonalDataBox(struct isds_ctx
*context
,
7525 const struct isds_DbOwnerInfo
*criteria
,
7526 struct isds_list
**boxes
) {
7527 return build_send_findbox_request_check_parse_drop_response(context
,
7528 BAD_CAST
"FindPersonalDataBox", 1, criteria
, boxes
);
7533 /* Convert a string with match markers into a plain string with list of
7534 * pointers to the matches
7535 * @string is an UTF-8 encoded non-constant string with match markers
7536 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7537 * The markers will be removed from the string.
7538 * @starts is a reallocated list of static pointers into the @string pointing
7539 * to places where match start markers occured.
7540 * @ends is a reallocated list of static pointers into the @string pointing
7541 * to places where match end markers occured.
7542 * @return IE_SUCCESS in case of no failure. */
7543 static isds_error
interpret_matches(xmlChar
*string
,
7544 struct isds_list
**starts
, struct isds_list
**ends
) {
7545 isds_error err
= IE_SUCCESS
;
7546 xmlChar
*pointer
, *destination
, *source
;
7547 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7549 isds_list_free(starts
);
7550 isds_list_free(ends
);
7551 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7552 if (NULL
== string
) return IE_SUCCESS
;
7554 for (pointer
= string
; *pointer
!= '\0';) {
7555 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7556 /* Remove the start marker */
7557 for (source
= pointer
+ 14, destination
= pointer
;
7558 *source
!= '\0'; source
++, destination
++) {
7559 *destination
= *source
;
7561 *destination
= '\0';
7562 /* Append the pointer into the list */
7563 item
= calloc(1, sizeof(*item
));
7568 item
->destructor
= (void (*)(void **))NULL
;
7569 item
->data
= pointer
;
7570 if (NULL
== prev_start
) *starts
= item
;
7571 else prev_start
->next
= item
;
7573 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7574 /* Remove the end marker */
7575 for (source
= pointer
+ 12, destination
= pointer
;
7576 *source
!= '\0'; source
++, destination
++) {
7577 *destination
= *source
;
7579 *destination
= '\0';
7580 /* Append the pointer into the list */
7581 item
= calloc(1, sizeof(*item
));
7586 item
->destructor
= (void (*)(void **))NULL
;
7587 item
->data
= pointer
;
7588 if (NULL
== prev_end
) *ends
= item
;
7589 else prev_end
->next
= item
;
7598 isds_list_free(starts
);
7599 isds_list_free(ends
);
7605 /* Convert isds:dbResult XML tree into structure
7606 * @context is ISDS context.
7607 * @fulltext_result is automatically reallocated found box structure.
7608 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7609 * @collect_matches is true to interpret match markers.
7610 * In case of error @result will be freed. */
7611 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7612 struct isds_fulltext_result
**fulltext_result
,
7613 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7614 isds_error err
= IE_SUCCESS
;
7615 xmlXPathObjectPtr result
= NULL
;
7616 char *string
= NULL
;
7618 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7619 if (NULL
== fulltext_result
) return IE_INVAL
;
7620 isds_fulltext_result_free(fulltext_result
);
7621 if (!xpath_ctx
) return IE_INVAL
;
7624 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7625 if (NULL
== *fulltext_result
) {
7631 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7633 EXTRACT_STRING("isds:dbType", string
);
7634 if (NULL
== string
) {
7636 isds_log_message(context
, _("Empty isds:dbType element"));
7639 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7641 if (err
== IE_ENUM
) {
7643 char *string_locale
= _isds_utf82locale(string
);
7644 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7646 free(string_locale
);
7652 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7653 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7655 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7656 if (err
) goto leave
;
7658 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7659 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7660 (*fulltext_result
)->dbEffectiveOVM
);
7662 EXTRACT_STRING("isds:dbSendOptions", string
);
7663 if (NULL
== string
) {
7665 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7668 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7669 (*fulltext_result
)->active
= 1;
7670 (*fulltext_result
)->public_sending
= 1;
7671 (*fulltext_result
)->commercial_sending
= 0;
7672 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7673 (*fulltext_result
)->active
= 1;
7674 (*fulltext_result
)->public_sending
= 1;
7675 (*fulltext_result
)->commercial_sending
= 1;
7676 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7677 (*fulltext_result
)->active
= 1;
7678 (*fulltext_result
)->public_sending
= 0;
7679 (*fulltext_result
)->commercial_sending
= 1;
7680 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7681 (*fulltext_result
)->active
= 1;
7682 (*fulltext_result
)->public_sending
= 0;
7683 (*fulltext_result
)->commercial_sending
= 0;
7684 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7685 (*fulltext_result
)->active
= 0;
7686 (*fulltext_result
)->public_sending
= 0;
7687 (*fulltext_result
)->commercial_sending
= 0;
7690 char *string_locale
= _isds_utf82locale(string
);
7691 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7693 free(string_locale
);
7698 /* Interpret match marks */
7699 if (collect_matches
) {
7700 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7701 &((*fulltext_result
)->name_match_start
),
7702 &((*fulltext_result
)->name_match_end
));
7703 if (err
) goto leave
;
7704 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7705 &((*fulltext_result
)->address_match_start
),
7706 &((*fulltext_result
)->address_match_end
));
7707 if (err
) goto leave
;
7711 if (err
) isds_fulltext_result_free(fulltext_result
);
7713 xmlXPathFreeObject(result
);
7716 #endif /* HAVE_LIBCURL */
7719 /* Find boxes matching a given full-text criteria.
7720 * @context is a session context
7721 * @query is a non-empty string which consists of words to search
7722 * @target selects box attributes to search for @query words. Pass NULL if you
7724 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7725 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7726 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7727 * which is DBTYPE_SYSTEM.
7728 * @page_size defines count of boxes to constitute a response page. It counts
7729 * from zero. Pass NULL to let server to use a default value (50 now).
7730 * @page_number defines ordinar number of the response page to return. It
7731 * counts from zero. Pass NULL to let server to use a default value (0 now).
7732 * @track_matches points to true for marking @query words found in the box
7733 * attributes. It points to false for not marking. Pass NULL to let the server
7734 * to use default value (false now).
7735 * @total_matching_boxes outputs reallocated number of all boxes matching the
7736 * query. Will be pointer to NULL if server did not provide the value.
7737 * Pass NULL if you don't care.
7738 * @current_page_beginning outputs reallocated ordinar number of the first box
7739 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7740 * server did not provide the value. Pass NULL if you don't care.
7741 * @current_page_size outputs reallocated count of boxes in the this @boxes
7742 * page. It will be pointer to NULL if the server did not provide the value.
7743 * Pass NULL if you don't care.
7744 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7745 * is the last one, false if more boxes match, NULL if the server did not
7746 * provude the value. Pass NULL if you don't care.
7747 * @boxes outputs reallocated list of isds_fulltext_result structures,
7750 * IE_SUCCESS if search succeeded
7751 * IE_2BIG if @page_size is too large
7752 * other code if something bad happens; output arguments will be NULL. */
7753 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7755 const isds_fulltext_target
*target
,
7756 const isds_DbType
*box_type
,
7757 const unsigned long int *page_size
,
7758 const unsigned long int *page_number
,
7759 const _Bool
*track_matches
,
7760 unsigned long int **total_matching_boxes
,
7761 unsigned long int **current_page_beginning
,
7762 unsigned long int **current_page_size
,
7764 struct isds_list
**boxes
) {
7765 isds_error err
= IE_SUCCESS
;
7767 xmlNsPtr isds_ns
= NULL
;
7768 xmlNodePtr request
= NULL
;
7769 xmlDocPtr response
= NULL
;
7771 xmlXPathContextPtr xpath_ctx
= NULL
;
7772 xmlXPathObjectPtr result
= NULL
;
7773 const xmlChar
*static_string
= NULL
;
7774 xmlChar
*string
= NULL
;
7776 const xmlChar
*codes
[] = {
7786 const char *meanings
[] = {
7787 N_("You are not allowed to perform the search"),
7788 N_("The query string is empty"),
7789 N_("Searched box ID is malformed"),
7790 N_("Searched organization ID is malformed"),
7791 N_("Invalid input"),
7792 N_("Requested page size is too large"),
7793 N_("Search engine internal error")
7795 const isds_error errors
[] = {
7804 struct code_map_isds_error map
= {
7806 .meanings
= meanings
,
7812 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7813 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7814 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7815 if (NULL
!= last_page
) zfree(*last_page
);
7816 isds_list_free(boxes
);
7818 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7819 zfree(context
->long_message
);
7821 if (NULL
== boxes
) return IE_INVAL
;
7823 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7824 isds_log_message(context
, _("Query string must be non-empty"));
7829 /* Check if connection is established
7830 * TODO: This check should be done downstairs. */
7831 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7833 /* Build FindDataBox request */
7834 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7835 if (NULL
== request
) {
7836 isds_log_message(context
,
7837 _("Could not build ISDSSearch2 request"));
7840 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7841 if(NULL
== isds_ns
) {
7842 isds_log_message(context
, _("Could not create ISDS name space"));
7843 xmlFreeNode(request
);
7846 xmlSetNs(request
, isds_ns
);
7848 INSERT_STRING(request
, "searchText", query
);
7850 if (NULL
!= target
) {
7851 static_string
= isds_fulltext_target2string(*(target
));
7852 if (NULL
== static_string
) {
7853 isds_printf_message(context
, _("Invalid target value: %d"),
7859 INSERT_STRING(request
, "searchType", static_string
);
7860 static_string
= NULL
;
7862 if (NULL
!= box_type
) {
7863 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7864 if (DBTYPE_SYSTEM
== *box_type
) {
7865 static_string
= BAD_CAST
"ALL";
7866 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
7867 static_string
= BAD_CAST
"OVM_MAIN";
7869 static_string
= isds_DbType2string(*(box_type
));
7870 if (NULL
== static_string
) {
7871 isds_printf_message(context
, _("Invalid box type value: %d"),
7878 INSERT_STRING(request
, "searchScope", static_string
);
7879 static_string
= NULL
;
7881 INSERT_ULONGINT(request
, "page", page_number
, string
);
7882 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7883 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7885 /* Send request and check response */
7886 err
= send_destroy_request_check_response(context
,
7887 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7888 &request
, &response
, NULL
, &map
);
7889 if (err
) goto leave
;
7891 /* Parse response */
7892 xpath_ctx
= xmlXPathNewContext(response
);
7893 if (NULL
== xpath_ctx
) {
7897 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7901 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7907 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7908 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7912 if (result
->nodesetval
->nodeNr
> 1) {
7913 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7917 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7918 xmlXPathFreeObject(result
); result
= NULL
;
7921 /* Extract counters */
7922 if (NULL
!= total_matching_boxes
) {
7923 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7925 if (NULL
!= current_page_size
) {
7926 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7928 if (NULL
!= current_page_beginning
) {
7929 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7931 if (NULL
!= last_page
) {
7932 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7934 xmlXPathFreeObject(result
); result
= NULL
;
7936 /* Extract boxes if they present */
7937 result
= xmlXPathEvalExpression(BAD_CAST
7938 "isds:dbResults/isds:dbResult", xpath_ctx
);
7939 if (NULL
== result
) {
7943 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7944 struct isds_list
*item
, *prev_item
= NULL
;
7945 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7946 item
= calloc(1, sizeof(*item
));
7952 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7953 if (i
== 0) *boxes
= item
;
7954 else prev_item
->next
= item
;
7957 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7958 err
= extract_dbResult(context
,
7959 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7960 (NULL
== track_matches
) ? 0 : *track_matches
);
7961 if (err
) goto leave
;
7967 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7968 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7969 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7970 if (NULL
!= last_page
) zfree(*last_page
);
7971 isds_list_free(boxes
);
7975 xmlFreeNode(request
);
7976 xmlXPathFreeObject(result
);
7977 xmlXPathFreeContext(xpath_ctx
);
7978 xmlFreeDoc(response
);
7981 isds_log(ILF_ISDS
, ILL_DEBUG
,
7982 _("ISDSSearch2 request processed by server successfully.\n"));
7983 #else /* not HAVE_LIBCURL */
7991 /* Get status of a box.
7992 * @context is ISDS session context.
7993 * @box_id is UTF-8 encoded box identifier as zero terminated string
7994 * @box_status is return value of box status.
7996 * IE_SUCCESS if box has been found and its status retrieved
7997 * IE_NOEXIST if box is not known to ISDS server
7998 * or other appropriate error.
7999 * You can use isds_DbState to enumerate box status. However out of enum
8000 * range value can be returned too. This is feature because ISDS
8001 * specification leaves the set of values open.
8002 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8003 * the box has been deleted, but ISDS still lists its former existence. */
8004 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
8005 long int *box_status
) {
8006 isds_error err
= IE_SUCCESS
;
8008 xmlNsPtr isds_ns
= NULL
;
8009 xmlNodePtr request
= NULL
, db_id
;
8010 xmlDocPtr response
= NULL
;
8011 xmlXPathContextPtr xpath_ctx
= NULL
;
8012 xmlXPathObjectPtr result
= NULL
;
8013 xmlChar
*string
= NULL
;
8015 const xmlChar
*codes
[] = {
8021 const char *meanings
[] = {
8022 "The box does not exist",
8023 "Box ID is malformed",
8026 const isds_error errors
[] = {
8031 struct code_map_isds_error map
= {
8033 .meanings
= meanings
,
8038 if (!context
) return IE_INVALID_CONTEXT
;
8039 zfree(context
->long_message
);
8040 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
8043 /* Check if connection is established
8044 * TODO: This check should be done downstairs. */
8045 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8048 /* Build CheckDataBox request */
8049 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
8051 isds_log_message(context
,
8052 _("Could build CheckDataBox request"));
8055 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8057 isds_log_message(context
, _("Could not create ISDS name space"));
8058 xmlFreeNode(request
);
8061 xmlSetNs(request
, isds_ns
);
8062 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
8064 isds_log_message(context
, _("Could not add dbID child to "
8065 "CheckDataBox element"));
8066 xmlFreeNode(request
);
8071 /* Send request and check response*/
8072 err
= send_destroy_request_check_response(context
,
8073 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
8074 &request
, &response
, NULL
, &map
);
8075 if (err
) goto leave
;
8079 xpath_ctx
= xmlXPathNewContext(response
);
8084 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8088 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
8094 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8095 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
8099 if (result
->nodesetval
->nodeNr
> 1) {
8100 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
8104 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8105 xmlXPathFreeObject(result
); result
= NULL
;
8107 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
8112 xmlXPathFreeObject(result
);
8113 xmlXPathFreeContext(xpath_ctx
);
8115 xmlFreeDoc(response
);
8118 isds_log(ILF_ISDS
, ILL_DEBUG
,
8119 _("CheckDataBox request processed by server successfully.\n"));
8120 #else /* not HAVE_LIBCURL */
8128 /* Get list of permissions to send commercial messages.
8129 * @context is ISDS session context.
8130 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8131 * @permissions is a reallocated list of permissions (struct
8132 * isds_commercial_permission*) to send commercial messages from @box_id. The
8133 * order of permissions is significant as the server applies the permissions
8134 * and associated pre-paid credits in the order. Empty list means no
8137 * IE_SUCCESS if the list has been obtained correctly,
8138 * or other appropriate error. */
8139 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8140 const char *box_id
, struct isds_list
**permissions
) {
8141 isds_error err
= IE_SUCCESS
;
8143 xmlDocPtr response
= NULL
;
8144 xmlXPathContextPtr xpath_ctx
= NULL
;
8145 xmlXPathObjectPtr result
= NULL
;
8148 if (!context
) return IE_INVALID_CONTEXT
;
8149 zfree(context
->long_message
);
8150 if (NULL
== permissions
) return IE_INVAL
;
8151 isds_list_free(permissions
);
8152 if (NULL
== box_id
) return IE_INVAL
;
8155 /* Check if connection is established */
8156 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8158 /* Do request and check for success */
8159 err
= build_send_dbid_request_check_response(context
,
8160 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8161 BAD_CAST box_id
, NULL
, &response
, NULL
);
8163 isds_log(ILF_ISDS
, ILL_DEBUG
,
8164 _("PDZInfo request processed by server successfully.\n"));
8168 /* Prepare structure */
8169 xpath_ctx
= xmlXPathNewContext(response
);
8174 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8179 /* Set context node */
8180 result
= xmlXPathEvalExpression(BAD_CAST
8181 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8187 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8188 struct isds_list
*prev_item
= NULL
;
8190 /* Iterate over all permission records */
8191 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8192 struct isds_list
*item
;
8194 /* Prepare structure */
8195 item
= calloc(1, sizeof(*item
));
8200 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8201 if (i
== 0) *permissions
= item
;
8202 else prev_item
->next
= item
;
8206 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8207 err
= extract_DbPDZRecord(context
,
8208 (struct isds_commercial_permission
**) (&item
->data
),
8210 if (err
) goto leave
;
8216 isds_list_free(permissions
);
8219 xmlXPathFreeObject(result
);
8220 xmlXPathFreeContext(xpath_ctx
);
8221 xmlFreeDoc(response
);
8223 #else /* not HAVE_LIBCURL */
8231 /* Get details about credit for sending pre-paid commercial messages.
8232 * @context is ISDS session context.
8233 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8234 * @from_date is first day of credit history to return in @history. Only
8235 * tm_year, tm_mon and tm_mday carry sane value.
8236 * @to_date is last day of credit history to return in @history. Only
8237 * tm_year, tm_mon and tm_mday carry sane value.
8238 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8239 * if you don't care. This and all other credit values are integers in
8240 * hundredths of Czech Crowns.
8241 * @email outputs notification e-mail address where notifications about credit
8242 * are sent. This is automatically reallocated string. Pass NULL if you don't
8243 * care. It can return NULL if no address is defined.
8244 * @history outputs auto-reallocated list of pointers to struct
8245 * isds_credit_event. Events in closed interval @from_time to @to_time are
8246 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8247 * are sorted by time.
8249 * IE_SUCCESS if the credit details have been obtained correctly,
8250 * or other appropriate error. Please note that server allows to retrieve
8251 * only limited history of events. */
8252 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8254 const struct tm
*from_date
, const struct tm
*to_date
,
8255 long int *credit
, char **email
, struct isds_list
**history
) {
8256 isds_error err
= IE_SUCCESS
;
8258 char *box_id_locale
= NULL
;
8259 xmlNodePtr request
= NULL
, node
;
8260 xmlNsPtr isds_ns
= NULL
;
8261 xmlChar
*string
= NULL
;
8263 xmlDocPtr response
= NULL
;
8264 xmlXPathContextPtr xpath_ctx
= NULL
;
8265 xmlXPathObjectPtr result
= NULL
;
8267 const xmlChar
*codes
[] = {
8275 const char *meanings
[] = {
8276 "Insufficient priviledges for the box",
8277 "The box does not exist",
8278 "Date is too long (history is not available after 15 months)",
8279 "Interval is too long (limit is 3 months)",
8282 const isds_error errors
[] = {
8289 struct code_map_isds_error map
= {
8291 .meanings
= meanings
,
8296 if (!context
) return IE_INVALID_CONTEXT
;
8297 zfree(context
->long_message
);
8299 /* Free output argument */
8300 if (NULL
!= credit
) *credit
= 0;
8301 if (NULL
!= email
) zfree(*email
);
8302 isds_list_free(history
);
8304 if (NULL
== box_id
) return IE_INVAL
;
8307 /* Check if connection is established */
8308 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8310 box_id_locale
= _isds_utf82locale((char*)box_id
);
8311 if (NULL
== box_id_locale
) {
8317 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8318 if (NULL
== request
) {
8319 isds_printf_message(context
,
8320 _("Could not build DataBoxCreditInfo request for %s box"),
8325 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8327 isds_log_message(context
, _("Could not create ISDS name space"));
8331 xmlSetNs(request
, isds_ns
);
8333 /* Add mandatory XSD:tIdDbInput child */
8334 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8335 /* Add mandatory dates elements with optional values */
8337 err
= tm2datestring(from_date
, &string
);
8339 isds_log_message(context
,
8340 _("Could not convert `from_date' argument to ISO date "
8344 INSERT_STRING(request
, "ciFromDate", string
);
8347 INSERT_STRING(request
, "ciFromDate", NULL
);
8350 err
= tm2datestring(to_date
, &string
);
8352 isds_log_message(context
,
8353 _("Could not convert `to_date' argument to ISO date "
8357 INSERT_STRING(request
, "ciTodate", string
);
8360 INSERT_STRING(request
, "ciTodate", NULL
);
8363 /* Send request and check response*/
8364 err
= send_destroy_request_check_response(context
,
8365 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8366 &request
, &response
, NULL
, &map
);
8367 if (err
) goto leave
;
8371 /* Set context to the root */
8372 xpath_ctx
= xmlXPathNewContext(response
);
8377 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8381 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8387 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8388 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8392 if (result
->nodesetval
->nodeNr
> 1) {
8393 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8397 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8398 xmlXPathFreeObject(result
); result
= NULL
;
8400 /* Extract common data */
8401 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8402 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8404 /* Extract records */
8405 if (NULL
== history
) goto leave
;
8406 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8412 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8413 struct isds_list
*prev_item
= NULL
;
8415 /* Iterate over all records */
8416 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8417 struct isds_list
*item
;
8419 /* Prepare structure */
8420 item
= calloc(1, sizeof(*item
));
8425 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8426 if (i
== 0) *history
= item
;
8427 else prev_item
->next
= item
;
8431 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8432 err
= extract_CiRecord(context
,
8433 (struct isds_credit_event
**) (&item
->data
),
8435 if (err
) goto leave
;
8441 isds_log(ILF_ISDS
, ILL_DEBUG
,
8442 _("DataBoxCreditInfo request processed by server successfully.\n"));
8445 isds_list_free(history
);
8446 if (NULL
!= email
) zfree(*email
)
8449 free(box_id_locale
);
8450 xmlXPathFreeObject(result
);
8451 xmlXPathFreeContext(xpath_ctx
);
8452 xmlFreeDoc(response
);
8454 #else /* not HAVE_LIBCURL */
8462 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8463 * code, destroy response and log success.
8464 * @context is ISDS session context.
8465 * @service_name is name of SERVICE_DB_MANIPULATION service
8466 * @box_id is UTF-8 encoded box identifier as zero terminated string
8467 * @approval is optional external approval of box manipulation
8468 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8469 * NULL, if you don't care. */
8470 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8471 struct isds_ctx
*context
, const xmlChar
*service_name
,
8472 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8473 xmlChar
**refnumber
) {
8474 isds_error err
= IE_SUCCESS
;
8476 xmlDocPtr response
= NULL
;
8479 if (!context
) return IE_INVALID_CONTEXT
;
8480 zfree(context
->long_message
);
8481 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8484 /* Check if connection is established */
8485 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8487 /* Do request and check for success */
8488 err
= build_send_dbid_request_check_response(context
,
8489 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8490 &response
, refnumber
);
8491 xmlFreeDoc(response
);
8494 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8495 isds_log(ILF_ISDS
, ILL_DEBUG
,
8496 _("%s request processed by server successfully.\n"),
8497 service_name_locale
);
8498 free(service_name_locale
);
8500 #else /* not HAVE_LIBCURL */
8508 /* Switch box into state where box can receive commercial messages (off by
8510 * @context is ISDS session context.
8511 * @box_id is UTF-8 encoded box identifier as zero terminated string
8512 * @allow is true for enable, false for disable commercial messages income
8513 * @approval is optional external approval of box manipulation
8514 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8515 * NULL, if you don't care. */
8516 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8517 const char *box_id
, const _Bool allow
,
8518 const struct isds_approval
*approval
, char **refnumber
) {
8519 return build_send_manipulationdbid_request_check_drop_response(context
,
8520 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8521 BAD_CAST
"ClearOpenAddressing",
8522 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8526 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8527 * message acceptance). This is just a box permission. Sender must apply
8528 * such role by sending each message.
8529 * @context is ISDS session context.
8530 * @box_id is UTF-8 encoded box identifier as zero terminated string
8531 * @allow is true for enable, false for disable OVM role permission
8532 * @approval is optional external approval of box manipulation
8533 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8534 * NULL, if you don't care. */
8535 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8536 const char *box_id
, const _Bool allow
,
8537 const struct isds_approval
*approval
, char **refnumber
) {
8538 return build_send_manipulationdbid_request_check_drop_response(context
,
8539 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8540 BAD_CAST
"ClearEffectiveOVM",
8541 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8545 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8546 * code, destroy response and log success.
8547 * @context is ISDS session context.
8548 * @service_name is name of SERVICE_DB_MANIPULATION service
8549 * @owner is structure describing box. aifoIsds, address->adCode,
8550 * address->adDistrict members are ignored.
8551 * @approval is optional external approval of box manipulation
8552 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8553 * NULL, if you don't care. */
8554 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8555 struct isds_ctx
*context
, const xmlChar
*service_name
,
8556 const struct isds_DbOwnerInfo
*owner
,
8557 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8558 isds_error err
= IE_SUCCESS
;
8560 char *service_name_locale
= NULL
;
8561 xmlNodePtr request
= NULL
, db_owner_info
;
8562 xmlNsPtr isds_ns
= NULL
;
8566 if (!context
) return IE_INVALID_CONTEXT
;
8567 zfree(context
->long_message
);
8568 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8571 service_name_locale
= _isds_utf82locale((char*)service_name
);
8572 if (!service_name_locale
) {
8578 request
= xmlNewNode(NULL
, service_name
);
8580 isds_printf_message(context
,
8581 _("Could not build %s request"), service_name_locale
);
8585 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8587 isds_log_message(context
, _("Could not create ISDS name space"));
8591 xmlSetNs(request
, isds_ns
);
8594 /* Add XSD:tOwnerInfoInput child*/
8595 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8596 err
= insert_DbOwnerInfo(context
, owner
, 0, db_owner_info
);
8597 if (err
) goto leave
;
8599 /* Add XSD:gExtApproval*/
8600 err
= insert_GExtApproval(context
, approval
, request
);
8601 if (err
) goto leave
;
8603 /* Send it to server and process response */
8604 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8605 service_name
, &request
, refnumber
);
8608 xmlFreeNode(request
);
8609 free(service_name_locale
);
8610 #else /* not HAVE_LIBCURL */
8618 /* Switch box accessibility state on request of box owner.
8619 * Despite the name, owner must do the request off-line. This function is
8620 * designed for such off-line meeting points (e.g. Czech POINT).
8621 * @context is ISDS session context.
8622 * @box identifies box to switch accessibility state. aifoIsds,
8623 * address->adCode, address->adDistrict members are ignored.
8624 * @allow is true for making accessible, false to disallow access.
8625 * @approval is optional external approval of box manipulation
8626 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8627 * NULL, if you don't care. */
8628 isds_error
isds_switch_box_accessibility_on_owner_request(
8629 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8630 const _Bool allow
, const struct isds_approval
*approval
,
8632 return build_send_manipulationdbowner_request_check_drop_response(context
,
8633 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8634 BAD_CAST
"DisableOwnDataBox",
8635 box
, approval
, (xmlChar
**) refnumber
);
8639 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8641 * @context is ISDS session context.
8642 * @box identifies box to switch accessibility state. aifoIsds,
8643 * address->adCode, address->adDistrict members are ignored.
8644 * @since is date since accessibility has been denied. This can be past too.
8645 * Only tm_year, tm_mon and tm_mday carry sane value.
8646 * @approval is optional external approval of box manipulation
8647 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8648 * NULL, if you don't care. */
8649 isds_error
isds_disable_box_accessibility_externaly(
8650 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8651 const struct tm
*since
, const struct isds_approval
*approval
,
8653 isds_error err
= IE_SUCCESS
;
8655 char *service_name_locale
= NULL
;
8656 xmlNodePtr request
= NULL
, node
;
8657 xmlNsPtr isds_ns
= NULL
;
8658 xmlChar
*string
= NULL
;
8662 if (!context
) return IE_INVALID_CONTEXT
;
8663 zfree(context
->long_message
);
8664 if (!box
|| !since
) return IE_INVAL
;
8668 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8670 isds_printf_message(context
,
8671 _("Could not build %s request"), "DisableDataBoxExternally");
8675 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8677 isds_log_message(context
, _("Could not create ISDS name space"));
8681 xmlSetNs(request
, isds_ns
);
8684 /* Add @box identification */
8685 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8686 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
8687 if (err
) goto leave
;
8689 /* Add @since date */
8690 err
= tm2datestring(since
, &string
);
8692 isds_log_message(context
,
8693 _("Could not convert `since' argument to ISO date string"));
8696 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8700 err
= insert_GExtApproval(context
, approval
, request
);
8701 if (err
) goto leave
;
8703 /* Send it to server and process response */
8704 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8705 BAD_CAST
"DisableDataBoxExternally", &request
,
8706 (xmlChar
**) refnumber
);
8710 xmlFreeNode(request
);
8711 free(service_name_locale
);
8712 #else /* not HAVE_LIBCURL */
8721 /* Insert struct isds_message data (envelope (recipient data optional) and
8722 * documents into XML tree
8723 * @context is session context
8724 * @outgoing_message is libisds structure with message data
8725 * @create_message is XML CreateMessage or CreateMultipleMessage element
8726 * @process_recipient true for recipient data serialization, false for no
8728 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8729 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8730 const _Bool process_recipient
) {
8732 isds_error err
= IE_SUCCESS
;
8733 xmlNodePtr envelope
, dm_files
, node
;
8734 xmlChar
*string
= NULL
;
8736 if (!context
) return IE_INVALID_CONTEXT
;
8737 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8740 /* Build envelope */
8741 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8743 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8744 "%s element"), create_message
->name
);
8748 if (!outgoing_message
->envelope
) {
8749 isds_log_message(context
, _("Outgoing message is missing envelope"));
8754 /* Insert optional message type */
8755 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8757 if (err
) goto leave
;
8759 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8760 outgoing_message
->envelope
->dmSenderOrgUnit
);
8761 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8762 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8764 if (process_recipient
) {
8765 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8766 isds_log_message(context
,
8767 _("Outgoing message is missing recipient box identifier"));
8771 INSERT_STRING(envelope
, "dbIDRecipient",
8772 outgoing_message
->envelope
->dbIDRecipient
);
8774 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8775 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8776 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8777 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8778 INSERT_STRING(envelope
, "dmToHands",
8779 outgoing_message
->envelope
->dmToHands
);
8782 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8784 INSERT_STRING(envelope
, "dmAnnotation",
8785 outgoing_message
->envelope
->dmAnnotation
);
8787 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8788 0, 50, "dmRecipientRefNumber");
8789 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8790 outgoing_message
->envelope
->dmRecipientRefNumber
);
8792 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8793 0, 50, "dmSenderRefNumber");
8794 INSERT_STRING(envelope
, "dmSenderRefNumber",
8795 outgoing_message
->envelope
->dmSenderRefNumber
);
8797 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8798 0, 50, "dmRecipientIdent");
8799 INSERT_STRING(envelope
, "dmRecipientIdent",
8800 outgoing_message
->envelope
->dmRecipientIdent
);
8802 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8803 0, 50, "dmSenderIdent");
8804 INSERT_STRING(envelope
, "dmSenderIdent",
8805 outgoing_message
->envelope
->dmSenderIdent
);
8807 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8808 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8809 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8810 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8811 INSERT_STRING(envelope
, "dmLegalTitleSect",
8812 outgoing_message
->envelope
->dmLegalTitleSect
);
8813 INSERT_STRING(envelope
, "dmLegalTitlePar",
8814 outgoing_message
->envelope
->dmLegalTitlePar
);
8815 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8816 outgoing_message
->envelope
->dmLegalTitlePoint
);
8818 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8819 outgoing_message
->envelope
->dmPersonalDelivery
);
8820 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8821 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8823 /* ???: Should we require value for dbEffectiveOVM sender?
8824 * ISDS has default as true */
8825 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8826 INSERT_BOOLEAN(envelope
, "dmOVM",
8827 outgoing_message
->envelope
->dmPublishOwnID
);
8830 /* Append dmFiles */
8831 if (!outgoing_message
->documents
) {
8832 isds_log_message(context
,
8833 _("Outgoing message is missing list of documents"));
8837 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8839 isds_printf_message(context
, _("Could not add dmFiles child to "
8840 "%s element"), create_message
->name
);
8845 /* Check for document hierarchy */
8846 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8847 if (err
) goto leave
;
8849 /* Process each document */
8850 for (struct isds_list
*item
=
8851 (struct isds_list
*) outgoing_message
->documents
;
8852 item
; item
= item
->next
) {
8854 isds_log_message(context
,
8855 _("List of documents contains empty item"));
8859 /* FIXME: Check for dmFileMetaType and for document references.
8860 * Only first document can be of MAIN type */
8861 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8864 if (err
) goto leave
;
8871 #endif /* HAVE_LIBCURL */
8874 /* Send a message via ISDS to a recipient
8875 * @context is session context
8876 * @outgoing_message is message to send; Some members are mandatory (like
8877 * dbIDRecipient), some are optional and some are irrelevant (especially data
8878 * about sender). Included pointer to isds_list documents must contain at
8879 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8880 * members will be filled with valid data from ISDS. Exact list of write
8881 * members is subject to change. Currently dmID is changed.
8882 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8883 isds_error
isds_send_message(struct isds_ctx
*context
,
8884 struct isds_message
*outgoing_message
) {
8886 isds_error err
= IE_SUCCESS
;
8888 xmlNsPtr isds_ns
= NULL
;
8889 xmlNodePtr request
= NULL
;
8890 xmlDocPtr response
= NULL
;
8891 xmlChar
*code
= NULL
, *message
= NULL
;
8892 xmlXPathContextPtr xpath_ctx
= NULL
;
8893 xmlXPathObjectPtr result
= NULL
;
8894 /*_Bool message_is_complete = 0;*/
8897 if (!context
) return IE_INVALID_CONTEXT
;
8898 zfree(context
->long_message
);
8899 if (!outgoing_message
) return IE_INVAL
;
8902 /* Check if connection is established
8903 * TODO: This check should be done downstairs. */
8904 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8907 /* Build CreateMessage request */
8908 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8910 isds_log_message(context
,
8911 _("Could not build CreateMessage request"));
8914 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8916 isds_log_message(context
, _("Could not create ISDS name space"));
8917 xmlFreeNode(request
);
8920 xmlSetNs(request
, isds_ns
);
8922 /* Append envelope and files */
8923 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8924 if (err
) goto leave
;
8927 /* Signal we can serialize message since now */
8928 /*message_is_complete = 1;*/
8931 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8934 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8936 /* Don't' destroy request, we want to provide it to application later */
8939 isds_log(ILF_ISDS
, ILL_DEBUG
,
8940 _("Processing ISDS response on CreateMessage "
8941 "request failed\n"));
8945 /* Check for response status */
8946 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8947 &code
, &message
, NULL
);
8949 isds_log(ILF_ISDS
, ILL_DEBUG
,
8950 _("ISDS response on CreateMessage request "
8951 "is missing status\n"));
8955 /* Request processed, but refused by server or server failed */
8956 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8957 char *box_id_locale
=
8958 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8959 char *code_locale
= _isds_utf82locale((char*)code
);
8960 char *message_locale
= _isds_utf82locale((char*)message
);
8961 isds_log(ILF_ISDS
, ILL_DEBUG
,
8962 _("Server did not accept message for %s on CreateMessage "
8963 "request (code=%s, message=%s)\n"),
8964 box_id_locale
, code_locale
, message_locale
);
8965 isds_log_message(context
, message_locale
);
8966 free(box_id_locale
);
8968 free(message_locale
);
8975 xpath_ctx
= xmlXPathNewContext(response
);
8980 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8984 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8990 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8991 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8995 if (result
->nodesetval
->nodeNr
> 1) {
8996 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
9000 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9001 xmlXPathFreeObject(result
); result
= NULL
;
9003 if (outgoing_message
->envelope
->dmID
) {
9004 free(outgoing_message
->envelope
->dmID
);
9005 outgoing_message
->envelope
->dmID
= NULL
;
9007 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
9008 if (!outgoing_message
->envelope
->dmID
) {
9009 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
9010 "but did not return assigned message ID\n"));
9014 /* TODO: Serialize message into structure member raw */
9015 /* XXX: Each web service transport message in different format.
9016 * Therefore it's not possible to save them directly.
9017 * To save them, one must figure out common format.
9018 * We can leave it on application, or we can implement the ESS format. */
9019 /*if (message_is_complete) {
9020 if (outgoing_message->envelope->dmID) {
9022 /* Add assigned message ID as first child*/
9023 /*xmlNodePtr dmid_text = xmlNewText(
9024 (xmlChar *) outgoing_message->envelope->dmID);
9025 if (!dmid_text) goto serialization_failed;
9027 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9029 if (!dmid_element) {
9030 xmlFreeNode(dmid_text);
9031 goto serialization_failed;
9034 xmlNodePtr dmid_element_with_text =
9035 xmlAddChild(dmid_element, dmid_text);
9036 if (!dmid_element_with_text) {
9037 xmlFreeNode(dmid_element);
9038 xmlFreeNode(dmid_text);
9039 goto serialization_failed;
9042 node = xmlAddPrevSibling(envelope->childern,
9043 dmid_element_with_text);
9045 xmlFreeNodeList(dmid_element_with_text);
9046 goto serialization_failed;
9050 /* Serialize message with ID into raw */
9051 /*buffer = serialize_element(envelope)*/
9054 serialization_failed:
9059 xmlXPathFreeObject(result
);
9060 xmlXPathFreeContext(xpath_ctx
);
9064 xmlFreeDoc(response
);
9065 xmlFreeNode(request
);
9068 isds_log(ILF_ISDS
, ILL_DEBUG
,
9069 _("CreateMessage request processed by server "
9070 "successfully.\n"));
9071 #else /* not HAVE_LIBCURL */
9079 /* Send a message via ISDS to a multiple recipients
9080 * @context is session context
9081 * @outgoing_message is message to send; Some members are mandatory,
9082 * some are optional and some are irrelevant (especially data
9083 * about sender). Data about recipient will be substituted by ISDS from
9084 * @copies. Included pointer to isds_list documents must
9085 * contain at least one document of FILEMETATYPE_MAIN.
9086 * @copies is list of isds_message_copy structures addressing all desired
9087 * recipients. This is read-write structure, some members will be filled with
9088 * valid data from ISDS (message IDs, error codes, error descriptions).
9090 * ISDS_SUCCESS if all messages have been sent
9091 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9092 * succeeded messages can be identified by copies->data->error),
9093 * or other error code if something other goes wrong. */
9094 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
9095 const struct isds_message
*outgoing_message
,
9096 struct isds_list
*copies
) {
9098 isds_error err
= IE_SUCCESS
;
9100 isds_error append_err
;
9101 xmlNsPtr isds_ns
= NULL
;
9102 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
9103 struct isds_list
*item
;
9104 struct isds_message_copy
*copy
;
9105 xmlDocPtr response
= NULL
;
9106 xmlChar
*code
= NULL
, *message
= NULL
;
9107 xmlXPathContextPtr xpath_ctx
= NULL
;
9108 xmlXPathObjectPtr result
= NULL
;
9109 xmlChar
*string
= NULL
;
9113 if (!context
) return IE_INVALID_CONTEXT
;
9114 zfree(context
->long_message
);
9115 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9118 /* Check if connection is established
9119 * TODO: This check should be done downstairs. */
9120 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9123 /* Build CreateMultipleMessage request */
9124 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9126 isds_log_message(context
,
9127 _("Could not build CreateMultipleMessage request"));
9130 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9132 isds_log_message(context
, _("Could not create ISDS name space"));
9133 xmlFreeNode(request
);
9136 xmlSetNs(request
, isds_ns
);
9139 /* Build recipients */
9140 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9142 isds_log_message(context
, _("Could not add dmRecipients child to "
9143 "CreateMultipleMessage element"));
9144 xmlFreeNode(request
);
9148 /* Insert each recipient */
9149 for (item
= copies
; item
; item
= item
->next
) {
9150 copy
= (struct isds_message_copy
*) item
->data
;
9152 isds_log_message(context
,
9153 _("`copies' list item contains empty data"));
9158 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9160 isds_log_message(context
, _("Could not add dmRecipient child to "
9161 "dmRecipients element"));
9166 if (!copy
->dbIDRecipient
) {
9167 isds_log_message(context
,
9168 _("Message copy is missing recipient box identifier"));
9172 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9173 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9174 copy
->dmRecipientOrgUnit
);
9175 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9176 copy
->dmRecipientOrgUnitNum
, string
);
9177 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9180 /* Append envelope and files */
9181 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9182 if (err
) goto leave
;
9185 isds_log(ILF_ISDS
, ILL_DEBUG
,
9186 _("Sending CreateMultipleMessage request to ISDS\n"));
9189 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9191 isds_log(ILF_ISDS
, ILL_DEBUG
,
9192 _("Processing ISDS response on CreateMultipleMessage "
9193 "request failed\n"));
9197 /* Check for response status */
9198 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9199 &code
, &message
, NULL
);
9201 isds_log(ILF_ISDS
, ILL_DEBUG
,
9202 _("ISDS response on CreateMultipleMessage request "
9203 "is missing status\n"));
9207 /* Request processed, but some copies failed */
9208 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9209 char *box_id_locale
=
9210 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9211 char *code_locale
= _isds_utf82locale((char*)code
);
9212 char *message_locale
= _isds_utf82locale((char*)message
);
9213 isds_log(ILF_ISDS
, ILL_DEBUG
,
9214 _("Server did accept message for multiple recipients "
9215 "on CreateMultipleMessage request but delivery to "
9216 "some of them failed (code=%s, message=%s)\n"),
9217 box_id_locale
, code_locale
, message_locale
);
9218 isds_log_message(context
, message_locale
);
9219 free(box_id_locale
);
9221 free(message_locale
);
9222 err
= IE_PARTIAL_SUCCESS
;
9225 /* Request refused by server as whole */
9226 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9227 char *box_id_locale
=
9228 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9229 char *code_locale
= _isds_utf82locale((char*)code
);
9230 char *message_locale
= _isds_utf82locale((char*)message
);
9231 isds_log(ILF_ISDS
, ILL_DEBUG
,
9232 _("Server did not accept message for multiple recipients "
9233 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9234 box_id_locale
, code_locale
, message_locale
);
9235 isds_log_message(context
, message_locale
);
9236 free(box_id_locale
);
9238 free(message_locale
);
9245 xpath_ctx
= xmlXPathNewContext(response
);
9250 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9254 result
= xmlXPathEvalExpression(
9255 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9256 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9262 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9263 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9268 /* Extract message ID and delivery status for each copy */
9269 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9270 item
= item
->next
, i
++) {
9271 copy
= (struct isds_message_copy
*) item
->data
;
9272 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9274 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9280 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9281 isds_printf_message(context
, _("ISDS returned unexpected number of "
9282 "message copy delivery states: %d"),
9283 result
->nodesetval
->nodeNr
);
9292 xmlXPathFreeObject(result
);
9293 xmlXPathFreeContext(xpath_ctx
);
9297 xmlFreeDoc(response
);
9298 xmlFreeNode(request
);
9301 isds_log(ILF_ISDS
, ILL_DEBUG
,
9302 _("CreateMultipleMessageResponse request processed by server "
9303 "successfully.\n"));
9304 #else /* not HAVE_LIBCURL */
9312 /* Get list of messages. This is common core for getting sent or received
9314 * Any criterion argument can be NULL, if you don't care about it.
9315 * @context is session context. Must not be NULL.
9316 * @outgoing_direction is true if you want list of outgoing messages,
9317 * it's false if you want incoming messages.
9318 * @from_time is minimal time and date of message sending inclusive.
9319 * @to_time is maximal time and date of message sending inclusive
9320 * @organization_unit_number is number of sender/recipient respectively.
9321 * @status_filter is bit field of isds_message_status values. Use special
9322 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9323 * all values, you can use bit-wise arithmetic if you want.)
9324 * @offset is index of first message we are interested in. First message is 1.
9325 * Set to 0 (or 1) if you don't care.
9326 * @number is maximal length of list you want to get as input value, outputs
9327 * number of messages matching these criteria. Can be NULL if you don't care
9328 * (applies to output value either).
9329 * @messages is automatically reallocated list of isds_message's. Be ware that
9330 * it returns only brief overview (envelope and some other fields) about each
9331 * message, not the complete message. FIXME: Specify exact fields.
9332 * The list is sorted by delivery time in ascending order.
9333 * Use NULL if you don't care about don't need the data (useful if you want to
9334 * know only the @number). If you provide &NULL, list will be allocated on
9335 * heap, if you provide pointer to non-NULL, list will be freed automatically
9336 * at first. Also in case of error the list will be NULLed.
9337 * @return IE_SUCCESS or appropriate error code. */
9338 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9339 _Bool outgoing_direction
,
9340 const struct timeval
*from_time
, const struct timeval
*to_time
,
9341 const long int *organization_unit_number
,
9342 const unsigned int status_filter
,
9343 const unsigned long int offset
, unsigned long int *number
,
9344 struct isds_list
**messages
) {
9346 isds_error err
= IE_SUCCESS
;
9348 xmlNsPtr isds_ns
= NULL
;
9349 xmlNodePtr request
= NULL
, node
;
9350 xmlDocPtr response
= NULL
;
9351 xmlChar
*code
= NULL
, *message
= NULL
;
9352 xmlXPathContextPtr xpath_ctx
= NULL
;
9353 xmlXPathObjectPtr result
= NULL
;
9354 xmlChar
*string
= NULL
;
9358 if (!context
) return IE_INVALID_CONTEXT
;
9359 zfree(context
->long_message
);
9361 /* Free former message list if any */
9362 if (messages
) isds_list_free(messages
);
9365 /* Check if connection is established
9366 * TODO: This check should be done downstairs. */
9367 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9369 /* Build GetListOf*Messages request */
9370 request
= xmlNewNode(NULL
,
9371 (outgoing_direction
) ?
9372 BAD_CAST
"GetListOfSentMessages" :
9373 BAD_CAST
"GetListOfReceivedMessages"
9376 isds_log_message(context
,
9377 (outgoing_direction
) ?
9378 _("Could not build GetListOfSentMessages request") :
9379 _("Could not build GetListOfReceivedMessages request")
9383 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9385 isds_log_message(context
, _("Could not create ISDS name space"));
9386 xmlFreeNode(request
);
9389 xmlSetNs(request
, isds_ns
);
9393 err
= timeval2timestring(from_time
, &string
);
9394 if (err
) goto leave
;
9396 INSERT_STRING(request
, "dmFromTime", string
);
9397 free(string
); string
= NULL
;
9400 err
= timeval2timestring(to_time
, &string
);
9401 if (err
) goto leave
;
9403 INSERT_STRING(request
, "dmToTime", string
);
9404 free(string
); string
= NULL
;
9406 if (outgoing_direction
) {
9407 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9408 organization_unit_number
, string
);
9410 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9411 organization_unit_number
, string
);
9414 if (status_filter
> MESSAGESTATE_ANY
) {
9415 isds_printf_message(context
,
9416 _("Invalid message state filter value: %ld"), status_filter
);
9420 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9423 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9425 INSERT_STRING(request
, "dmOffset", "1");
9428 /* number 0 means no limit */
9429 if (number
&& *number
== 0) {
9430 INSERT_STRING(request
, "dmLimit", NULL
);
9432 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9436 isds_log(ILF_ISDS
, ILL_DEBUG
,
9437 (outgoing_direction
) ?
9438 _("Sending GetListOfSentMessages request to ISDS\n") :
9439 _("Sending GetListOfReceivedMessages request to ISDS\n")
9443 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9444 xmlFreeNode(request
); request
= NULL
;
9447 isds_log(ILF_ISDS
, ILL_DEBUG
,
9448 (outgoing_direction
) ?
9449 _("Processing ISDS response on GetListOfSentMessages "
9450 "request failed\n") :
9451 _("Processing ISDS response on GetListOfReceivedMessages "
9457 /* Check for response status */
9458 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9459 &code
, &message
, NULL
);
9461 isds_log(ILF_ISDS
, ILL_DEBUG
,
9462 (outgoing_direction
) ?
9463 _("ISDS response on GetListOfSentMessages request "
9464 "is missing status\n") :
9465 _("ISDS response on GetListOfReceivedMessages request "
9466 "is missing status\n")
9471 /* Request processed, but nothing found */
9472 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9473 char *code_locale
= _isds_utf82locale((char*)code
);
9474 char *message_locale
= _isds_utf82locale((char*)message
);
9475 isds_log(ILF_ISDS
, ILL_DEBUG
,
9476 (outgoing_direction
) ?
9477 _("Server refused GetListOfSentMessages request "
9478 "(code=%s, message=%s)\n") :
9479 _("Server refused GetListOfReceivedMessages request "
9480 "(code=%s, message=%s)\n"),
9481 code_locale
, message_locale
);
9482 isds_log_message(context
, message_locale
);
9484 free(message_locale
);
9491 xpath_ctx
= xmlXPathNewContext(response
);
9496 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9500 result
= xmlXPathEvalExpression(
9501 (outgoing_direction
) ?
9502 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9503 "isds:dmRecords/isds:dmRecord" :
9504 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9505 "isds:dmRecords/isds:dmRecord",
9512 /* Fill output arguments in */
9513 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9514 struct isds_envelope
*envelope
;
9515 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9517 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9518 /* Create new message */
9519 item
= calloc(1, sizeof(*item
));
9524 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9525 item
->data
= calloc(1, sizeof(struct isds_message
));
9527 isds_list_free(&item
);
9532 /* Extract envelope data */
9533 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9535 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9537 isds_list_free(&item
);
9541 /* Attach extracted envelope */
9542 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9544 /* Append new message into the list */
9546 *messages
= last_item
= item
;
9548 last_item
->next
= item
;
9553 if (number
) *number
= count
;
9557 isds_list_free(messages
);
9561 xmlXPathFreeObject(result
);
9562 xmlXPathFreeContext(xpath_ctx
);
9566 xmlFreeDoc(response
);
9567 xmlFreeNode(request
);
9570 isds_log(ILF_ISDS
, ILL_DEBUG
,
9571 (outgoing_direction
) ?
9572 _("GetListOfSentMessages request processed by server "
9573 "successfully.\n") :
9574 _("GetListOfReceivedMessages request processed by server "
9577 #else /* not HAVE_LIBCURL */
9584 /* Get list of outgoing (already sent) messages.
9585 * Any criterion argument can be NULL, if you don't care about it.
9586 * @context is session context. Must not be NULL.
9587 * @from_time is minimal time and date of message sending inclusive.
9588 * @to_time is maximal time and date of message sending inclusive
9589 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9590 * @status_filter is bit field of isds_message_status values. Use special
9591 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9592 * all values, you can use bit-wise arithmetic if you want.)
9593 * @offset is index of first message we are interested in. First message is 1.
9594 * Set to 0 (or 1) if you don't care.
9595 * @number is maximal length of list you want to get as input value, outputs
9596 * number of messages matching these criteria. Can be NULL if you don't care
9597 * (applies to output value either).
9598 * @messages is automatically reallocated list of isds_message's. Be ware that
9599 * it returns only brief overview (envelope and some other fields) about each
9600 * message, not the complete message. FIXME: Specify exact fields.
9601 * The list is sorted by delivery time in ascending order.
9602 * Use NULL if you don't care about the meta data (useful if you want to know
9603 * only the @number). If you provide &NULL, list will be allocated on heap,
9604 * if you provide pointer to non-NULL, list will be freed automatically at
9605 * first. Also in case of error the list will be NULLed.
9606 * @return IE_SUCCESS or appropriate error code. */
9607 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9608 const struct timeval
*from_time
, const struct timeval
*to_time
,
9609 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9610 const unsigned long int offset
, unsigned long int *number
,
9611 struct isds_list
**messages
) {
9613 return isds_get_list_of_messages(
9615 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9621 /* Get list of incoming (addressed to you) messages.
9622 * Any criterion argument can be NULL, if you don't care about it.
9623 * @context is session context. Must not be NULL.
9624 * @from_time is minimal time and date of message sending inclusive.
9625 * @to_time is maximal time and date of message sending inclusive
9626 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9627 * @status_filter is bit field of isds_message_status values. Use special
9628 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9629 * all values, you can use bit-wise arithmetic if you want.)
9630 * @offset is index of first message we are interested in. First message is 1.
9631 * Set to 0 (or 1) if you don't care.
9632 * @number is maximal length of list you want to get as input value, outputs
9633 * number of messages matching these criteria. Can be NULL if you don't care
9634 * (applies to output value either).
9635 * @messages is automatically reallocated list of isds_message's. Be ware that
9636 * it returns only brief overview (envelope and some other fields) about each
9637 * message, not the complete message. FIXME: Specify exact fields.
9638 * Use NULL if you don't care about the meta data (useful if you want to know
9639 * only the @number). If you provide &NULL, list will be allocated on heap,
9640 * if you provide pointer to non-NULL, list will be freed automatically at
9641 * first. Also in case of error the list will be NULLed.
9642 * @return IE_SUCCESS or appropriate error code. */
9643 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9644 const struct timeval
*from_time
, const struct timeval
*to_time
,
9645 const long int *dmRecipientOrgUnitNum
,
9646 const unsigned int status_filter
,
9647 const unsigned long int offset
, unsigned long int *number
,
9648 struct isds_list
**messages
) {
9650 return isds_get_list_of_messages(
9652 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9658 /* Get list of sent message state changes.
9659 * Any criterion argument can be NULL, if you don't care about it.
9660 * @context is session context. Must not be NULL.
9661 * @from_time is minimal time and date of status changes inclusive
9662 * @to_time is maximal time and date of status changes inclusive
9663 * @changed_states is automatically reallocated list of
9664 * isds_message_status_change's. If you provide &NULL, list will be allocated
9665 * on heap, if you provide pointer to non-NULL, list will be freed
9666 * automatically at first. Also in case of error the list will be NULLed.
9667 * XXX: The list item ordering is not specified.
9668 * XXX: Server provides only `recent' changes.
9669 * @return IE_SUCCESS or appropriate error code. */
9670 isds_error
isds_get_list_of_sent_message_state_changes(
9671 struct isds_ctx
*context
,
9672 const struct timeval
*from_time
, const struct timeval
*to_time
,
9673 struct isds_list
**changed_states
) {
9675 isds_error err
= IE_SUCCESS
;
9677 xmlNsPtr isds_ns
= NULL
;
9678 xmlNodePtr request
= NULL
, node
;
9679 xmlDocPtr response
= NULL
;
9680 xmlXPathContextPtr xpath_ctx
= NULL
;
9681 xmlXPathObjectPtr result
= NULL
;
9682 xmlChar
*string
= NULL
;
9686 if (!context
) return IE_INVALID_CONTEXT
;
9687 zfree(context
->long_message
);
9689 /* Free former message list if any */
9690 isds_list_free(changed_states
);
9693 /* Check if connection is established
9694 * TODO: This check should be done downstairs. */
9695 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9697 /* Build GetMessageStateChanges request */
9698 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9700 isds_log_message(context
,
9701 _("Could not build GetMessageStateChanges request"));
9704 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9706 isds_log_message(context
, _("Could not create ISDS name space"));
9707 xmlFreeNode(request
);
9710 xmlSetNs(request
, isds_ns
);
9714 err
= timeval2timestring(from_time
, &string
);
9715 if (err
) goto leave
;
9717 INSERT_STRING(request
, "dmFromTime", string
);
9721 err
= timeval2timestring(to_time
, &string
);
9722 if (err
) goto leave
;
9724 INSERT_STRING(request
, "dmToTime", string
);
9729 err
= send_destroy_request_check_response(context
,
9730 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9731 &response
, NULL
, NULL
);
9732 if (err
) goto leave
;
9736 xpath_ctx
= xmlXPathNewContext(response
);
9741 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9745 result
= xmlXPathEvalExpression(
9746 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9747 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9753 /* Fill output arguments in */
9754 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9755 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9757 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9758 /* Create new status change */
9759 item
= calloc(1, sizeof(*item
));
9765 (void(*)(void**)) &isds_message_status_change_free
;
9767 /* Extract message status change */
9768 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9769 err
= extract_StateChangesRecord(context
,
9770 (struct isds_message_status_change
**) &item
->data
,
9773 isds_list_free(&item
);
9777 /* Append new message status change into the list */
9778 if (!*changed_states
) {
9779 *changed_states
= last_item
= item
;
9781 last_item
->next
= item
;
9789 isds_list_free(changed_states
);
9793 xmlXPathFreeObject(result
);
9794 xmlXPathFreeContext(xpath_ctx
);
9795 xmlFreeDoc(response
);
9796 xmlFreeNode(request
);
9799 isds_log(ILF_ISDS
, ILL_DEBUG
,
9800 _("GetMessageStateChanges request processed by server "
9801 "successfully.\n"));
9802 #else /* not HAVE_LIBCURL */
9810 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9812 * @context is session context
9813 * @service is ISDS WS service handler
9814 * @service_name is name of SERVICE_DM_OPERATIONS
9815 * @message_id is message ID to send as service argument to ISDS
9816 * @response is reallocated server SOAP body response as XML document
9817 * @raw_response is reallocated bit stream with response body. Use
9818 * NULL if you don't care
9819 * @raw_response_length is size of @raw_response in bytes
9820 * @code is reallocated ISDS status code
9821 * @status_message is reallocated ISDS status message
9822 * @return error coded from lower layer, context message will be set up
9824 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9825 const isds_service service
, const xmlChar
*service_name
,
9826 const char *message_id
,
9827 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9828 xmlChar
**code
, xmlChar
**status_message
) {
9830 isds_error err
= IE_SUCCESS
;
9831 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9832 xmlNodePtr request
= NULL
, node
;
9833 xmlNsPtr isds_ns
= NULL
;
9835 if (!context
) return IE_INVALID_CONTEXT
;
9836 if (!service_name
|| !message_id
) return IE_INVAL
;
9837 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9838 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9840 /* Free output argument */
9841 xmlFreeDoc(*response
); *response
= NULL
;
9842 if (raw_response
) zfree(*raw_response
);
9844 zfree(*status_message
);
9847 /* Check if connection is established
9848 * TODO: This check should be done downstairs. */
9849 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9851 service_name_locale
= _isds_utf82locale((char*)service_name
);
9852 message_id_locale
= _isds_utf82locale(message_id
);
9853 if (!service_name_locale
|| !message_id_locale
) {
9859 request
= xmlNewNode(NULL
, service_name
);
9861 isds_printf_message(context
,
9862 _("Could not build %s request for %s message ID"),
9863 service_name_locale
, message_id_locale
);
9867 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9869 isds_log_message(context
, _("Could not create ISDS name space"));
9873 xmlSetNs(request
, isds_ns
);
9876 /* Add requested ID */
9877 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9878 if (err
) goto leave
;
9879 INSERT_STRING(request
, "dmID", message_id
);
9882 isds_log(ILF_ISDS
, ILL_DEBUG
,
9883 _("Sending %s request for %s message ID to ISDS\n"),
9884 service_name_locale
, message_id_locale
);
9887 err
= _isds(context
, service
, request
, response
,
9888 raw_response
, raw_response_length
);
9889 xmlFreeNode(request
); request
= NULL
;
9892 isds_log(ILF_ISDS
, ILL_DEBUG
,
9893 _("Processing ISDS response on %s request failed\n"),
9894 service_name_locale
);
9898 /* Check for response status */
9899 err
= isds_response_status(context
, service
, *response
,
9900 code
, status_message
, NULL
);
9902 isds_log(ILF_ISDS
, ILL_DEBUG
,
9903 _("ISDS response on %s request is missing status\n"),
9904 service_name_locale
);
9908 /* Request processed, but nothing found */
9909 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9910 char *code_locale
= _isds_utf82locale((char*) *code
);
9911 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9912 isds_log(ILF_ISDS
, ILL_DEBUG
,
9913 _("Server refused %s request for %s message ID "
9914 "(code=%s, message=%s)\n"),
9915 service_name_locale
, message_id_locale
,
9916 code_locale
, status_message_locale
);
9917 isds_log_message(context
, status_message_locale
);
9919 free(status_message_locale
);
9925 free(message_id_locale
);
9926 free(service_name_locale
);
9927 xmlFreeNode(request
);
9932 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9933 * signed data and free ISDS response.
9934 * @context is session context
9935 * @message_id is UTF-8 encoded message ID for logging purpose
9936 * @response is parsed XML document. It will be freed and NULLed in the middle
9937 * of function run to save memory. This is not guaranteed in case of error.
9938 * @request_name is name of ISDS request used to construct response root
9939 * element name and for logging purpose.
9940 * @raw is reallocated output buffer with DER encoded CMS data
9941 * @raw_length is size of @raw buffer in bytes
9942 * @returns standard error codes, in case of error, @raw will be freed and
9943 * NULLed, @response sometimes. */
9944 static isds_error
find_extract_signed_data_free_response(
9945 struct isds_ctx
*context
, const xmlChar
*message_id
,
9946 xmlDocPtr
*response
, const xmlChar
*request_name
,
9947 void **raw
, size_t *raw_length
) {
9949 isds_error err
= IE_SUCCESS
;
9950 char *xpath_expression
= NULL
;
9951 xmlXPathContextPtr xpath_ctx
= NULL
;
9952 xmlXPathObjectPtr result
= NULL
;
9953 char *encoded_structure
= NULL
;
9955 if (!context
) return IE_INVALID_CONTEXT
;
9956 if (!raw
) return IE_INVAL
;
9958 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9961 /* Build XPath expression */
9962 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9963 "Response/isds:dmSignature");
9964 if (!xpath_expression
) return IE_NOMEM
;
9967 xpath_ctx
= xmlXPathNewContext(*response
);
9972 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9976 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9981 /* Empty response */
9982 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9983 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9984 isds_printf_message(context
,
9985 _("Server did not return any signed data for message ID `%s' "
9987 message_id_locale
, request_name
);
9988 free(message_id_locale
);
9992 /* More responses */
9993 if (result
->nodesetval
->nodeNr
> 1) {
9994 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9995 isds_printf_message(context
,
9996 _("Server did return more signed data for message ID `%s' "
9998 message_id_locale
, request_name
);
9999 free(message_id_locale
);
10004 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10006 /* Extract PKCS#7 structure */
10007 EXTRACT_STRING(".", encoded_structure
);
10008 if (!encoded_structure
) {
10009 isds_log_message(context
, _("dmSignature element is empty"));
10012 /* Here we have delivery info as standalone CMS in encoded_structure.
10013 * We don't need any other data, free them: */
10014 xmlXPathFreeObject(result
); result
= NULL
;
10015 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
10016 xmlFreeDoc(*response
); *response
= NULL
;
10019 /* Decode PKCS#7 to DER format */
10020 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
10021 if (*raw_length
== (size_t) -1) {
10022 isds_log_message(context
,
10023 _("Error while Base64-decoding PKCS#7 structure"));
10034 free(encoded_structure
);
10035 xmlXPathFreeObject(result
);
10036 xmlXPathFreeContext(xpath_ctx
);
10037 free(xpath_expression
);
10041 #endif /* HAVE_LIBCURL */
10044 /* Download incoming message envelope identified by ID.
10045 * @context is session context
10046 * @message_id is message identifier (you can get them from
10047 * isds_get_list_of_received_messages())
10048 * @message is automatically reallocated message retrieved from ISDS.
10049 * It will miss documents per se. Use isds_get_received_message(), if you are
10050 * interested in documents (content) too.
10051 * Returned hash and timestamp require documents to be verifiable. */
10052 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
10053 const char *message_id
, struct isds_message
**message
) {
10055 isds_error err
= IE_SUCCESS
;
10057 xmlDocPtr response
= NULL
;
10058 xmlChar
*code
= NULL
, *status_message
= NULL
;
10059 xmlXPathContextPtr xpath_ctx
= NULL
;
10060 xmlXPathObjectPtr result
= NULL
;
10063 if (!context
) return IE_INVALID_CONTEXT
;
10064 zfree(context
->long_message
);
10066 /* Free former message if any */
10067 if (!message
) return IE_INVAL
;
10068 isds_message_free(message
);
10071 /* Do request and check for success */
10072 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10073 BAD_CAST
"MessageEnvelopeDownload", message_id
,
10074 &response
, NULL
, NULL
, &code
, &status_message
);
10075 if (err
) goto leave
;
10078 xpath_ctx
= xmlXPathNewContext(response
);
10083 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10087 result
= xmlXPathEvalExpression(
10088 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
10089 "isds:dmReturnedMessageEnvelope",
10095 /* Empty response */
10096 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10097 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10098 isds_printf_message(context
,
10099 _("Server did not return any envelope for ID `%s' "
10100 "on MessageEnvelopeDownload request"), message_id_locale
);
10101 free(message_id_locale
);
10105 /* More envelops */
10106 if (result
->nodesetval
->nodeNr
> 1) {
10107 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10108 isds_printf_message(context
,
10109 _("Server did return more envelopes for ID `%s' "
10110 "on MessageEnvelopeDownload request"), message_id_locale
);
10111 free(message_id_locale
);
10116 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10118 /* Extract the envelope (= message without documents, hence 0) */
10119 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10120 if (err
) goto leave
;
10122 /* Save XML blob */
10123 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10124 &(*message
)->raw_length
);
10128 isds_message_free(message
);
10131 xmlXPathFreeObject(result
);
10132 xmlXPathFreeContext(xpath_ctx
);
10135 free(status_message
);
10136 if (!*message
|| !(*message
)->xml
) {
10137 xmlFreeDoc(response
);
10141 isds_log(ILF_ISDS
, ILL_DEBUG
,
10142 _("MessageEnvelopeDownload request processed by server "
10145 #else /* not HAVE_LIBCURL */
10152 /* Load delivery info of any format from buffer.
10153 * @context is session context
10154 * @raw_type advertises format of @buffer content. Only delivery info types
10156 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10157 * retrieve such data from message->raw after calling
10158 * isds_get_signed_delivery_info().
10159 * @length is length of buffer in bytes.
10160 * @message is automatically reallocated message parsed from @buffer.
10161 * @strategy selects how buffer will be attached into raw isds_message member.
10163 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10164 const isds_raw_type raw_type
,
10165 const void *buffer
, const size_t length
,
10166 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10168 isds_error err
= IE_SUCCESS
;
10169 message_ns_type message_ns
;
10170 xmlDocPtr message_doc
= NULL
;
10171 xmlXPathContextPtr xpath_ctx
= NULL
;
10172 xmlXPathObjectPtr result
= NULL
;
10173 void *xml_stream
= NULL
;
10174 size_t xml_stream_length
= 0;
10176 if (!context
) return IE_INVALID_CONTEXT
;
10177 zfree(context
->long_message
);
10178 if (!message
) return IE_INVAL
;
10179 isds_message_free(message
);
10180 if (!buffer
) return IE_INVAL
;
10183 /* Select buffer format and extract XML from CMS*/
10184 switch (raw_type
) {
10185 case RAWTYPE_DELIVERYINFO
:
10186 message_ns
= MESSAGE_NS_UNSIGNED
;
10187 xml_stream
= (void *) buffer
;
10188 xml_stream_length
= length
;
10191 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10192 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10193 xml_stream
= (void *) buffer
;
10194 xml_stream_length
= length
;
10197 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10198 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10199 err
= _isds_extract_cms_data(context
, buffer
, length
,
10200 &xml_stream
, &xml_stream_length
);
10201 if (err
) goto leave
;
10205 isds_log_message(context
, _("Bad raw delivery representation type"));
10210 isds_log(ILF_ISDS
, ILL_DEBUG
,
10211 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10212 xml_stream_length
, xml_stream
);
10214 /* Convert delivery info XML stream into XPath context */
10215 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10216 if (!message_doc
) {
10220 xpath_ctx
= xmlXPathNewContext(message_doc
);
10225 /* XXX: Name spaces mangled for signed delivery info:
10226 * http://isds.czechpoint.cz/v20/delivery:
10228 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10230 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10231 * <p:dmID>170272</p:dmID>
10234 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10236 * </q:dmEvents>...</q:dmEvents>
10238 * </q:GetDeliveryInfoResponse>
10240 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10244 result
= xmlXPathEvalExpression(
10245 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10251 /* Empty delivery info */
10252 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10253 isds_printf_message(context
,
10254 _("XML document is not sisds:dmDelivery document"));
10258 /* More delivery info's */
10259 if (result
->nodesetval
->nodeNr
> 1) {
10260 isds_printf_message(context
,
10261 _("XML document has more sisds:dmDelivery elements"));
10265 /* One delivery info */
10266 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10268 /* Extract the envelope (= message without documents, hence 0).
10269 * XXX: extract_TReturnedMessage() can obtain attachments size,
10270 * but delivery info carries none. It's coded as option elements,
10271 * so it should work. */
10272 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10273 if (err
) goto leave
;
10275 /* Extract events */
10276 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10277 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10278 if (err
) { err
= IE_ERROR
; goto leave
; }
10279 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10280 if (err
) goto leave
;
10282 /* Append raw CMS structure into message */
10283 (*message
)->raw_type
= raw_type
;
10284 switch (strategy
) {
10285 case BUFFER_DONT_STORE
:
10288 (*message
)->raw
= malloc(length
);
10289 if (!(*message
)->raw
) {
10293 memcpy((*message
)->raw
, buffer
, length
);
10294 (*message
)->raw_length
= length
;
10297 (*message
)->raw
= (void *) buffer
;
10298 (*message
)->raw_length
= length
;
10307 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10308 isds_message_free(message
);
10311 xmlXPathFreeObject(result
);
10312 xmlXPathFreeContext(xpath_ctx
);
10313 if (!*message
|| !(*message
)->xml
) {
10314 xmlFreeDoc(message_doc
);
10316 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10319 isds_log(ILF_ISDS
, ILL_DEBUG
,
10320 _("Delivery info loaded successfully.\n"));
10325 /* Download signed delivery info-sheet of given message identified by ID.
10326 * @context is session context
10327 * @message_id is message identifier (you can get them from
10328 * isds_get_list_of_{sent,received}_messages())
10329 * @message is automatically reallocated message retrieved from ISDS.
10330 * It will miss documents per se. Use isds_get_signed_received_message(),
10331 * if you are interested in documents (content). OTOH, only this function
10332 * can get list events message has gone through. */
10333 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10334 const char *message_id
, struct isds_message
**message
) {
10336 isds_error err
= IE_SUCCESS
;
10338 xmlDocPtr response
= NULL
;
10339 xmlChar
*code
= NULL
, *status_message
= NULL
;
10341 size_t raw_length
= 0;
10344 if (!context
) return IE_INVALID_CONTEXT
;
10345 zfree(context
->long_message
);
10347 /* Free former message if any */
10348 if (!message
) return IE_INVAL
;
10349 isds_message_free(message
);
10352 /* Do request and check for success */
10353 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10354 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10355 &response
, NULL
, NULL
, &code
, &status_message
);
10356 if (err
) goto leave
;
10358 /* Find signed delivery info, extract it into raw and maybe free
10360 err
= find_extract_signed_data_free_response(context
,
10361 (xmlChar
*)message_id
, &response
,
10362 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10363 if (err
) goto leave
;
10365 /* Parse delivery info */
10366 err
= isds_load_delivery_info(context
,
10367 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10368 message
, BUFFER_MOVE
);
10369 if (err
) goto leave
;
10375 isds_message_free(message
);
10380 free(status_message
);
10381 xmlFreeDoc(response
);
10384 isds_log(ILF_ISDS
, ILL_DEBUG
,
10385 _("GetSignedDeliveryInfo request processed by server "
10388 #else /* not HAVE_LIBCURL */
10395 /* Download delivery info-sheet of given message identified by ID.
10396 * @context is session context
10397 * @message_id is message identifier (you can get them from
10398 * isds_get_list_of_{sent,received}_messages())
10399 * @message is automatically reallocated message retrieved from ISDS.
10400 * It will miss documents per se. Use isds_get_received_message(), if you are
10401 * interested in documents (content). OTOH, only this function can get list
10402 * of events message has gone through. */
10403 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10404 const char *message_id
, struct isds_message
**message
) {
10406 isds_error err
= IE_SUCCESS
;
10408 xmlDocPtr response
= NULL
;
10409 xmlChar
*code
= NULL
, *status_message
= NULL
;
10410 xmlNodePtr delivery_node
= NULL
;
10412 size_t raw_length
= 0;
10415 if (!context
) return IE_INVALID_CONTEXT
;
10416 zfree(context
->long_message
);
10418 /* Free former message if any */
10419 if (!message
) return IE_INVAL
;
10420 isds_message_free(message
);
10423 /* Do request and check for success */
10424 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10425 BAD_CAST
"GetDeliveryInfo", message_id
,
10426 &response
, NULL
, NULL
, &code
, &status_message
);
10427 if (err
) goto leave
;
10430 /* Serialize delivery info */
10431 delivery_node
= xmlDocGetRootElement(response
);
10432 if (!delivery_node
) {
10433 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10434 isds_printf_message(context
,
10435 _("Server did not return any delivery info for ID `%s' "
10436 "on GetDeliveryInfo request"), message_id_locale
);
10437 free(message_id_locale
);
10441 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10442 if (err
) goto leave
;
10444 /* Parse delivery info */
10445 /* TODO: Here we parse the response second time. We could single delivery
10446 * parser from isds_load_delivery_info() to make things faster. */
10447 err
= isds_load_delivery_info(context
,
10448 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10449 message
, BUFFER_MOVE
);
10450 if (err
) goto leave
;
10457 isds_message_free(message
);
10462 free(status_message
);
10463 xmlFreeDoc(response
);
10466 isds_log(ILF_ISDS
, ILL_DEBUG
,
10467 _("GetDeliveryInfo request processed by server "
10470 #else /* not HAVE_LIBCURL */
10477 /* Download incoming message identified by ID.
10478 * @context is session context
10479 * @message_id is message identifier (you can get them from
10480 * isds_get_list_of_received_messages())
10481 * @message is automatically reallocated message retrieved from ISDS */
10482 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10483 const char *message_id
, struct isds_message
**message
) {
10485 isds_error err
= IE_SUCCESS
;
10487 xmlDocPtr response
= NULL
;
10488 void *xml_stream
= NULL
;
10489 size_t xml_stream_length
;
10490 xmlChar
*code
= NULL
, *status_message
= NULL
;
10491 xmlXPathContextPtr xpath_ctx
= NULL
;
10492 xmlXPathObjectPtr result
= NULL
;
10493 char *phys_path
= NULL
;
10494 size_t phys_start
, phys_end
;
10497 if (!context
) return IE_INVALID_CONTEXT
;
10498 zfree(context
->long_message
);
10500 /* Free former message if any */
10501 if (NULL
== message
) return IE_INVAL
;
10502 if (message
) isds_message_free(message
);
10505 /* Do request and check for success */
10506 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10507 BAD_CAST
"MessageDownload", message_id
,
10508 &response
, &xml_stream
, &xml_stream_length
,
10509 &code
, &status_message
);
10510 if (err
) goto leave
;
10513 xpath_ctx
= xmlXPathNewContext(response
);
10518 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10522 result
= xmlXPathEvalExpression(
10523 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10529 /* Empty response */
10530 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10531 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10532 isds_printf_message(context
,
10533 _("Server did not return any message for ID `%s' "
10534 "on MessageDownload request"), message_id_locale
);
10535 free(message_id_locale
);
10539 /* More messages */
10540 if (result
->nodesetval
->nodeNr
> 1) {
10541 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10542 isds_printf_message(context
,
10543 _("Server did return more messages for ID `%s' "
10544 "on MessageDownload request"), message_id_locale
);
10545 free(message_id_locale
);
10550 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10552 /* Extract the message */
10553 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10554 if (err
) goto leave
;
10556 /* Locate raw XML blob */
10557 phys_path
= strdup(
10558 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10559 PHYSXML_ELEMENT_SEPARATOR
10560 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10561 PHYSXML_ELEMENT_SEPARATOR
10562 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10568 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10569 phys_path
, &phys_start
, &phys_end
);
10572 isds_log_message(context
,
10573 _("Substring with isds:MessageDownloadResponse element "
10574 "could not be located in raw SOAP message"));
10577 /* Save XML blob */
10578 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10579 &(*message)->raw_length);*/
10580 /* TODO: Store name space declarations from ancestors */
10581 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10582 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10583 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10584 (*message
)->raw
= malloc((*message
)->raw_length
);
10585 if (!(*message
)->raw
) {
10589 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10594 isds_message_free(message
);
10599 xmlXPathFreeObject(result
);
10600 xmlXPathFreeContext(xpath_ctx
);
10603 free(status_message
);
10605 if (!*message
|| !(*message
)->xml
) {
10606 xmlFreeDoc(response
);
10610 isds_log(ILF_ISDS
, ILL_DEBUG
,
10611 _("MessageDownload request processed by server "
10614 #else /* not HAVE_LIBCURL */
10621 /* Load message of any type from buffer.
10622 * @context is session context
10623 * @raw_type defines content type of @buffer. Only message types are allowed.
10624 * @buffer is message raw representation. Format (CMS, plain signed,
10625 * message direction) is defined in @raw_type. You can retrieve such data
10626 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10627 * @length is length of buffer in bytes.
10628 * @message is automatically reallocated message parsed from @buffer.
10629 * @strategy selects how buffer will be attached into raw isds_message member.
10631 isds_error
isds_load_message(struct isds_ctx
*context
,
10632 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10633 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10635 isds_error err
= IE_SUCCESS
;
10636 void *xml_stream
= NULL
;
10637 size_t xml_stream_length
= 0;
10638 message_ns_type message_ns
;
10639 xmlDocPtr message_doc
= NULL
;
10640 xmlXPathContextPtr xpath_ctx
= NULL
;
10641 xmlXPathObjectPtr result
= NULL
;
10643 if (!context
) return IE_INVALID_CONTEXT
;
10644 zfree(context
->long_message
);
10645 if (!message
) return IE_INVAL
;
10646 isds_message_free(message
);
10647 if (!buffer
) return IE_INVAL
;
10650 /* Select buffer format and extract XML from CMS*/
10651 switch (raw_type
) {
10652 case RAWTYPE_INCOMING_MESSAGE
:
10653 message_ns
= MESSAGE_NS_UNSIGNED
;
10654 xml_stream
= (void *) buffer
;
10655 xml_stream_length
= length
;
10658 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10659 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10660 xml_stream
= (void *) buffer
;
10661 xml_stream_length
= length
;
10664 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10665 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10666 err
= _isds_extract_cms_data(context
, buffer
, length
,
10667 &xml_stream
, &xml_stream_length
);
10668 if (err
) goto leave
;
10671 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10672 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10673 xml_stream
= (void *) buffer
;
10674 xml_stream_length
= length
;
10677 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10678 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10679 err
= _isds_extract_cms_data(context
, buffer
, length
,
10680 &xml_stream
, &xml_stream_length
);
10681 if (err
) goto leave
;
10685 isds_log_message(context
, _("Bad raw message representation type"));
10690 isds_log(ILF_ISDS
, ILL_DEBUG
,
10691 _("Loading message:\n%.*s\nEnd of message\n"),
10692 xml_stream_length
, xml_stream
);
10694 /* Convert messages XML stream into XPath context */
10695 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10696 if (!message_doc
) {
10700 xpath_ctx
= xmlXPathNewContext(message_doc
);
10705 /* XXX: Standard name space for unsigned incoming direction:
10706 * http://isds.czechpoint.cz/v20/
10708 * XXX: Name spaces mangled for signed outgoing direction:
10709 * http://isds.czechpoint.cz/v20/SentMessage:
10711 * <q:MessageDownloadResponse
10712 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10713 * <q:dmReturnedMessage>
10714 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10715 * <p:dmID>151916</p:dmID>
10718 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10720 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10721 * </q:dmReturnedMessage>
10722 * </q:MessageDownloadResponse>
10724 * XXX: Name spaces mangled for signed incoming direction:
10725 * http://isds.czechpoint.cz/v20/message:
10727 * <q:MessageDownloadResponse
10728 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10729 * <q:dmReturnedMessage>
10730 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10731 * <p:dmID>151916</p:dmID>
10734 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10736 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10737 * </q:dmReturnedMessage>
10738 * </q:MessageDownloadResponse>
10740 * Stupidity of ISDS developers is unlimited */
10741 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10745 result
= xmlXPathEvalExpression(
10746 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10752 /* Empty message */
10753 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10754 isds_printf_message(context
,
10755 _("XML document does not contain "
10756 "sisds:dmReturnedMessage element"));
10760 /* More messages */
10761 if (result
->nodesetval
->nodeNr
> 1) {
10762 isds_printf_message(context
,
10763 _("XML document has more sisds:dmReturnedMessage elements"));
10768 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10770 /* Extract the message */
10771 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10772 if (err
) goto leave
;
10774 /* Append raw buffer into message */
10775 (*message
)->raw_type
= raw_type
;
10776 switch (strategy
) {
10777 case BUFFER_DONT_STORE
:
10780 (*message
)->raw
= malloc(length
);
10781 if (!(*message
)->raw
) {
10785 memcpy((*message
)->raw
, buffer
, length
);
10786 (*message
)->raw_length
= length
;
10789 (*message
)->raw
= (void *) buffer
;
10790 (*message
)->raw_length
= length
;
10800 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10801 isds_message_free(message
);
10804 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10805 xmlXPathFreeObject(result
);
10806 xmlXPathFreeContext(xpath_ctx
);
10807 if (!*message
|| !(*message
)->xml
) {
10808 xmlFreeDoc(message_doc
);
10812 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10817 /* Determine type of raw message or delivery info according some heuristics.
10818 * It does not validate the raw blob.
10819 * @context is session context
10820 * @raw_type returns content type of @buffer. Valid only if exit code of this
10821 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10822 * reallocated memory.
10823 * @buffer is message raw representation.
10824 * @length is length of buffer in bytes. */
10825 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10826 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10828 void *xml_stream
= NULL
;
10829 size_t xml_stream_length
= 0;
10830 xmlDocPtr document
= NULL
;
10831 xmlNodePtr root
= NULL
;
10833 if (!context
) return IE_INVALID_CONTEXT
;
10834 zfree(context
->long_message
);
10835 if (length
== 0 || !buffer
) return IE_INVAL
;
10836 if (!raw_type
) return IE_INVAL
;
10839 err
= _isds_extract_cms_data(context
, buffer
, length
,
10840 &xml_stream
, &xml_stream_length
);
10842 xml_stream
= (void *) buffer
;
10843 xml_stream_length
= (size_t) length
;
10848 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10850 isds_printf_message(context
,
10851 _("Could not parse data as XML document"));
10856 /* Get root element */
10857 root
= xmlDocGetRootElement(document
);
10859 isds_printf_message(context
,
10860 _("XML document is missing root element"));
10865 if (!root
->ns
|| !root
->ns
->href
) {
10866 isds_printf_message(context
,
10867 _("Root element does not belong to any name space"));
10872 /* Test name space */
10873 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10874 if (xml_stream
== buffer
)
10875 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10877 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10878 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10879 if (xml_stream
== buffer
)
10880 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10882 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10883 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10884 if (xml_stream
== buffer
)
10885 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10887 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10888 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10889 if (xml_stream
!= buffer
) {
10890 isds_printf_message(context
,
10891 _("Document in ISDS name space is encapsulated into CMS" ));
10893 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10894 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10895 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10896 *raw_type
= RAWTYPE_DELIVERYINFO
;
10898 isds_printf_message(context
,
10899 _("Unknown root element in ISDS name space"));
10903 isds_printf_message(context
,
10904 _("Unknown name space"));
10909 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10910 xmlFreeDoc(document
);
10915 /* Download signed incoming/outgoing message identified by ID.
10916 * @context is session context
10917 * @output is true for outgoing message, false for incoming message
10918 * @message_id is message identifier (you can get them from
10919 * isds_get_list_of_{sent,received}_messages())
10920 * @message is automatically reallocated message retrieved from ISDS. The raw
10921 * member will be filled with PKCS#7 structure in DER format. */
10922 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10923 const _Bool outgoing
, const char *message_id
,
10924 struct isds_message
**message
) {
10926 isds_error err
= IE_SUCCESS
;
10928 xmlDocPtr response
= NULL
;
10929 xmlChar
*code
= NULL
, *status_message
= NULL
;
10930 xmlXPathContextPtr xpath_ctx
= NULL
;
10931 xmlXPathObjectPtr result
= NULL
;
10932 char *encoded_structure
= NULL
;
10934 size_t raw_length
= 0;
10937 if (!context
) return IE_INVALID_CONTEXT
;
10938 zfree(context
->long_message
);
10939 if (!message
) return IE_INVAL
;
10940 isds_message_free(message
);
10943 /* Do request and check for success */
10944 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10945 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10946 BAD_CAST
"SignedMessageDownload",
10947 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10948 if (err
) goto leave
;
10950 /* Find signed message, extract it into raw and maybe free
10952 err
= find_extract_signed_data_free_response(context
,
10953 (xmlChar
*)message_id
, &response
,
10954 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10955 BAD_CAST
"SignedMessageDownload",
10956 &raw
, &raw_length
);
10957 if (err
) goto leave
;
10959 /* Parse message */
10960 err
= isds_load_message(context
,
10961 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10962 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10963 raw
, raw_length
, message
, BUFFER_MOVE
);
10964 if (err
) goto leave
;
10970 isds_message_free(message
);
10973 free(encoded_structure
);
10974 xmlXPathFreeObject(result
);
10975 xmlXPathFreeContext(xpath_ctx
);
10979 free(status_message
);
10980 xmlFreeDoc(response
);
10983 isds_log(ILF_ISDS
, ILL_DEBUG
,
10985 _("SignedSentMessageDownload request processed by server "
10986 "successfully.\n") :
10987 _("SignedMessageDownload request processed by server "
10990 #else /* not HAVE_LIBCURL */
10997 /* Download signed incoming message identified by ID.
10998 * @context is session context
10999 * @message_id is message identifier (you can get them from
11000 * isds_get_list_of_received_messages())
11001 * @message is automatically reallocated message retrieved from ISDS. The raw
11002 * member will be filled with PKCS#7 structure in DER format. */
11003 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
11004 const char *message_id
, struct isds_message
**message
) {
11005 return isds_get_signed_message(context
, 0, message_id
, message
);
11009 /* Download signed outgoing message identified by ID.
11010 * @context is session context
11011 * @message_id is message identifier (you can get them from
11012 * isds_get_list_of_sent_messages())
11013 * @message is automatically reallocated message retrieved from ISDS. The raw
11014 * member will be filled with PKCS#7 structure in DER format. */
11015 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
11016 const char *message_id
, struct isds_message
**message
) {
11017 return isds_get_signed_message(context
, 1, message_id
, message
);
11021 /* Get type and name of user who sent a message identified by ID.
11022 * @context is session context
11023 * @message_id is message identifier
11024 * @sender_type is pointer to automatically allocated type of sender detected
11025 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11026 * library or to the server, NULL will be returned. Pass NULL if you don't
11028 * @raw_sender_type is automatically reallocated UTF-8 string describing
11029 * sender type or NULL if not known to server. Pass NULL if you don't care.
11030 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11031 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11032 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
11033 const char *message_id
, isds_sender_type
**sender_type
,
11034 char **raw_sender_type
, char **sender_name
) {
11035 isds_error err
= IE_SUCCESS
;
11037 xmlDocPtr response
= NULL
;
11038 xmlChar
*code
= NULL
, *status_message
= NULL
;
11039 xmlXPathContextPtr xpath_ctx
= NULL
;
11040 xmlXPathObjectPtr result
= NULL
;
11041 char *type_string
= NULL
;
11044 if (!context
) return IE_INVALID_CONTEXT
;
11045 zfree(context
->long_message
);
11046 if (sender_type
) zfree(*sender_type
);
11047 if (raw_sender_type
) zfree(*raw_sender_type
);
11048 if (sender_name
) zfree(*sender_name
);
11049 if (!message_id
) return IE_INVAL
;
11052 /* Do request and check for success */
11053 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11054 BAD_CAST
"GetMessageAuthor",
11055 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11056 if (err
) goto leave
;
11059 xpath_ctx
= xmlXPathNewContext(response
);
11064 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11068 result
= xmlXPathEvalExpression(
11069 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
11074 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11075 isds_log_message(context
,
11076 _("Missing GetMessageAuthorResponse element"));
11080 if (result
->nodesetval
->nodeNr
> 1) {
11081 isds_log_message(context
,
11082 _("Multiple GetMessageAuthorResponse element"));
11086 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11087 xmlXPathFreeObject(result
); result
= NULL
;
11089 /* Fill output arguments in */
11090 EXTRACT_STRING("isds:userType", type_string
);
11091 if (NULL
!= type_string
) {
11092 if (NULL
!= sender_type
) {
11093 *sender_type
= calloc(1, sizeof(**sender_type
));
11094 if (NULL
== *sender_type
) {
11099 err
= string2isds_sender_type((xmlChar
*)type_string
,
11102 zfree(*sender_type
);
11103 if (err
== IE_ENUM
) {
11105 char *type_string_locale
= _isds_utf82locale(type_string
);
11106 isds_log(ILF_ISDS
, ILL_WARNING
,
11107 _("Unknown isds:userType value: %s"),
11108 type_string_locale
);
11109 free(type_string_locale
);
11114 if (NULL
!= sender_name
)
11115 EXTRACT_STRING("isds:authorName", *sender_name
);
11119 if (NULL
!= sender_type
) zfree(*sender_type
);
11120 zfree(type_string
);
11121 if (NULL
!= sender_name
) zfree(*sender_name
);
11123 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11125 xmlXPathFreeObject(result
);
11126 xmlXPathFreeContext(xpath_ctx
);
11129 free(status_message
);
11130 xmlFreeDoc(response
);
11133 isds_log(ILF_ISDS
, ILL_DEBUG
,
11134 _("GetMessageAuthor request processed by server "
11135 "successfully.\n"));
11136 #else /* not HAVE_LIBCURL */
11143 /* Retrieve hash of message identified by ID stored in ISDS.
11144 * @context is session context
11145 * @message_id is message identifier
11146 * @hash is automatically reallocated message hash downloaded from ISDS.
11147 * Message must exist in system and must not be deleted. */
11148 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11149 const char *message_id
, struct isds_hash
**hash
) {
11151 isds_error err
= IE_SUCCESS
;
11153 xmlDocPtr response
= NULL
;
11154 xmlChar
*code
= NULL
, *status_message
= NULL
;
11155 xmlXPathContextPtr xpath_ctx
= NULL
;
11156 xmlXPathObjectPtr result
= NULL
;
11159 if (!context
) return IE_INVALID_CONTEXT
;
11160 zfree(context
->long_message
);
11162 isds_hash_free(hash
);
11165 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11166 BAD_CAST
"VerifyMessage", message_id
,
11167 &response
, NULL
, NULL
, &code
, &status_message
);
11168 if (err
) goto leave
;
11172 xpath_ctx
= xmlXPathNewContext(response
);
11177 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11181 result
= xmlXPathEvalExpression(
11182 BAD_CAST
"/isds:VerifyMessageResponse",
11188 /* Empty response */
11189 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11190 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11191 isds_printf_message(context
,
11192 _("Server did not return any response for ID `%s' "
11193 "on VerifyMessage request"), message_id_locale
);
11194 free(message_id_locale
);
11198 /* More responses */
11199 if (result
->nodesetval
->nodeNr
> 1) {
11200 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11201 isds_printf_message(context
,
11202 _("Server did return more responses for ID `%s' "
11203 "on VerifyMessage request"), message_id_locale
);
11204 free(message_id_locale
);
11209 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11211 /* Extract the hash */
11212 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11216 isds_hash_free(hash
);
11219 xmlXPathFreeObject(result
);
11220 xmlXPathFreeContext(xpath_ctx
);
11223 free(status_message
);
11224 xmlFreeDoc(response
);
11227 isds_log(ILF_ISDS
, ILL_DEBUG
,
11228 _("VerifyMessage request processed by server "
11231 #else /* not HAVE_LIBCURL */
11238 /* Erase message specified by @message_id from long term storage. Other
11239 * message cannot be erased on user request.
11240 * @context is session context
11241 * @message_id is message identifier.
11242 * @incoming is true for incoming message, false for outgoing message.
11244 * IE_SUCCESS if message has ben removed
11245 * IE_INVAL if message does not exist in long term storage or message
11246 * belongs to different box
11247 * TODO: IE_NOEPRM if user has no permission to erase a message */
11248 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11249 const char *message_id
, _Bool incoming
) {
11250 isds_error err
= IE_SUCCESS
;
11252 xmlNodePtr request
= NULL
, node
;
11253 xmlNsPtr isds_ns
= NULL
;
11254 xmlDocPtr response
= NULL
;
11255 xmlChar
*code
= NULL
, *status_message
= NULL
;
11258 if (!context
) return IE_INVALID_CONTEXT
;
11259 zfree(context
->long_message
);
11260 if (NULL
== message_id
) return IE_INVAL
;
11262 /* Check if connection is established
11263 * TODO: This check should be done downstairs. */
11264 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11267 /* Build request */
11268 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11270 isds_log_message(context
,
11271 _("Could build EraseMessage request"));
11274 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11276 isds_log_message(context
, _("Could not create ISDS name space"));
11277 xmlFreeNode(request
);
11280 xmlSetNs(request
, isds_ns
);
11282 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11283 if (err
) goto leave
;
11284 INSERT_STRING(request
, "dmID", message_id
);
11286 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11290 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11291 "message ID %s to ISDS\n"), message_id
);
11292 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11293 xmlFreeNode(request
); request
= NULL
;
11296 isds_log(ILF_ISDS
, ILL_DEBUG
,
11297 _("Processing ISDS response on EraseMessage request "
11302 /* Check for response status */
11303 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11304 &code
, &status_message
, NULL
);
11306 isds_log(ILF_ISDS
, ILL_DEBUG
,
11307 _("ISDS response on EraseMessage request is missing "
11312 /* Check server status code */
11313 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11314 isds_log_message(context
, _("Message to erase belongs to other box"));
11316 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11317 isds_log_message(context
, _("Message to erase is not saved in "
11318 "long term storage or the direction does not match"));
11320 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11321 char *code_locale
= _isds_utf82locale((char*) code
);
11322 char *message_locale
= _isds_utf82locale((char*) status_message
);
11323 isds_log(ILF_ISDS
, ILL_DEBUG
,
11324 _("Server refused EraseMessage request "
11325 "(code=%s, message=%s)\n"),
11326 code_locale
, message_locale
);
11327 isds_log_message(context
, message_locale
);
11329 free(message_locale
);
11336 free(status_message
);
11337 xmlFreeDoc(response
);
11338 xmlFreeNode(request
);
11341 isds_log(ILF_ISDS
, ILL_DEBUG
,
11342 _("EraseMessage request processed by server "
11345 #else /* not HAVE_LIBCURL */
11352 /* Mark message as read. This is a transactional commit function to acknowledge
11353 * to ISDS the message has been downloaded and processed by client properly.
11354 * @context is session context
11355 * @message_id is message identifier. */
11356 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11357 const char *message_id
) {
11359 isds_error err
= IE_SUCCESS
;
11361 xmlDocPtr response
= NULL
;
11362 xmlChar
*code
= NULL
, *status_message
= NULL
;
11365 if (!context
) return IE_INVALID_CONTEXT
;
11366 zfree(context
->long_message
);
11369 /* Do request and check for success */
11370 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11371 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11372 &response
, NULL
, NULL
, &code
, &status_message
);
11375 free(status_message
);
11376 xmlFreeDoc(response
);
11379 isds_log(ILF_ISDS
, ILL_DEBUG
,
11380 _("MarkMessageAsDownloaded request processed by server "
11383 #else /* not HAVE_LIBCURL */
11390 /* Mark message as received by recipient. This is applicable only to
11391 * commercial message. Use envelope->dmType message member to distinguish
11392 * commercial message from government message. Government message is
11393 * received automatically (by law), commercial message on recipient request.
11394 * @context is session context
11395 * @message_id is message identifier. */
11396 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11397 const char *message_id
) {
11399 isds_error err
= IE_SUCCESS
;
11401 xmlDocPtr response
= NULL
;
11402 xmlChar
*code
= NULL
, *status_message
= NULL
;
11405 if (!context
) return IE_INVALID_CONTEXT
;
11406 zfree(context
->long_message
);
11409 /* Do request and check for success */
11410 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11411 BAD_CAST
"ConfirmDelivery", message_id
,
11412 &response
, NULL
, NULL
, &code
, &status_message
);
11415 free(status_message
);
11416 xmlFreeDoc(response
);
11419 isds_log(ILF_ISDS
, ILL_DEBUG
,
11420 _("ConfirmDelivery request processed by server "
11423 #else /* not HAVE_LIBCURL */
11430 /* Send document for authorized conversion into Czech POINT system.
11431 * This is public anonymous service, no log-in necessary. Special context is
11432 * used to reuse keep-a-live HTTPS connection.
11433 * @context is Czech POINT session context. DO NOT use context connected to
11434 * ISDS server. Use new context or context used by this function previously.
11435 * @document is document to convert. Only data, data_length, dmFileDescr and
11436 * is_xml members are significant. Be ware that not all document formats can be
11437 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11438 * @id is reallocated identifier assigned by Czech POINT system to
11439 * your document on submit. Use is to tell it to Czech POINT officer.
11440 * @date is reallocated document submit date (submitted documents
11441 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11443 isds_error
czp_convert_document(struct isds_ctx
*context
,
11444 const struct isds_document
*document
,
11445 char **id
, struct tm
**date
) {
11446 isds_error err
= IE_SUCCESS
;
11448 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11449 xmlNodePtr request
= NULL
, node
;
11450 xmlDocPtr response
= NULL
;
11452 xmlXPathContextPtr xpath_ctx
= NULL
;
11453 xmlXPathObjectPtr result
= NULL
;
11454 long int status
= -1;
11455 long int *status_ptr
= &status
;
11456 char *string
= NULL
;
11460 if (!context
) return IE_INVALID_CONTEXT
;
11461 zfree(context
->long_message
);
11462 if (!document
|| !id
|| !date
) return IE_INVAL
;
11464 if (document
->is_xml
) {
11465 isds_log_message(context
,
11466 _("XML documents cannot be submitted to conversion"));
11470 /* Free output arguments */
11475 /* Store configuration */
11476 context
->type
= CTX_TYPE_CZP
;
11477 free(context
->url
);
11478 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11479 if (!(context
->url
))
11482 /* Prepare CURL handle if not yet connected */
11483 if (!context
->curl
) {
11484 context
->curl
= curl_easy_init();
11485 if (!(context
->curl
))
11489 /* Build conversion request */
11490 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11492 isds_log_message(context
,
11493 _("Could not build Czech POINT conversion request"));
11496 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11498 isds_log_message(context
,
11499 _("Could not create Czech POINT deposit name space"));
11500 xmlFreeNode(request
);
11503 xmlSetNs(request
, deposit_ns
);
11505 /* Insert children. They are in empty namespace! */
11506 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11508 isds_log_message(context
, _("Could not create empty name space"));
11512 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11513 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11514 document
->dmFileDescr
);
11516 /* Document encoded in Base64 */
11517 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11518 document
->data
, document
->data_length
);
11519 if (err
) goto leave
;
11521 isds_log(ILF_ISDS
, ILL_DEBUG
,
11522 _("Submitting document for conversion into Czech POINT deposit"));
11524 /* Send conversion request */
11525 err
= _czp_czpdeposit(context
, request
, &response
);
11526 xmlFreeNode(request
); request
= NULL
;
11529 czp_do_close_connection(context
);
11534 /* Extract response */
11535 xpath_ctx
= xmlXPathNewContext(response
);
11540 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11544 result
= xmlXPathEvalExpression(
11545 BAD_CAST
"/deposit:saveDocumentResponse/return",
11551 /* Empty response */
11552 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11553 isds_printf_message(context
,
11554 _("Missing `return' element in Czech POINT deposit response"));
11558 /* More responses */
11559 if (result
->nodesetval
->nodeNr
> 1) {
11560 isds_printf_message(context
,
11561 _("Multiple `return' element in Czech POINT deposit response"));
11566 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11569 EXTRACT_LONGINT("status", status_ptr
, 1);
11571 EXTRACT_STRING("statusMsg", string
);
11572 char *string_locale
= _isds_utf82locale(string
);
11573 isds_printf_message(context
,
11574 _("Czech POINT deposit refused document for conversion "
11575 "(code=%ld, message=%s)"),
11576 status
, string_locale
);
11577 free(string_locale
);
11582 /* Get document ID */
11583 EXTRACT_STRING("documentID", *id
);
11585 /* Get submit date */
11586 EXTRACT_STRING("dateInserted", string
);
11588 *date
= calloc(1, sizeof(**date
));
11593 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11595 if (err
== IE_NOTSUP
) {
11597 char *string_locale
= _isds_utf82locale(string
);
11598 isds_printf_message(context
,
11599 _("Invalid dateInserted value: %s"), string_locale
);
11600 free(string_locale
);
11608 xmlXPathFreeObject(result
);
11609 xmlXPathFreeContext(xpath_ctx
);
11611 xmlFreeDoc(response
);
11612 xmlFreeNode(request
);
11615 char *id_locale
= _isds_utf82locale((char *) *id
);
11616 isds_log(ILF_ISDS
, ILL_DEBUG
,
11617 _("Document %s has been submitted for conversion "
11618 "to server successfully\n"), id_locale
);
11621 #else /* not HAVE_LIBCURL */
11628 /* Close possibly opened connection to Czech POINT document deposit.
11629 * @context is Czech POINT session context. */
11630 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11631 if (!context
) return IE_INVALID_CONTEXT
;
11632 zfree(context
->long_message
);
11634 return czp_do_close_connection(context
);
11641 /* Send request for new box creation in testing ISDS instance.
11642 * It's not possible to request for a production box currently, as it
11643 * communicates via e-mail.
11644 * XXX: This function does not work either. Server complains about invalid
11646 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11648 * @context is special session context for box creation request. DO NOT use
11649 * standard context as it could reveal your password. Use fresh new context or
11650 * context previously used by this function.
11651 * @box is box description to create including single primary user (in case of
11652 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11653 * ignored. It outputs box ID assigned by ISDS in dbID element.
11654 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11655 * box, or contact address of PFO box owner). The email member is mandatory as
11656 * it will be used to deliver credentials.
11657 * @former_names is former name of box owner. Pass NULL if you don't care.
11658 * @approval is optional external approval of box manipulation
11659 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11660 * NULL, if you don't care.*/
11661 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11662 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11663 const char *former_names
, const struct isds_approval
*approval
,
11664 char **refnumber
) {
11665 isds_error err
= IE_SUCCESS
;
11667 xmlNodePtr request
= NULL
;
11668 xmlDocPtr response
= NULL
;
11669 xmlXPathContextPtr xpath_ctx
= NULL
;
11670 xmlXPathObjectPtr result
= NULL
;
11674 if (!context
) return IE_INVALID_CONTEXT
;
11675 zfree(context
->long_message
);
11676 if (!box
) return IE_INVAL
;
11679 if (!box
->email
|| box
->email
[0] == '\0') {
11680 isds_log_message(context
, _("E-mail field is mandatory"));
11684 /* Scratch box ID */
11687 /* Store configuration */
11688 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11689 free(context
->url
);
11690 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11691 if (!(context
->url
))
11694 /* Prepare CURL handle if not yet connected */
11695 if (!context
->curl
) {
11696 context
->curl
= curl_easy_init();
11697 if (!(context
->curl
))
11701 /* Build CreateDataBox request */
11702 err
= build_CreateDBInput_request(context
,
11703 &request
, BAD_CAST
"CreateDataBox",
11704 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11705 if (err
) goto leave
;
11707 /* Send it to server and process response */
11708 err
= send_destroy_request_check_response(context
,
11709 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11710 &response
, (xmlChar
**) refnumber
, NULL
);
11711 if (err
) goto leave
;
11713 /* Extract box ID */
11714 xpath_ctx
= xmlXPathNewContext(response
);
11719 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11723 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11726 xmlXPathFreeObject(result
);
11727 xmlXPathFreeContext(xpath_ctx
);
11728 xmlFreeDoc(response
);
11729 xmlFreeNode(request
);
11732 isds_log(ILF_ISDS
, ILL_DEBUG
,
11733 _("CreateDataBox request processed by server successfully.\n"));
11735 #else /* not HAVE_LIBCURL */
11743 /* Submit CMS signed message to ISDS to verify its originality. This is
11744 * stronger form of isds_verify_message_hash() because ISDS does more checks
11745 * than simple one (potentialy old weak) hash comparison.
11746 * @context is session context
11747 * @message is memory with raw CMS signed message bit stream
11748 * @length is @message size in bytes
11750 * IE_SUCCESS if message originates in ISDS
11751 * IE_NOTEQUAL if message is unknown to ISDS
11752 * other code for other errors */
11753 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11754 const void *message
, size_t length
) {
11755 isds_error err
= IE_SUCCESS
;
11757 xmlNsPtr isds_ns
= NULL
;
11758 xmlNodePtr request
= NULL
;
11759 xmlDocPtr response
= NULL
;
11760 xmlXPathContextPtr xpath_ctx
= NULL
;
11761 xmlXPathObjectPtr result
= NULL
;
11762 _Bool
*authentic
= NULL
;
11765 if (!context
) return IE_INVALID_CONTEXT
;
11766 zfree(context
->long_message
);
11767 if (!message
|| length
== 0) return IE_INVAL
;
11770 /* Check if connection is established
11771 * TODO: This check should be done downstairs. */
11772 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11775 /* Build AuthenticateMessage request */
11776 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11778 isds_log_message(context
,
11779 _("Could not build AuthenticateMessage request"));
11782 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11784 isds_log_message(context
, _("Could not create ISDS name space"));
11785 xmlFreeNode(request
);
11788 xmlSetNs(request
, isds_ns
);
11790 /* Insert Base64 encoded message */
11791 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11793 if (err
) goto leave
;
11795 /* Send request to server and process response */
11796 err
= send_destroy_request_check_response(context
,
11797 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11798 &response
, NULL
, NULL
);
11799 if (err
) goto leave
;
11802 /* ISDS has decided */
11803 xpath_ctx
= xmlXPathNewContext(response
);
11808 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11813 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11816 isds_log_message(context
,
11817 _("Server did not return any response on "
11818 "AuthenticateMessage request"));
11823 isds_log(ILF_ISDS
, ILL_DEBUG
,
11824 _("ISDS authenticated the message successfully\n"));
11826 isds_log_message(context
, _("ISDS does not know the message"));
11833 xmlXPathFreeObject(result
);
11834 xmlXPathFreeContext(xpath_ctx
);
11836 xmlFreeDoc(response
);
11837 xmlFreeNode(request
);
11838 #else /* not HAVE_LIBCURL */
11846 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11847 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11849 * @context is session context
11850 * @input_data is memory with raw CMS signed message or delivery info bit
11851 * stream to re-sign
11852 * @input_length is @input_data size in bytes
11853 * @output_data is pointer to auto-allocated memory where to store re-signed
11854 * input data blob. Caller must free it.
11855 * @output_data is pointer where to store @output_data size in bytes
11856 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11857 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11859 * IE_SUCCESS if CMS blob has been re-signed successfully
11860 * other code for other errors */
11861 isds_error
isds_resign_message(struct isds_ctx
*context
,
11862 const void *input_data
, size_t input_length
,
11863 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11864 isds_error err
= IE_SUCCESS
;
11866 xmlNsPtr isds_ns
= NULL
;
11867 xmlNodePtr request
= NULL
;
11868 xmlDocPtr response
= NULL
;
11869 xmlXPathContextPtr xpath_ctx
= NULL
;
11870 xmlXPathObjectPtr result
= NULL
;
11871 char *string
= NULL
;
11872 const xmlChar
*codes
[] = {
11879 const char *meanings
[] = {
11881 "Message is not original",
11882 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11883 "Time stamp could not been generated in time"
11885 const isds_error errors
[] = {
11891 struct code_map_isds_error map
= {
11893 .meanings
= meanings
,
11898 if (NULL
!= output_data
) *output_data
= NULL
;
11899 if (NULL
!= output_length
) *output_length
= 0;
11900 if (NULL
!= valid_to
) *valid_to
= NULL
;
11902 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11903 zfree(context
->long_message
);
11904 if (NULL
== input_data
|| 0 == input_length
) {
11905 isds_log_message(context
, _("Empty CMS blob on input"));
11908 if (NULL
== output_data
|| NULL
== output_length
) {
11909 isds_log_message(context
,
11910 _("NULL pointer provided for output CMS blob"));
11915 /* Check if connection is established
11916 * TODO: This check should be done downstairs. */
11917 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11920 /* Build Re-signISDSDocument request */
11921 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11923 isds_log_message(context
,
11924 _("Could not build Re-signISDSDocument request"));
11927 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11929 isds_log_message(context
, _("Could not create ISDS name space"));
11930 xmlFreeNode(request
);
11933 xmlSetNs(request
, isds_ns
);
11935 /* Insert Base64 encoded CMS blob */
11936 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11937 input_data
, input_length
);
11938 if (err
) goto leave
;
11940 /* Send request to server and process response */
11941 err
= send_destroy_request_check_response(context
,
11942 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11943 &response
, NULL
, &map
);
11944 if (err
) goto leave
;
11947 /* Extract re-signed data */
11948 xpath_ctx
= xmlXPathNewContext(response
);
11953 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11957 result
= xmlXPathEvalExpression(
11958 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11963 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11964 isds_log_message(context
,
11965 _("Missing Re-signISDSDocumentResponse element"));
11969 if (result
->nodesetval
->nodeNr
> 1) {
11970 isds_log_message(context
,
11971 _("Multiple Re-signISDSDocumentResponse element"));
11975 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11976 xmlXPathFreeObject(result
); result
= NULL
;
11978 EXTRACT_STRING("isds:dmResultDoc", string
);
11979 /* Decode non-empty data */
11980 if (NULL
!= string
&& string
[0] != '\0') {
11981 *output_length
= _isds_b64decode(string
, output_data
);
11982 if (*output_length
== (size_t) -1) {
11983 isds_log_message(context
,
11984 _("Error while Base64-decoding re-signed data"));
11989 isds_log_message(context
, _("Server did not send re-signed data"));
11995 if (NULL
!= valid_to
) {
11996 /* Get time stamp expiration date */
11997 EXTRACT_STRING("isds:dmValidTo", string
);
11998 if (NULL
!= string
) {
11999 *valid_to
= calloc(1, sizeof(**valid_to
));
12004 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
12006 if (err
== IE_NOTSUP
) {
12008 char *string_locale
= _isds_utf82locale(string
);
12009 isds_printf_message(context
,
12010 _("Invalid dmValidTo value: %s"), string_locale
);
12011 free(string_locale
);
12021 xmlXPathFreeObject(result
);
12022 xmlXPathFreeContext(xpath_ctx
);
12024 xmlFreeDoc(response
);
12025 xmlFreeNode(request
);
12026 #else /* not HAVE_LIBCURL */
12033 #undef INSERT_ELEMENT
12034 #undef CHECK_FOR_STRING_LENGTH
12035 #undef INSERT_STRING_ATTRIBUTE
12036 #undef INSERT_ULONGINTNOPTR
12037 #undef INSERT_ULONGINT
12038 #undef INSERT_LONGINT
12039 #undef INSERT_BOOLEAN
12040 #undef INSERT_SCALAR_BOOLEAN
12041 #undef INSERT_STRING
12042 #undef INSERT_STRING_WITH_NS
12043 #undef EXTRACT_STRING_ATTRIBUTE
12044 #undef EXTRACT_ULONGINT
12045 #undef EXTRACT_LONGINT
12046 #undef EXTRACT_BOOLEAN
12047 #undef EXTRACT_STRING
12050 /* Compute hash of message from raw representation and store it into envelope.
12051 * Original hash structure will be destroyed in envelope.
12052 * @context is session context
12053 * @message is message carrying raw XML message blob
12054 * @algorithm is desired hash algorithm to use */
12055 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
12056 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
12057 isds_error err
= IE_SUCCESS
;
12059 void *xml_stream
= NULL
;
12060 size_t xml_stream_length
;
12061 size_t phys_start
, phys_end
;
12062 char *phys_path
= NULL
;
12063 struct isds_hash
*new_hash
= NULL
;
12066 if (!context
) return IE_INVALID_CONTEXT
;
12067 zfree(context
->long_message
);
12068 if (!message
) return IE_INVAL
;
12070 if (!message
->raw
) {
12071 isds_log_message(context
,
12072 _("Message does not carry raw representation"));
12076 switch (message
->raw_type
) {
12077 case RAWTYPE_INCOMING_MESSAGE
:
12079 xml_stream
= message
->raw
;
12080 xml_stream_length
= message
->raw_length
;
12083 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
12084 nsuri
= SISDS_INCOMING_NS
;
12085 xml_stream
= message
->raw
;
12086 xml_stream_length
= message
->raw_length
;
12089 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
12090 nsuri
= SISDS_INCOMING_NS
;
12091 err
= _isds_extract_cms_data(context
,
12092 message
->raw
, message
->raw_length
,
12093 &xml_stream
, &xml_stream_length
);
12094 if (err
) goto leave
;
12097 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
12098 nsuri
= SISDS_OUTGOING_NS
;
12099 xml_stream
= message
->raw
;
12100 xml_stream_length
= message
->raw_length
;
12103 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
12104 nsuri
= SISDS_OUTGOING_NS
;
12105 err
= _isds_extract_cms_data(context
,
12106 message
->raw
, message
->raw_length
,
12107 &xml_stream
, &xml_stream_length
);
12108 if (err
) goto leave
;
12112 isds_log_message(context
, _("Bad raw representation type"));
12118 /* XXX: Hash is computed from original string representing isds:dmDm
12119 * subtree. That means no encoding, white space, xmlns attributes changes.
12120 * In other words, input for hash can be invalid XML stream. */
12121 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12122 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12123 PHYSXML_ELEMENT_SEPARATOR
,
12124 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12125 PHYSXML_ELEMENT_SEPARATOR
12126 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12130 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12131 phys_path
, &phys_start
, &phys_end
);
12134 isds_log_message(context
,
12135 _("Substring with isds:dmDM element could not be located "
12136 "in raw message"));
12142 new_hash
= calloc(1, sizeof(*new_hash
));
12147 new_hash
->algorithm
= algorithm
;
12148 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12151 isds_log_message(context
, _("Could not compute message hash"));
12155 /* Save computed hash */
12156 if (!message
->envelope
) {
12157 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12158 if (!message
->envelope
) {
12163 isds_hash_free(&message
->envelope
->hash
);
12164 message
->envelope
->hash
= new_hash
;
12168 isds_hash_free(&new_hash
);
12172 if (xml_stream
!= message
->raw
) free(xml_stream
);
12177 /* Compare two hashes.
12178 * @h1 is first hash
12179 * @h2 is another hash
12181 * IE_SUCCESS if hashes equal
12182 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12183 * IE_ENUM if not comparable, but both structures defined
12184 * IE_INVAL if some of the structures are undefined (NULL)
12185 * IE_ERROR if internal error occurs */
12186 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12187 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12188 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12189 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12190 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12191 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12193 for (size_t i
= 0; i
< h1
->length
; i
++) {
12194 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12195 return IE_NOTEQUAL
;
12201 /* Check message has gone through ISDS by comparing message hash stored in
12202 * ISDS and locally computed hash. You must provide message with valid raw
12203 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12204 * This is convenient wrapper for isds_download_message_hash(),
12205 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12206 * @context is session context
12207 * @message is message with valid raw and envelope member; envelope->hash
12208 * member will be changed during function run. Use envelope on heap only.
12210 * IE_SUCCESS if message originates in ISDS
12211 * IE_NOTEQUAL if message is unknown to ISDS
12212 * other code for other errors */
12213 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12214 struct isds_message
*message
) {
12215 isds_error err
= IE_SUCCESS
;
12216 struct isds_hash
*downloaded_hash
= NULL
;
12218 if (!context
) return IE_INVALID_CONTEXT
;
12219 zfree(context
->long_message
);
12220 if (!message
) return IE_INVAL
;
12222 if (!message
->envelope
) {
12223 isds_log_message(context
,
12224 _("Given message structure is missing envelope"));
12227 if (!message
->raw
) {
12228 isds_log_message(context
,
12229 _("Given message structure is missing raw representation"));
12233 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12235 if (err
) goto leave
;
12237 err
= isds_compute_message_hash(context
, message
,
12238 downloaded_hash
->algorithm
);
12239 if (err
) goto leave
;
12241 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12244 isds_hash_free(&downloaded_hash
);
12249 /* Search for document by document ID in list of documents. IDs are compared
12251 * @documents is list of isds_documents
12252 * @id is document identifier
12253 * @return first matching document or NULL. */
12254 const struct isds_document
*isds_find_document_by_id(
12255 const struct isds_list
*documents
, const char *id
) {
12256 const struct isds_list
*item
;
12257 const struct isds_document
*document
;
12259 for (item
= documents
; item
; item
= item
->next
) {
12260 document
= (struct isds_document
*) item
->data
;
12261 if (!document
) continue;
12263 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12271 /* Normalize @mime_type to be proper MIME type.
12272 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12273 * guess regular MIME type (e.g. "application/pdf").
12274 * @mime_type is UTF-8 encoded MIME type to fix
12275 * @return original @mime_type if no better interpretation exists, or
12276 * constant static UTF-8 encoded string with proper MIME type. */
12277 const char *isds_normalize_mime_type(const char *mime_type
) {
12278 if (!mime_type
) return NULL
;
12280 for (size_t offset
= 0;
12281 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12283 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12284 extension_map_mime
[offset
]))
12285 return (const char *) extension_map_mime
[offset
+ 1];
12292 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12293 struct isds_message **message);
12294 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12295 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12296 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12297 struct isds_address **address);
12299 int isds_message_free(struct isds_message **message);
12300 int isds_address_free(struct isds_address **address);
12304 /* Makes known all relevant namespaces to given XPath context
12305 * @xpath_ctx is XPath context
12306 * @message_ns selects proper message name space. Unsigned and signed
12307 * messages and delivery info's differ in prefix and URI. */
12308 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12309 const message_ns_type message_ns
) {
12310 const xmlChar
*message_namespace
= NULL
;
12312 if (!xpath_ctx
) return IE_ERROR
;
12314 switch(message_ns
) {
12316 message_namespace
= BAD_CAST ISDS1_NS
; break;
12317 case MESSAGE_NS_UNSIGNED
:
12318 message_namespace
= BAD_CAST ISDS_NS
; break;
12319 case MESSAGE_NS_SIGNED_INCOMING
:
12320 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12321 case MESSAGE_NS_SIGNED_OUTGOING
:
12322 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12323 case MESSAGE_NS_SIGNED_DELIVERY
:
12324 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12329 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12331 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12333 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12335 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12337 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12339 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))