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 /* Deallocate struct isds_box_state_period recursively and NULL it */
465 void isds_box_state_period_free(struct isds_box_state_period
**period
) {
466 if (NULL
== period
|| NULL
== *period
) return;
471 /* *DUP_OR_ERROR macros needs error label */
472 #define STRDUP_OR_ERROR(new, template) { \
476 (new) = strdup(template); \
477 if (!new) goto error; \
481 #define FLATDUP_OR_ERROR(new, template) { \
485 (new) = malloc(sizeof(*(new))); \
486 if (!new) goto error; \
487 memcpy((new), (template), sizeof(*(template))); \
491 /* Copy structure isds_pki_credentials recursively. */
492 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
493 const struct isds_pki_credentials
*template) {
494 struct isds_pki_credentials
*new = NULL
;
496 if(!template) return NULL
;
498 new = calloc(1, sizeof(*new));
499 if (!new) return NULL
;
501 STRDUP_OR_ERROR(new->engine
, template->engine
);
502 new->certificate_format
= template->certificate_format
;
503 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
504 new->key_format
= template->key_format
;
505 STRDUP_OR_ERROR(new->key
, template->key
);
506 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
511 isds_pki_credentials_free(&new);
516 /* Copy structure isds_PersonName recursively */
517 struct isds_PersonName
*isds_PersonName_duplicate(
518 const struct isds_PersonName
*src
) {
519 struct isds_PersonName
*new = NULL
;
521 if (!src
) return NULL
;
523 new = calloc(1, sizeof(*new));
524 if (!new) return NULL
;
526 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
527 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
528 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
529 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
534 isds_PersonName_free(&new);
539 /* Copy structure isds_BirthInfo recursively */
540 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
541 const struct isds_BirthInfo
*template) {
542 struct isds_BirthInfo
*new = NULL
;
544 if (!template) return NULL
;
546 new = calloc(1, sizeof(*new));
547 if (!new) return NULL
;
549 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
550 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
551 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
552 STRDUP_OR_ERROR(new->biState
, template->biState
);
557 isds_BirthInfo_free(&new);
562 /* Copy structure isds_Address recursively */
563 struct isds_Address
*isds_Address_duplicate(
564 const struct isds_Address
*src
) {
565 struct isds_Address
*new = NULL
;
567 if (!src
) return NULL
;
569 new = calloc(1, sizeof(*new));
570 if (!new) return NULL
;
572 FLATDUP_OR_ERROR(new->adCode
, src
->adCode
);
573 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
574 STRDUP_OR_ERROR(new->adDistrict
, src
->adDistrict
);
575 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
576 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
577 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
578 src
->adNumberInMunicipality
);
579 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
580 STRDUP_OR_ERROR(new->adState
, src
->adState
);
585 isds_Address_free(&new);
590 /* Copy structure isds_DbOwnerInfo recursively */
591 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
592 const struct isds_DbOwnerInfo
*src
) {
593 struct isds_DbOwnerInfo
*new = NULL
;
594 if (!src
) return NULL
;
596 new = calloc(1, sizeof(*new));
597 if (!new) return NULL
;
599 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
600 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
601 STRDUP_OR_ERROR(new->ic
, src
->ic
);
603 if (src
->personName
) {
604 if (!(new->personName
=
605 isds_PersonName_duplicate(src
->personName
)))
609 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
611 if (src
->birthInfo
) {
612 if (!(new->birthInfo
=
613 isds_BirthInfo_duplicate(src
->birthInfo
)))
618 if (!(new->address
= isds_Address_duplicate(src
->address
)))
622 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
623 STRDUP_OR_ERROR(new->email
, src
->email
);
624 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
625 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
626 FLATDUP_OR_ERROR(new->aifoIsds
, src
->aifoIsds
);
627 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
628 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
629 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
630 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
635 isds_DbOwnerInfo_free(&new);
640 /* Copy structure isds_DbUserInfo recursively */
641 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
642 const struct isds_DbUserInfo
*src
) {
643 struct isds_DbUserInfo
*new = NULL
;
644 if (!src
) return NULL
;
646 new = calloc(1, sizeof(*new));
647 if (!new) return NULL
;
649 STRDUP_OR_ERROR(new->userID
, src
->userID
);
650 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
651 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
653 if (src
->personName
) {
654 if (!(new->personName
=
655 isds_PersonName_duplicate(src
->personName
)))
660 if (!(new->address
= isds_Address_duplicate(src
->address
)))
664 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
665 STRDUP_OR_ERROR(new->ic
, src
->ic
);
666 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
667 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
668 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
669 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
670 STRDUP_OR_ERROR(new->caState
, src
->caState
);
671 STRDUP_OR_ERROR(new->aifo_ticket
, src
->aifo_ticket
);
676 isds_DbUserInfo_free(&new);
681 /* Copy structure isds_box_state_period recursively */
682 struct isds_box_state_period
*isds_box_state_period_duplicate(
683 const struct isds_box_state_period
*src
) {
684 struct isds_box_state_period
*new = NULL
;
685 if (!src
) return NULL
;
687 new = calloc(1, sizeof(*new));
688 if (!new) return NULL
;
690 memcpy(&new->from
, &src
->from
, sizeof(src
->from
));
691 memcpy(&new->to
, &src
->to
, sizeof(src
->to
));
692 new->dbState
= src
->dbState
;
697 #undef FLATDUP_OR_ERROR
698 #undef STRDUP_OR_ERROR
701 /* Logs libxml2 errors. Should be registered to libxml2 library.
702 * @ctx is unused currently
703 * @msg is printf-like formated message from libxml2 (UTF-8?)
704 * @... are variadic arguments for @msg */
705 static void log_xml(void *ctx
, const char *msg
, ...) {
709 /* Silent warning for unused function argument.
710 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
716 isds_vasprintf(&text
, msg
, ap
);
720 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
725 /* Initialize ISDS library.
726 * Global function, must be called before other functions.
727 * If it fails you can not use ISDS library and must call isds_cleanup() to
728 * free partially initialized global variables. */
729 isds_error
isds_init(void) {
730 /* NULL global variables */
731 log_facilities
= ILF_ALL
;
732 log_level
= ILL_WARNING
;
734 log_callback_data
= NULL
;
737 /* Initialize gettext */
738 bindtextdomain(PACKAGE
, LOCALEDIR
);
742 /* Initialize CURL */
743 if (curl_global_init(CURL_GLOBAL_ALL
)) {
744 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
747 #endif /* HAVE_LIBCURL */
749 /* Initialise cryptographic back-ends. */
750 if (IE_SUCCESS
!= _isds_init_crypto()) {
751 isds_log(ILF_ISDS
, ILL_CRIT
,
752 _("Initialization of cryptographic back-end failed\n"));
756 /* This can _exit() current program. Find not so assertive check. */
758 xmlSetGenericErrorFunc(NULL
, log_xml
);
761 if (_isds_init_expat(&version_expat
)) {
762 isds_log(ILF_ISDS
, ILL_CRIT
,
763 _("expat library initialization failed\n"));
767 /* Allocate global variables */
774 /* Deinitialize ISDS library.
775 * Global function, must be called as last library function. */
776 isds_error
isds_cleanup(void) {
782 curl_global_cleanup();
789 /* Return version string of this library. Version of dependencies can be
790 * embedded. Do no try to parse it. You must free it. */
791 char *isds_version(void) {
794 isds_asprintf(&buffer
,
796 # ifndef USE_OPENSSL_BACKEND
797 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
799 _("%s (%s, %s, %s, libxml2 %s)"),
802 # ifndef USE_OPENSSL_BACKEND
803 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
805 _("%s (%s, %s, libxml2 %s)"),
812 #ifndef USE_OPENSSL_BACKEND
813 version_gpgme
, version_gcrypt
,
817 version_expat
, xmlParserVersion
);
822 /* Return text description of ISDS error */
823 const char *isds_strerror(const isds_error error
) {
826 return(_("Success")); break;
828 return(_("Unspecified error")); break;
830 return(_("Not supported")); break;
832 return(_("Invalid value")); break;
833 case IE_INVALID_CONTEXT
:
834 return(_("Invalid context")); break;
835 case IE_NOT_LOGGED_IN
:
836 return(_("Not logged in")); break;
837 case IE_CONNECTION_CLOSED
:
838 return(_("Connection closed")); break;
840 return(_("Timed out")); break;
842 return(_("Not exist")); break;
844 return(_("Out of memory")); break;
846 return(_("Network problem")); break;
848 return(_("HTTP problem")); break;
850 return(_("SOAP problem")); break;
852 return(_("XML problem")); break;
854 return(_("ISDS server problem")); break;
856 return(_("Invalid enum value")); break;
858 return(_("Invalid date value")); break;
860 return(_("Too big")); break;
862 return(_("Too small")); break;
864 return(_("Value not unique")); break;
866 return(_("Values not equal")); break;
867 case IE_PARTIAL_SUCCESS
:
868 return(_("Some suboperations failed")); break;
870 return(_("Operation aborted")); break;
872 return(_("Security problem")); break;
874 return(_("Unknown error"));
879 /* Create ISDS context.
880 * Each context can be used for different sessions to (possibly) different
881 * ISDS server with different credentials. */
882 struct isds_ctx
*isds_ctx_create(void) {
883 struct isds_ctx
*context
;
884 context
= malloc(sizeof(*context
));
885 if (context
) memset(context
, 0, sizeof(*context
));
890 /* Close possibly opened connection to Czech POINT document deposit without
891 * resetting long_message buffer.
892 * XXX: Do not use czp_close_connection() if you do not want to destroy log
894 * @context is Czech POINT session context. */
895 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
896 if (!context
) return IE_INVALID_CONTEXT
;
897 _isds_close_connection(context
);
902 /* Discard credentials.
903 * @context is ISDS context
904 * @discard_saved_username is true for removing saved username, false for
906 * Only that. It does not cause log out, connection close or similar. */
907 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
908 _Bool discard_saved_username
) {
909 if(!context
) return IE_INVALID_CONTEXT
;
911 if (context
->username
) {
912 memset(context
->username
, 0, strlen(context
->username
));
913 zfree(context
->username
);
915 if (context
->password
) {
916 memset(context
->password
, 0, strlen(context
->password
));
917 zfree(context
->password
);
919 isds_pki_credentials_free(&context
->pki_credentials
);
920 if (discard_saved_username
&& context
->saved_username
) {
921 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
922 zfree(context
->saved_username
);
927 #endif /* HAVE_LIBCURL */
930 /* Destroy ISDS context and free memory.
931 * @context will be NULLed on success. */
932 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
933 if (!context
|| !*context
) {
934 return IE_INVALID_CONTEXT
;
938 /* Discard credentials and close connection */
939 switch ((*context
)->type
) {
940 case CTX_TYPE_NONE
: break;
941 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
943 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
944 czp_do_close_connection(*context
); break;
948 _isds_discard_credentials(*context
, 1);
950 /* Free other structures */
951 free((*context
)->url
);
952 free((*context
)->tls_verify_server
);
953 free((*context
)->tls_ca_file
);
954 free((*context
)->tls_ca_dir
);
955 free((*context
)->tls_crl_file
);
956 #endif /* HAVE_LIBCURL */
957 free((*context
)->long_message
);
965 /* Return long message text produced by library function, e.g. detailed error
966 * message. Returned pointer is only valid until new library function is
967 * called for the same context. Could be NULL, especially if NULL context is
968 * supplied. Return string is locale encoded. */
969 char *isds_long_message(const struct isds_ctx
*context
) {
970 if (!context
) return NULL
;
971 return context
->long_message
;
975 /* Stores message into context' long_message buffer.
976 * Application can pick the message up using isds_long_message().
977 * NULL @message truncates the buffer but does not deallocate it.
978 * @message is coded in locale encoding */
979 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
980 const char *message
) {
984 if (!context
) return IE_INVALID_CONTEXT
;
986 /* FIXME: Check for integer overflow */
987 length
= 1 + ((message
) ? strlen(message
) : 0);
988 buffer
= realloc(context
->long_message
, length
);
989 if (!buffer
) return IE_NOMEM
;
992 strcpy(buffer
, message
);
996 context
->long_message
= buffer
;
1001 /* Appends message into context' long_message buffer.
1002 * Application can pick the message up using isds_long_message().
1003 * NULL message has void effect. */
1004 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
1005 const char *message
) {
1007 size_t old_length
, length
;
1009 if (!context
) return IE_INVALID_CONTEXT
;
1010 if (!message
) return IE_SUCCESS
;
1011 if (!context
->long_message
)
1012 return isds_log_message(context
, message
);
1014 old_length
= strlen(context
->long_message
);
1015 /* FIXME: Check for integer overflow */
1016 length
= 1 + old_length
+ strlen(message
);
1017 buffer
= realloc(context
->long_message
, length
);
1018 if (!buffer
) return IE_NOMEM
;
1020 strcpy(buffer
+ old_length
, message
);
1022 context
->long_message
= buffer
;
1027 /* Stores formatted message into context' long_message buffer.
1028 * Application can pick the message up using isds_long_message(). */
1029 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
1030 const char *format
, ...) {
1034 if (!context
) return IE_INVALID_CONTEXT
;
1035 va_start(ap
, format
);
1036 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
1039 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
1044 * @facilities is bit mask of isds_log_facility values,
1045 * @level is verbosity level. */
1046 void isds_set_logging(const unsigned int facilities
,
1047 const isds_log_level level
) {
1048 log_facilities
= facilities
;
1053 /* Register callback function libisds calls when new global log message is
1054 * produced by library. Library logs to stderr by default.
1055 * @callback is function provided by application libisds will call. See type
1056 * definition for @callback argument explanation. Pass NULL to revert logging to
1057 * default behaviour.
1058 * @data is application specific data @callback gets as last argument */
1059 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1060 log_callback
= callback
;
1061 log_callback_data
= data
;
1065 /* Log @message in class @facility with log @level into global log. @message
1066 * is printf(3) formatting string, variadic arguments may be necessary.
1067 * For debugging purposes. */
1068 _hidden isds_error
isds_log(const isds_log_facility facility
,
1069 const isds_log_level level
, const char *message
, ...) {
1071 char *buffer
= NULL
;
1074 if (level
> log_level
) return IE_SUCCESS
;
1075 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1076 if (!message
) return IE_INVAL
;
1079 /* Pass message to application supplied callback function */
1080 va_start(ap
, message
);
1081 length
= isds_vasprintf(&buffer
, message
, ap
);
1088 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1092 /* Default: Log it to stderr */
1093 va_start(ap
, message
);
1094 vfprintf(stderr
, message
, ap
);
1096 /* Line buffered printf is default.
1104 /* Set timeout in milliseconds for each network job like connecting to server
1105 * or sending message. Use 0 to disable timeout limits. */
1106 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1107 const unsigned int timeout
) {
1108 if (!context
) return IE_INVALID_CONTEXT
;
1109 zfree(context
->long_message
);
1112 context
->timeout
= timeout
;
1114 if (context
->curl
) {
1117 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1119 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1120 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1123 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1124 context
->timeout
/ 1000);
1125 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1126 if (curl_err
) return IE_ERROR
;
1130 #else /* not HAVE_LIBCURL */
1136 /* Register callback function libisds calls periodically during HTTP data
1138 * @context is session context
1139 * @callback is function provided by application libisds will call. See type
1140 * definition for @callback argument explanation.
1141 * @data is application specific data @callback gets as last argument */
1142 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1143 isds_progress_callback callback
, void *data
) {
1144 if (!context
) return IE_INVALID_CONTEXT
;
1145 zfree(context
->long_message
);
1148 context
->progress_callback
= callback
;
1149 context
->progress_callback_data
= data
;
1152 #else /* not HAVE_LIBCURL */
1158 /* Change context settings.
1159 * @context is context which setting will be applied to
1160 * @option is name of option. It determines the type of last argument. See
1161 * isds_option definition for more info.
1162 * @... is value of new setting. Type is determined by @option
1164 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1166 isds_error err
= IE_SUCCESS
;
1169 char *pointer
, *string
;
1172 if (!context
) return IE_INVALID_CONTEXT
;
1173 zfree(context
->long_message
);
1175 va_start(ap
, option
);
1177 #define REPLACE_VA_BOOLEAN(destination) { \
1178 if (!(destination)) { \
1179 (destination) = malloc(sizeof(*(destination))); \
1180 if (!(destination)) { \
1181 err = IE_NOMEM; goto leave; \
1184 *(destination) = (_Bool) !!va_arg(ap, int); \
1187 #define REPLACE_VA_STRING(destination) { \
1188 string = va_arg(ap, char *); \
1190 pointer = realloc((destination), 1 + strlen(string)); \
1191 if (!pointer) { err = IE_NOMEM; goto leave; } \
1192 strcpy(pointer, string); \
1193 (destination) = pointer; \
1195 free(destination); \
1196 (destination) = NULL; \
1201 case IOPT_TLS_VERIFY_SERVER
:
1203 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1205 err
= IE_NOTSUP
; goto leave
;
1208 case IOPT_TLS_CA_FILE
:
1210 REPLACE_VA_STRING(context
->tls_ca_file
);
1212 err
= IE_NOTSUP
; goto leave
;
1215 case IOPT_TLS_CA_DIRECTORY
:
1217 REPLACE_VA_STRING(context
->tls_ca_dir
);
1219 err
= IE_NOTSUP
; goto leave
;
1222 case IOPT_TLS_CRL_FILE
:
1224 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1225 REPLACE_VA_STRING(context
->tls_crl_file
);
1227 isds_log_message(context
,
1228 _("Curl library does not support CRL definition"));
1230 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1232 err
= IE_NOTSUP
; goto leave
;
1233 #endif /* not HAVE_LIBCURL */
1235 case IOPT_NORMALIZE_MIME_TYPE
:
1236 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1240 err
= IE_ENUM
; goto leave
;
1243 #undef REPLACE_VA_STRING
1244 #undef REPLACE_VA_BOOLEAN
1253 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1254 * Destination for NULL argument will not be touched.
1255 * Destination pointers must be freed before calling this function.
1256 * If @username is @context->saved_username, the saved_username will not be
1257 * replaced. The saved_username is clobbered only if context has set otp
1259 * Return IE_SUCCESS on success. */
1260 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1261 const char *username
, const char *password
,
1262 const struct isds_pki_credentials
*pki_credentials
) {
1263 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1265 /* FIXME: mlock password
1266 * (I have a library) */
1269 context
->username
= strdup(username
);
1270 if (context
->otp
&& context
->saved_username
!= username
)
1271 context
->saved_username
= strdup(username
);
1274 if (NULL
== context
->otp_credentials
)
1275 context
->password
= strdup(password
);
1277 context
->password
= _isds_astrcat(password
,
1278 context
->otp_credentials
->otp_code
);
1280 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1282 if ((NULL
!= username
&& NULL
== context
->username
) ||
1283 (NULL
!= password
&& NULL
== context
->password
) ||
1284 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1285 (context
->otp
&& NULL
!= context
->username
&&
1286 NULL
== context
->saved_username
)) {
1295 /* Connect and log into ISDS server.
1296 * All required arguments will be copied, you do not have to keep them after
1298 * ISDS supports six different authentication methods. Exact method is
1299 * selected on @username, @password, @pki_credentials, and @otp arguments:
1300 * - If @pki_credentials == NULL, @username and @password must be supplied
1302 * - If @otp == NULL, simple authentication by username and password will
1304 * - If @otp != NULL, authentication by username and password and OTP
1306 * - If @pki_credentials != NULL, then
1307 * - If @username == NULL, only certificate will be used
1308 * - If @username != NULL, then
1309 * - If @password == NULL, then certificate will be used and
1310 * @username shifts meaning to box ID. This is used for hosted
1312 * - Otherwise all three arguments will be used.
1313 * Please note, that different cases require different certificate type
1314 * (system qualified one or commercial non qualified one). This library
1315 * does not check such political issues. Please see ISDS Specification
1317 * @url is base address of ISDS web service. Pass extern isds_locator
1318 * variable to use production ISDS instance without client certificate
1319 * authentication (or extern isds_cert_locator with client certificate
1320 * authentication or extern isds_otp_locators with OTP authentication).
1321 * Passing NULL has the same effect, autoselection between isds_locator,
1322 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1323 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1324 * isds_otp_testing_locator) variable to select testing instance.
1325 * @username is user name of ISDS user or box ID
1326 * @password is user's secret password
1327 * @pki_credentials defines public key cryptographic material to use in client
1329 * @otp selects one-time password authentication method to use, defines OTP
1330 * code (if known) and returns fine grade resolution of OTP procedure.
1332 * IE_SUCCESS if authentication succeeds
1333 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1334 * requested, fine grade reason will be set into @otp->resolution. Error
1335 * message from server can be obtained by isds_long_message() call.
1336 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1337 * server has sent OTP code through side channel. Application is expected to
1338 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1339 * this call to complete second phase of TOTP authentication;
1340 * or other appropriate error. */
1341 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1342 const char *username
, const char *password
,
1343 const struct isds_pki_credentials
*pki_credentials
,
1344 struct isds_otp
*otp
) {
1346 isds_error err
= IE_NOT_LOGGED_IN
;
1347 isds_error soap_err
;
1348 xmlNsPtr isds_ns
= NULL
;
1349 xmlNodePtr request
= NULL
;
1350 #endif /* HAVE_LIBCURL */
1352 if (!context
) return IE_INVALID_CONTEXT
;
1353 zfree(context
->long_message
);
1356 /* Close connection if already logged in */
1357 if (context
->curl
) {
1358 _isds_close_connection(context
);
1361 /* Store configuration */
1362 context
->type
= CTX_TYPE_ISDS
;
1363 zfree(context
->url
);
1365 /* Mangle base URI according to requested authentication method */
1366 if (NULL
== pki_credentials
) {
1367 isds_log(ILF_SEC
, ILL_INFO
,
1368 _("Selected authentication method: no certificate, "
1369 "username and password\n"));
1370 if (!username
|| !password
) {
1371 isds_log_message(context
,
1372 _("Both username and password must be supplied"));
1375 context
->otp_credentials
= otp
;
1376 context
->otp
= (NULL
!= context
->otp_credentials
);
1378 if (!context
->otp
) {
1379 /* Default locator is official system (without certificate or
1381 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1383 const char *authenticator_uri
= NULL
;
1384 if (!url
) url
= isds_otp_locator
;
1385 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1386 switch (context
->otp_credentials
->method
) {
1388 isds_log(ILF_SEC
, ILL_INFO
,
1389 _("Selected authentication method: "
1390 "HMAC-based one-time password\n"));
1392 "%sas/processLogin?type=hotp&uri=%sapps/";
1395 isds_log(ILF_SEC
, ILL_INFO
,
1396 _("Selected authentication method: "
1397 "Time-based one-time password\n"));
1398 if (context
->otp_credentials
->otp_code
== NULL
) {
1399 isds_log(ILF_SEC
, ILL_INFO
,
1400 _("OTP code has not been provided by "
1401 "application, requesting server for "
1404 "%sas/processLogin?type=totp&sendSms=true&"
1407 isds_log(ILF_SEC
, ILL_INFO
,
1408 _("OTP code has been provided by "
1409 "application, not requesting server "
1412 "%sas/processLogin?type=totp&"
1417 isds_log_message(context
,
1418 _("Unknown one-time password authentication "
1419 "method requested by application"));
1422 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
, url
))
1426 /* Default locator is official system (with client certificate) */
1428 context
->otp_credentials
= NULL
;
1429 if (!url
) url
= isds_cert_locator
;
1432 isds_log(ILF_SEC
, ILL_INFO
,
1433 _("Selected authentication method: system certificate, "
1434 "no username and no password\n"));
1436 context
->url
= _isds_astrcat(url
, "cert/");
1439 isds_log(ILF_SEC
, ILL_INFO
,
1440 _("Selected authentication method: system certificate, "
1441 "box ID and no password\n"));
1442 context
->url
= _isds_astrcat(url
, "hspis/");
1444 isds_log(ILF_SEC
, ILL_INFO
,
1445 _("Selected authentication method: commercial "
1446 "certificate, username and password\n"));
1447 context
->url
= _isds_astrcat(url
, "certds/");
1451 if (!(context
->url
))
1454 /* Prepare CURL handle */
1455 context
->curl
= curl_easy_init();
1456 if (!(context
->curl
))
1459 /* Build log-in request */
1460 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1462 isds_log_message(context
, _("Could not build ISDS log-in request"));
1465 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1467 isds_log_message(context
, _("Could not create ISDS name space"));
1468 xmlFreeNode(request
);
1471 xmlSetNs(request
, isds_ns
);
1473 /* Store credentials */
1474 _isds_discard_credentials(context
, 1);
1475 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1476 _isds_discard_credentials(context
, 1);
1477 xmlFreeNode(request
);
1481 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1484 /* XXX: ISDS documentation does not specify response body for
1485 * DummyOperation request. However real server sends back
1486 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1487 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1488 * SOAP body content, e.g. the dmStatus element. */
1490 /* Send log-in request */
1491 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1494 /* Revert context URL from OTP authentication service URL to OTP web
1495 * service base URL for subsequent calls. Potenial isds_login() retry
1496 * will re-set context URL again. */
1497 zfree(context
->url
);
1498 context
->url
= _isds_astrcat(url
, "apps/");
1499 if (context
->url
== NULL
) {
1500 soap_err
= IE_NOMEM
;
1502 /* Detach pointer to OTP credentials from context */
1503 context
->otp_credentials
= NULL
;
1506 /* Remove credentials */
1507 _isds_discard_credentials(context
, 0);
1509 /* Destroy log-in request */
1510 xmlFreeNode(request
);
1513 _isds_close_connection(context
);
1517 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1518 * authentication succeeded if soap_err == IE_SUCCESS */
1522 isds_log(ILF_ISDS
, ILL_DEBUG
,
1523 _("User %s has been logged into server %s successfully\n"),
1526 #else /* not HAVE_LIBCURL */
1532 /* Log out from ISDS server discards credentials and connection configuration. */
1533 isds_error
isds_logout(struct isds_ctx
*context
) {
1534 if (!context
) return IE_INVALID_CONTEXT
;
1535 zfree(context
->long_message
);
1538 if (context
->curl
) {
1540 isds_error err
= _isds_invalidate_otp_cookie(context
);
1541 if (err
) return err
;
1544 /* Close connection */
1545 _isds_close_connection(context
);
1547 /* Discard credentials for sure. They should not survive isds_login(),
1548 * even successful .*/
1549 _isds_discard_credentials(context
, 1);
1551 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1553 _isds_discard_credentials(context
, 1);
1555 zfree(context
->url
);
1557 #else /* not HAVE_LIBCURL */
1563 /* Verify connection to ISDS is alive and server is responding.
1564 * Send dummy request to ISDS and expect dummy response. */
1565 isds_error
isds_ping(struct isds_ctx
*context
) {
1567 isds_error soap_err
;
1568 xmlNsPtr isds_ns
= NULL
;
1569 xmlNodePtr request
= NULL
;
1570 #endif /* HAVE_LIBCURL */
1572 if (!context
) return IE_INVALID_CONTEXT
;
1573 zfree(context
->long_message
);
1576 /* Check if connection is established */
1577 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1580 /* Build dummy request */
1581 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1583 isds_log_message(context
, _("Could build ISDS dummy request"));
1586 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1588 isds_log_message(context
, _("Could not create ISDS name space"));
1589 xmlFreeNode(request
);
1592 xmlSetNs(request
, isds_ns
);
1594 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1596 /* XXX: ISDS documentation does not specify response body for
1597 * DummyOperation request. However real server sends back
1598 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1599 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1600 * SOAP body content, e.g. the dmStatus element. */
1602 /* Send dummy request */
1603 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1605 /* Destroy log-in request */
1606 xmlFreeNode(request
);
1609 isds_log(ILF_ISDS
, ILL_DEBUG
,
1610 _("ISDS server could not be contacted\n"));
1614 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1615 * authentication succeeded if soap_err == IE_SUCCESS */
1618 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1621 #else /* not HAVE_LIBCURL */
1627 /* Send bogus request to ISDS.
1628 * Just for test purposes */
1629 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1632 xmlNsPtr isds_ns
= NULL
;
1633 xmlNodePtr request
= NULL
;
1634 xmlDocPtr response
= NULL
;
1635 xmlChar
*code
= NULL
, *message
= NULL
;
1638 if (!context
) return IE_INVALID_CONTEXT
;
1639 zfree(context
->long_message
);
1642 /* Check if connection is established */
1643 if (!context
->curl
) {
1644 /* Testing printf message */
1645 isds_printf_message(context
, "%s", _("I said connection closed"));
1646 return IE_CONNECTION_CLOSED
;
1650 /* Build dummy request */
1651 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1653 isds_log_message(context
, _("Could build ISDS bogus request"));
1656 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1658 isds_log_message(context
, _("Could not create ISDS name space"));
1659 xmlFreeNode(request
);
1662 xmlSetNs(request
, isds_ns
);
1664 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1666 /* Sent bogus request */
1667 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1669 /* Destroy request */
1670 xmlFreeNode(request
);
1673 isds_log(ILF_ISDS
, ILL_DEBUG
,
1674 _("Processing ISDS response on bogus request failed\n"));
1675 xmlFreeDoc(response
);
1679 /* Check for response status */
1680 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1681 &code
, &message
, NULL
);
1683 isds_log(ILF_ISDS
, ILL_DEBUG
,
1684 _("ISDS response on bogus request is missing status\n"));
1687 xmlFreeDoc(response
);
1690 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1691 char *code_locale
= _isds_utf82locale((char*)code
);
1692 char *message_locale
= _isds_utf82locale((char*)message
);
1693 isds_log(ILF_ISDS
, ILL_DEBUG
,
1694 _("Server refused bogus request (code=%s, message=%s)\n"),
1695 code_locale
, message_locale
);
1696 /* XXX: Literal error messages from ISDS are Czech messages
1697 * (English sometimes) in UTF-8. It's hard to catch them for
1698 * translation. Successfully gettextized would return in locale
1699 * encoding, unsuccessfully translated would pass in UTF-8. */
1700 isds_log_message(context
, message_locale
);
1702 free(message_locale
);
1705 xmlFreeDoc(response
);
1712 xmlFreeDoc(response
);
1714 isds_log(ILF_ISDS
, ILL_DEBUG
,
1715 _("Bogus message accepted by server. This should not happen.\n"));
1718 #else /* not HAVE_LIBCURL */
1725 /* Serialize XML subtree to buffer preserving XML indentation.
1726 * @context is session context
1727 * @subtree is XML element to be serialized (with children)
1728 * @buffer is automatically reallocated buffer where serialize to
1729 * @length is size of serialized stream in bytes
1730 * @return standard error code, free @buffer in case of error */
1731 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1732 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1733 isds_error err
= IE_SUCCESS
;
1734 xmlBufferPtr xml_buffer
= NULL
;
1735 xmlSaveCtxtPtr save_ctx
= NULL
;
1736 xmlDocPtr subtree_doc
= NULL
;
1737 xmlNodePtr subtree_copy
;
1741 if (!context
) return IE_INVALID_CONTEXT
;
1742 if (!buffer
) return IE_INVAL
;
1744 if (!subtree
|| !length
) return IE_INVAL
;
1746 /* Make temporary XML document with @subtree root element */
1747 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1748 * It can result in not well-formed on invalid XML tree (e.g. name space
1749 * prefix definition can miss. */
1752 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1754 isds_log_message(context
, _("Could not build temporary document"));
1759 /* XXX: Copy subtree and attach the copy to document.
1760 * One node can not bee attached into more document at the same time.
1761 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1763 * XXX: Check xmlSaveTree() too. */
1764 subtree_copy
= xmlCopyNodeList(subtree
);
1765 if (!subtree_copy
) {
1766 isds_log_message(context
, _("Could not copy subtree"));
1770 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1772 /* Only this way we get namespace definition as @xmlns:isds,
1773 * otherwise we get namespace prefix without definition */
1774 /* FIXME: Don't overwrite original default namespace */
1775 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1777 isds_log_message(context
, _("Could not create ISDS name space"));
1781 xmlSetNs(subtree_copy
, isds_ns
);
1784 /* Serialize the document into buffer */
1785 xml_buffer
= xmlBufferCreate();
1787 isds_log_message(context
, _("Could not create xmlBuffer"));
1791 /* Last argument 0 means to not format the XML tree */
1792 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1794 isds_log_message(context
, _("Could not create XML serializer"));
1798 /* XXX: According LibXML documentation, this function does not return
1799 * meaningful value yet */
1800 xmlSaveDoc(save_ctx
, subtree_doc
);
1801 if (-1 == xmlSaveFlush(save_ctx
)) {
1802 isds_log_message(context
,
1803 _("Could not serialize XML subtree"));
1807 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1808 * even after xmlSaveFlush(). Thus close it here */
1809 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1812 /* Store and detach buffer from xml_buffer */
1813 *buffer
= xml_buffer
->content
;
1814 *length
= xml_buffer
->use
;
1815 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1818 new_buffer
= realloc(*buffer
, *length
);
1819 if (new_buffer
) *buffer
= new_buffer
;
1827 xmlSaveClose(save_ctx
);
1828 xmlBufferFree(xml_buffer
);
1829 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1832 #endif /* HAVE_LIBCURL */
1836 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1837 * @context is session context
1838 * @document is original document where @nodeset points to
1839 * @nodeset is XPath node set to dump (recursively)
1840 * @buffer is automatically reallocated buffer where serialize to
1841 * @length is size of serialized stream in bytes
1842 * @return standard error code, free @buffer in case of error */
1843 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1844 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1845 void **buffer
, size_t *length
) {
1846 isds_error err
= IE_SUCCESS
;
1847 xmlBufferPtr xml_buffer
= NULL
;
1850 if (!context
) return IE_INVALID_CONTEXT
;
1851 if (!buffer
) return IE_INVAL
;
1853 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1856 /* Empty node set results into NULL buffer */
1857 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1861 /* Resulting the document into buffer */
1862 xml_buffer
= xmlBufferCreate();
1864 isds_log_message(context
, _("Could not create xmlBuffer"));
1869 /* Iterate over all nodes */
1870 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1872 * XXX: xmlNodeDump() appends to xml_buffer. */
1874 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1875 isds_log_message(context
, _("Could not dump XML node"));
1881 /* Store and detach buffer from xml_buffer */
1882 *buffer
= xml_buffer
->content
;
1883 *length
= xml_buffer
->use
;
1884 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1887 new_buffer
= realloc(*buffer
, *length
);
1888 if (new_buffer
) *buffer
= new_buffer
;
1897 xmlBufferFree(xml_buffer
);
1903 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1904 * @context is session context
1905 * @document is original document where @nodeset points to
1906 * @nodeset is XPath node set to dump (recursively)
1907 * @buffer is automatically reallocated buffer where serialize to
1908 * @length is size of serialized stream in bytes
1909 * @return standard error code, free @buffer in case of error */
1910 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1911 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1912 void **buffer
, size_t *length
) {
1913 isds_error err
= IE_SUCCESS
;
1914 xmlBufferPtr xml_buffer
= NULL
;
1915 xmlSaveCtxtPtr save_ctx
= NULL
;
1918 if (!context
) return IE_INVALID_CONTEXT
;
1919 if (!buffer
) return IE_INVAL
;
1921 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1924 /* Empty node set results into NULL buffer */
1925 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1929 /* Resulting the document into buffer */
1930 xml_buffer
= xmlBufferCreate();
1932 isds_log_message(context
, _("Could not create xmlBuffer"));
1936 if (xmlSubstituteEntitiesDefault(1)) {
1937 isds_log_message(context
, _("Could not disable attribute escaping"));
1941 /* Last argument means:
1942 * 0 to not format the XML tree
1943 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1944 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1945 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1947 isds_log_message(context
, _("Could not create XML serializer"));
1951 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1952 isds_log_message(context, _("Could not disable attribute escaping"));
1958 /* Iterate over all nodes */
1959 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1961 * XXX: xmlNodeDump() appends to xml_buffer. */
1963 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1965 /* XXX: According LibXML documentation, this function does not return
1966 * meaningful value yet */
1967 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1968 if (-1 == xmlSaveFlush(save_ctx
)) {
1969 isds_log_message(context
,
1970 _("Could not serialize XML subtree"));
1976 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1977 * even after xmlSaveFlush(). Thus close it here */
1978 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1980 /* Store and detach buffer from xml_buffer */
1981 *buffer
= xml_buffer
->content
;
1982 *length
= xml_buffer
->use
;
1983 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1986 new_buffer
= realloc(*buffer
, *length
);
1987 if (new_buffer
) *buffer
= new_buffer
;
1995 xmlSaveClose(save_ctx
);
1996 xmlBufferFree(xml_buffer
);
2003 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
2004 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
2005 if (!string
|| !type
) return IE_INVAL
;
2007 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
2009 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
2011 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
2012 *type
= DBTYPE_PFO_ADVOK
;
2013 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
2014 *type
= DBTYPE_PFO_DANPOR
;
2015 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
2016 *type
= DBTYPE_PFO_INSSPR
;
2017 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
2019 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
2020 *type
= DBTYPE_PO_ZAK
;
2021 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
2022 *type
= DBTYPE_PO_REQ
;
2023 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
2025 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
2026 *type
= DBTYPE_OVM_NOTAR
;
2027 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
2028 *type
= DBTYPE_OVM_EXEKUT
;
2029 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
2030 *type
= DBTYPE_OVM_REQ
;
2037 /* Convert ISDS dbType enum @type to UTF-8 string.
2038 * @Return pointer to static string, or NULL if unknown enum value */
2039 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2041 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2042 * of view of generic public SOAP interface. */
2043 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2044 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2045 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2046 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2047 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2048 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2049 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2050 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2051 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2052 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2053 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2054 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2055 default: return NULL
; break;
2060 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2061 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2062 if (!string
|| !type
) return IE_INVAL
;
2064 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2065 *type
= USERTYPE_PRIMARY
;
2066 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2067 *type
= USERTYPE_ENTRUSTED
;
2068 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2069 *type
= USERTYPE_ADMINISTRATOR
;
2070 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2071 *type
= USERTYPE_OFFICIAL
;
2072 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2073 *type
= USERTYPE_OFFICIAL_CERT
;
2074 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2075 *type
= USERTYPE_LIQUIDATOR
;
2082 /* Convert ISDS userType enum @type to UTF-8 string.
2083 * @Return pointer to static string, or NULL if unknown enum value */
2084 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2086 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2087 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2088 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2089 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2090 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2091 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2092 default: return NULL
; break;
2097 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2098 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2099 isds_sender_type
*type
) {
2100 if (!string
|| !type
) return IE_INVAL
;
2102 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2103 *type
= SENDERTYPE_PRIMARY
;
2104 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2105 *type
= SENDERTYPE_ENTRUSTED
;
2106 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2107 *type
= SENDERTYPE_ADMINISTRATOR
;
2108 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2109 *type
= SENDERTYPE_OFFICIAL
;
2110 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2111 *type
= SENDERTYPE_VIRTUAL
;
2112 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2113 *type
= SENDERTYPE_OFFICIAL_CERT
;
2114 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2115 *type
= SENDERTYPE_LIQUIDATOR
;
2122 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2123 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2124 isds_payment_type
*type
) {
2125 if (!string
|| !type
) return IE_INVAL
;
2127 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2128 *type
= PAYMENT_SENDER
;
2129 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2130 *type
= PAYMENT_RESPONSE
;
2131 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2132 *type
= PAYMENT_SPONSOR
;
2133 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2134 *type
= PAYMENT_SPONSOR_LIMITED
;
2135 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2136 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2137 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2138 *type
= PAYMENT_STAMP
;
2145 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2146 * ciEventType is integer but we convert it from string representation
2148 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2149 isds_credit_event_type
*type
) {
2150 if (!string
|| !type
) return IE_INVAL
;
2152 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2153 *type
= ISDS_CREDIT_CHARGED
;
2154 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2155 *type
= ISDS_CREDIT_DISCHARGED
;
2156 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2157 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2158 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2159 *type
= ISDS_CREDIT_STORAGE_SET
;
2160 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2161 *type
= ISDS_CREDIT_EXPIRED
;
2168 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2169 * @Return pointer to static string, or NULL if unknown enum value */
2170 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2172 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2173 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2174 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2175 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2176 default: return NULL
; break;
2181 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2182 * ISDSSearch2/searchType value.
2183 * @Return pointer to static string, or NULL if unknown enum value */
2184 static const xmlChar
*isds_fulltext_target2string(
2185 const isds_fulltext_target type
) {
2187 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2188 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2189 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2190 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2191 default: return NULL
; break;
2194 #endif /* HAVE_LIBCURL */
2197 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2198 * @Return IE_ENUM if @string is not valid enum member */
2199 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2200 isds_FileMetaType
*type
) {
2201 if (!string
|| !type
) return IE_INVAL
;
2203 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2204 *type
= FILEMETATYPE_MAIN
;
2205 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2206 *type
= FILEMETATYPE_ENCLOSURE
;
2207 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2208 *type
= FILEMETATYPE_SIGNATURE
;
2209 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2210 *type
= FILEMETATYPE_META
;
2217 /* Convert UTF-8 @string to ISDS hash @algorithm.
2218 * @Return IE_ENUM if @string is not valid enum member */
2219 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2220 isds_hash_algorithm
*algorithm
) {
2221 if (!string
|| !algorithm
) return IE_INVAL
;
2223 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2224 *algorithm
= HASH_ALGORITHM_MD5
;
2225 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2226 *algorithm
= HASH_ALGORITHM_SHA_1
;
2227 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2228 *algorithm
= HASH_ALGORITHM_SHA_224
;
2229 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2230 *algorithm
= HASH_ALGORITHM_SHA_256
;
2231 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2232 *algorithm
= HASH_ALGORITHM_SHA_384
;
2233 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2234 *algorithm
= HASH_ALGORITHM_SHA_512
;
2242 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2243 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2244 if (!time
|| !string
) return IE_INVAL
;
2246 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2247 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2254 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2255 * respects the @time microseconds too. */
2256 static isds_error
timeval2timestring(const struct timeval
*time
,
2259 time_t seconds_as_time_t
;
2261 if (!time
|| !string
) return IE_INVAL
;
2263 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2264 * 32-bit long in Microsoft API. Convert value to the type expected by
2266 seconds_as_time_t
= time
->tv_sec
;
2267 if (!gmtime_r(&seconds_as_time_t
, &broken
)) return IE_DATE
;
2268 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2270 /* TODO: small negative year should be formatted as "-0012". This is not
2271 * true for glibc "%04d". We should implement it.
2272 * time->tv_usec type is su_seconds_t which is required to be signed
2273 * integer to accomodate values from range [-1, 1000000].
2274 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2275 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2276 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2277 * of the range check above. */
2278 if (-1 == isds_asprintf((char **) string
,
2279 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32
,
2280 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2281 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2282 (int32_t)time
->tv_usec
))
2287 #endif /* HAVE_LIBCURL */
2290 /* Convert UTF-8 ISO 8601 date-time @string to static struct timeval.
2291 * It respects microseconds too. Microseconds are rounded half up.
2292 * In case of error, @time will be undefined. */
2293 static isds_error
timestring2static_timeval(const xmlChar
*string
,
2294 struct timeval
*time
) {
2296 char *offset
, *delim
, *endptr
;
2297 const int subsecond_resolution
= 6;
2298 char subseconds
[subsecond_resolution
+ 1];
2300 int offset_hours
, offset_minutes
;
2302 long int long_number
;
2307 if (!time
) return IE_INVAL
;
2312 memset(&broken
, 0, sizeof(broken
));
2313 memset(time
, 0, sizeof(*time
));
2316 /* xsd:date is ISO 8601 string, thus ASCII */
2317 /*TODO: negative year */
2321 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2322 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2323 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2328 broken
.tm_year
-= 1900;
2330 broken
.tm_isdst
= -1;
2331 offset
= (char*)string
+ i
;
2333 /* Parse date and time without subseconds and offset */
2334 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2340 /* Get subseconds */
2341 if (*offset
== '.' ) {
2344 /* Copy first 6 digits, pad it with zeros.
2345 * Current server implementation uses only millisecond resolution. */
2346 /* TODO: isdigit() is locale sensitive */
2348 i
< subsecond_resolution
&& isdigit(*offset
);
2350 subseconds
[i
] = *offset
;
2352 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2353 /* Check 7th digit for rounding */
2354 if (*offset
>= '5') round_up
= 1;
2357 for (; i
< subsecond_resolution
; i
++) {
2358 subseconds
[i
] = '0';
2360 subseconds
[subsecond_resolution
] = '\0';
2362 /* Convert it into integer */
2363 long_number
= strtol(subseconds
, &endptr
, 10);
2364 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2365 long_number
== LONG_MAX
) {
2368 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2369 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2370 * microseconds" and "the type shall be a signed integer capable of
2371 * storing values at least in the range [-1, 1000000]. */
2372 if (long_number
< -1 || long_number
>= 1000000) {
2375 time
->tv_usec
= long_number
;
2377 /* Round the subseconds */
2379 if (999999 == time
->tv_usec
) {
2387 /* move to the zone offset delimiter or signal NULL*/
2388 delim
= strchr(offset
, '-');
2390 delim
= strchr(offset
, '+');
2392 delim
= strchr(offset
, 'Z');
2396 /* Get zone offset */
2397 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2398 * "" equals to "Z" and it means UTC zone. */
2399 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2400 * colon separator */
2401 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2402 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2405 if (*offset
== '+') {
2406 broken
.tm_hour
-= offset_hours
;
2407 broken
.tm_min
-= offset_minutes
;
2409 broken
.tm_hour
+= offset_hours
;
2410 broken
.tm_min
+= offset_minutes
;
2414 /* Convert to time_t */
2415 time
->tv_sec
= _isds_timegm(&broken
);
2416 if (time
->tv_sec
== (time_t) -1) {
2424 /* Convert UTF-8 ISO 8601 date-time @string to reallocated struct timeval.
2425 * It respects microseconds too. Microseconds are rounded half up.
2426 * In case of error, @time will be freed. */
2427 static isds_error
timestring2timeval(const xmlChar
*string
,
2428 struct timeval
**time
) {
2431 if (!time
) return IE_INVAL
;
2438 *time
= calloc(1, sizeof(**time
));
2439 if (!*time
) return IE_NOMEM
;
2441 memset(*time
, 0, sizeof(**time
));
2444 error
= timestring2static_timeval(string
, *time
);
2453 /* Convert unsigned int into isds_message_status.
2454 * @context is session context
2455 * @number is pointer to number value. NULL will be treated as invalid value.
2456 * @status is automatically reallocated status
2457 * @return IE_SUCCESS, or error code and free status */
2458 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2459 const unsigned long int *number
, isds_message_status
**status
) {
2460 if (!context
) return IE_INVALID_CONTEXT
;
2461 if (!status
) return IE_INVAL
;
2463 free(*status
); *status
= NULL
;
2464 if (!number
) return IE_INVAL
;
2466 if (*number
< 1 || *number
> 10) {
2467 isds_printf_message(context
, _("Invalid message status value: %lu"),
2472 *status
= malloc(sizeof(**status
));
2473 if (!*status
) return IE_NOMEM
;
2475 **status
= 1 << *number
;
2480 /* Convert event description string into isds_event members type and
2482 * @string is raw event description starting with event prefix
2483 * @event is structure where to store type and stripped description to
2484 * @return standard error code, unknown prefix is not classified as an error.
2486 static isds_error
eventstring2event(const xmlChar
*string
,
2487 struct isds_event
* event
) {
2488 const xmlChar
*known_prefixes
[] = {
2499 const isds_event_type types
[] = {
2500 EVENT_ENTERED_SYSTEM
,
2501 EVENT_ACCEPTED_BY_RECIPIENT
,
2502 EVENT_ACCEPTED_BY_FICTION
,
2503 EVENT_UNDELIVERABLE
,
2504 EVENT_COMMERCIAL_ACCEPTED
,
2506 EVENT_PRIMARY_LOGIN
,
2507 EVENT_ENTRUSTED_LOGIN
,
2513 if (!string
|| !event
) return IE_INVAL
;
2516 event
->type
= malloc(sizeof(*event
->type
));
2517 if (!(event
->type
)) return IE_NOMEM
;
2519 zfree(event
->description
);
2521 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2523 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2525 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2526 /* Prefix is known */
2527 *event
->type
= types
[index
];
2529 /* Strip prefix from description and spaces */
2530 /* TODO: Recognize all white spaces from UCS blank class and
2531 * operate on UTF-8 chars. */
2532 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2533 event
->description
= strdup((char *) (string
+ length
));
2534 if (!(event
->description
)) return IE_NOMEM
;
2540 /* Unknown event prefix.
2541 * XSD allows any string */
2542 char *string_locale
= _isds_utf82locale((char *) string
);
2543 isds_log(ILF_ISDS
, ILL_WARNING
,
2544 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2545 free(string_locale
);
2547 *event
->type
= EVENT_UKNOWN
;
2548 event
->description
= strdup((char *) string
);
2549 if (!(event
->description
)) return IE_NOMEM
;
2555 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2556 * and leave label */
2557 #define EXTRACT_STRING(element, string) { \
2558 xmlXPathFreeObject(result); \
2559 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2560 if (NULL == (result)) { \
2564 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2565 if (result->nodesetval->nodeNr > 1) { \
2566 isds_printf_message(context, _("Multiple %s element"), element); \
2570 (string) = (char *) \
2571 xmlXPathCastNodeSetToString(result->nodesetval); \
2572 if (NULL == (string)) { \
2579 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2581 char *string = NULL; \
2582 EXTRACT_STRING(element, string); \
2585 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2586 if (!(booleanPtr)) { \
2592 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2593 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2594 *(booleanPtr) = 1; \
2595 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2596 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2597 *(booleanPtr) = 0; \
2599 char *string_locale = _isds_utf82locale((char*)string); \
2600 isds_printf_message(context, \
2601 _("%s value is not valid boolean: %s"), \
2602 element, string_locale); \
2603 free(string_locale); \
2613 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2615 char *string = NULL; \
2616 EXTRACT_STRING(element, string); \
2618 if (NULL == string) { \
2619 isds_printf_message(context, _("%s element is empty"), element); \
2623 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2624 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2626 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2627 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2630 char *string_locale = _isds_utf82locale((char*)string); \
2631 isds_printf_message(context, \
2632 _("%s value is not valid boolean: %s"), \
2633 element, string_locale); \
2634 free(string_locale); \
2643 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2645 char *string = NULL; \
2646 EXTRACT_STRING(element, string); \
2651 number = strtol((char*)string, &endptr, 10); \
2653 if (*endptr != '\0') { \
2654 char *string_locale = _isds_utf82locale((char *)string); \
2655 isds_printf_message(context, \
2656 _("%s is not valid integer: %s"), \
2657 element, string_locale); \
2658 free(string_locale); \
2664 if (number == LONG_MIN || number == LONG_MAX) { \
2665 char *string_locale = _isds_utf82locale((char *)string); \
2666 isds_printf_message(context, \
2667 _("%s value out of range of long int: %s"), \
2668 element, string_locale); \
2669 free(string_locale); \
2675 free(string); string = NULL; \
2677 if (!(preallocated)) { \
2678 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2679 if (!(longintPtr)) { \
2684 *(longintPtr) = number; \
2688 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2690 char *string = NULL; \
2691 EXTRACT_STRING(element, string); \
2696 number = strtol((char*)string, &endptr, 10); \
2698 if (*endptr != '\0') { \
2699 char *string_locale = _isds_utf82locale((char *)string); \
2700 isds_printf_message(context, \
2701 _("%s is not valid integer: %s"), \
2702 element, string_locale); \
2703 free(string_locale); \
2709 if (number == LONG_MIN || number == LONG_MAX) { \
2710 char *string_locale = _isds_utf82locale((char *)string); \
2711 isds_printf_message(context, \
2712 _("%s value out of range of long int: %s"), \
2713 element, string_locale); \
2714 free(string_locale); \
2720 free(string); string = NULL; \
2722 isds_printf_message(context, \
2723 _("%s value is negative: %ld"), element, number); \
2728 if (!(preallocated)) { \
2729 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2730 if (!(ulongintPtr)) { \
2735 *(ulongintPtr) = number; \
2739 #define EXTRACT_DATE(element, tmPtr) { \
2740 char *string = NULL; \
2741 EXTRACT_STRING(element, string); \
2742 if (NULL != string) { \
2743 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2744 if (NULL == (tmPtr)) { \
2749 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2751 if (err == IE_NOTSUP) { \
2753 char *string_locale = _isds_utf82locale(string); \
2754 char *element_locale = _isds_utf82locale(element); \
2755 isds_printf_message(context, _("Invalid %s value: %s"), \
2756 element_locale, string_locale); \
2757 free(string_locale); \
2758 free(element_locale); \
2767 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2768 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2770 if ((required) && (!string)) { \
2771 char *attribute_locale = _isds_utf82locale(attribute); \
2772 char *element_locale = \
2773 _isds_utf82locale((char *)xpath_ctx->node->name); \
2774 isds_printf_message(context, \
2775 _("Could not extract required %s attribute value from " \
2776 "%s element"), attribute_locale, element_locale); \
2777 free(element_locale); \
2778 free(attribute_locale); \
2785 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2787 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2788 (xmlChar *) (string)); \
2790 isds_printf_message(context, \
2791 _("Could not add %s child to %s element"), \
2792 element, (parent)->name); \
2798 #define INSERT_STRING(parent, element, string) \
2799 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2801 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2803 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2804 else { INSERT_STRING(parent, element, "false"); } \
2807 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2810 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2812 INSERT_STRING(parent, element, NULL); \
2816 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2817 if ((longintPtr)) { \
2818 /* FIXME: locale sensitive */ \
2819 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2823 INSERT_STRING(parent, element, buffer) \
2824 free(buffer); (buffer) = NULL; \
2825 } else { INSERT_STRING(parent, element, NULL) } \
2828 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2829 if ((ulongintPtr)) { \
2830 /* FIXME: locale sensitive */ \
2831 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2835 INSERT_STRING(parent, element, buffer) \
2836 free(buffer); (buffer) = NULL; \
2837 } else { INSERT_STRING(parent, element, NULL) } \
2840 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2842 /* FIXME: locale sensitive */ \
2843 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2847 INSERT_STRING(parent, element, buffer) \
2848 free(buffer); (buffer) = NULL; \
2851 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2853 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2855 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2856 (xmlChar *) (string)); \
2857 if (!attribute_node) { \
2858 isds_printf_message(context, _("Could not add %s " \
2859 "attribute to %s element"), \
2860 (attribute), (parent)->name); \
2866 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2868 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2869 if (length > (maximum)) { \
2870 isds_printf_message(context, \
2871 ngettext("%s has more than %d characters", \
2872 "%s has more than %d characters", (maximum)), \
2873 (name), (maximum)); \
2877 if (length < (minimum)) { \
2878 isds_printf_message(context, \
2879 ngettext("%s has less than %d characters", \
2880 "%s has less than %d characters", (minimum)), \
2881 (name), (minimum)); \
2888 #define INSERT_ELEMENT(child, parent, element) \
2890 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2892 isds_printf_message(context, \
2893 _("Could not add %s child to %s element"), \
2894 (element), (parent)->name); \
2901 /* Find child element by name in given XPath context and switch context onto
2902 * it. The child must be uniq and must exist. Otherwise fails.
2903 * @context is ISDS context
2904 * @child is child element name
2905 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2906 * into it child. In error case, the @xpath_ctx keeps original value. */
2907 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2908 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2909 isds_error err
= IE_SUCCESS
;
2910 xmlXPathObjectPtr result
= NULL
;
2912 if (!context
) return IE_INVALID_CONTEXT
;
2913 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2916 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2923 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2924 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2925 char *child_locale
= _isds_utf82locale((char*) child
);
2926 isds_printf_message(context
,
2927 _("%s element does not contain %s child"),
2928 parent_locale
, child_locale
);
2930 free(parent_locale
);
2936 if (result
->nodesetval
->nodeNr
> 1) {
2937 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2938 char *child_locale
= _isds_utf82locale((char*) child
);
2939 isds_printf_message(context
,
2940 _("%s element contains multiple %s children"),
2941 parent_locale
, child_locale
);
2943 free(parent_locale
);
2948 /* Switch context */
2949 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2952 xmlXPathFreeObject(result
);
2959 /* Find and convert XSD:gPersonName group in current node into structure
2960 * @context is ISDS context
2961 * @personName is automatically reallocated person name structure. If no member
2962 * value is found, will be freed.
2963 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2965 * In case of error @personName will be freed. */
2966 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2967 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2968 isds_error err
= IE_SUCCESS
;
2969 xmlXPathObjectPtr result
= NULL
;
2971 if (!context
) return IE_INVALID_CONTEXT
;
2972 if (!personName
) return IE_INVAL
;
2973 isds_PersonName_free(personName
);
2974 if (!xpath_ctx
) return IE_INVAL
;
2977 *personName
= calloc(1, sizeof(**personName
));
2983 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2984 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2985 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2986 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2988 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2989 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2990 isds_PersonName_free(personName
);
2993 if (err
) isds_PersonName_free(personName
);
2994 xmlXPathFreeObject(result
);
2999 /* Find and convert XSD:gAddress group extended with relevant
3000 * tdbPersonalOwnerinfo members in current node into structure
3001 * @context is ISDS context
3002 * @address is automatically reallocated address structure. If no member
3003 * value is found, will be freed.
3004 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
3006 * In case of error @address will be freed. */
3007 static isds_error
extract_gAddress(struct isds_ctx
*context
,
3008 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
3009 isds_error err
= IE_SUCCESS
;
3010 xmlXPathObjectPtr result
= NULL
;
3012 if (!context
) return IE_INVALID_CONTEXT
;
3013 if (!address
) return IE_INVAL
;
3014 isds_Address_free(address
);
3015 if (!xpath_ctx
) return IE_INVAL
;
3018 *address
= calloc(1, sizeof(**address
));
3024 EXTRACT_LONGINT("isds:adCode", (*address
)->adCode
, 0);
3025 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
3026 EXTRACT_STRING("isds:adDistrict", (*address
)->adDistrict
);
3027 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
3028 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
3029 EXTRACT_STRING("isds:adNumberInMunicipality",
3030 (*address
)->adNumberInMunicipality
);
3031 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
3032 EXTRACT_STRING("isds:adState", (*address
)->adState
);
3034 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
3035 !(*address
)->adNumberInStreet
&&
3036 !(*address
)->adNumberInMunicipality
&&
3037 !(*address
)->adZipCode
&& !(*address
)->adState
)
3038 isds_Address_free(address
);
3041 if (err
) isds_Address_free(address
);
3042 xmlXPathFreeObject(result
);
3047 /* Find and convert isds:biDate element in current node into structure
3048 * @context is ISDS context
3049 * @biDate is automatically reallocated birth date structure. If no member
3050 * value is found, will be freed.
3051 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3053 * In case of error @biDate will be freed. */
3054 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3055 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3056 isds_error err
= IE_SUCCESS
;
3057 xmlXPathObjectPtr result
= NULL
;
3058 char *string
= NULL
;
3060 if (!context
) return IE_INVALID_CONTEXT
;
3061 if (!biDate
) return IE_INVAL
;
3063 if (!xpath_ctx
) return IE_INVAL
;
3065 EXTRACT_STRING("isds:biDate", string
);
3067 *biDate
= calloc(1, sizeof(**biDate
));
3072 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3074 if (err
== IE_NOTSUP
) {
3076 char *string_locale
= _isds_utf82locale(string
);
3077 isds_printf_message(context
,
3078 _("Invalid isds:biDate value: %s"), string_locale
);
3079 free(string_locale
);
3086 if (err
) zfree(*biDate
);
3088 xmlXPathFreeObject(result
);
3093 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3094 * @context is ISDS context
3095 * @db_owner_info is automatically reallocated box owner info structure
3096 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3097 * XSD:tdbPersonalOwenerInfo element
3098 * In case of error @db_owner_info will be freed. */
3099 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3100 struct isds_DbOwnerInfo
**db_owner_info
,
3101 xmlXPathContextPtr xpath_ctx
) {
3102 isds_error err
= IE_SUCCESS
;
3103 xmlXPathObjectPtr result
= NULL
;
3104 char *string
= NULL
;
3106 if (!context
) return IE_INVALID_CONTEXT
;
3107 if (!db_owner_info
) return IE_INVAL
;
3108 isds_DbOwnerInfo_free(db_owner_info
);
3109 if (!xpath_ctx
) return IE_INVAL
;
3112 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3113 if (!*db_owner_info
) {
3118 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3120 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info
)->aifoIsds
);
3122 EXTRACT_STRING("isds:dbType", string
);
3124 (*db_owner_info
)->dbType
=
3125 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3126 if (!(*db_owner_info
)->dbType
) {
3130 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3132 zfree((*db_owner_info
)->dbType
);
3133 if (err
== IE_ENUM
) {
3135 char *string_locale
= _isds_utf82locale(string
);
3136 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3138 free(string_locale
);
3145 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3147 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3149 if (err
) goto leave
;
3151 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3153 (*db_owner_info
)->birthInfo
=
3154 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3155 if (!(*db_owner_info
)->birthInfo
) {
3159 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3161 if (err
) goto leave
;
3162 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3163 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3164 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3165 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3166 !(*db_owner_info
)->birthInfo
->biCity
&&
3167 !(*db_owner_info
)->birthInfo
->biCounty
&&
3168 !(*db_owner_info
)->birthInfo
->biState
)
3169 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3171 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3172 if (err
) goto leave
;
3174 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3175 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3176 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3177 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3178 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3180 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3182 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3183 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3184 (*db_owner_info
)->dbOpenAddressing
);
3187 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3189 xmlXPathFreeObject(result
);
3194 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3195 * @context is session context
3196 * @owner is libisds structure with box description.
3197 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3198 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3199 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3200 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3202 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3203 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3204 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3205 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3207 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3208 const struct isds_DbOwnerInfo
*owner
, _Bool pfo_subtype
,
3209 xmlNodePtr db_owner_info
) {
3211 isds_error err
= IE_SUCCESS
;
3213 xmlChar
*string
= NULL
;
3214 const xmlChar
*type_string
= NULL
;
3216 if (!context
) return IE_INVALID_CONTEXT
;
3217 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3220 /* XXX: All the elements except email and telNumber are mandatory. */
3221 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3222 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3225 INSERT_BOOLEAN(db_owner_info
, "aifoIsds", owner
->aifoIsds
);
3230 if (owner
->dbType
) {
3231 type_string
= isds_DbType2string(*(owner
->dbType
));
3233 isds_printf_message(context
, _("Invalid dbType value: %d"),
3239 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3241 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3244 INSERT_STRING(db_owner_info
, "pnFirstName",
3245 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnFirstName
);
3246 INSERT_STRING(db_owner_info
, "pnMiddleName",
3247 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnMiddleName
);
3248 INSERT_STRING(db_owner_info
, "pnLastName",
3249 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnLastName
);
3251 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3252 (NULL
== owner
->personName
) ? NULL
:
3253 owner
->personName
->pnLastNameAtBirth
);
3255 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3258 if (NULL
!= owner
->birthInfo
&& NULL
!= owner
->birthInfo
->biDate
) {
3259 err
= tm2datestring(owner
->birthInfo
->biDate
, &string
);
3260 if (err
) goto leave
;
3262 INSERT_STRING(db_owner_info
, "biDate", string
);
3265 INSERT_STRING(db_owner_info
, "biCity",
3266 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCity
);
3267 INSERT_STRING(db_owner_info
, "biCounty",
3268 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCounty
);
3269 INSERT_STRING(db_owner_info
, "biState",
3270 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biState
);
3273 INSERT_LONGINT(db_owner_info
, "adCode",
3274 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCode
,
3277 INSERT_STRING(db_owner_info
, "adCity",
3278 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCity
);
3280 INSERT_STRING(db_owner_info
, "adDistrict",
3281 (NULL
== owner
->address
) ? NULL
: owner
->address
->adDistrict
);
3283 INSERT_STRING(db_owner_info
, "adStreet",
3284 (NULL
== owner
->address
) ? NULL
: owner
->address
->adStreet
);
3285 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3286 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInStreet
);
3287 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3288 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInMunicipality
);
3289 INSERT_STRING(db_owner_info
, "adZipCode",
3290 (NULL
== owner
->address
) ? NULL
: owner
->address
->adZipCode
);
3291 INSERT_STRING(db_owner_info
, "adState",
3292 (NULL
== owner
->address
) ? NULL
: owner
->address
->adState
);
3294 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3297 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3298 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3300 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3301 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3303 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3304 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3306 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3308 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3309 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3310 owner
->dbOpenAddressing
);
3319 /* Convert XSD:tDbUserInfo XML tree into structure
3320 * @context is ISDS context
3321 * @db_user_info is automatically reallocated user info structure
3322 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3323 * In case of error @db_user_info will be freed. */
3324 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3325 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3326 isds_error err
= IE_SUCCESS
;
3327 xmlXPathObjectPtr result
= NULL
;
3328 char *string
= NULL
;
3330 if (!context
) return IE_INVALID_CONTEXT
;
3331 if (!db_user_info
) return IE_INVAL
;
3332 isds_DbUserInfo_free(db_user_info
);
3333 if (!xpath_ctx
) return IE_INVAL
;
3336 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3337 if (!*db_user_info
) {
3342 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info
)->aifo_ticket
, 0);
3344 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3346 EXTRACT_STRING("isds:userType", string
);
3348 (*db_user_info
)->userType
=
3349 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3350 if (!(*db_user_info
)->userType
) {
3354 err
= string2isds_UserType((xmlChar
*)string
,
3355 (*db_user_info
)->userType
);
3357 zfree((*db_user_info
)->userType
);
3358 if (err
== IE_ENUM
) {
3360 char *string_locale
= _isds_utf82locale(string
);
3361 isds_printf_message(context
,
3362 _("Unknown isds:userType value: %s"), string_locale
);
3363 free(string_locale
);
3370 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3372 (*db_user_info
)->personName
=
3373 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3374 if (!(*db_user_info
)->personName
) {
3379 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3381 if (err
) goto leave
;
3383 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3384 if (err
) goto leave
;
3386 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3387 if (err
) goto leave
;
3389 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3390 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3392 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3393 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3394 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3396 /* ???: Default value is "CZ" according specification. Should we provide
3398 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3401 if (err
) isds_DbUserInfo_free(db_user_info
);
3403 xmlXPathFreeObject(result
);
3408 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3409 * @context is session context
3410 * @user is libisds structure with user description
3411 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3412 * @db_user_info is XML element of XSD:tDbUserInfo */
3413 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3414 const struct isds_DbUserInfo
*user
, _Bool honor_aifo_ticket
,
3415 xmlNodePtr db_user_info
) {
3417 isds_error err
= IE_SUCCESS
;
3419 xmlAttrPtr attribute_node
;
3420 xmlChar
*string
= NULL
;
3422 if (!context
) return IE_INVALID_CONTEXT
;
3423 if (!user
|| !db_user_info
) return IE_INVAL
;
3425 /* Build XSD:tDbUserInfo */
3427 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3428 * allow it everywhere. */
3429 if (honor_aifo_ticket
&& user
->aifo_ticket
) {
3430 INSERT_STRING_ATTRIBUTE(db_user_info
, "AIFOTicket", user
->aifo_ticket
);
3433 if (user
->personName
) {
3434 INSERT_STRING(db_user_info
, "pnFirstName",
3435 user
->personName
->pnFirstName
);
3436 INSERT_STRING(db_user_info
, "pnMiddleName",
3437 user
->personName
->pnMiddleName
);
3438 INSERT_STRING(db_user_info
, "pnLastName",
3439 user
->personName
->pnLastName
);
3440 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3441 user
->personName
->pnLastNameAtBirth
);
3443 if (user
->address
) {
3444 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3445 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3446 INSERT_STRING(db_user_info
, "adNumberInStreet",
3447 user
->address
->adNumberInStreet
);
3448 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3449 user
->address
->adNumberInMunicipality
);
3450 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3451 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3454 if (!tm2datestring(user
->biDate
, &string
))
3455 INSERT_STRING(db_user_info
, "biDate", string
);
3458 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3459 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3462 if (user
->userType
) {
3463 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3465 isds_printf_message(context
, _("Invalid userType value: %d"),
3470 INSERT_STRING(db_user_info
, "userType", type_string
);
3473 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3474 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3475 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3476 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3477 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3478 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3479 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3480 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3481 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3489 /* Convert XSD:tPDZRec XML tree into structure
3490 * @context is ISDS context
3491 * @permission is automatically reallocated commercial permission structure
3492 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3493 * In case of error @permission will be freed. */
3494 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3495 struct isds_commercial_permission
**permission
,
3496 xmlXPathContextPtr xpath_ctx
) {
3497 isds_error err
= IE_SUCCESS
;
3498 xmlXPathObjectPtr result
= NULL
;
3499 char *string
= NULL
;
3501 if (!context
) return IE_INVALID_CONTEXT
;
3502 if (!permission
) return IE_INVAL
;
3503 isds_commercial_permission_free(permission
);
3504 if (!xpath_ctx
) return IE_INVAL
;
3507 *permission
= calloc(1, sizeof(**permission
));
3513 EXTRACT_STRING("isds:PDZType", string
);
3515 err
= string2isds_payment_type((xmlChar
*)string
,
3516 &(*permission
)->type
);
3518 if (err
== IE_ENUM
) {
3520 char *string_locale
= _isds_utf82locale(string
);
3521 isds_printf_message(context
,
3522 _("Unknown isds:PDZType value: %s"), string_locale
);
3523 free(string_locale
);
3530 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3531 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3533 EXTRACT_STRING("isds:PDZExpire", string
);
3535 err
= timestring2timeval((xmlChar
*) string
,
3536 &((*permission
)->expiration
));
3538 char *string_locale
= _isds_utf82locale(string
);
3539 if (err
== IE_DATE
) err
= IE_ISDS
;
3540 isds_printf_message(context
,
3541 _("Could not convert PDZExpire as ISO time: %s"),
3543 free(string_locale
);
3549 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3550 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3553 if (err
) isds_commercial_permission_free(permission
);
3555 xmlXPathFreeObject(result
);
3560 /* Convert XSD:tCiRecord XML tree into structure
3561 * @context is ISDS context
3562 * @event is automatically reallocated commercial credit event structure
3563 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3564 * In case of error @event will be freed. */
3565 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3566 struct isds_credit_event
**event
,
3567 xmlXPathContextPtr xpath_ctx
) {
3568 isds_error err
= IE_SUCCESS
;
3569 xmlXPathObjectPtr result
= NULL
;
3570 char *string
= NULL
;
3571 long int *number_ptr
;
3573 if (!context
) return IE_INVALID_CONTEXT
;
3574 if (!event
) return IE_INVAL
;
3575 isds_credit_event_free(event
);
3576 if (!xpath_ctx
) return IE_INVAL
;
3579 *event
= calloc(1, sizeof(**event
));
3585 EXTRACT_STRING("isds:ciEventTime", string
);
3587 err
= timestring2timeval((xmlChar
*) string
,
3590 char *string_locale
= _isds_utf82locale(string
);
3591 if (err
== IE_DATE
) err
= IE_ISDS
;
3592 isds_printf_message(context
,
3593 _("Could not convert ciEventTime as ISO time: %s"),
3595 free(string_locale
);
3601 EXTRACT_STRING("isds:ciEventType", string
);
3603 err
= string2isds_credit_event_type((xmlChar
*)string
,
3606 if (err
== IE_ENUM
) {
3608 char *string_locale
= _isds_utf82locale(string
);
3609 isds_printf_message(context
,
3610 _("Unknown isds:ciEventType value: %s"), string_locale
);
3611 free(string_locale
);
3618 number_ptr
= &((*event
)->credit_change
);
3619 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3620 number_ptr
= &(*event
)->new_credit
;
3621 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3623 switch((*event
)->type
) {
3624 case ISDS_CREDIT_CHARGED
:
3625 EXTRACT_STRING("isds:ciTransID",
3626 (*event
)->details
.charged
.transaction
);
3628 case ISDS_CREDIT_DISCHARGED
:
3629 EXTRACT_STRING("isds:ciTransID",
3630 (*event
)->details
.discharged
.transaction
);
3632 case ISDS_CREDIT_MESSAGE_SENT
:
3633 EXTRACT_STRING("isds:ciRecipientID",
3634 (*event
)->details
.message_sent
.recipient
);
3635 EXTRACT_STRING("isds:ciPDZID",
3636 (*event
)->details
.message_sent
.message_id
);
3638 case ISDS_CREDIT_STORAGE_SET
:
3639 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3640 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3641 EXTRACT_DATE("isds:ciNewFrom",
3642 (*event
)->details
.storage_set
.new_valid_from
);
3643 EXTRACT_DATE("isds:ciNewTo",
3644 (*event
)->details
.storage_set
.new_valid_to
);
3645 EXTRACT_LONGINT("isds:ciOldCapacity",
3646 (*event
)->details
.storage_set
.old_capacity
, 0);
3647 EXTRACT_DATE("isds:ciOldFrom",
3648 (*event
)->details
.storage_set
.old_valid_from
);
3649 EXTRACT_DATE("isds:ciOldTo",
3650 (*event
)->details
.storage_set
.old_valid_to
);
3651 EXTRACT_STRING("isds:ciDoneBy",
3652 (*event
)->details
.storage_set
.initiator
);
3654 case ISDS_CREDIT_EXPIRED
:
3659 if (err
) isds_credit_event_free(event
);
3661 xmlXPathFreeObject(result
);
3666 #endif /* HAVE_LIBCURL */
3669 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3670 * isds_envelope structure. The envelope is automatically allocated but not
3671 * reallocated. The date are just appended into envelope structure.
3672 * @context is ISDS context
3673 * @envelope is automatically allocated message envelope structure
3674 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3675 * In case of error @envelope will be freed. */
3676 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3677 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3678 isds_error err
= IE_SUCCESS
;
3679 xmlXPathObjectPtr result
= NULL
;
3681 if (!context
) return IE_INVALID_CONTEXT
;
3682 if (!envelope
) return IE_INVAL
;
3683 if (!xpath_ctx
) return IE_INVAL
;
3687 /* Allocate envelope */
3688 *envelope
= calloc(1, sizeof(**envelope
));
3694 /* Else free former data */
3695 zfree((*envelope
)->dmSenderOrgUnit
);
3696 zfree((*envelope
)->dmSenderOrgUnitNum
);
3697 zfree((*envelope
)->dbIDRecipient
);
3698 zfree((*envelope
)->dmRecipientOrgUnit
);
3699 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3700 zfree((*envelope
)->dmToHands
);
3701 zfree((*envelope
)->dmAnnotation
);
3702 zfree((*envelope
)->dmRecipientRefNumber
);
3703 zfree((*envelope
)->dmSenderRefNumber
);
3704 zfree((*envelope
)->dmRecipientIdent
);
3705 zfree((*envelope
)->dmSenderIdent
);
3706 zfree((*envelope
)->dmLegalTitleLaw
);
3707 zfree((*envelope
)->dmLegalTitleYear
);
3708 zfree((*envelope
)->dmLegalTitleSect
);
3709 zfree((*envelope
)->dmLegalTitlePar
);
3710 zfree((*envelope
)->dmLegalTitlePoint
);
3711 zfree((*envelope
)->dmPersonalDelivery
);
3712 zfree((*envelope
)->dmAllowSubstDelivery
);
3715 /* Extract envelope elements added by sender or ISDS
3716 * (XSD: gMessageEnvelopeSub type) */
3717 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3718 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3719 (*envelope
)->dmSenderOrgUnitNum
, 0);
3720 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3721 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3722 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3723 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3724 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3725 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3726 EXTRACT_STRING("isds:dmRecipientRefNumber",
3727 (*envelope
)->dmRecipientRefNumber
);
3728 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3729 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3730 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3732 /* Extract envelope elements regarding law reference */
3733 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3734 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3735 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3736 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3737 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3739 /* Extract envelope other elements */
3740 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3741 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3742 (*envelope
)->dmAllowSubstDelivery
);
3745 if (err
) isds_envelope_free(envelope
);
3746 xmlXPathFreeObject(result
);
3752 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3753 * isds_envelope structure. The envelope is automatically allocated but not
3754 * reallocated. The date are just appended into envelope structure.
3755 * @context is ISDS context
3756 * @envelope is automatically allocated message envelope structure
3757 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3758 * In case of error @envelope will be freed. */
3759 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3760 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3761 isds_error err
= IE_SUCCESS
;
3762 xmlXPathObjectPtr result
= NULL
;
3764 if (!context
) return IE_INVALID_CONTEXT
;
3765 if (!envelope
) return IE_INVAL
;
3766 if (!xpath_ctx
) return IE_INVAL
;
3770 /* Allocate envelope */
3771 *envelope
= calloc(1, sizeof(**envelope
));
3777 /* Else free former data */
3778 zfree((*envelope
)->dmID
);
3779 zfree((*envelope
)->dbIDSender
);
3780 zfree((*envelope
)->dmSender
);
3781 zfree((*envelope
)->dmSenderAddress
);
3782 zfree((*envelope
)->dmSenderType
);
3783 zfree((*envelope
)->dmRecipient
);
3784 zfree((*envelope
)->dmRecipientAddress
);
3785 zfree((*envelope
)->dmAmbiguousRecipient
);
3788 /* Extract envelope elements added by ISDS
3789 * (XSD: gMessageEnvelope type) */
3790 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3791 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3792 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3793 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3794 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3795 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3796 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3797 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3798 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3799 (*envelope
)->dmAmbiguousRecipient
);
3801 /* Extract envelope elements added by sender and ISDS
3802 * (XSD: gMessageEnvelope type) */
3803 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3804 if (err
) goto leave
;
3807 if (err
) isds_envelope_free(envelope
);
3808 xmlXPathFreeObject(result
);
3813 /* Convert other envelope elements from XML tree into isds_envelope structure:
3814 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3815 * The envelope is automatically allocated but not reallocated.
3816 * The data are just appended into envelope structure.
3817 * @context is ISDS context
3818 * @envelope is automatically allocated message envelope structure
3819 * @xpath_ctx is XPath context with current node as parent desired elements
3820 * In case of error @envelope will be freed. */
3821 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3822 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3823 isds_error err
= IE_SUCCESS
;
3824 xmlXPathObjectPtr result
= NULL
;
3825 char *string
= NULL
;
3826 unsigned long int *unumber
= NULL
;
3828 if (!context
) return IE_INVALID_CONTEXT
;
3829 if (!envelope
) return IE_INVAL
;
3830 if (!xpath_ctx
) return IE_INVAL
;
3835 *envelope
= calloc(1, sizeof(**envelope
));
3842 zfree((*envelope
)->dmMessageStatus
);
3843 zfree((*envelope
)->dmAttachmentSize
);
3844 zfree((*envelope
)->dmDeliveryTime
);
3845 zfree((*envelope
)->dmAcceptanceTime
);
3849 /* dmMessageStatus element is mandatory */
3850 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3852 isds_log_message(context
,
3853 _("Missing mandatory sisds:dmMessageStatus integer"));
3857 err
= uint2isds_message_status(context
, unumber
,
3858 &((*envelope
)->dmMessageStatus
));
3860 if (err
== IE_ENUM
) err
= IE_ISDS
;
3863 free(unumber
); unumber
= NULL
;
3865 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3868 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3870 err
= timestring2timeval((xmlChar
*) string
,
3871 &((*envelope
)->dmDeliveryTime
));
3873 char *string_locale
= _isds_utf82locale(string
);
3874 if (err
== IE_DATE
) err
= IE_ISDS
;
3875 isds_printf_message(context
,
3876 _("Could not convert dmDeliveryTime as ISO time: %s"),
3878 free(string_locale
);
3884 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3886 err
= timestring2timeval((xmlChar
*) string
,
3887 &((*envelope
)->dmAcceptanceTime
));
3889 char *string_locale
= _isds_utf82locale(string
);
3890 if (err
== IE_DATE
) err
= IE_ISDS
;
3891 isds_printf_message(context
,
3892 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3894 free(string_locale
);
3901 if (err
) isds_envelope_free(envelope
);
3904 xmlXPathFreeObject(result
);
3909 /* Convert message type attribute of current element into isds_envelope
3911 * TODO: This function can be incorporated into append_status_size_times() as
3912 * they are called always together.
3913 * The envelope is automatically allocated but not reallocated.
3914 * The data are just appended into envelope structure.
3915 * @context is ISDS context
3916 * @envelope is automatically allocated message envelope structure
3917 * @xpath_ctx is XPath context with current node as parent of attribute
3918 * carrying message type
3919 * In case of error @envelope will be freed. */
3920 static isds_error
append_message_type(struct isds_ctx
*context
,
3921 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3922 isds_error err
= IE_SUCCESS
;
3924 if (!context
) return IE_INVALID_CONTEXT
;
3925 if (!envelope
) return IE_INVAL
;
3926 if (!xpath_ctx
) return IE_INVAL
;
3931 *envelope
= calloc(1, sizeof(**envelope
));
3938 zfree((*envelope
)->dmType
);
3942 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3944 if (!(*envelope
)->dmType
) {
3945 /* Use default value */
3946 (*envelope
)->dmType
= strdup("V");
3947 if (!(*envelope
)->dmType
) {
3951 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3952 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3953 isds_printf_message(context
,
3954 _("Message type in dmType attribute is not 1 character long: "
3963 if (err
) isds_envelope_free(envelope
);
3969 /* Convert dmType isds_envelope member into XML attribute and append it to
3971 * @context is ISDS context
3972 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3973 * @dm_envelope is XML element the resulting attribute will be appended to.
3974 * @return error code, in case of error context' message is filled. */
3975 static isds_error
insert_message_type(struct isds_ctx
*context
,
3976 const char *type
, xmlNodePtr dm_envelope
) {
3977 isds_error err
= IE_SUCCESS
;
3978 xmlAttrPtr attribute_node
;
3980 if (!context
) return IE_INVALID_CONTEXT
;
3981 if (!dm_envelope
) return IE_INVAL
;
3983 /* Insert optional message type */
3985 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3986 char *type_locale
= _isds_utf82locale(type
);
3987 isds_printf_message(context
,
3988 _("Message type in envelope is not 1 character long: %s"),
3994 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
4000 #endif /* HAVE_LIBCURL */
4003 /* Extract message document into reallocated document structure
4004 * @context is ISDS context
4005 * @document is automatically reallocated message documents structure
4006 * @xpath_ctx is XPath context with current node as isds:dmFile
4007 * In case of error @document will be freed. */
4008 static isds_error
extract_document(struct isds_ctx
*context
,
4009 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
4010 isds_error err
= IE_SUCCESS
;
4011 xmlXPathObjectPtr result
= NULL
;
4012 xmlNodePtr file_node
;
4013 char *string
= NULL
;
4015 if (!context
) return IE_INVALID_CONTEXT
;
4016 if (!document
) return IE_INVAL
;
4017 isds_document_free(document
);
4018 if (!xpath_ctx
) return IE_INVAL
;
4019 file_node
= xpath_ctx
->node
;
4021 *document
= calloc(1, sizeof(**document
));
4027 /* Extract document meta data */
4028 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
4029 if (context
->normalize_mime_type
) {
4030 const char *normalized_type
=
4031 isds_normalize_mime_type((*document
)->dmMimeType
);
4032 if (NULL
!= normalized_type
&&
4033 normalized_type
!= (*document
)->dmMimeType
) {
4034 char *new_type
= strdup(normalized_type
);
4035 if (NULL
== new_type
) {
4036 isds_printf_message(context
,
4037 _("Not enough memory to normalize document MIME type"));
4041 free((*document
)->dmMimeType
);
4042 (*document
)->dmMimeType
= new_type
;
4046 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
4047 err
= string2isds_FileMetaType((xmlChar
*)string
,
4048 &((*document
)->dmFileMetaType
));
4050 char *meta_type_locale
= _isds_utf82locale(string
);
4051 isds_printf_message(context
,
4052 _("Document has invalid dmFileMetaType attribute value: %s"),
4054 free(meta_type_locale
);
4060 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
4061 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
4062 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
4063 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
4066 /* Extract document data.
4067 * Base64 encoded blob or XML subtree must be presented. */
4069 /* Check for dmEncodedContent */
4070 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
4077 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4078 /* Here we have Base64 blob */
4079 (*document
)->is_xml
= 0;
4081 if (result
->nodesetval
->nodeNr
> 1) {
4082 isds_printf_message(context
,
4083 _("Document has more dmEncodedContent elements"));
4088 xmlXPathFreeObject(result
); result
= NULL
;
4089 EXTRACT_STRING("isds:dmEncodedContent", string
);
4091 /* Decode non-empty document */
4092 if (string
&& string
[0] != '\0') {
4093 (*document
)->data_length
=
4094 _isds_b64decode(string
, &((*document
)->data
));
4095 if ((*document
)->data_length
== (size_t) -1) {
4096 isds_printf_message(context
,
4097 _("Error while Base64-decoding document content"));
4103 /* No Base64 blob, try XML document */
4104 xmlXPathFreeObject(result
); result
= NULL
;
4105 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4112 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4113 /* Here we have XML document */
4114 (*document
)->is_xml
= 1;
4116 if (result
->nodesetval
->nodeNr
> 1) {
4117 isds_printf_message(context
,
4118 _("Document has more dmXMLContent elements"));
4123 /* XXX: We cannot serialize the content simply because:
4124 * - XML document may point out of its scope (e.g. to message
4126 * - isds:dmXMLContent can contain more elements, no element,
4128 * - it's not the XML way
4129 * Thus we provide the only right solution: XML DOM. Let's
4130 * application to cope with this hot potato :) */
4131 (*document
)->xml_node_list
=
4132 result
->nodesetval
->nodeTab
[0]->children
;
4134 /* No base64 blob, nor XML document */
4135 isds_printf_message(context
,
4136 _("Document has no dmEncodedContent, nor dmXMLContent "
4145 if (err
) isds_document_free(document
);
4147 xmlXPathFreeObject(result
);
4148 xpath_ctx
->node
= file_node
;
4154 /* Extract message documents into reallocated list of documents
4155 * @context is ISDS context
4156 * @documents is automatically reallocated message documents list structure
4157 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4158 * In case of error @documents will be freed. */
4159 static isds_error
extract_documents(struct isds_ctx
*context
,
4160 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4161 isds_error err
= IE_SUCCESS
;
4162 xmlXPathObjectPtr result
= NULL
;
4163 xmlNodePtr files_node
;
4164 struct isds_list
*document
, *prev_document
= NULL
;
4166 if (!context
) return IE_INVALID_CONTEXT
;
4167 if (!documents
) return IE_INVAL
;
4168 isds_list_free(documents
);
4169 if (!xpath_ctx
) return IE_INVAL
;
4170 files_node
= xpath_ctx
->node
;
4172 /* Find documents */
4173 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4180 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4181 isds_printf_message(context
,
4182 _("Message does not contain any document"));
4188 /* Iterate over documents */
4189 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4191 /* Allocate and append list item */
4192 document
= calloc(1, sizeof(*document
));
4197 document
->destructor
= (void (*)(void **))isds_document_free
;
4198 if (i
== 0) *documents
= document
;
4199 else prev_document
->next
= document
;
4200 prev_document
= document
;
4202 /* Extract document */
4203 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4204 err
= extract_document(context
,
4205 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4206 if (err
) goto leave
;
4211 if (err
) isds_list_free(documents
);
4212 xmlXPathFreeObject(result
);
4213 xpath_ctx
->node
= files_node
;
4219 /* Convert isds:dmRecord XML tree into structure
4220 * @context is ISDS context
4221 * @envelope is automatically reallocated message envelope structure
4222 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4223 * In case of error @envelope will be freed. */
4224 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4225 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4226 isds_error err
= IE_SUCCESS
;
4227 xmlXPathObjectPtr result
= NULL
;
4229 if (!context
) return IE_INVALID_CONTEXT
;
4230 if (!envelope
) return IE_INVAL
;
4231 isds_envelope_free(envelope
);
4232 if (!xpath_ctx
) return IE_INVAL
;
4235 *envelope
= calloc(1, sizeof(**envelope
));
4242 /* Extract tRecord data */
4243 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4245 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4246 * dmAcceptanceTime. */
4247 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4248 if (err
) goto leave
;
4250 /* Extract envelope elements added by sender and ISDS
4251 * (XSD: gMessageEnvelope type) */
4252 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4253 if (err
) goto leave
;
4255 /* Get message type */
4256 err
= append_message_type(context
, envelope
, xpath_ctx
);
4257 if (err
) goto leave
;
4261 if (err
) isds_envelope_free(envelope
);
4262 xmlXPathFreeObject(result
);
4267 /* Convert XSD:tStateChangesRecord type XML tree into structure
4268 * @context is ISDS context
4269 * @changed_status is automatically reallocated message state change structure
4270 * @xpath_ctx is XPath context with current node as element of
4271 * XSD:tStateChangesRecord type
4272 * In case of error @changed_status will be freed. */
4273 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4274 struct isds_message_status_change
**changed_status
,
4275 xmlXPathContextPtr xpath_ctx
) {
4276 isds_error err
= IE_SUCCESS
;
4277 xmlXPathObjectPtr result
= NULL
;
4278 unsigned long int *unumber
= NULL
;
4279 char *string
= NULL
;
4281 if (!context
) return IE_INVALID_CONTEXT
;
4282 if (!changed_status
) return IE_INVAL
;
4283 isds_message_status_change_free(changed_status
);
4284 if (!xpath_ctx
) return IE_INVAL
;
4287 *changed_status
= calloc(1, sizeof(**changed_status
));
4288 if (!*changed_status
) {
4294 /* Extract tGetStateChangesInput data */
4295 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4297 /* dmEventTime is mandatory */
4298 EXTRACT_STRING("isds:dmEventTime", string
);
4300 err
= timestring2timeval((xmlChar
*) string
,
4301 &((*changed_status
)->time
));
4303 char *string_locale
= _isds_utf82locale(string
);
4304 if (err
== IE_DATE
) err
= IE_ISDS
;
4305 isds_printf_message(context
,
4306 _("Could not convert dmEventTime as ISO time: %s"),
4308 free(string_locale
);
4314 /* dmMessageStatus element is mandatory */
4315 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4317 isds_log_message(context
,
4318 _("Missing mandatory isds:dmMessageStatus integer"));
4322 err
= uint2isds_message_status(context
, unumber
,
4323 &((*changed_status
)->dmMessageStatus
));
4325 if (err
== IE_ENUM
) err
= IE_ISDS
;
4334 if (err
) isds_message_status_change_free(changed_status
);
4335 xmlXPathFreeObject(result
);
4338 #endif /* HAVE_LIBCURL */
4341 /* Find and convert isds:dmHash XML tree into structure
4342 * @context is ISDS context
4343 * @envelope is automatically reallocated message hash structure
4344 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4345 * In case of error @hash will be freed. */
4346 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4347 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4348 isds_error err
= IE_SUCCESS
;
4349 xmlNodePtr old_ctx_node
;
4350 xmlXPathObjectPtr result
= NULL
;
4351 char *string
= NULL
;
4353 if (!context
) return IE_INVALID_CONTEXT
;
4354 if (!hash
) return IE_INVAL
;
4355 isds_hash_free(hash
);
4356 if (!xpath_ctx
) return IE_INVAL
;
4358 old_ctx_node
= xpath_ctx
->node
;
4360 *hash
= calloc(1, sizeof(**hash
));
4367 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4368 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4377 /* Get hash algorithm */
4378 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4379 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4381 if (err
== IE_ENUM
) {
4382 char *string_locale
= _isds_utf82locale(string
);
4383 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4385 free(string_locale
);
4391 /* Get hash value */
4392 EXTRACT_STRING(".", string
);
4394 isds_printf_message(context
,
4395 _("sisds:dmHash element is missing hash value"));
4399 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4400 if ((*hash
)->length
== (size_t) -1) {
4401 isds_printf_message(context
,
4402 _("Error while Base64-decoding hash value"));
4408 if (err
) isds_hash_free(hash
);
4410 xmlXPathFreeObject(result
);
4411 xpath_ctx
->node
= old_ctx_node
;
4416 /* Find and append isds:dmQTimestamp XML tree into envelope.
4417 * Because one service is allowed to miss time-stamp content, and we think
4418 * other could too (flaw in specification), this function is deliberated and
4419 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4420 * @context is ISDS context
4421 * @envelope is automatically allocated envelope structure
4422 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4424 * In case of error @envelope will be freed. */
4425 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4426 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4427 isds_error err
= IE_SUCCESS
;
4428 xmlXPathObjectPtr result
= NULL
;
4429 char *string
= NULL
;
4431 if (!context
) return IE_INVALID_CONTEXT
;
4432 if (!envelope
) return IE_INVAL
;
4434 isds_envelope_free(envelope
);
4439 *envelope
= calloc(1, sizeof(**envelope
));
4445 zfree((*envelope
)->timestamp
);
4446 (*envelope
)->timestamp_length
= 0;
4449 /* Get dmQTimestamp */
4450 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4452 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4455 (*envelope
)->timestamp_length
=
4456 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4457 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4458 isds_printf_message(context
,
4459 _("Error while Base64-decoding time stamp value"));
4465 if (err
) isds_envelope_free(envelope
);
4467 xmlXPathFreeObject(result
);
4472 /* Convert XSD tReturnedMessage XML tree into message structure.
4473 * It does not store serialized XML tree into message->raw.
4474 * It does store (pointer to) parsed XML tree into message->xml if needed.
4475 * @context is ISDS context
4476 * @include_documents Use true if documents must be extracted
4477 * (tReturnedMessage XSD type), use false if documents shall be omitted
4478 * (tReturnedMessageEnvelope).
4479 * @message is automatically reallocated message structure
4480 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4482 * In case of error @message will be freed. */
4483 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4484 const _Bool include_documents
, struct isds_message
**message
,
4485 xmlXPathContextPtr xpath_ctx
) {
4486 isds_error err
= IE_SUCCESS
;
4487 xmlNodePtr message_node
;
4489 if (!context
) return IE_INVALID_CONTEXT
;
4490 if (!message
) return IE_INVAL
;
4491 isds_message_free(message
);
4492 if (!xpath_ctx
) return IE_INVAL
;
4495 *message
= calloc(1, sizeof(**message
));
4501 /* Save message XPATH context node */
4502 message_node
= xpath_ctx
->node
;
4506 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4507 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4508 if (err
) { err
= IE_ERROR
; goto leave
; }
4509 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4510 if (err
) goto leave
;
4512 if (include_documents
) {
4513 struct isds_list
*item
;
4515 /* Extract dmFiles */
4516 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4518 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4519 err
= IE_ISDS
; goto leave
;
4521 if (err
) { err
= IE_ERROR
; goto leave
; }
4522 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4523 if (err
) goto leave
;
4525 /* Store xmlDoc of this message if needed */
4526 /* Only if we got a XML document in all the documents. */
4527 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4528 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4529 (*message
)->xml
= xpath_ctx
->doc
;
4536 /* Restore context to message */
4537 xpath_ctx
->node
= message_node
;
4539 /* Extract dmHash */
4540 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4542 if (err
) goto leave
;
4544 /* Extract dmQTimestamp, */
4545 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4547 if (err
) goto leave
;
4549 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4550 * dmAcceptanceTime. */
4551 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4552 if (err
) goto leave
;
4554 /* Get message type */
4555 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4556 if (err
) goto leave
;
4559 if (err
) isds_message_free(message
);
4564 /* Extract message event into reallocated isds_event structure
4565 * @context is ISDS context
4566 * @event is automatically reallocated message event structure
4567 * @xpath_ctx is XPath context with current node as isds:dmEvent
4568 * In case of error @event will be freed. */
4569 static isds_error
extract_event(struct isds_ctx
*context
,
4570 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4571 isds_error err
= IE_SUCCESS
;
4572 xmlXPathObjectPtr result
= NULL
;
4573 xmlNodePtr event_node
;
4574 char *string
= NULL
;
4576 if (!context
) return IE_INVALID_CONTEXT
;
4577 if (!event
) return IE_INVAL
;
4578 isds_event_free(event
);
4579 if (!xpath_ctx
) return IE_INVAL
;
4580 event_node
= xpath_ctx
->node
;
4582 *event
= calloc(1, sizeof(**event
));
4588 /* Extract event data.
4589 * All elements are optional according XSD. That's funny. */
4590 EXTRACT_STRING("sisds:dmEventTime", string
);
4592 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4594 char *string_locale
= _isds_utf82locale(string
);
4595 if (err
== IE_DATE
) err
= IE_ISDS
;
4596 isds_printf_message(context
,
4597 _("Could not convert dmEventTime as ISO time: %s"),
4599 free(string_locale
);
4605 /* dmEventDescr element has prefix and the rest */
4606 EXTRACT_STRING("sisds:dmEventDescr", string
);
4608 err
= eventstring2event((xmlChar
*) string
, *event
);
4609 if (err
) goto leave
;
4614 if (err
) isds_event_free(event
);
4616 xmlXPathFreeObject(result
);
4617 xpath_ctx
->node
= event_node
;
4622 /* Convert element of XSD tEventsArray type from XML tree into
4623 * isds_list of isds_event's structure. The list is automatically reallocated.
4624 * @context is ISDS context
4625 * @events is automatically reallocated list of event structures
4626 * @xpath_ctx is XPath context with current node as tEventsArray
4627 * In case of error @events will be freed. */
4628 static isds_error
extract_events(struct isds_ctx
*context
,
4629 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4630 isds_error err
= IE_SUCCESS
;
4631 xmlXPathObjectPtr result
= NULL
;
4632 xmlNodePtr events_node
;
4633 struct isds_list
*event
, *prev_event
= NULL
;
4635 if (!context
) return IE_INVALID_CONTEXT
;
4636 if (!events
) return IE_INVAL
;
4637 if (!xpath_ctx
) return IE_INVAL
;
4638 events_node
= xpath_ctx
->node
;
4641 isds_list_free(events
);
4644 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4651 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4652 isds_printf_message(context
,
4653 _("Delivery info does not contain any event"));
4659 /* Iterate over events */
4660 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4662 /* Allocate and append list item */
4663 event
= calloc(1, sizeof(*event
));
4668 event
->destructor
= (void (*)(void **))isds_event_free
;
4669 if (i
== 0) *events
= event
;
4670 else prev_event
->next
= event
;
4674 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4675 err
= extract_event(context
,
4676 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4677 if (err
) goto leave
;
4682 if (err
) isds_list_free(events
);
4683 xmlXPathFreeObject(result
);
4684 xpath_ctx
->node
= events_node
;
4690 /* Insert Base64 encoded data as element with text child.
4691 * @context is session context
4692 * @parent is XML node to append @element with @data as child
4693 * @ns is XML namespace of @element, use NULL to inherit from @parent
4694 * @element is UTF-8 encoded name of new element
4695 * @data is bit stream to encode into @element
4696 * @length is size of @data in bytes
4697 * @return standard error code and fill long error message if needed */
4698 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4699 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4700 const void *data
, size_t length
) {
4701 isds_error err
= IE_SUCCESS
;
4704 if (!context
) return IE_INVALID_CONTEXT
;
4705 if (!data
&& length
> 0) return IE_INVAL
;
4706 if (!parent
|| !element
) return IE_INVAL
;
4708 xmlChar
*base64data
= NULL
;
4709 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4711 isds_printf_message(context
,
4712 ngettext("Not enough memory to encode %zd byte into Base64",
4713 "Not enough memory to encode %zd bytes into Base64",
4719 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4727 /* Convert isds_document structure into XML tree and append to dmFiles node.
4728 * @context is session context
4729 * @document is ISDS document
4730 * @dm_files is XML element the resulting tree will be appended to as a child.
4731 * @return error code, in case of error context' message is filled. */
4732 static isds_error
insert_document(struct isds_ctx
*context
,
4733 struct isds_document
*document
, xmlNodePtr dm_files
) {
4734 isds_error err
= IE_SUCCESS
;
4735 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4736 xmlAttrPtr attribute_node
;
4738 if (!context
) return IE_INVALID_CONTEXT
;
4739 if (!document
|| !dm_files
) return IE_INVAL
;
4741 /* Allocate new dmFile */
4742 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4744 isds_printf_message(context
, _("Could not allocate main dmFile"));
4748 /* Append the new dmFile.
4749 * XXX: Main document must go first */
4750 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4751 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4753 file
= xmlAddChild(dm_files
, new_file
);
4756 xmlFreeNode(new_file
); new_file
= NULL
;
4757 isds_printf_message(context
, _("Could not add dmFile child to "
4758 "%s element"), dm_files
->name
);
4763 /* @dmMimeType is required */
4764 if (!document
->dmMimeType
) {
4765 isds_log_message(context
,
4766 _("Document is missing mandatory MIME type definition"));
4770 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4772 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4774 isds_printf_message(context
,
4775 _("Document has unknown dmFileMetaType: %ld"),
4776 document
->dmFileMetaType
);
4780 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4782 if (document
->dmFileGuid
) {
4783 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4785 if (document
->dmUpFileGuid
) {
4786 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4789 /* @dmFileDescr is required */
4790 if (!document
->dmFileDescr
) {
4791 isds_log_message(context
,
4792 _("Document is missing mandatory description (title)"));
4796 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4798 if (document
->dmFormat
) {
4799 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4803 /* Insert content (body) of the document. */
4804 if (document
->is_xml
) {
4805 /* XML document requested */
4807 /* Allocate new dmXMLContent */
4808 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4810 isds_printf_message(context
,
4811 _("Could not allocate dmXMLContent element"));
4816 node
= xmlAddChild(file
, xmlcontent
);
4818 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4819 isds_printf_message(context
,
4820 _("Could not add dmXMLContent child to %s element"),
4826 /* Copy non-empty node list */
4827 if (document
->xml_node_list
) {
4828 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4829 document
->xml_node_list
);
4831 isds_printf_message(context
,
4832 _("Not enough memory to copy XML document"));
4837 if (!xmlAddChildList(node
, content
)) {
4838 xmlFreeNodeList(content
);
4839 isds_printf_message(context
,
4840 _("Error while adding XML document into dmXMLContent"));
4844 /* XXX: We cannot free the content here because it's part of node's
4845 * document since now. It will be freed with it automatically. */
4848 /* Binary document requested */
4849 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4850 document
->data
, document
->data_length
);
4851 if (err
) goto leave
;
4859 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4860 * The copy must be preallocated, the date are just appended into structure.
4861 * @context is ISDS context
4862 * @copy is message copy structure
4863 * @xpath_ctx is XPath context with current node as tMStatus */
4864 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4865 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4866 isds_error err
= IE_SUCCESS
;
4867 xmlXPathObjectPtr result
= NULL
;
4868 char *code
= NULL
, *message
= NULL
;
4870 if (!context
) return IE_INVALID_CONTEXT
;
4871 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4873 /* Free old values */
4874 zfree(copy
->dmStatus
);
4877 /* Get error specific to this copy */
4878 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4880 isds_log_message(context
,
4881 _("Missing isds:dmStatusCode under "
4882 "XSD:tMStatus type element"));
4887 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4888 /* This copy failed */
4889 copy
->error
= IE_ISDS
;
4890 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4892 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4893 if (!copy
->dmStatus
) {
4894 copy
->dmStatus
= code
;
4898 copy
->dmStatus
= code
;
4902 /* This copy succeeded. In this case only, message ID is valid */
4903 copy
->error
= IE_SUCCESS
;
4905 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4907 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4908 "but did not returned assigned message ID\n"));
4916 xmlXPathFreeObject(result
);
4921 /* Insert struct isds_approval data (box approval) into XML tree
4922 * @context is session context
4923 * @approval is libisds structure with approval description. NULL is
4925 * @parent is XML element to append @approval to */
4926 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4927 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4929 isds_error err
= IE_SUCCESS
;
4932 if (!context
) return IE_INVALID_CONTEXT
;
4933 if (!parent
) return IE_INVAL
;
4935 if (!approval
) return IE_SUCCESS
;
4937 /* Build XSD:gExtApproval */
4938 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4939 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4946 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4948 * @context is session context
4949 * @service_name is name of SERVICE_DB_ACCESS
4950 * @response is reallocated server SOAP body response as XML document
4951 * @raw_response is reallocated bit stream with response body. Use
4952 * NULL if you don't care
4953 * @raw_response_length is size of @raw_response in bytes
4954 * @code is reallocated ISDS status code
4955 * @status_message is reallocated ISDS status message
4956 * @return error coded from lower layer, context message will be set up
4958 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4959 const xmlChar
*service_name
,
4960 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4961 xmlChar
**code
, xmlChar
**status_message
) {
4963 isds_error err
= IE_SUCCESS
;
4964 char *service_name_locale
= NULL
;
4965 xmlNodePtr request
= NULL
, node
;
4966 xmlNsPtr isds_ns
= NULL
;
4968 if (!context
) return IE_INVALID_CONTEXT
;
4969 if (!service_name
) return IE_INVAL
;
4970 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4971 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4973 /* Free output argument */
4974 xmlFreeDoc(*response
); *response
= NULL
;
4975 if (raw_response
) zfree(*raw_response
);
4977 zfree(*status_message
);
4980 /* Check if connection is established
4981 * TODO: This check should be done downstairs. */
4982 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4984 service_name_locale
= _isds_utf82locale((char*)service_name
);
4985 if (!service_name_locale
) {
4991 request
= xmlNewNode(NULL
, service_name
);
4993 isds_printf_message(context
,
4994 _("Could not build %s request"), service_name_locale
);
4998 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5000 isds_log_message(context
, _("Could not create ISDS name space"));
5004 xmlSetNs(request
, isds_ns
);
5007 /* Add XSD:tDummyInput child */
5008 INSERT_STRING(request
, "dbDummy", NULL
);
5011 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5012 service_name_locale
);
5015 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
5016 raw_response
, raw_response_length
);
5017 xmlFreeNode(request
); request
= NULL
;
5020 isds_log(ILF_ISDS
, ILL_DEBUG
,
5021 _("Processing ISDS response on %s request failed\n"),
5022 service_name_locale
);
5026 /* Check for response status */
5027 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
5028 code
, status_message
, NULL
);
5030 isds_log(ILF_ISDS
, ILL_DEBUG
,
5031 _("ISDS response on %s request is missing status\n"),
5032 service_name_locale
);
5036 /* Request processed, but nothing found */
5037 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
5038 char *code_locale
= _isds_utf82locale((char*) *code
);
5039 char *status_message_locale
=
5040 _isds_utf82locale((char*) *status_message
);
5041 isds_log(ILF_ISDS
, ILL_DEBUG
,
5042 _("Server refused %s request (code=%s, message=%s)\n"),
5043 service_name_locale
, code_locale
, status_message_locale
);
5044 isds_log_message(context
, status_message_locale
);
5046 free(status_message_locale
);
5052 free(service_name_locale
);
5053 xmlFreeNode(request
);
5059 /* Get data about logged in user and his box.
5060 * @context is session context
5061 * @db_owner_info is reallocated box owner description. It will be freed on
5063 * @return error code from lower layer, context message will be set up
5065 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
5066 struct isds_DbOwnerInfo
**db_owner_info
) {
5067 isds_error err
= IE_SUCCESS
;
5069 xmlDocPtr response
= NULL
;
5070 xmlChar
*code
= NULL
, *message
= NULL
;
5071 xmlXPathContextPtr xpath_ctx
= NULL
;
5072 xmlXPathObjectPtr result
= NULL
;
5073 char *string
= NULL
;
5076 if (!context
) return IE_INVALID_CONTEXT
;
5077 zfree(context
->long_message
);
5078 if (!db_owner_info
) return IE_INVAL
;
5079 isds_DbOwnerInfo_free(db_owner_info
);
5082 /* Check if connection is established */
5083 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5086 /* Do request and check for success */
5087 err
= build_send_check_dbdummy_request(context
,
5088 BAD_CAST
"GetOwnerInfoFromLogin",
5089 &response
, NULL
, NULL
, &code
, &message
);
5090 if (err
) goto leave
;
5094 /* Prepare structure */
5095 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5096 if (!*db_owner_info
) {
5100 xpath_ctx
= xmlXPathNewContext(response
);
5105 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5110 /* Set context node */
5111 result
= xmlXPathEvalExpression(BAD_CAST
5112 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5117 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5118 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5122 if (result
->nodesetval
->nodeNr
> 1) {
5123 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5127 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5128 xmlXPathFreeObject(result
); result
= NULL
;
5131 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5136 isds_DbOwnerInfo_free(db_owner_info
);
5140 xmlXPathFreeObject(result
);
5141 xmlXPathFreeContext(xpath_ctx
);
5145 xmlFreeDoc(response
);
5148 isds_log(ILF_ISDS
, ILL_DEBUG
,
5149 _("GetOwnerInfoFromLogin request processed by server "
5150 "successfully.\n"));
5151 #else /* not HAVE_LIBCURL */
5159 /* Get data about logged in user. */
5160 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5161 struct isds_DbUserInfo
**db_user_info
) {
5162 isds_error err
= IE_SUCCESS
;
5164 xmlDocPtr response
= NULL
;
5165 xmlChar
*code
= NULL
, *message
= NULL
;
5166 xmlXPathContextPtr xpath_ctx
= NULL
;
5167 xmlXPathObjectPtr result
= NULL
;
5170 if (!context
) return IE_INVALID_CONTEXT
;
5171 zfree(context
->long_message
);
5172 if (!db_user_info
) return IE_INVAL
;
5173 isds_DbUserInfo_free(db_user_info
);
5176 /* Check if connection is established */
5177 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5180 /* Do request and check for success */
5181 err
= build_send_check_dbdummy_request(context
,
5182 BAD_CAST
"GetUserInfoFromLogin",
5183 &response
, NULL
, NULL
, &code
, &message
);
5184 if (err
) goto leave
;
5188 /* Prepare structure */
5189 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5190 if (!*db_user_info
) {
5194 xpath_ctx
= xmlXPathNewContext(response
);
5199 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5204 /* Set context node */
5205 result
= xmlXPathEvalExpression(BAD_CAST
5206 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5211 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5212 isds_log_message(context
, _("Missing dbUserInfo element"));
5216 if (result
->nodesetval
->nodeNr
> 1) {
5217 isds_log_message(context
, _("Multiple dbUserInfo element"));
5221 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5222 xmlXPathFreeObject(result
); result
= NULL
;
5225 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5229 isds_DbUserInfo_free(db_user_info
);
5232 xmlXPathFreeObject(result
);
5233 xmlXPathFreeContext(xpath_ctx
);
5237 xmlFreeDoc(response
);
5240 isds_log(ILF_ISDS
, ILL_DEBUG
,
5241 _("GetUserInfoFromLogin request processed by server "
5242 "successfully.\n"));
5243 #else /* not HAVE_LIBCURL */
5251 /* Get expiration time of current password
5252 * @context is session context
5253 * @expiration is automatically reallocated time when password expires. If
5254 * password expiration is disabled, NULL will be returned. In case of error
5255 * it will be nulled too. */
5256 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5257 struct timeval
**expiration
) {
5258 isds_error err
= IE_SUCCESS
;
5260 xmlDocPtr response
= NULL
;
5261 xmlChar
*code
= NULL
, *message
= NULL
;
5262 xmlXPathContextPtr xpath_ctx
= NULL
;
5263 xmlXPathObjectPtr result
= NULL
;
5264 char *string
= NULL
;
5267 if (!context
) return IE_INVALID_CONTEXT
;
5268 zfree(context
->long_message
);
5269 if (!expiration
) return IE_INVAL
;
5273 /* Check if connection is established */
5274 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5277 /* Do request and check for success */
5278 err
= build_send_check_dbdummy_request(context
,
5279 BAD_CAST
"GetPasswordInfo",
5280 &response
, NULL
, NULL
, &code
, &message
);
5281 if (err
) goto leave
;
5285 xpath_ctx
= xmlXPathNewContext(response
);
5290 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5295 /* Set context node */
5296 result
= xmlXPathEvalExpression(BAD_CAST
5297 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5302 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5303 isds_log_message(context
,
5304 _("Missing GetPasswordInfoResponse element"));
5308 if (result
->nodesetval
->nodeNr
> 1) {
5309 isds_log_message(context
,
5310 _("Multiple GetPasswordInfoResponse element"));
5314 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5315 xmlXPathFreeObject(result
); result
= NULL
;
5317 /* Extract expiration date */
5318 EXTRACT_STRING("isds:pswExpDate", string
);
5320 /* And convert it if any returned. Otherwise expiration is disabled. */
5321 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5323 char *string_locale
= _isds_utf82locale(string
);
5324 if (err
== IE_DATE
) err
= IE_ISDS
;
5325 isds_printf_message(context
,
5326 _("Could not convert pswExpDate as ISO time: %s"),
5328 free(string_locale
);
5341 xmlXPathFreeObject(result
);
5342 xmlXPathFreeContext(xpath_ctx
);
5346 xmlFreeDoc(response
);
5349 isds_log(ILF_ISDS
, ILL_DEBUG
,
5350 _("GetPasswordInfo request processed by server "
5351 "successfully.\n"));
5352 #else /* not HAVE_LIBCURL */
5361 /* Request delivering new TOTP code from ISDS through side channel before
5362 * changing password.
5363 * @context is session context
5364 * @password is current password.
5365 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5366 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5367 * function for more details.
5368 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5369 * NULL, if you don't care.
5370 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5372 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5373 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5374 isds_error err
= IE_SUCCESS
;
5375 char *saved_url
= NULL
; /* No copy */
5376 #if HAVE_CURL_REAUTHORIZATION_BUG
5377 CURL
*saved_curl
= NULL
; /* No copy */
5379 xmlNsPtr isds_ns
= NULL
;
5380 xmlNodePtr request
= NULL
;
5381 xmlDocPtr response
= NULL
;
5382 xmlChar
*code
= NULL
, *message
= NULL
;
5383 const xmlChar
*codes
[] = {
5388 const char *meanings
[] = {
5389 N_("Unexpected error"),
5390 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5391 N_("One-time code could not been sent. Try later again.")
5393 const isds_otp_resolution resolutions
[] = {
5394 OTP_RESOLUTION_UNKNOWN
,
5395 OTP_RESOLUTION_TO_FAST
,
5396 OTP_RESOLUTION_TOTP_NOT_SENT
5399 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5400 zfree(context
->long_message
);
5401 if (NULL
== password
) {
5402 isds_log_message(context
,
5403 _("Second argument (password) of isds_change_password() "
5408 /* Check if connection is established
5409 * TODO: This check should be done downstairs. */
5410 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5412 if (!context
->otp
) {
5413 isds_log_message(context
, _("This function requires OTP-authenticated "
5415 return IE_INVALID_CONTEXT
;
5418 isds_log_message(context
, _("If one-time password authentication "
5419 "method is in use, requesting new OTP code requires "
5420 "one-time credentials argument either"));
5423 if (otp
->method
!= OTP_TIME
) {
5424 isds_log_message(context
, _("Requesting new time-based OTP code from "
5425 "server requires one-time password authentication "
5429 if (otp
->otp_code
!= NULL
) {
5430 isds_log_message(context
, _("Requesting new time-based OTP code from "
5431 "server requires undefined OTP code member in "
5432 "one-time credentials argument"));
5438 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5440 isds_log_message(context
, _("Could not build SendSMSCode request"));
5443 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5445 isds_log_message(context
, _("Could not create ISDS name space"));
5446 xmlFreeNode(request
);
5449 xmlSetNs(request
, isds_ns
);
5451 /* Change URL temporarily for sending this request only */
5453 char *new_url
= NULL
;
5454 if ((err
= _isds_build_url_from_context(context
,
5455 "%.*sasws/changePassword", &new_url
))) {
5458 saved_url
= context
->url
;
5459 context
->url
= new_url
;
5462 /* Store credentials for sending this request only */
5463 context
->otp_credentials
= otp
;
5464 _isds_discard_credentials(context
, 0);
5465 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5467 _isds_discard_credentials(context
, 0);
5470 #if HAVE_CURL_REAUTHORIZATION_BUG
5471 saved_curl
= context
->curl
;
5472 context
->curl
= curl_easy_init();
5473 if (NULL
== context
->curl
) {
5477 if (context
->timeout
) {
5478 err
= isds_set_timeout(context
, context
->timeout
);
5479 if (err
) goto leave
;
5483 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5486 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5488 /* Remove temporal credentials */
5489 _isds_discard_credentials(context
, 0);
5490 /* Detach pointer to OTP credentials from context */
5491 context
->otp_credentials
= NULL
;
5492 /* Keep context->otp true to keep signaling this is OTP session */
5494 /* Destroy request */
5495 xmlFreeNode(request
); request
= NULL
;
5498 isds_log(ILF_ISDS
, ILL_DEBUG
,
5499 _("Processing ISDS response on SendSMSCode request failed\n"));
5503 /* Check for response status */
5504 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5505 &code
, &message
, (xmlChar
**)refnumber
);
5507 isds_log(ILF_ISDS
, ILL_DEBUG
,
5508 _("ISDS response on SendSMSCode request is missing "
5513 /* Check for error */
5514 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5515 char *code_locale
= _isds_utf82locale((char*)code
);
5516 char *message_locale
= _isds_utf82locale((char*)message
);
5518 isds_log(ILF_ISDS
, ILL_DEBUG
,
5519 _("Server refused to send new code on SendSMSCode "
5520 "request (code=%s, message=%s)\n"),
5521 code_locale
, message_locale
);
5523 /* Check for known error codes */
5524 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5525 if (!xmlStrcmp(code
, codes
[i
])) break;
5527 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5528 isds_log_message(context
, _(meanings
[i
]));
5529 /* Mimic otp->resolution according to the code, specification does
5530 * prescribe OTP header to be available. */
5531 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5532 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5533 otp
->resolution
= resolutions
[i
];
5535 isds_log_message(context
, message_locale
);
5538 free(message_locale
);
5544 /* Otherwise new code sent successfully */
5545 /* Mimic otp->resolution according to the code, specification does
5546 * prescribe OTP header to be available. */
5547 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5548 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5551 if (NULL
!= saved_url
) {
5552 /* Revert URL to original one */
5553 zfree(context
->url
);
5554 context
->url
= saved_url
;
5556 #if HAVE_CURL_REAUTHORIZATION_BUG
5557 if (NULL
!= saved_curl
) {
5558 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5559 context
->curl
= saved_curl
;
5565 xmlFreeDoc(response
);
5566 xmlFreeNode(request
);
5569 isds_log(ILF_ISDS
, ILL_DEBUG
,
5570 _("New OTP code has been sent successfully on SendSMSCode "
5576 /* Convert response status code to isds_error code and set long message
5577 * @context is context to save long message to
5578 * @map is mapping from codes to errors and messages. Pass NULL for generic
5580 * @code is status code to translate
5581 * @message is non-localized status message to put into long message in case
5582 * of uknown error. It can be NULL if server did not provide any.
5583 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5584 * invalid invocation. */
5585 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5586 const struct code_map_isds_error
*map
,
5587 const xmlChar
*code
, const xmlChar
*message
) {
5589 isds_log_message(context
,
5590 _("NULL status code passed to statuscode2isds_error()"));
5595 /* Check for known error codes */
5596 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5597 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5598 isds_log_message(context
, _(map
->meanings
[i
]));
5599 return map
->errors
[i
];
5605 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5606 char *message_locale
= _isds_utf82locale((char*)message
);
5607 if (NULL
== message_locale
)
5608 isds_log_message(context
, _("ISDS server returned unknown error"));
5610 isds_log_message(context
, message_locale
);
5611 free(message_locale
);
5620 /* Change user password in ISDS.
5621 * User must supply old password, new password will takes effect after some
5622 * time, current session can continue. Password must fulfill some constraints.
5623 * @context is session context
5624 * @old_password is current password.
5625 * @new_password is requested new password
5626 * @otp auxiliary data required if one-time password authentication is in use,
5627 * defines OTP code (if known) and returns fine grade resolution of OTP
5628 * procedure. Pass NULL, if one-time password authentication is not needed.
5629 * Please note the @otp argument must match OTP method used at log-in time. See
5630 * isds_login() function for more details.
5631 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5632 * NULL, if you don't care.
5633 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5634 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5635 * awaiting OTP code that has been delivered by side channel to the user. */
5636 isds_error
isds_change_password(struct isds_ctx
*context
,
5637 const char *old_password
, const char *new_password
,
5638 struct isds_otp
*otp
, char **refnumber
) {
5639 isds_error err
= IE_SUCCESS
;
5641 char *saved_url
= NULL
; /* No copy */
5642 #if HAVE_CURL_REAUTHORIZATION_BUG
5643 CURL
*saved_curl
= NULL
; /* No copy */
5645 xmlNsPtr isds_ns
= NULL
;
5646 xmlNodePtr request
= NULL
, node
;
5647 xmlDocPtr response
= NULL
;
5648 xmlChar
*code
= NULL
, *message
= NULL
;
5649 const xmlChar
*codes
[] = {
5662 const char *meanings
[] = {
5663 N_("Password length must be between 8 and 32 characters"),
5664 N_("Password cannot be reused"), /* Server does not distinguish 1067
5665 and 1091 on ChangePasswordOTP */
5666 N_("Password contains forbidden character"),
5667 N_("Password must contain at least one upper-case letter, "
5668 "one lower-case, and one digit"),
5669 N_("Password cannot contain sequence of three identical characters"),
5670 N_("Password cannot contain user identifier"),
5671 N_("Password is too simmple"),
5672 N_("Old password is not valid"),
5673 N_("Password cannot be reused"),
5674 N_("Unexpected error"),
5675 N_("LDAP update error")
5679 if (!context
) return IE_INVALID_CONTEXT
;
5680 zfree(context
->long_message
);
5681 if (NULL
!= refnumber
)
5683 if (NULL
== old_password
) {
5684 isds_log_message(context
,
5685 _("Second argument (old password) of isds_change_password() "
5689 if (NULL
== otp
&& NULL
== new_password
) {
5690 isds_log_message(context
,
5691 _("Third argument (new password) of isds_change_password() "
5697 /* Check if connection is established
5698 * TODO: This check should be done downstairs. */
5699 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5701 if (context
->otp
&& NULL
== otp
) {
5702 isds_log_message(context
, _("If one-time password authentication "
5703 "method is in use, changing password requires one-time "
5704 "credentials either"));
5708 /* Build ChangeISDSPassword request */
5709 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5710 BAD_CAST
"ChangePasswordOTP");
5712 isds_log_message(context
, (NULL
== otp
) ?
5713 _("Could not build ChangeISDSPassword request") :
5714 _("Could not build ChangePasswordOTP request"));
5717 isds_ns
= xmlNewNs(request
,
5718 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5721 isds_log_message(context
, _("Could not create ISDS name space"));
5722 xmlFreeNode(request
);
5725 xmlSetNs(request
, isds_ns
);
5727 INSERT_STRING(request
, "dbOldPassword", old_password
);
5728 INSERT_STRING(request
, "dbNewPassword", new_password
);
5731 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5732 switch (otp
->method
) {
5734 isds_log(ILF_SEC
, ILL_INFO
,
5735 _("Selected authentication method: "
5736 "HMAC-based one-time password\n"));
5737 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5740 isds_log(ILF_SEC
, ILL_INFO
,
5741 _("Selected authentication method: "
5742 "Time-based one-time password\n"));
5743 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5744 if (otp
->otp_code
== NULL
) {
5745 isds_log(ILF_SEC
, ILL_INFO
,
5746 _("OTP code has not been provided by "
5747 "application, requesting server for "
5749 err
= _isds_request_totp_code(context
, old_password
, otp
,
5751 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5755 isds_log(ILF_SEC
, ILL_INFO
,
5756 _("OTP code has been provided by "
5757 "application, not requesting server "
5762 isds_log_message(context
,
5763 _("Unknown one-time password authentication "
5764 "method requested by application"));
5769 /* Change URL temporarily for sending this request only */
5771 char *new_url
= NULL
;
5772 if ((err
= _isds_build_url_from_context(context
,
5773 "%.*sasws/changePassword", &new_url
))) {
5776 saved_url
= context
->url
;
5777 context
->url
= new_url
;
5780 /* Store credentials for sending this request only */
5781 context
->otp_credentials
= otp
;
5782 _isds_discard_credentials(context
, 0);
5783 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5784 old_password
, NULL
))) {
5785 _isds_discard_credentials(context
, 0);
5788 #if HAVE_CURL_REAUTHORIZATION_BUG
5789 saved_curl
= context
->curl
;
5790 context
->curl
= curl_easy_init();
5791 if (NULL
== context
->curl
) {
5795 if (context
->timeout
) {
5796 err
= isds_set_timeout(context
, context
->timeout
);
5797 if (err
) goto leave
;
5802 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5803 _("Sending ChangeISDSPassword request to ISDS\n") :
5804 _("Sending ChangePasswordOTP request to ISDS\n"));
5807 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5808 request
, &response
, NULL
, NULL
);
5811 /* Remove temporal credentials */
5812 _isds_discard_credentials(context
, 0);
5813 /* Detach pointer to OTP credentials from context */
5814 context
->otp_credentials
= NULL
;
5815 /* Keep context->otp true to keep signaling this is OTP session */
5818 /* Destroy request */
5819 xmlFreeNode(request
); request
= NULL
;
5822 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5823 _("Processing ISDS response on ChangeISDSPassword "
5824 "request failed\n") :
5825 _("Processing ISDS response on ChangePasswordOTP "
5826 "request failed\n"));
5830 /* Check for response status */
5831 err
= isds_response_status(context
,
5832 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5833 &code
, &message
, (xmlChar
**)refnumber
);
5835 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5836 _("ISDS response on ChangeISDSPassword request is missing "
5838 _("ISDS response on ChangePasswordOTP request is missing "
5843 /* Check for known error codes */
5844 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5845 if (!xmlStrcmp(code
, codes
[i
])) {
5846 char *code_locale
= _isds_utf82locale((char*)code
);
5847 char *message_locale
= _isds_utf82locale((char*)message
);
5848 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5849 _("Server refused to change password on ChangeISDSPassword "
5850 "request (code=%s, message=%s)\n") :
5851 _("Server refused to change password on ChangePasswordOTP "
5852 "request (code=%s, message=%s)\n"),
5853 code_locale
, message_locale
);
5855 free(message_locale
);
5856 isds_log_message(context
, _(meanings
[i
]));
5863 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5864 char *code_locale
= _isds_utf82locale((char*)code
);
5865 char *message_locale
= _isds_utf82locale((char*)message
);
5866 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5867 _("Server refused to change password on ChangeISDSPassword "
5868 "request (code=%s, message=%s)\n") :
5869 _("Server refused to change password on ChangePasswordOTP "
5870 "request (code=%s, message=%s)\n"),
5871 code_locale
, message_locale
);
5872 isds_log_message(context
, message_locale
);
5874 free(message_locale
);
5879 /* Otherwise password changed successfully */
5882 if (NULL
!= saved_url
) {
5883 /* Revert URL to original one */
5884 zfree(context
->url
);
5885 context
->url
= saved_url
;
5887 #if HAVE_CURL_REAUTHORIZATION_BUG
5888 if (NULL
!= saved_curl
) {
5889 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5890 context
->curl
= saved_curl
;
5896 xmlFreeDoc(response
);
5897 xmlFreeNode(request
);
5900 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5901 _("Password changed successfully on ChangeISDSPassword "
5903 _("Password changed successfully on ChangePasswordOTP "
5905 #else /* not HAVE_LIBCURL */
5914 /* Generic middle part with request sending and response check.
5915 * It sends prepared request and checks for error code.
5916 * @context is ISDS session context.
5917 * @service is ISDS service handler
5918 * @service_name is name in scope of given @service
5919 * @request is XML tree with request. Will be freed to save memory.
5920 * @response is XML document outputting ISDS response.
5921 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5922 * @map is mapping from status code to library error. Pass NULL if no special
5923 * handling is requested.
5924 * NULL, if you don't care. */
5925 static isds_error
send_destroy_request_check_response(
5926 struct isds_ctx
*context
,
5927 const isds_service service
, const xmlChar
*service_name
,
5928 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5929 const struct code_map_isds_error
*map
) {
5930 isds_error err
= IE_SUCCESS
;
5931 char *service_name_locale
= NULL
;
5932 xmlChar
*code
= NULL
, *message
= NULL
;
5935 if (!context
) return IE_INVALID_CONTEXT
;
5936 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5940 /* Check if connection is established
5941 * TODO: This check should be done downstairs. */
5942 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5944 service_name_locale
= _isds_utf82locale((char*) service_name
);
5945 if (!service_name_locale
) {
5950 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5951 service_name_locale
);
5954 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5955 xmlFreeNode(*request
); *request
= NULL
;
5958 isds_log(ILF_ISDS
, ILL_DEBUG
,
5959 _("Processing ISDS response on %s request failed\n"),
5960 service_name_locale
);
5964 /* Check for response status */
5965 err
= isds_response_status(context
, service
, *response
,
5966 &code
, &message
, refnumber
);
5968 isds_log(ILF_ISDS
, ILL_DEBUG
,
5969 _("ISDS response on %s request is missing status\n"),
5970 service_name_locale
);
5974 err
= statuscode2isds_error(context
, map
, code
, message
);
5976 /* Request processed, but server failed */
5977 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5978 char *code_locale
= _isds_utf82locale((char*) code
);
5979 char *message_locale
= _isds_utf82locale((char*) message
);
5980 isds_log(ILF_ISDS
, ILL_DEBUG
,
5981 _("Server refused %s request (code=%s, message=%s)\n"),
5982 service_name_locale
, code_locale
, message_locale
);
5984 free(message_locale
);
5992 if (err
&& *response
) {
5993 xmlFreeDoc(*response
);
5997 xmlFreeNode(*request
);
6000 free(service_name_locale
);
6006 /* Generic bottom half with request sending.
6007 * It sends prepared request, checks for error code, destroys response and
6008 * request and log success or failure.
6009 * @context is ISDS session context.
6010 * @service is ISDS service handler
6011 * @service_name is name in scope of given @service
6012 * @request is XML tree with request. Will be freed to save memory.
6013 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6014 * NULL, if you don't care. */
6015 static isds_error
send_request_check_drop_response(
6016 struct isds_ctx
*context
,
6017 const isds_service service
, const xmlChar
*service_name
,
6018 xmlNodePtr
*request
, xmlChar
**refnumber
) {
6019 isds_error err
= IE_SUCCESS
;
6020 xmlDocPtr response
= NULL
;
6023 if (!context
) return IE_INVALID_CONTEXT
;
6024 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
6027 /* Send request and check response*/
6028 err
= send_destroy_request_check_response(context
,
6029 service
, service_name
, request
, &response
, refnumber
, NULL
);
6031 xmlFreeDoc(response
);
6034 xmlFreeNode(*request
);
6039 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6040 isds_log(ILF_ISDS
, ILL_DEBUG
,
6041 _("%s request processed by server successfully.\n"),
6042 service_name_locale
);
6043 free(service_name_locale
);
6050 /* Insert isds_credentials_delivery structure into XML request if not NULL
6051 * @context is session context
6052 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6053 * credentials delivery. The email field is passed.
6054 * @parent is XML element where to insert */
6055 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
6056 const struct isds_credentials_delivery
*credentials_delivery
,
6057 xmlNodePtr parent
) {
6058 isds_error err
= IE_SUCCESS
;
6061 if (!context
) return IE_INVALID_CONTEXT
;
6062 if (!parent
) return IE_INVAL
;
6064 if (credentials_delivery
) {
6065 /* Following elements are valid only for services:
6066 * NewAccessData, AddDataBoxUser, CreateDataBox */
6067 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
6068 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
6076 /* Extract credentials delivery from ISDS response.
6077 * @context is session context
6078 * @credentials_delivery is pointer to valid structure to fill in returned
6079 * user's password (and new log-in name). If NULL, do not extract the data.
6080 * @response is pointer to XML document with ISDS response
6081 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6082 * @return IE_SUCCESS even if new user name has not been found because it's not
6083 * clear whether it's returned always. */
6084 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
6085 struct isds_credentials_delivery
*credentials_delivery
,
6086 xmlDocPtr response
, const char *request_name
) {
6087 isds_error err
= IE_SUCCESS
;
6088 xmlXPathContextPtr xpath_ctx
= NULL
;
6089 xmlXPathObjectPtr result
= NULL
;
6090 char *xpath_query
= NULL
;
6092 if (!context
) return IE_INVALID_CONTEXT
;
6093 if (credentials_delivery
) {
6094 zfree(credentials_delivery
->token
);
6095 zfree(credentials_delivery
->new_user_name
);
6097 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6100 /* Extract optional token */
6101 if (credentials_delivery
) {
6102 xpath_ctx
= xmlXPathNewContext(response
);
6107 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6112 /* Verify root element */
6113 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6118 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6123 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6124 char *request_name_locale
= _isds_utf82locale(request_name
);
6125 isds_log(ILF_ISDS
, ILL_WARNING
,
6126 _("Wrong element in ISDS response for %s request "
6127 "while extracting credentials delivery details\n"),
6128 request_name_locale
);
6129 free(request_name_locale
);
6133 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6136 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6138 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6140 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6141 if (!credentials_delivery
->token
) {
6142 char *request_name_locale
= _isds_utf82locale(request_name
);
6143 isds_log(ILF_ISDS
, ILL_ERR
,
6144 _("ISDS did not return token on %s request "
6145 "even if requested\n"), request_name_locale
);
6146 free(request_name_locale
);
6153 xmlXPathFreeObject(result
);
6154 xmlXPathFreeContext(xpath_ctx
);
6160 /* Build XSD:tCreateDBInput request type for box creating.
6161 * @context is session context
6162 * @request outputs built XML tree
6163 * @service_name is request name of SERVICE_DB_MANIPULATION service
6164 * @box is box description to create including single primary user (in case of
6165 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6167 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6168 * box, or contact address of PFO box owner)
6169 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6170 * @upper_box_id is optional ID of supper box if currently created box is
6172 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6174 * @credentials_delivery is valid pointer if ISDS should return token that box
6175 * owner can use to obtain his new credentials in on-line way. Then valid email
6176 * member value should be supplied.
6177 * @approval is optional external approval of box manipulation */
6178 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6179 xmlNodePtr
*request
, const xmlChar
*service_name
,
6180 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6181 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6182 const xmlChar
*ceo_label
,
6183 const struct isds_credentials_delivery
*credentials_delivery
,
6184 const struct isds_approval
*approval
) {
6185 isds_error err
= IE_SUCCESS
;
6186 xmlNsPtr isds_ns
= NULL
;
6187 xmlNodePtr node
, dbPrimaryUsers
;
6188 xmlChar
*string
= NULL
;
6189 const struct isds_list
*item
;
6192 if (!context
) return IE_INVALID_CONTEXT
;
6193 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6197 /* Build CreateDataBox-similar request */
6198 *request
= xmlNewNode(NULL
, service_name
);
6200 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6201 isds_printf_message(context
, _("Could build %s request"),
6202 service_name_locale
);
6203 free(service_name_locale
);
6206 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6207 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6209 isds_log_message(context
, _("Could not create ISDS1 name space"));
6210 xmlFreeNode(*request
);
6214 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6216 isds_log_message(context
, _("Could not create ISDS name space"));
6217 xmlFreeNode(*request
);
6221 xmlSetNs(*request
, isds_ns
);
6223 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6224 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6225 if (err
) goto leave
;
6228 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6229 * verbose documentation allows none dbUserInfo */
6230 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6231 for (item
= users
; item
; item
= item
->next
) {
6233 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6234 err
= insert_DbUserInfo(context
,
6235 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6236 if (err
) goto leave
;
6240 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6241 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6242 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6244 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6245 if (err
) goto leave
;
6247 err
= insert_GExtApproval(context
, approval
, *request
);
6248 if (err
) goto leave
;
6252 xmlFreeNode(*request
);
6258 #endif /* HAVE_LIBCURL */
6262 * @context is session context
6263 * @box is box description to create including single primary user (in case of
6264 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6265 * ignored. It outputs box ID assigned by ISDS in dbID element.
6266 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6267 * box, or contact address of PFO box owner)
6268 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6269 * @upper_box_id is optional ID of supper box if currently created box is
6271 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6272 * @credentials_delivery is NULL if new password should be delivered off-line
6273 * to box owner. It is valid pointer if owner should obtain new password on-line
6274 * on dedicated web server. Then input @credentials_delivery.email value is
6275 * his e-mail address he must provide to dedicated web server together
6276 * with output reallocated @credentials_delivery.token member. Output
6277 * member @credentials_delivery.new_user_name is unused up on this call.
6278 * @approval is optional external approval of box manipulation
6279 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6280 * NULL, if you don't care.*/
6281 isds_error
isds_add_box(struct isds_ctx
*context
,
6282 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6283 const char *former_names
, const char *upper_box_id
,
6284 const char *ceo_label
,
6285 struct isds_credentials_delivery
*credentials_delivery
,
6286 const struct isds_approval
*approval
, char **refnumber
) {
6287 isds_error err
= IE_SUCCESS
;
6289 xmlNodePtr request
= NULL
;
6290 xmlDocPtr response
= NULL
;
6291 xmlXPathContextPtr xpath_ctx
= NULL
;
6292 xmlXPathObjectPtr result
= NULL
;
6296 if (!context
) return IE_INVALID_CONTEXT
;
6297 zfree(context
->long_message
);
6298 if (credentials_delivery
) {
6299 zfree(credentials_delivery
->token
);
6300 zfree(credentials_delivery
->new_user_name
);
6302 if (!box
) return IE_INVAL
;
6305 /* Scratch box ID */
6308 /* Build CreateDataBox request */
6309 err
= build_CreateDBInput_request(context
,
6310 &request
, BAD_CAST
"CreateDataBox",
6311 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6312 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6313 if (err
) goto leave
;
6315 /* Send it to server and process response */
6316 err
= send_destroy_request_check_response(context
,
6317 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6318 &response
, (xmlChar
**) refnumber
, NULL
);
6320 /* Extract box ID */
6321 xpath_ctx
= xmlXPathNewContext(response
);
6326 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6330 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6332 /* Extract optional token */
6333 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6337 xmlXPathFreeObject(result
);
6338 xmlXPathFreeContext(xpath_ctx
);
6339 xmlFreeDoc(response
);
6340 xmlFreeNode(request
);
6343 isds_log(ILF_ISDS
, ILL_DEBUG
,
6344 _("CreateDataBox request processed by server successfully.\n"));
6346 #else /* not HAVE_LIBCURL */
6354 /* Notify ISDS about new PFO entity.
6355 * This function has no real effect.
6356 * @context is session context
6357 * @box is PFO description including single primary user. aifoIsds,
6358 * address->adCode, address->adDistrict members are ignored.
6359 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6360 * @former_names is optional undocumented string. Pass NULL if you don't care.
6361 * @upper_box_id is optional ID of supper box if currently created box is
6363 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6364 * @approval is optional external approval of box manipulation
6365 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6366 * NULL, if you don't care.*/
6367 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6368 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6369 const char *former_names
, const char *upper_box_id
,
6370 const char *ceo_label
, const struct isds_approval
*approval
,
6372 isds_error err
= IE_SUCCESS
;
6374 xmlNodePtr request
= NULL
;
6377 if (!context
) return IE_INVALID_CONTEXT
;
6378 zfree(context
->long_message
);
6379 if (!box
) return IE_INVAL
;
6382 /* Build CreateDataBoxPFOInfo request */
6383 err
= build_CreateDBInput_request(context
,
6384 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6385 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6386 (xmlChar
*) ceo_label
, NULL
, approval
);
6387 if (err
) goto leave
;
6389 /* Send it to server and process response */
6390 err
= send_request_check_drop_response(context
,
6391 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6392 (xmlChar
**) refnumber
);
6393 /* XXX: XML Schema names output dbID element but textual documentation
6394 * states no box identifier is returned. */
6396 xmlFreeNode(request
);
6397 #else /* not HAVE_LIBCURL */
6404 /* Common implementation for removing given box.
6405 * @context is session context
6406 * @service_name is UTF-8 encoded name fo ISDS service
6407 * @box is box description to delete. aifoIsds, address->adCode,
6408 * address->adDistrict members are ignored.
6409 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6410 * carry sane value. If NULL, do not inject this information into request.
6411 * @approval is optional external approval of box manipulation
6412 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6413 * NULL, if you don't care.*/
6414 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6415 const xmlChar
*service_name
,
6416 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6417 const struct isds_approval
*approval
, char **refnumber
) {
6418 isds_error err
= IE_SUCCESS
;
6420 xmlNsPtr isds_ns
= NULL
;
6421 xmlNodePtr request
= NULL
;
6423 xmlChar
*string
= NULL
;
6427 if (!context
) return IE_INVALID_CONTEXT
;
6428 zfree(context
->long_message
);
6429 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6433 /* Build DeleteDataBox(Promptly) request */
6434 request
= xmlNewNode(NULL
, service_name
);
6436 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6437 isds_printf_message(context
,
6438 _("Could build %s request"), service_name_locale
);
6439 free(service_name_locale
);
6442 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6444 isds_log_message(context
, _("Could not create ISDS name space"));
6445 xmlFreeNode(request
);
6448 xmlSetNs(request
, isds_ns
);
6450 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6451 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6452 if (err
) goto leave
;
6455 err
= tm2datestring(since
, &string
);
6457 isds_log_message(context
,
6458 _("Could not convert `since' argument to ISO date string"));
6461 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6465 err
= insert_GExtApproval(context
, approval
, request
);
6466 if (err
) goto leave
;
6469 /* Send it to server and process response */
6470 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6471 service_name
, &request
, (xmlChar
**) refnumber
);
6474 xmlFreeNode(request
);
6476 #else /* not HAVE_LIBCURL */
6483 /* Remove given box permanently.
6484 * @context is session context
6485 * @box is box description to delete. aifoIsds, address->adCode,
6486 * address->adDistrict members are ignored.
6487 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
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_delete_box(struct isds_ctx
*context
,
6493 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6494 const struct isds_approval
*approval
, char **refnumber
) {
6495 if (!context
) return IE_INVALID_CONTEXT
;
6496 zfree(context
->long_message
);
6497 if (!box
|| !since
) return IE_INVAL
;
6499 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6500 box
, since
, approval
, refnumber
);
6504 /* Undocumented function.
6505 * @context is session context
6506 * @box is box description to delete. aifoIsds, address->adCode,
6507 * address->adDistrict members are ignored.
6508 * @approval is optional external approval of box manipulation
6509 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6510 * NULL, if you don't care.*/
6511 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6512 const struct isds_DbOwnerInfo
*box
,
6513 const struct isds_approval
*approval
, char **refnumber
) {
6514 if (!context
) return IE_INVALID_CONTEXT
;
6515 zfree(context
->long_message
);
6516 if (!box
) return IE_INVAL
;
6518 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6519 box
, NULL
, approval
, refnumber
);
6523 /* Update data about given box.
6524 * @context is session context
6525 * @old_box current box description. aifoIsds, address->adCode,
6526 * address->adDistrict members are ignored.
6527 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6528 * address->adDistrict members are ignored.
6529 * @approval is optional external approval of box manipulation
6530 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6531 * NULL, if you don't care.*/
6532 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6533 const struct isds_DbOwnerInfo
*old_box
,
6534 const struct isds_DbOwnerInfo
*new_box
,
6535 const struct isds_approval
*approval
, char **refnumber
) {
6536 isds_error err
= IE_SUCCESS
;
6538 xmlNsPtr isds_ns
= NULL
;
6539 xmlNodePtr request
= NULL
;
6544 if (!context
) return IE_INVALID_CONTEXT
;
6545 zfree(context
->long_message
);
6546 if (!old_box
|| !new_box
) return IE_INVAL
;
6550 /* Build UpdateDataBoxDescr request */
6551 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6553 isds_log_message(context
,
6554 _("Could build UpdateDataBoxDescr request"));
6557 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6559 isds_log_message(context
, _("Could not create ISDS name space"));
6560 xmlFreeNode(request
);
6563 xmlSetNs(request
, isds_ns
);
6565 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6566 err
= insert_DbOwnerInfo(context
, old_box
, 0, node
);
6567 if (err
) goto leave
;
6569 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6570 err
= insert_DbOwnerInfo(context
, new_box
, 0, node
);
6571 if (err
) goto leave
;
6573 err
= insert_GExtApproval(context
, approval
, request
);
6574 if (err
) goto leave
;
6577 /* Send it to server and process response */
6578 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6579 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6582 xmlFreeNode(request
);
6583 #else /* not HAVE_LIBCURL */
6592 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6594 * @context is session context
6595 * @service is SOAP service
6596 * @service_name is name of request in @service
6597 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6598 * @box_id is box ID of interest
6599 * @approval is optional external approval of box manipulation
6600 * @response is server SOAP body response as XML document
6601 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6602 * NULL, if you don't care.
6603 * @return error coded from lower layer, context message will be set up
6605 static isds_error
build_send_dbid_request_check_response(
6606 struct isds_ctx
*context
, const isds_service service
,
6607 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6608 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6609 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6611 isds_error err
= IE_SUCCESS
;
6612 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6613 xmlNodePtr request
= NULL
, node
;
6614 xmlNsPtr isds_ns
= NULL
;
6616 if (!context
) return IE_INVALID_CONTEXT
;
6617 if (!service_name
|| !box_id
) return IE_INVAL
;
6618 if (!response
) return IE_INVAL
;
6620 /* Free output argument */
6621 xmlFreeDoc(*response
); *response
= NULL
;
6623 /* Prepare strings */
6624 service_name_locale
= _isds_utf82locale((char*)service_name
);
6625 if (!service_name_locale
) {
6629 box_id_locale
= _isds_utf82locale((char*)box_id
);
6630 if (!box_id_locale
) {
6636 request
= xmlNewNode(NULL
, service_name
);
6638 isds_printf_message(context
,
6639 _("Could not build %s request for %s box"), service_name_locale
,
6644 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6646 isds_log_message(context
, _("Could not create ISDS name space"));
6650 xmlSetNs(request
, isds_ns
);
6652 /* Add XSD:tIdDbInput children */
6653 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6654 INSERT_STRING(request
, box_id_element
, box_id
);
6655 err
= insert_GExtApproval(context
, approval
, request
);
6656 if (err
) goto leave
;
6658 /* Send request and check response*/
6659 err
= send_destroy_request_check_response(context
,
6660 service
, service_name
, &request
, response
, refnumber
, NULL
);
6663 free(service_name_locale
);
6664 free(box_id_locale
);
6665 xmlFreeNode(request
);
6668 #endif /* HAVE_LIBCURL */
6671 /* Get data about all users assigned to given box.
6672 * @context is session context
6674 * @users is automatically reallocated list of struct isds_DbUserInfo */
6675 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6676 struct isds_list
**users
) {
6677 isds_error err
= IE_SUCCESS
;
6679 xmlDocPtr response
= NULL
;
6680 xmlXPathContextPtr xpath_ctx
= NULL
;
6681 xmlXPathObjectPtr result
= NULL
;
6683 struct isds_list
*item
, *prev_item
= NULL
;
6686 if (!context
) return IE_INVALID_CONTEXT
;
6687 zfree(context
->long_message
);
6688 if (!users
|| !box_id
) return IE_INVAL
;
6689 isds_list_free(users
);
6693 /* Do request and check for success */
6694 err
= build_send_dbid_request_check_response(context
,
6695 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6696 BAD_CAST box_id
, NULL
, &response
, NULL
);
6697 if (err
) goto leave
;
6701 /* Prepare structure */
6702 xpath_ctx
= xmlXPathNewContext(response
);
6707 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6712 /* Set context node */
6713 result
= xmlXPathEvalExpression(BAD_CAST
6714 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6720 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6721 /* Iterate over all users */
6722 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6724 /* Prepare structure */
6725 item
= calloc(1, sizeof(*item
));
6730 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6731 if (i
== 0) *users
= item
;
6732 else prev_item
->next
= item
;
6736 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6737 err
= extract_DbUserInfo(context
,
6738 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6739 if (err
) goto leave
;
6745 isds_list_free(users
);
6748 xmlXPathFreeObject(result
);
6749 xmlXPathFreeContext(xpath_ctx
);
6750 xmlFreeDoc(response
);
6753 isds_log(ILF_ISDS
, ILL_DEBUG
,
6754 _("GetDataBoxUsers request processed by server "
6755 "successfully.\n"));
6756 #else /* not HAVE_LIBCURL */
6764 /* Update data about user assigned to given box.
6765 * @context is session context
6766 * @box is box identification. aifoIsds, address->adCode,
6767 * address->adDistrict members are ignored.
6768 * @old_user identifies user to update, aifo_ticket member is ignored
6769 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6770 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6771 * NULL, if you don't care.*/
6772 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6773 const struct isds_DbOwnerInfo
*box
,
6774 const struct isds_DbUserInfo
*old_user
,
6775 const struct isds_DbUserInfo
*new_user
,
6777 isds_error err
= IE_SUCCESS
;
6779 xmlNsPtr isds_ns
= NULL
;
6780 xmlNodePtr request
= NULL
;
6785 if (!context
) return IE_INVALID_CONTEXT
;
6786 zfree(context
->long_message
);
6787 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6791 /* Build UpdateDataBoxUser request */
6792 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6794 isds_log_message(context
,
6795 _("Could build UpdateDataBoxUser request"));
6798 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6800 isds_log_message(context
, _("Could not create ISDS name space"));
6801 xmlFreeNode(request
);
6804 xmlSetNs(request
, isds_ns
);
6806 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6807 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6808 if (err
) goto leave
;
6810 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6811 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
6812 if (err
) goto leave
;
6814 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6815 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
6816 if (err
) goto leave
;
6818 /* Send it to server and process response */
6819 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6820 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6823 xmlFreeNode(request
);
6824 #else /* not HAVE_LIBCURL */
6832 /* Undocumented function.
6833 * @context is session context
6834 * @box_id is UTF-8 encoded box identifier
6835 * @token is UTF-8 encoded temporary password
6836 * @user_id outputs UTF-8 encoded reallocated user identifier
6837 * @password outpus UTF-8 encoded reallocated user password
6838 * Output arguments will be nulled in case of error */
6839 isds_error
isds_activate(struct isds_ctx
*context
,
6840 const char *box_id
, const char *token
,
6841 char **user_id
, char **password
) {
6842 isds_error err
= IE_SUCCESS
;
6844 xmlNsPtr isds_ns
= NULL
;
6845 xmlNodePtr request
= NULL
, node
;
6846 xmlDocPtr response
= NULL
;
6847 xmlXPathContextPtr xpath_ctx
= NULL
;
6848 xmlXPathObjectPtr result
= NULL
;
6852 if (!context
) return IE_INVALID_CONTEXT
;
6853 zfree(context
->long_message
);
6855 if (user_id
) zfree(*user_id
);
6856 if (password
) zfree(*password
);
6858 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6862 /* Build Activate request */
6863 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6865 isds_log_message(context
, _("Could build Activate request"));
6868 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6870 isds_log_message(context
, _("Could not create ISDS name space"));
6871 xmlFreeNode(request
);
6874 xmlSetNs(request
, isds_ns
);
6876 INSERT_STRING(request
, "dbAccessDataId", token
);
6877 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6878 INSERT_STRING(request
, "dbID", box_id
);
6881 /* Send request and check response*/
6882 err
= send_destroy_request_check_response(context
,
6883 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6884 &response
, NULL
, NULL
);
6885 if (err
) goto leave
;
6889 xpath_ctx
= xmlXPathNewContext(response
);
6894 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6898 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6904 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6905 isds_log_message(context
, _("Missing ActivateResponse element"));
6909 if (result
->nodesetval
->nodeNr
> 1) {
6910 isds_log_message(context
, _("Multiple ActivateResponse element"));
6914 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6915 xmlXPathFreeObject(result
); result
= NULL
;
6917 EXTRACT_STRING("isds:userId", *user_id
);
6919 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6920 "but did not return `userId' element.\n"));
6922 EXTRACT_STRING("isds:password", *password
);
6924 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6925 "but did not return `password' element.\n"));
6928 xmlXPathFreeObject(result
);
6929 xmlXPathFreeContext(xpath_ctx
);
6930 xmlFreeDoc(response
);
6931 xmlFreeNode(request
);
6934 isds_log(ILF_ISDS
, ILL_DEBUG
,
6935 _("Activate request processed by server successfully.\n"));
6936 #else /* not HAVE_LIBCURL */
6944 /* Reset credentials of user assigned to given box.
6945 * @context is session context
6946 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6947 * members are ignored.
6948 * @user identifies user to reset password, aifo_ticket member is ignored
6949 * @fee_paid is true if fee has been paid, false otherwise
6950 * @approval is optional external approval of box manipulation
6951 * @credentials_delivery is NULL if new password should be delivered off-line
6952 * to the user. It is valid pointer if user should obtain new password on-line
6953 * on dedicated web server. Then input @credentials_delivery.email value is
6954 * user's e-mail address user must provide to dedicated web server together
6955 * with @credentials_delivery.token. The output reallocated token user needs
6956 * to use to authorize on the web server to view his new password. Output
6957 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6958 * ISDS changed up on this call. (No reason why server could change the name
6960 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6961 * NULL, if you don't care.*/
6962 isds_error
isds_reset_password(struct isds_ctx
*context
,
6963 const struct isds_DbOwnerInfo
*box
,
6964 const struct isds_DbUserInfo
*user
,
6965 const _Bool fee_paid
, const struct isds_approval
*approval
,
6966 struct isds_credentials_delivery
*credentials_delivery
,
6968 isds_error err
= IE_SUCCESS
;
6970 xmlNsPtr isds_ns
= NULL
;
6971 xmlNodePtr request
= NULL
, node
;
6972 xmlDocPtr response
= NULL
;
6976 if (!context
) return IE_INVALID_CONTEXT
;
6977 zfree(context
->long_message
);
6979 if (credentials_delivery
) {
6980 zfree(credentials_delivery
->token
);
6981 zfree(credentials_delivery
->new_user_name
);
6983 if (!box
|| !user
) return IE_INVAL
;
6987 /* Build NewAccessData request */
6988 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6990 isds_log_message(context
,
6991 _("Could build NewAccessData request"));
6994 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6996 isds_log_message(context
, _("Could not create ISDS name space"));
6997 xmlFreeNode(request
);
7000 xmlSetNs(request
, isds_ns
);
7002 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7003 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7004 if (err
) goto leave
;
7006 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7007 err
= insert_DbUserInfo(context
, user
, 0, node
);
7008 if (err
) goto leave
;
7010 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
7012 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7013 if (err
) goto leave
;
7015 err
= insert_GExtApproval(context
, approval
, request
);
7016 if (err
) goto leave
;
7018 /* Send request and check response*/
7019 err
= send_destroy_request_check_response(context
,
7020 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
7021 &response
, (xmlChar
**) refnumber
, NULL
);
7022 if (err
) goto leave
;
7025 /* Extract optional token */
7026 err
= extract_credentials_delivery(context
, credentials_delivery
,
7027 response
, "NewAccessData");
7030 xmlFreeDoc(response
);
7031 xmlFreeNode(request
);
7034 isds_log(ILF_ISDS
, ILL_DEBUG
,
7035 _("NewAccessData request processed by server "
7036 "successfully.\n"));
7037 #else /* not HAVE_LIBCURL */
7045 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7046 * code, destroy response and log success.
7047 * @context is ISDS session context.
7048 * @service_name is name of SERVICE_DB_MANIPULATION service
7049 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7050 * members are ignored.
7051 * @user identifies user to remove
7052 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7053 * @credentials_delivery is NULL if new user's password should be delivered
7054 * off-line to the user. It is valid pointer if user should obtain new
7055 * password on-line on dedicated web server. Then input
7056 * @credentials_delivery.email value is user's e-mail address user must
7057 * provide to dedicated web server together with @credentials_delivery.token.
7058 * The output reallocated token user needs to use to authorize on the web
7059 * server to view his new password. Output reallocated
7060 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7061 * assingned or changed up on this call.
7062 * @approval is optional external approval of box manipulation
7063 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7064 * NULL, if you don't care. */
7065 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
7066 struct isds_ctx
*context
, const xmlChar
*service_name
,
7067 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7068 _Bool honor_aifo_ticket
,
7069 struct isds_credentials_delivery
*credentials_delivery
,
7070 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7071 isds_error err
= IE_SUCCESS
;
7073 xmlNsPtr isds_ns
= NULL
;
7074 xmlNodePtr request
= NULL
, node
;
7075 xmlDocPtr response
= NULL
;
7079 if (!context
) return IE_INVALID_CONTEXT
;
7080 zfree(context
->long_message
);
7081 if (credentials_delivery
) {
7082 zfree(credentials_delivery
->token
);
7083 zfree(credentials_delivery
->new_user_name
);
7085 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7090 /* Build NewAccessData or similar request */
7091 request
= xmlNewNode(NULL
, service_name
);
7093 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7094 isds_printf_message(context
, _("Could not build %s request"),
7095 service_name_locale
);
7096 free(service_name_locale
);
7099 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7101 isds_log_message(context
, _("Could not create ISDS name space"));
7102 xmlFreeNode(request
);
7105 xmlSetNs(request
, isds_ns
);
7107 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7108 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7109 if (err
) goto leave
;
7111 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7112 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7113 if (err
) goto leave
;
7115 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7116 if (err
) goto leave
;
7118 err
= insert_GExtApproval(context
, approval
, request
);
7119 if (err
) goto leave
;
7122 /* Send request and check response*/
7123 err
= send_destroy_request_check_response(context
,
7124 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7127 xmlFreeNode(request
);
7130 /* Pick up credentials_delivery if requested */
7131 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7132 (char *)service_name
);
7135 xmlFreeDoc(response
);
7136 if (request
) xmlFreeNode(request
);
7139 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7140 isds_log(ILF_ISDS
, ILL_DEBUG
,
7141 _("%s request processed by server successfully.\n"),
7142 service_name_locale
);
7143 free(service_name_locale
);
7145 #else /* not HAVE_LIBCURL */
7153 /* Assign new user to given box.
7154 * @context is session context
7155 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7156 * members are ignored.
7157 * @user defines new user to add
7158 * @credentials_delivery is NULL if new user's password should be delivered
7159 * off-line to the user. It is valid pointer if user should obtain new
7160 * password on-line on dedicated web server. Then input
7161 * @credentials_delivery.email value is user's e-mail address user must
7162 * provide to dedicated web server together with @credentials_delivery.token.
7163 * The output reallocated token user needs to use to authorize on the web
7164 * server to view his new password. Output reallocated
7165 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7166 * assingned up on this call.
7167 * @approval is optional external approval of box manipulation
7168 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7169 * NULL, if you don't care.*/
7170 isds_error
isds_add_user(struct isds_ctx
*context
,
7171 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7172 struct isds_credentials_delivery
*credentials_delivery
,
7173 const struct isds_approval
*approval
, char **refnumber
) {
7174 return build_send_manipulationboxuser_request_check_drop_response(context
,
7175 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7176 approval
, (xmlChar
**) refnumber
);
7180 /* Remove user assigned to given box.
7181 * @context is session context
7182 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7183 * members are ignored.
7184 * @user identifies user to remove, aifo_ticket member is ignored
7185 * @approval is optional external approval of box manipulation
7186 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7187 * NULL, if you don't care.*/
7188 isds_error
isds_delete_user(struct isds_ctx
*context
,
7189 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7190 const struct isds_approval
*approval
, char **refnumber
) {
7191 return build_send_manipulationboxuser_request_check_drop_response(context
,
7192 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7193 (xmlChar
**) refnumber
);
7197 /* Get list of boxes in ZIP archive.
7198 * @context is session context
7199 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7200 * System recognizes following values currently: ALL (all boxes), UPG
7201 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7202 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7203 * commercial messages). This argument is a string because specification
7204 * states new values can appear in the future. Not all list types are
7205 * available to all users.
7206 * @buffer is automatically reallocated memory to store the list of boxes. The
7207 * list is zipped CSV file.
7208 * @buffer_length is size of @buffer data in bytes.
7209 * In case of error @buffer will be freed and @buffer_length will be
7211 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7212 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7213 isds_error err
= IE_SUCCESS
;
7215 xmlNsPtr isds_ns
= NULL
;
7216 xmlNodePtr request
= NULL
, node
;
7217 xmlDocPtr response
= NULL
;
7218 xmlXPathContextPtr xpath_ctx
= NULL
;
7219 xmlXPathObjectPtr result
= NULL
;
7220 char *string
= NULL
;
7224 if (!context
) return IE_INVALID_CONTEXT
;
7225 zfree(context
->long_message
);
7226 if (buffer
) zfree(*buffer
);
7227 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7231 /* Check if connection is established
7232 * TODO: This check should be done downstairs. */
7233 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7236 /* Build AuthenticateMessage request */
7237 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7239 isds_log_message(context
,
7240 _("Could not build GetDataBoxList request"));
7243 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7245 isds_log_message(context
, _("Could not create ISDS name space"));
7246 xmlFreeNode(request
);
7249 xmlSetNs(request
, isds_ns
);
7250 INSERT_STRING(request
, "dblType", list_identifier
);
7252 /* Send request to server and process response */
7253 err
= send_destroy_request_check_response(context
,
7254 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7255 &response
, NULL
, NULL
);
7256 if (err
) goto leave
;
7259 /* Extract Base-64 encoded ZIP file */
7260 xpath_ctx
= xmlXPathNewContext(response
);
7265 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7269 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7271 /* Decode non-empty archive */
7272 if (string
&& string
[0] != '\0') {
7273 *buffer_length
= _isds_b64decode(string
, buffer
);
7274 if (*buffer_length
== (size_t) -1) {
7275 isds_printf_message(context
,
7276 _("Error while Base64-decoding box list archive"));
7285 xmlXPathFreeObject(result
);
7286 xmlXPathFreeContext(xpath_ctx
);
7287 xmlFreeDoc(response
);
7288 xmlFreeNode(request
);
7291 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7292 "processed by server successfully.\n"));
7294 #else /* not HAVE_LIBCURL */
7302 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7303 * send it, check for error code, extract list of results, destroy response
7305 * @context is ISDS session context.
7306 * @service_name is name of SERVICE_DB_SEARCH service
7307 * @pfo_service is false if tDbOwnerInfo request should be built from
7308 * @criteria and corresponding result extracted. It is true if
7309 * tDbPersonalOwnerInfoRequest request should be built. The request and
7310 * response differ subset of significant isds_DbOwnerInfo structure members.
7311 * @criteria is filter. You should fill in at least some members.
7312 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7313 * members will be ignored. If @pfo_service is true, dbType, ic,
7314 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7315 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7317 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7318 * possibly empty. Input NULL or valid old structure. The same memebers as
7319 * in described for @criteria argument will be NULL according to @pfo_service
7322 * IE_SUCCESS if search succeeded, @boxes contains useful data
7323 * IE_NOEXIST if no such box exists, @boxes will be NULL
7324 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7325 * contains still valid data
7326 * other code if something bad happens. @boxes will be NULL. */
7327 static isds_error
build_send_findbox_request_check_parse_drop_response(
7328 struct isds_ctx
*context
, const xmlChar
*service_name
,
7329 _Bool pfo_service
, const struct isds_DbOwnerInfo
*criteria
,
7330 struct isds_list
**boxes
) {
7331 isds_error err
= IE_SUCCESS
;
7333 char *service_name_locale
= NULL
;
7334 _Bool truncated
= 0;
7335 xmlNsPtr isds_ns
= NULL
;
7336 xmlNodePtr request
= NULL
;
7337 xmlDocPtr response
= NULL
;
7338 xmlChar
*code
= NULL
, *message
= NULL
;
7339 xmlNodePtr db_owner_info
;
7340 xmlXPathContextPtr xpath_ctx
= NULL
;
7341 xmlXPathObjectPtr result
= NULL
;
7342 xmlChar
*string
= NULL
;
7346 if (!context
) return IE_INVALID_CONTEXT
;
7347 zfree(context
->long_message
);
7348 if (!boxes
) return IE_INVAL
;
7349 isds_list_free(boxes
);
7356 /* Check if connection is established
7357 * TODO: This check should be done downstairs. */
7358 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7359 service_name_locale
= _isds_utf82locale((char *) service_name
);
7362 request
= xmlNewNode(NULL
, service_name
);
7364 isds_printf_message(context
, _("Could not build %s request"),
7365 service_name_locale
);
7366 free(service_name_locale
);
7369 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7371 isds_log_message(context
, _("Could not create ISDS name space"));
7372 free(service_name_locale
);
7373 xmlFreeNode(request
);
7376 xmlSetNs(request
, isds_ns
);
7377 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7378 if (!db_owner_info
) {
7379 isds_printf_message(context
,
7380 _("Could not add dbOwnerInfo child to %s element"),
7381 service_name_locale
);
7382 free(service_name_locale
);
7383 xmlFreeNode(request
);
7387 err
= insert_DbOwnerInfo(context
, criteria
, pfo_service
, db_owner_info
);
7388 if (err
) goto leave
;
7392 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
7393 service_name_locale
);
7394 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7396 /* Destroy request */
7397 xmlFreeNode(request
); request
= NULL
;
7400 isds_log(ILF_ISDS
, ILL_DEBUG
,
7401 _("Processing ISDS response on %s request failed\n"),
7402 service_name_locale
);
7406 /* Check for response status */
7407 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7408 &code
, &message
, NULL
);
7410 isds_log(ILF_ISDS
, ILL_DEBUG
,
7411 _("ISDS response on %s request is missing status\n"),
7412 service_name_locale
);
7416 /* Request processed, but nothing found */
7417 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7418 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7419 char *code_locale
= _isds_utf82locale((char*)code
);
7420 char *message_locale
= _isds_utf82locale((char*)message
);
7421 isds_log(ILF_ISDS
, ILL_DEBUG
,
7422 _("Server did not find any box on %s request "
7423 "(code=%s, message=%s)\n"), service_name_locale
,
7424 code_locale
, message_locale
);
7425 isds_log_message(context
, message_locale
);
7427 free(message_locale
);
7432 /* Warning, not an error */
7433 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7434 char *code_locale
= _isds_utf82locale((char*)code
);
7435 char *message_locale
= _isds_utf82locale((char*)message
);
7436 isds_log(ILF_ISDS
, ILL_DEBUG
,
7437 _("Server truncated response on %s request "
7438 "(code=%s, message=%s)\n"), service_name_locale
,
7439 code_locale
, message_locale
);
7440 isds_log_message(context
, message_locale
);
7442 free(message_locale
);
7447 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7448 char *code_locale
= _isds_utf82locale((char*)code
);
7449 char *message_locale
= _isds_utf82locale((char*)message
);
7450 isds_log(ILF_ISDS
, ILL_DEBUG
,
7451 _("Server refused %s request (code=%s, message=%s)\n"),
7452 service_name_locale
, code_locale
, message_locale
);
7453 isds_log_message(context
, message_locale
);
7455 free(message_locale
);
7460 xpath_ctx
= xmlXPathNewContext(response
);
7465 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7470 /* Extract boxes if they present */
7471 if (-1 == isds_asprintf((char **)&string
,
7472 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7477 result
= xmlXPathEvalExpression(string
, xpath_ctx
);
7483 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7484 struct isds_list
*item
, *prev_item
= NULL
;
7485 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7486 item
= calloc(1, sizeof(*item
));
7492 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7493 if (i
== 0) *boxes
= item
;
7494 else prev_item
->next
= item
;
7497 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7498 err
= extract_DbOwnerInfo(context
,
7499 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7500 if (err
) goto leave
;
7506 isds_list_free(boxes
);
7508 if (truncated
) err
= IE_2BIG
;
7512 xmlFreeNode(request
);
7513 xmlXPathFreeObject(result
);
7514 xmlXPathFreeContext(xpath_ctx
);
7518 xmlFreeDoc(response
);
7521 isds_log(ILF_ISDS
, ILL_DEBUG
,
7522 _("%s request processed by server successfully.\n"),
7523 service_name_locale
);
7524 free(service_name_locale
);
7525 #else /* not HAVE_LIBCURL */
7533 /* Find boxes suiting given criteria.
7534 * @criteria is filter. You should fill in at least some members. aifoIsds,
7535 * address->adCode, address->adDistrict members are ignored.
7536 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7537 * possibly empty. Input NULL or valid old structure.
7539 * IE_SUCCESS if search succeeded, @boxes contains useful data
7540 * IE_NOEXIST if no such box exists, @boxes will be NULL
7541 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7542 * contains still valid data
7543 * other code if something bad happens. @boxes will be NULL. */
7544 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7545 const struct isds_DbOwnerInfo
*criteria
,
7546 struct isds_list
**boxes
) {
7547 return build_send_findbox_request_check_parse_drop_response(context
,
7548 BAD_CAST
"FindDataBox", 0, criteria
, boxes
);
7552 /* Find accessible FO-type boxes suiting given criteria.
7553 * @criteria is filter. You should fill in at least some members. dbType, ic,
7554 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7555 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7556 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7557 * possibly empty. Input NULL or valid old structure.
7559 * IE_SUCCESS if search succeeded, @boxes contains useful data
7560 * IE_NOEXIST if no such box exists, @boxes will be NULL
7561 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7562 * contains still valid data
7563 * other code if something bad happens. @boxes will be NULL. */
7564 isds_error
isds_FindPersonalDataBox(struct isds_ctx
*context
,
7565 const struct isds_DbOwnerInfo
*criteria
,
7566 struct isds_list
**boxes
) {
7567 return build_send_findbox_request_check_parse_drop_response(context
,
7568 BAD_CAST
"FindPersonalDataBox", 1, criteria
, boxes
);
7573 /* Convert a string with match markers into a plain string with list of
7574 * pointers to the matches
7575 * @string is an UTF-8 encoded non-constant string with match markers
7576 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7577 * The markers will be removed from the string.
7578 * @starts is a reallocated list of static pointers into the @string pointing
7579 * to places where match start markers occured.
7580 * @ends is a reallocated list of static pointers into the @string pointing
7581 * to places where match end markers occured.
7582 * @return IE_SUCCESS in case of no failure. */
7583 static isds_error
interpret_matches(xmlChar
*string
,
7584 struct isds_list
**starts
, struct isds_list
**ends
) {
7585 isds_error err
= IE_SUCCESS
;
7586 xmlChar
*pointer
, *destination
, *source
;
7587 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7589 isds_list_free(starts
);
7590 isds_list_free(ends
);
7591 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7592 if (NULL
== string
) return IE_SUCCESS
;
7594 for (pointer
= string
; *pointer
!= '\0';) {
7595 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7596 /* Remove the start marker */
7597 for (source
= pointer
+ 14, destination
= pointer
;
7598 *source
!= '\0'; source
++, destination
++) {
7599 *destination
= *source
;
7601 *destination
= '\0';
7602 /* Append the pointer into the list */
7603 item
= calloc(1, sizeof(*item
));
7608 item
->destructor
= (void (*)(void **))NULL
;
7609 item
->data
= pointer
;
7610 if (NULL
== prev_start
) *starts
= item
;
7611 else prev_start
->next
= item
;
7613 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7614 /* Remove the end marker */
7615 for (source
= pointer
+ 12, destination
= pointer
;
7616 *source
!= '\0'; source
++, destination
++) {
7617 *destination
= *source
;
7619 *destination
= '\0';
7620 /* Append the pointer into the list */
7621 item
= calloc(1, sizeof(*item
));
7626 item
->destructor
= (void (*)(void **))NULL
;
7627 item
->data
= pointer
;
7628 if (NULL
== prev_end
) *ends
= item
;
7629 else prev_end
->next
= item
;
7638 isds_list_free(starts
);
7639 isds_list_free(ends
);
7645 /* Convert isds:dbResult XML tree into structure
7646 * @context is ISDS context.
7647 * @fulltext_result is automatically reallocated found box structure.
7648 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7649 * @collect_matches is true to interpret match markers.
7650 * In case of error @result will be freed. */
7651 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7652 struct isds_fulltext_result
**fulltext_result
,
7653 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7654 isds_error err
= IE_SUCCESS
;
7655 xmlXPathObjectPtr result
= NULL
;
7656 char *string
= NULL
;
7658 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7659 if (NULL
== fulltext_result
) return IE_INVAL
;
7660 isds_fulltext_result_free(fulltext_result
);
7661 if (!xpath_ctx
) return IE_INVAL
;
7664 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7665 if (NULL
== *fulltext_result
) {
7671 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7673 EXTRACT_STRING("isds:dbType", string
);
7674 if (NULL
== string
) {
7676 isds_log_message(context
, _("Empty isds:dbType element"));
7679 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7681 if (err
== IE_ENUM
) {
7683 char *string_locale
= _isds_utf82locale(string
);
7684 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7686 free(string_locale
);
7692 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7693 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7695 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7696 if (err
) goto leave
;
7698 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7699 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7700 (*fulltext_result
)->dbEffectiveOVM
);
7702 EXTRACT_STRING("isds:dbSendOptions", string
);
7703 if (NULL
== string
) {
7705 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7708 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7709 (*fulltext_result
)->active
= 1;
7710 (*fulltext_result
)->public_sending
= 1;
7711 (*fulltext_result
)->commercial_sending
= 0;
7712 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7713 (*fulltext_result
)->active
= 1;
7714 (*fulltext_result
)->public_sending
= 1;
7715 (*fulltext_result
)->commercial_sending
= 1;
7716 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7717 (*fulltext_result
)->active
= 1;
7718 (*fulltext_result
)->public_sending
= 0;
7719 (*fulltext_result
)->commercial_sending
= 1;
7720 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7721 (*fulltext_result
)->active
= 1;
7722 (*fulltext_result
)->public_sending
= 0;
7723 (*fulltext_result
)->commercial_sending
= 0;
7724 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7725 (*fulltext_result
)->active
= 0;
7726 (*fulltext_result
)->public_sending
= 0;
7727 (*fulltext_result
)->commercial_sending
= 0;
7730 char *string_locale
= _isds_utf82locale(string
);
7731 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7733 free(string_locale
);
7738 /* Interpret match marks */
7739 if (collect_matches
) {
7740 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7741 &((*fulltext_result
)->name_match_start
),
7742 &((*fulltext_result
)->name_match_end
));
7743 if (err
) goto leave
;
7744 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7745 &((*fulltext_result
)->address_match_start
),
7746 &((*fulltext_result
)->address_match_end
));
7747 if (err
) goto leave
;
7751 if (err
) isds_fulltext_result_free(fulltext_result
);
7753 xmlXPathFreeObject(result
);
7756 #endif /* HAVE_LIBCURL */
7759 /* Find boxes matching a given full-text criteria.
7760 * @context is a session context
7761 * @query is a non-empty string which consists of words to search
7762 * @target selects box attributes to search for @query words. Pass NULL if you
7764 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7765 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7766 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7767 * which is DBTYPE_SYSTEM.
7768 * @page_size defines count of boxes to constitute a response page. It counts
7769 * from zero. Pass NULL to let server to use a default value (50 now).
7770 * @page_number defines ordinar number of the response page to return. It
7771 * counts from zero. Pass NULL to let server to use a default value (0 now).
7772 * @track_matches points to true for marking @query words found in the box
7773 * attributes. It points to false for not marking. Pass NULL to let the server
7774 * to use default value (false now).
7775 * @total_matching_boxes outputs reallocated number of all boxes matching the
7776 * query. Will be pointer to NULL if server did not provide the value.
7777 * Pass NULL if you don't care.
7778 * @current_page_beginning outputs reallocated ordinar number of the first box
7779 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7780 * server did not provide the value. Pass NULL if you don't care.
7781 * @current_page_size outputs reallocated count of boxes in the this @boxes
7782 * page. It will be pointer to NULL if the server did not provide the value.
7783 * Pass NULL if you don't care.
7784 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7785 * is the last one, false if more boxes match, NULL if the server did not
7786 * provude the value. Pass NULL if you don't care.
7787 * @boxes outputs reallocated list of isds_fulltext_result structures,
7790 * IE_SUCCESS if search succeeded
7791 * IE_2BIG if @page_size is too large
7792 * other code if something bad happens; output arguments will be NULL. */
7793 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7795 const isds_fulltext_target
*target
,
7796 const isds_DbType
*box_type
,
7797 const unsigned long int *page_size
,
7798 const unsigned long int *page_number
,
7799 const _Bool
*track_matches
,
7800 unsigned long int **total_matching_boxes
,
7801 unsigned long int **current_page_beginning
,
7802 unsigned long int **current_page_size
,
7804 struct isds_list
**boxes
) {
7805 isds_error err
= IE_SUCCESS
;
7807 xmlNsPtr isds_ns
= NULL
;
7808 xmlNodePtr request
= NULL
;
7809 xmlDocPtr response
= NULL
;
7811 xmlXPathContextPtr xpath_ctx
= NULL
;
7812 xmlXPathObjectPtr result
= NULL
;
7813 const xmlChar
*static_string
= NULL
;
7814 xmlChar
*string
= NULL
;
7816 const xmlChar
*codes
[] = {
7826 const char *meanings
[] = {
7827 N_("You are not allowed to perform the search"),
7828 N_("The query string is empty"),
7829 N_("Searched box ID is malformed"),
7830 N_("Searched organization ID is malformed"),
7831 N_("Invalid input"),
7832 N_("Requested page size is too large"),
7833 N_("Search engine internal error")
7835 const isds_error errors
[] = {
7844 struct code_map_isds_error map
= {
7846 .meanings
= meanings
,
7852 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7853 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7854 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7855 if (NULL
!= last_page
) zfree(*last_page
);
7856 isds_list_free(boxes
);
7858 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7859 zfree(context
->long_message
);
7861 if (NULL
== boxes
) return IE_INVAL
;
7863 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7864 isds_log_message(context
, _("Query string must be non-empty"));
7869 /* Check if connection is established
7870 * TODO: This check should be done downstairs. */
7871 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7873 /* Build FindDataBox request */
7874 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7875 if (NULL
== request
) {
7876 isds_log_message(context
,
7877 _("Could not build ISDSSearch2 request"));
7880 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7881 if(NULL
== isds_ns
) {
7882 isds_log_message(context
, _("Could not create ISDS name space"));
7883 xmlFreeNode(request
);
7886 xmlSetNs(request
, isds_ns
);
7888 INSERT_STRING(request
, "searchText", query
);
7890 if (NULL
!= target
) {
7891 static_string
= isds_fulltext_target2string(*(target
));
7892 if (NULL
== static_string
) {
7893 isds_printf_message(context
, _("Invalid target value: %d"),
7899 INSERT_STRING(request
, "searchType", static_string
);
7900 static_string
= NULL
;
7902 if (NULL
!= box_type
) {
7903 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7904 if (DBTYPE_SYSTEM
== *box_type
) {
7905 static_string
= BAD_CAST
"ALL";
7906 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
7907 static_string
= BAD_CAST
"OVM_MAIN";
7909 static_string
= isds_DbType2string(*(box_type
));
7910 if (NULL
== static_string
) {
7911 isds_printf_message(context
, _("Invalid box type value: %d"),
7918 INSERT_STRING(request
, "searchScope", static_string
);
7919 static_string
= NULL
;
7921 INSERT_ULONGINT(request
, "page", page_number
, string
);
7922 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7923 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7925 /* Send request and check response */
7926 err
= send_destroy_request_check_response(context
,
7927 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7928 &request
, &response
, NULL
, &map
);
7929 if (err
) goto leave
;
7931 /* Parse response */
7932 xpath_ctx
= xmlXPathNewContext(response
);
7933 if (NULL
== xpath_ctx
) {
7937 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7941 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7947 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7948 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7952 if (result
->nodesetval
->nodeNr
> 1) {
7953 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7957 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7958 xmlXPathFreeObject(result
); result
= NULL
;
7961 /* Extract counters */
7962 if (NULL
!= total_matching_boxes
) {
7963 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7965 if (NULL
!= current_page_size
) {
7966 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7968 if (NULL
!= current_page_beginning
) {
7969 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7971 if (NULL
!= last_page
) {
7972 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7974 xmlXPathFreeObject(result
); result
= NULL
;
7976 /* Extract boxes if they present */
7977 result
= xmlXPathEvalExpression(BAD_CAST
7978 "isds:dbResults/isds:dbResult", xpath_ctx
);
7979 if (NULL
== result
) {
7983 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7984 struct isds_list
*item
, *prev_item
= NULL
;
7985 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7986 item
= calloc(1, sizeof(*item
));
7992 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7993 if (i
== 0) *boxes
= item
;
7994 else prev_item
->next
= item
;
7997 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7998 err
= extract_dbResult(context
,
7999 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
8000 (NULL
== track_matches
) ? 0 : *track_matches
);
8001 if (err
) goto leave
;
8007 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
8008 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
8009 if (NULL
!= current_page_size
) zfree(*current_page_size
);
8010 if (NULL
!= last_page
) zfree(*last_page
);
8011 isds_list_free(boxes
);
8015 xmlFreeNode(request
);
8016 xmlXPathFreeObject(result
);
8017 xmlXPathFreeContext(xpath_ctx
);
8018 xmlFreeDoc(response
);
8021 isds_log(ILF_ISDS
, ILL_DEBUG
,
8022 _("ISDSSearch2 request processed by server successfully.\n"));
8023 #else /* not HAVE_LIBCURL */
8031 /* Get status of a box.
8032 * @context is ISDS session context.
8033 * @box_id is UTF-8 encoded box identifier as zero terminated string
8034 * @box_status is return value of box status.
8036 * IE_SUCCESS if box has been found and its status retrieved
8037 * IE_NOEXIST if box is not known to ISDS server
8038 * or other appropriate error.
8039 * You can use isds_DbState to enumerate box status. However out of enum
8040 * range value can be returned too. This is feature because ISDS
8041 * specification leaves the set of values open.
8042 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8043 * the box has been deleted, but ISDS still lists its former existence. */
8044 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
8045 long int *box_status
) {
8046 isds_error err
= IE_SUCCESS
;
8048 xmlNsPtr isds_ns
= NULL
;
8049 xmlNodePtr request
= NULL
, db_id
;
8050 xmlDocPtr response
= NULL
;
8051 xmlXPathContextPtr xpath_ctx
= NULL
;
8052 xmlXPathObjectPtr result
= NULL
;
8053 xmlChar
*string
= NULL
;
8055 const xmlChar
*codes
[] = {
8061 const char *meanings
[] = {
8062 "The box does not exist",
8063 "Box ID is malformed",
8066 const isds_error errors
[] = {
8071 struct code_map_isds_error map
= {
8073 .meanings
= meanings
,
8078 if (!context
) return IE_INVALID_CONTEXT
;
8079 zfree(context
->long_message
);
8080 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
8083 /* Check if connection is established
8084 * TODO: This check should be done downstairs. */
8085 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8088 /* Build CheckDataBox request */
8089 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
8091 isds_log_message(context
,
8092 _("Could build CheckDataBox request"));
8095 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8097 isds_log_message(context
, _("Could not create ISDS name space"));
8098 xmlFreeNode(request
);
8101 xmlSetNs(request
, isds_ns
);
8102 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
8104 isds_log_message(context
, _("Could not add dbID child to "
8105 "CheckDataBox element"));
8106 xmlFreeNode(request
);
8111 /* Send request and check response*/
8112 err
= send_destroy_request_check_response(context
,
8113 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
8114 &request
, &response
, NULL
, &map
);
8115 if (err
) goto leave
;
8119 xpath_ctx
= xmlXPathNewContext(response
);
8124 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8128 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
8134 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8135 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
8139 if (result
->nodesetval
->nodeNr
> 1) {
8140 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
8144 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8145 xmlXPathFreeObject(result
); result
= NULL
;
8147 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
8152 xmlXPathFreeObject(result
);
8153 xmlXPathFreeContext(xpath_ctx
);
8155 xmlFreeDoc(response
);
8158 isds_log(ILF_ISDS
, ILL_DEBUG
,
8159 _("CheckDataBox request processed by server successfully.\n"));
8160 #else /* not HAVE_LIBCURL */
8169 /* Convert XSD:tdbPeriod XML tree into structure
8170 * @context is ISDS context.
8171 * @period is automatically reallocated found box status period structure.
8172 * @xpath_ctx is XPath context with current node as element of
8173 * XSD:tDbPeriod type.
8174 * In case of error @period will be freed. */
8175 static isds_error
extract_Period(struct isds_ctx
*context
,
8176 struct isds_box_state_period
**period
, xmlXPathContextPtr xpath_ctx
) {
8177 isds_error err
= IE_SUCCESS
;
8178 xmlXPathObjectPtr result
= NULL
;
8179 char *string
= NULL
;
8180 long int *dbState_ptr
;
8182 if (NULL
== context
) return IE_INVALID_CONTEXT
;
8183 if (NULL
== period
) return IE_INVAL
;
8184 isds_box_state_period_free(period
);
8185 if (!xpath_ctx
) return IE_INVAL
;
8188 *period
= calloc(1, sizeof(**period
));
8189 if (NULL
== *period
) {
8195 EXTRACT_STRING("isds:PeriodFrom", string
);
8196 if (NULL
== string
) {
8198 isds_log_message(context
,
8199 _("Could not find PeriodFrom element value"));
8202 err
= timestring2static_timeval((xmlChar
*) string
,
8203 &((*period
)->from
));
8205 char *string_locale
= _isds_utf82locale(string
);
8206 if (err
== IE_DATE
) err
= IE_ISDS
;
8207 isds_printf_message(context
,
8208 _("Could not convert PeriodFrom as ISO time: %s"),
8210 free(string_locale
);
8215 EXTRACT_STRING("isds:PeriodTo", string
);
8216 if (NULL
== string
) {
8218 isds_log_message(context
,
8219 _("Could not find PeriodTo element value"));
8222 err
= timestring2static_timeval((xmlChar
*) string
,
8225 char *string_locale
= _isds_utf82locale(string
);
8226 if (err
== IE_DATE
) err
= IE_ISDS
;
8227 isds_printf_message(context
,
8228 _("Could not convert PeriodTo as ISO time: %s"),
8230 free(string_locale
);
8235 dbState_ptr
= &((*period
)->dbState
);
8236 EXTRACT_LONGINT("isds:DbState", dbState_ptr
, 1);
8239 if (err
) isds_box_state_period_free(period
);
8241 xmlXPathFreeObject(result
);
8244 #endif /* HAVE_LIBCURL */
8247 /* Get history of box state changes.
8248 * @context is ISDS session context.
8249 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8250 * @from_time is first second of history to return in @history. Server ignores
8251 * subseconds. NULL means time of creating the box.
8252 * @to_time is last second of history to return in @history. Server ignores
8253 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8254 * interval is closed from both ends. NULL means now.
8255 * @history outputs auto-reallocated list of pointers to struct
8256 * isds_box_state_period. Each item describes a continues time when the box
8257 * was in one state. The state is 1 for accessible box. Otherwise the box
8258 * is inaccessible (priviledged users will get exact box state as enumerated
8259 * in isds_DbState, other users 0).
8261 * IE_SUCCESS if the history has been obtained correctly,
8262 * or other appropriate error. Please note that server allows to retrieve
8263 * the history only to some users. */
8264 isds_error
isds_get_box_state_history(struct isds_ctx
*context
,
8266 const struct timeval
*from_time
, const struct timeval
*to_time
,
8267 struct isds_list
**history
) {
8268 isds_error err
= IE_SUCCESS
;
8270 char *box_id_locale
= NULL
;
8271 xmlNodePtr request
= NULL
, node
;
8272 xmlNsPtr isds_ns
= NULL
;
8273 xmlChar
*string
= NULL
;
8275 xmlDocPtr response
= NULL
;
8276 xmlXPathContextPtr xpath_ctx
= NULL
;
8277 xmlXPathObjectPtr result
= NULL
;
8280 if (!context
) return IE_INVALID_CONTEXT
;
8281 zfree(context
->long_message
);
8283 /* Free output argument */
8284 isds_list_free(history
);
8287 /* Check if connection is established */
8288 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8290 /* ??? XML schema allows empty box ID, textual documentation
8291 * requries the value. */
8292 /* Allow undefined box_id */
8293 if (NULL
!= box_id
) {
8294 box_id_locale
= _isds_utf82locale((char*)box_id
);
8295 if (NULL
== box_id_locale
) {
8302 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxActivityStatus");
8303 if (NULL
== request
) {
8304 isds_printf_message(context
,
8305 _("Could not build GetDataBoxActivityStatus request "
8311 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8313 isds_log_message(context
, _("Could not create ISDS name space"));
8317 xmlSetNs(request
, isds_ns
);
8319 /* Add mandatory XSD:tIdDbInput child */
8320 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8321 /* Add times elements only when defined */
8322 /* ???: XML schema requires the values, textual documentation does not. */
8324 err
= timeval2timestring(from_time
, &string
);
8326 isds_log_message(context
,
8327 _("Could not convert `from_time' argument to ISO time "
8331 INSERT_STRING(request
, "baFrom", string
);
8335 err
= timeval2timestring(to_time
, &string
);
8337 isds_log_message(context
,
8338 _("Could not convert `to_time' argument to ISO time "
8342 INSERT_STRING(request
, "baTo", string
);
8346 /* Send request and check response*/
8347 err
= send_destroy_request_check_response(context
,
8348 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxActivityStatus",
8349 &request
, &response
, NULL
, NULL
);
8350 if (err
) goto leave
;
8354 /* Set context to the root */
8355 xpath_ctx
= xmlXPathNewContext(response
);
8360 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8364 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:GetDataBoxActivityStatusResponse",
8370 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8371 isds_log_message(context
, _("Missing GetDataBoxActivityStatusResponse element"));
8375 if (result
->nodesetval
->nodeNr
> 1) {
8376 isds_log_message(context
, _("Multiple GetDataBoxActivityStatusResponse element"));
8380 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8381 xmlXPathFreeObject(result
); result
= NULL
;
8383 /* Ignore dbID, it's the same as the input argument. */
8385 /* Extract records */
8386 if (NULL
== history
) goto leave
;
8387 result
= xmlXPathEvalExpression(BAD_CAST
"isds:Periods/isds:Period",
8393 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8394 struct isds_list
*prev_item
= NULL
;
8396 /* Iterate over all records */
8397 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8398 struct isds_list
*item
;
8400 /* Prepare structure */
8401 item
= calloc(1, sizeof(*item
));
8406 item
->destructor
= (void(*)(void**))isds_box_state_period_free
;
8407 if (i
== 0) *history
= item
;
8408 else prev_item
->next
= item
;
8412 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8413 err
= extract_Period(context
,
8414 (struct isds_box_state_period
**) (&item
->data
),
8416 if (err
) goto leave
;
8422 isds_log(ILF_ISDS
, ILL_DEBUG
,
8423 _("GetDataBoxActivityStatus request for %s box "
8424 "processed by server successfully.\n"), box_id_locale
);
8427 isds_list_free(history
);
8430 free(box_id_locale
);
8431 xmlXPathFreeObject(result
);
8432 xmlXPathFreeContext(xpath_ctx
);
8433 xmlFreeDoc(response
);
8435 #else /* not HAVE_LIBCURL */
8443 /* Get list of permissions to send commercial messages.
8444 * @context is ISDS session context.
8445 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8446 * @permissions is a reallocated list of permissions (struct
8447 * isds_commercial_permission*) to send commercial messages from @box_id. The
8448 * order of permissions is significant as the server applies the permissions
8449 * and associated pre-paid credits in the order. Empty list means no
8452 * IE_SUCCESS if the list has been obtained correctly,
8453 * or other appropriate error. */
8454 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8455 const char *box_id
, struct isds_list
**permissions
) {
8456 isds_error err
= IE_SUCCESS
;
8458 xmlDocPtr response
= NULL
;
8459 xmlXPathContextPtr xpath_ctx
= NULL
;
8460 xmlXPathObjectPtr result
= NULL
;
8463 if (!context
) return IE_INVALID_CONTEXT
;
8464 zfree(context
->long_message
);
8465 if (NULL
== permissions
) return IE_INVAL
;
8466 isds_list_free(permissions
);
8467 if (NULL
== box_id
) return IE_INVAL
;
8470 /* Check if connection is established */
8471 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8473 /* Do request and check for success */
8474 err
= build_send_dbid_request_check_response(context
,
8475 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8476 BAD_CAST box_id
, NULL
, &response
, NULL
);
8478 isds_log(ILF_ISDS
, ILL_DEBUG
,
8479 _("PDZInfo request processed by server successfully.\n"));
8483 /* Prepare structure */
8484 xpath_ctx
= xmlXPathNewContext(response
);
8489 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8494 /* Set context node */
8495 result
= xmlXPathEvalExpression(BAD_CAST
8496 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8502 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8503 struct isds_list
*prev_item
= NULL
;
8505 /* Iterate over all permission records */
8506 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8507 struct isds_list
*item
;
8509 /* Prepare structure */
8510 item
= calloc(1, sizeof(*item
));
8515 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8516 if (i
== 0) *permissions
= item
;
8517 else prev_item
->next
= item
;
8521 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8522 err
= extract_DbPDZRecord(context
,
8523 (struct isds_commercial_permission
**) (&item
->data
),
8525 if (err
) goto leave
;
8531 isds_list_free(permissions
);
8534 xmlXPathFreeObject(result
);
8535 xmlXPathFreeContext(xpath_ctx
);
8536 xmlFreeDoc(response
);
8538 #else /* not HAVE_LIBCURL */
8546 /* Get details about credit for sending pre-paid commercial messages.
8547 * @context is ISDS session context.
8548 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8549 * @from_date is first day of credit history to return in @history. Only
8550 * tm_year, tm_mon and tm_mday carry sane value.
8551 * @to_date is last day of credit history to return in @history. Only
8552 * tm_year, tm_mon and tm_mday carry sane value.
8553 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8554 * if you don't care. This and all other credit values are integers in
8555 * hundredths of Czech Crowns.
8556 * @email outputs notification e-mail address where notifications about credit
8557 * are sent. This is automatically reallocated string. Pass NULL if you don't
8558 * care. It can return NULL if no address is defined.
8559 * @history outputs auto-reallocated list of pointers to struct
8560 * isds_credit_event. Events in closed interval @from_time to @to_time are
8561 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8562 * are sorted by time.
8564 * IE_SUCCESS if the credit details have been obtained correctly,
8565 * or other appropriate error. Please note that server allows to retrieve
8566 * only limited history of events. */
8567 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8569 const struct tm
*from_date
, const struct tm
*to_date
,
8570 long int *credit
, char **email
, struct isds_list
**history
) {
8571 isds_error err
= IE_SUCCESS
;
8573 char *box_id_locale
= NULL
;
8574 xmlNodePtr request
= NULL
, node
;
8575 xmlNsPtr isds_ns
= NULL
;
8576 xmlChar
*string
= NULL
;
8578 xmlDocPtr response
= NULL
;
8579 xmlXPathContextPtr xpath_ctx
= NULL
;
8580 xmlXPathObjectPtr result
= NULL
;
8582 const xmlChar
*codes
[] = {
8590 const char *meanings
[] = {
8591 "Insufficient priviledges for the box",
8592 "The box does not exist",
8593 "Date is too long (history is not available after 15 months)",
8594 "Interval is too long (limit is 3 months)",
8597 const isds_error errors
[] = {
8604 struct code_map_isds_error map
= {
8606 .meanings
= meanings
,
8611 if (!context
) return IE_INVALID_CONTEXT
;
8612 zfree(context
->long_message
);
8614 /* Free output argument */
8615 if (NULL
!= credit
) *credit
= 0;
8616 if (NULL
!= email
) zfree(*email
);
8617 isds_list_free(history
);
8619 if (NULL
== box_id
) return IE_INVAL
;
8622 /* Check if connection is established */
8623 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8625 box_id_locale
= _isds_utf82locale((char*)box_id
);
8626 if (NULL
== box_id_locale
) {
8632 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8633 if (NULL
== request
) {
8634 isds_printf_message(context
,
8635 _("Could not build DataBoxCreditInfo request for %s box"),
8640 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8642 isds_log_message(context
, _("Could not create ISDS name space"));
8646 xmlSetNs(request
, isds_ns
);
8648 /* Add mandatory XSD:tIdDbInput child */
8649 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8650 /* Add mandatory dates elements with optional values */
8652 err
= tm2datestring(from_date
, &string
);
8654 isds_log_message(context
,
8655 _("Could not convert `from_date' argument to ISO date "
8659 INSERT_STRING(request
, "ciFromDate", string
);
8662 INSERT_STRING(request
, "ciFromDate", NULL
);
8665 err
= tm2datestring(to_date
, &string
);
8667 isds_log_message(context
,
8668 _("Could not convert `to_date' argument to ISO date "
8672 INSERT_STRING(request
, "ciTodate", string
);
8675 INSERT_STRING(request
, "ciTodate", NULL
);
8678 /* Send request and check response*/
8679 err
= send_destroy_request_check_response(context
,
8680 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8681 &request
, &response
, NULL
, &map
);
8682 if (err
) goto leave
;
8686 /* Set context to the root */
8687 xpath_ctx
= xmlXPathNewContext(response
);
8692 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8696 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8702 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8703 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8707 if (result
->nodesetval
->nodeNr
> 1) {
8708 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8712 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8713 xmlXPathFreeObject(result
); result
= NULL
;
8715 /* Extract common data */
8716 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8717 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8719 /* Extract records */
8720 if (NULL
== history
) goto leave
;
8721 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8727 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8728 struct isds_list
*prev_item
= NULL
;
8730 /* Iterate over all records */
8731 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8732 struct isds_list
*item
;
8734 /* Prepare structure */
8735 item
= calloc(1, sizeof(*item
));
8740 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8741 if (i
== 0) *history
= item
;
8742 else prev_item
->next
= item
;
8746 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8747 err
= extract_CiRecord(context
,
8748 (struct isds_credit_event
**) (&item
->data
),
8750 if (err
) goto leave
;
8756 isds_log(ILF_ISDS
, ILL_DEBUG
,
8757 _("DataBoxCreditInfo request processed by server successfully.\n"));
8760 isds_list_free(history
);
8761 if (NULL
!= email
) zfree(*email
)
8764 free(box_id_locale
);
8765 xmlXPathFreeObject(result
);
8766 xmlXPathFreeContext(xpath_ctx
);
8767 xmlFreeDoc(response
);
8769 #else /* not HAVE_LIBCURL */
8777 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8778 * code, destroy response and log success.
8779 * @context is ISDS session context.
8780 * @service_name is name of SERVICE_DB_MANIPULATION service
8781 * @box_id is UTF-8 encoded box identifier as zero terminated string
8782 * @approval is optional external approval of box manipulation
8783 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8784 * NULL, if you don't care. */
8785 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8786 struct isds_ctx
*context
, const xmlChar
*service_name
,
8787 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8788 xmlChar
**refnumber
) {
8789 isds_error err
= IE_SUCCESS
;
8791 xmlDocPtr response
= NULL
;
8794 if (!context
) return IE_INVALID_CONTEXT
;
8795 zfree(context
->long_message
);
8796 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8799 /* Check if connection is established */
8800 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8802 /* Do request and check for success */
8803 err
= build_send_dbid_request_check_response(context
,
8804 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8805 &response
, refnumber
);
8806 xmlFreeDoc(response
);
8809 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8810 isds_log(ILF_ISDS
, ILL_DEBUG
,
8811 _("%s request processed by server successfully.\n"),
8812 service_name_locale
);
8813 free(service_name_locale
);
8815 #else /* not HAVE_LIBCURL */
8823 /* Switch box into state where box can receive commercial messages (off by
8825 * @context is ISDS session context.
8826 * @box_id is UTF-8 encoded box identifier as zero terminated string
8827 * @allow is true for enable, false for disable commercial messages income
8828 * @approval is optional external approval of box manipulation
8829 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8830 * NULL, if you don't care. */
8831 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8832 const char *box_id
, const _Bool allow
,
8833 const struct isds_approval
*approval
, char **refnumber
) {
8834 return build_send_manipulationdbid_request_check_drop_response(context
,
8835 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8836 BAD_CAST
"ClearOpenAddressing",
8837 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8841 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8842 * message acceptance). This is just a box permission. Sender must apply
8843 * such role by sending each message.
8844 * @context is ISDS session context.
8845 * @box_id is UTF-8 encoded box identifier as zero terminated string
8846 * @allow is true for enable, false for disable OVM role permission
8847 * @approval is optional external approval of box manipulation
8848 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8849 * NULL, if you don't care. */
8850 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8851 const char *box_id
, const _Bool allow
,
8852 const struct isds_approval
*approval
, char **refnumber
) {
8853 return build_send_manipulationdbid_request_check_drop_response(context
,
8854 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8855 BAD_CAST
"ClearEffectiveOVM",
8856 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8860 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8861 * code, destroy response and log success.
8862 * @context is ISDS session context.
8863 * @service_name is name of SERVICE_DB_MANIPULATION service
8864 * @owner is structure describing box. aifoIsds, address->adCode,
8865 * address->adDistrict members are ignored.
8866 * @approval is optional external approval of box manipulation
8867 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8868 * NULL, if you don't care. */
8869 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8870 struct isds_ctx
*context
, const xmlChar
*service_name
,
8871 const struct isds_DbOwnerInfo
*owner
,
8872 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8873 isds_error err
= IE_SUCCESS
;
8875 char *service_name_locale
= NULL
;
8876 xmlNodePtr request
= NULL
, db_owner_info
;
8877 xmlNsPtr isds_ns
= NULL
;
8881 if (!context
) return IE_INVALID_CONTEXT
;
8882 zfree(context
->long_message
);
8883 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8886 service_name_locale
= _isds_utf82locale((char*)service_name
);
8887 if (!service_name_locale
) {
8893 request
= xmlNewNode(NULL
, service_name
);
8895 isds_printf_message(context
,
8896 _("Could not build %s request"), service_name_locale
);
8900 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8902 isds_log_message(context
, _("Could not create ISDS name space"));
8906 xmlSetNs(request
, isds_ns
);
8909 /* Add XSD:tOwnerInfoInput child*/
8910 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8911 err
= insert_DbOwnerInfo(context
, owner
, 0, db_owner_info
);
8912 if (err
) goto leave
;
8914 /* Add XSD:gExtApproval*/
8915 err
= insert_GExtApproval(context
, approval
, request
);
8916 if (err
) goto leave
;
8918 /* Send it to server and process response */
8919 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8920 service_name
, &request
, refnumber
);
8923 xmlFreeNode(request
);
8924 free(service_name_locale
);
8925 #else /* not HAVE_LIBCURL */
8933 /* Switch box accessibility state on request of box owner.
8934 * Despite the name, owner must do the request off-line. This function is
8935 * designed for such off-line meeting points (e.g. Czech POINT).
8936 * @context is ISDS session context.
8937 * @box identifies box to switch accessibility state. aifoIsds,
8938 * address->adCode, address->adDistrict members are ignored.
8939 * @allow is true for making accessible, false to disallow access.
8940 * @approval is optional external approval of box manipulation
8941 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8942 * NULL, if you don't care. */
8943 isds_error
isds_switch_box_accessibility_on_owner_request(
8944 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8945 const _Bool allow
, const struct isds_approval
*approval
,
8947 return build_send_manipulationdbowner_request_check_drop_response(context
,
8948 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8949 BAD_CAST
"DisableOwnDataBox",
8950 box
, approval
, (xmlChar
**) refnumber
);
8954 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8956 * @context is ISDS session context.
8957 * @box identifies box to switch accessibility state. aifoIsds,
8958 * address->adCode, address->adDistrict members are ignored.
8959 * @since is date since accessibility has been denied. This can be past too.
8960 * Only tm_year, tm_mon and tm_mday carry sane value.
8961 * @approval is optional external approval of box manipulation
8962 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8963 * NULL, if you don't care. */
8964 isds_error
isds_disable_box_accessibility_externaly(
8965 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8966 const struct tm
*since
, const struct isds_approval
*approval
,
8968 isds_error err
= IE_SUCCESS
;
8970 char *service_name_locale
= NULL
;
8971 xmlNodePtr request
= NULL
, node
;
8972 xmlNsPtr isds_ns
= NULL
;
8973 xmlChar
*string
= NULL
;
8977 if (!context
) return IE_INVALID_CONTEXT
;
8978 zfree(context
->long_message
);
8979 if (!box
|| !since
) return IE_INVAL
;
8983 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8985 isds_printf_message(context
,
8986 _("Could not build %s request"), "DisableDataBoxExternally");
8990 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8992 isds_log_message(context
, _("Could not create ISDS name space"));
8996 xmlSetNs(request
, isds_ns
);
8999 /* Add @box identification */
9000 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
9001 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
9002 if (err
) goto leave
;
9004 /* Add @since date */
9005 err
= tm2datestring(since
, &string
);
9007 isds_log_message(context
,
9008 _("Could not convert `since' argument to ISO date string"));
9011 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
9015 err
= insert_GExtApproval(context
, approval
, request
);
9016 if (err
) goto leave
;
9018 /* Send it to server and process response */
9019 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
9020 BAD_CAST
"DisableDataBoxExternally", &request
,
9021 (xmlChar
**) refnumber
);
9025 xmlFreeNode(request
);
9026 free(service_name_locale
);
9027 #else /* not HAVE_LIBCURL */
9036 /* Insert struct isds_message data (envelope (recipient data optional) and
9037 * documents into XML tree
9038 * @context is session context
9039 * @outgoing_message is libisds structure with message data
9040 * @create_message is XML CreateMessage or CreateMultipleMessage element
9041 * @process_recipient true for recipient data serialization, false for no
9043 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
9044 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
9045 const _Bool process_recipient
) {
9047 isds_error err
= IE_SUCCESS
;
9048 xmlNodePtr envelope
, dm_files
, node
;
9049 xmlChar
*string
= NULL
;
9051 if (!context
) return IE_INVALID_CONTEXT
;
9052 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
9055 /* Build envelope */
9056 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
9058 isds_printf_message(context
, _("Could not add dmEnvelope child to "
9059 "%s element"), create_message
->name
);
9063 if (!outgoing_message
->envelope
) {
9064 isds_log_message(context
, _("Outgoing message is missing envelope"));
9069 /* Insert optional message type */
9070 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
9072 if (err
) goto leave
;
9074 INSERT_STRING(envelope
, "dmSenderOrgUnit",
9075 outgoing_message
->envelope
->dmSenderOrgUnit
);
9076 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
9077 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
9079 if (process_recipient
) {
9080 if (!outgoing_message
->envelope
->dbIDRecipient
) {
9081 isds_log_message(context
,
9082 _("Outgoing message is missing recipient box identifier"));
9086 INSERT_STRING(envelope
, "dbIDRecipient",
9087 outgoing_message
->envelope
->dbIDRecipient
);
9089 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
9090 outgoing_message
->envelope
->dmRecipientOrgUnit
);
9091 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
9092 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
9093 INSERT_STRING(envelope
, "dmToHands",
9094 outgoing_message
->envelope
->dmToHands
);
9097 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
9099 INSERT_STRING(envelope
, "dmAnnotation",
9100 outgoing_message
->envelope
->dmAnnotation
);
9102 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
9103 0, 50, "dmRecipientRefNumber");
9104 INSERT_STRING(envelope
, "dmRecipientRefNumber",
9105 outgoing_message
->envelope
->dmRecipientRefNumber
);
9107 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
9108 0, 50, "dmSenderRefNumber");
9109 INSERT_STRING(envelope
, "dmSenderRefNumber",
9110 outgoing_message
->envelope
->dmSenderRefNumber
);
9112 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
9113 0, 50, "dmRecipientIdent");
9114 INSERT_STRING(envelope
, "dmRecipientIdent",
9115 outgoing_message
->envelope
->dmRecipientIdent
);
9117 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
9118 0, 50, "dmSenderIdent");
9119 INSERT_STRING(envelope
, "dmSenderIdent",
9120 outgoing_message
->envelope
->dmSenderIdent
);
9122 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
9123 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
9124 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
9125 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
9126 INSERT_STRING(envelope
, "dmLegalTitleSect",
9127 outgoing_message
->envelope
->dmLegalTitleSect
);
9128 INSERT_STRING(envelope
, "dmLegalTitlePar",
9129 outgoing_message
->envelope
->dmLegalTitlePar
);
9130 INSERT_STRING(envelope
, "dmLegalTitlePoint",
9131 outgoing_message
->envelope
->dmLegalTitlePoint
);
9133 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
9134 outgoing_message
->envelope
->dmPersonalDelivery
);
9135 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
9136 outgoing_message
->envelope
->dmAllowSubstDelivery
);
9138 /* ???: Should we require value for dbEffectiveOVM sender?
9139 * ISDS has default as true */
9140 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
9141 INSERT_BOOLEAN(envelope
, "dmOVM",
9142 outgoing_message
->envelope
->dmPublishOwnID
);
9145 /* Append dmFiles */
9146 if (!outgoing_message
->documents
) {
9147 isds_log_message(context
,
9148 _("Outgoing message is missing list of documents"));
9152 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
9154 isds_printf_message(context
, _("Could not add dmFiles child to "
9155 "%s element"), create_message
->name
);
9160 /* Check for document hierarchy */
9161 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
9162 if (err
) goto leave
;
9164 /* Process each document */
9165 for (struct isds_list
*item
=
9166 (struct isds_list
*) outgoing_message
->documents
;
9167 item
; item
= item
->next
) {
9169 isds_log_message(context
,
9170 _("List of documents contains empty item"));
9174 /* FIXME: Check for dmFileMetaType and for document references.
9175 * Only first document can be of MAIN type */
9176 err
= insert_document(context
, (struct isds_document
*) item
->data
,
9179 if (err
) goto leave
;
9186 #endif /* HAVE_LIBCURL */
9189 /* Send a message via ISDS to a recipient
9190 * @context is session context
9191 * @outgoing_message is message to send; Some members are mandatory (like
9192 * dbIDRecipient), some are optional and some are irrelevant (especially data
9193 * about sender). Included pointer to isds_list documents must contain at
9194 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9195 * members will be filled with valid data from ISDS. Exact list of write
9196 * members is subject to change. Currently dmID is changed.
9197 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9198 isds_error
isds_send_message(struct isds_ctx
*context
,
9199 struct isds_message
*outgoing_message
) {
9201 isds_error err
= IE_SUCCESS
;
9203 xmlNsPtr isds_ns
= NULL
;
9204 xmlNodePtr request
= NULL
;
9205 xmlDocPtr response
= NULL
;
9206 xmlChar
*code
= NULL
, *message
= NULL
;
9207 xmlXPathContextPtr xpath_ctx
= NULL
;
9208 xmlXPathObjectPtr result
= NULL
;
9209 /*_Bool message_is_complete = 0;*/
9212 if (!context
) return IE_INVALID_CONTEXT
;
9213 zfree(context
->long_message
);
9214 if (!outgoing_message
) return IE_INVAL
;
9217 /* Check if connection is established
9218 * TODO: This check should be done downstairs. */
9219 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9222 /* Build CreateMessage request */
9223 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
9225 isds_log_message(context
,
9226 _("Could not build CreateMessage request"));
9229 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9231 isds_log_message(context
, _("Could not create ISDS name space"));
9232 xmlFreeNode(request
);
9235 xmlSetNs(request
, isds_ns
);
9237 /* Append envelope and files */
9238 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
9239 if (err
) goto leave
;
9242 /* Signal we can serialize message since now */
9243 /*message_is_complete = 1;*/
9246 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
9249 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9251 /* Don't' destroy request, we want to provide it to application later */
9254 isds_log(ILF_ISDS
, ILL_DEBUG
,
9255 _("Processing ISDS response on CreateMessage "
9256 "request failed\n"));
9260 /* Check for response status */
9261 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9262 &code
, &message
, NULL
);
9264 isds_log(ILF_ISDS
, ILL_DEBUG
,
9265 _("ISDS response on CreateMessage request "
9266 "is missing status\n"));
9270 /* Request processed, but refused by server or server failed */
9271 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9272 char *box_id_locale
=
9273 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9274 char *code_locale
= _isds_utf82locale((char*)code
);
9275 char *message_locale
= _isds_utf82locale((char*)message
);
9276 isds_log(ILF_ISDS
, ILL_DEBUG
,
9277 _("Server did not accept message for %s on CreateMessage "
9278 "request (code=%s, message=%s)\n"),
9279 box_id_locale
, code_locale
, message_locale
);
9280 isds_log_message(context
, message_locale
);
9281 free(box_id_locale
);
9283 free(message_locale
);
9290 xpath_ctx
= xmlXPathNewContext(response
);
9295 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9299 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
9305 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9306 isds_log_message(context
, _("Missing CreateMessageResponse element"));
9310 if (result
->nodesetval
->nodeNr
> 1) {
9311 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
9315 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9316 xmlXPathFreeObject(result
); result
= NULL
;
9318 if (outgoing_message
->envelope
->dmID
) {
9319 free(outgoing_message
->envelope
->dmID
);
9320 outgoing_message
->envelope
->dmID
= NULL
;
9322 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
9323 if (!outgoing_message
->envelope
->dmID
) {
9324 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
9325 "but did not return assigned message ID\n"));
9329 /* TODO: Serialize message into structure member raw */
9330 /* XXX: Each web service transport message in different format.
9331 * Therefore it's not possible to save them directly.
9332 * To save them, one must figure out common format.
9333 * We can leave it on application, or we can implement the ESS format. */
9334 /*if (message_is_complete) {
9335 if (outgoing_message->envelope->dmID) {
9337 /* Add assigned message ID as first child*/
9338 /*xmlNodePtr dmid_text = xmlNewText(
9339 (xmlChar *) outgoing_message->envelope->dmID);
9340 if (!dmid_text) goto serialization_failed;
9342 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9344 if (!dmid_element) {
9345 xmlFreeNode(dmid_text);
9346 goto serialization_failed;
9349 xmlNodePtr dmid_element_with_text =
9350 xmlAddChild(dmid_element, dmid_text);
9351 if (!dmid_element_with_text) {
9352 xmlFreeNode(dmid_element);
9353 xmlFreeNode(dmid_text);
9354 goto serialization_failed;
9357 node = xmlAddPrevSibling(envelope->childern,
9358 dmid_element_with_text);
9360 xmlFreeNodeList(dmid_element_with_text);
9361 goto serialization_failed;
9365 /* Serialize message with ID into raw */
9366 /*buffer = serialize_element(envelope)*/
9369 serialization_failed:
9374 xmlXPathFreeObject(result
);
9375 xmlXPathFreeContext(xpath_ctx
);
9379 xmlFreeDoc(response
);
9380 xmlFreeNode(request
);
9383 isds_log(ILF_ISDS
, ILL_DEBUG
,
9384 _("CreateMessage request processed by server "
9385 "successfully.\n"));
9386 #else /* not HAVE_LIBCURL */
9394 /* Send a message via ISDS to a multiple recipients
9395 * @context is session context
9396 * @outgoing_message is message to send; Some members are mandatory,
9397 * some are optional and some are irrelevant (especially data
9398 * about sender). Data about recipient will be substituted by ISDS from
9399 * @copies. Included pointer to isds_list documents must
9400 * contain at least one document of FILEMETATYPE_MAIN.
9401 * @copies is list of isds_message_copy structures addressing all desired
9402 * recipients. This is read-write structure, some members will be filled with
9403 * valid data from ISDS (message IDs, error codes, error descriptions).
9405 * ISDS_SUCCESS if all messages have been sent
9406 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9407 * succeeded messages can be identified by copies->data->error),
9408 * or other error code if something other goes wrong. */
9409 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
9410 const struct isds_message
*outgoing_message
,
9411 struct isds_list
*copies
) {
9413 isds_error err
= IE_SUCCESS
;
9415 isds_error append_err
;
9416 xmlNsPtr isds_ns
= NULL
;
9417 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
9418 struct isds_list
*item
;
9419 struct isds_message_copy
*copy
;
9420 xmlDocPtr response
= NULL
;
9421 xmlChar
*code
= NULL
, *message
= NULL
;
9422 xmlXPathContextPtr xpath_ctx
= NULL
;
9423 xmlXPathObjectPtr result
= NULL
;
9424 xmlChar
*string
= NULL
;
9428 if (!context
) return IE_INVALID_CONTEXT
;
9429 zfree(context
->long_message
);
9430 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9433 /* Check if connection is established
9434 * TODO: This check should be done downstairs. */
9435 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9438 /* Build CreateMultipleMessage request */
9439 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9441 isds_log_message(context
,
9442 _("Could not build CreateMultipleMessage request"));
9445 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9447 isds_log_message(context
, _("Could not create ISDS name space"));
9448 xmlFreeNode(request
);
9451 xmlSetNs(request
, isds_ns
);
9454 /* Build recipients */
9455 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9457 isds_log_message(context
, _("Could not add dmRecipients child to "
9458 "CreateMultipleMessage element"));
9459 xmlFreeNode(request
);
9463 /* Insert each recipient */
9464 for (item
= copies
; item
; item
= item
->next
) {
9465 copy
= (struct isds_message_copy
*) item
->data
;
9467 isds_log_message(context
,
9468 _("`copies' list item contains empty data"));
9473 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9475 isds_log_message(context
, _("Could not add dmRecipient child to "
9476 "dmRecipients element"));
9481 if (!copy
->dbIDRecipient
) {
9482 isds_log_message(context
,
9483 _("Message copy is missing recipient box identifier"));
9487 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9488 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9489 copy
->dmRecipientOrgUnit
);
9490 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9491 copy
->dmRecipientOrgUnitNum
, string
);
9492 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9495 /* Append envelope and files */
9496 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9497 if (err
) goto leave
;
9500 isds_log(ILF_ISDS
, ILL_DEBUG
,
9501 _("Sending CreateMultipleMessage request to ISDS\n"));
9504 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9506 isds_log(ILF_ISDS
, ILL_DEBUG
,
9507 _("Processing ISDS response on CreateMultipleMessage "
9508 "request failed\n"));
9512 /* Check for response status */
9513 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9514 &code
, &message
, NULL
);
9516 isds_log(ILF_ISDS
, ILL_DEBUG
,
9517 _("ISDS response on CreateMultipleMessage request "
9518 "is missing status\n"));
9522 /* Request processed, but some copies failed */
9523 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9524 char *box_id_locale
=
9525 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9526 char *code_locale
= _isds_utf82locale((char*)code
);
9527 char *message_locale
= _isds_utf82locale((char*)message
);
9528 isds_log(ILF_ISDS
, ILL_DEBUG
,
9529 _("Server did accept message for multiple recipients "
9530 "on CreateMultipleMessage request but delivery to "
9531 "some of them failed (code=%s, message=%s)\n"),
9532 box_id_locale
, code_locale
, message_locale
);
9533 isds_log_message(context
, message_locale
);
9534 free(box_id_locale
);
9536 free(message_locale
);
9537 err
= IE_PARTIAL_SUCCESS
;
9540 /* Request refused by server as whole */
9541 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9542 char *box_id_locale
=
9543 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9544 char *code_locale
= _isds_utf82locale((char*)code
);
9545 char *message_locale
= _isds_utf82locale((char*)message
);
9546 isds_log(ILF_ISDS
, ILL_DEBUG
,
9547 _("Server did not accept message for multiple recipients "
9548 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9549 box_id_locale
, code_locale
, message_locale
);
9550 isds_log_message(context
, message_locale
);
9551 free(box_id_locale
);
9553 free(message_locale
);
9560 xpath_ctx
= xmlXPathNewContext(response
);
9565 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9569 result
= xmlXPathEvalExpression(
9570 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9571 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9577 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9578 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9583 /* Extract message ID and delivery status for each copy */
9584 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9585 item
= item
->next
, i
++) {
9586 copy
= (struct isds_message_copy
*) item
->data
;
9587 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9589 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9595 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9596 isds_printf_message(context
, _("ISDS returned unexpected number of "
9597 "message copy delivery states: %d"),
9598 result
->nodesetval
->nodeNr
);
9607 xmlXPathFreeObject(result
);
9608 xmlXPathFreeContext(xpath_ctx
);
9612 xmlFreeDoc(response
);
9613 xmlFreeNode(request
);
9616 isds_log(ILF_ISDS
, ILL_DEBUG
,
9617 _("CreateMultipleMessageResponse request processed by server "
9618 "successfully.\n"));
9619 #else /* not HAVE_LIBCURL */
9627 /* Get list of messages. This is common core for getting sent or received
9629 * Any criterion argument can be NULL, if you don't care about it.
9630 * @context is session context. Must not be NULL.
9631 * @outgoing_direction is true if you want list of outgoing messages,
9632 * it's false if you want incoming messages.
9633 * @from_time is minimal time and date of message sending inclusive.
9634 * @to_time is maximal time and date of message sending inclusive
9635 * @organization_unit_number is number of sender/recipient respectively.
9636 * @status_filter is bit field of isds_message_status values. Use special
9637 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9638 * all values, you can use bit-wise arithmetic if you want.)
9639 * @offset is index of first message we are interested in. First message is 1.
9640 * Set to 0 (or 1) if you don't care.
9641 * @number is maximal length of list you want to get as input value, outputs
9642 * number of messages matching these criteria. Can be NULL if you don't care
9643 * (applies to output value either).
9644 * @messages is automatically reallocated list of isds_message's. Be ware that
9645 * it returns only brief overview (envelope and some other fields) about each
9646 * message, not the complete message. FIXME: Specify exact fields.
9647 * The list is sorted by delivery time in ascending order.
9648 * Use NULL if you don't care about don't need the data (useful if you want to
9649 * know only the @number). If you provide &NULL, list will be allocated on
9650 * heap, if you provide pointer to non-NULL, list will be freed automatically
9651 * at first. Also in case of error the list will be NULLed.
9652 * @return IE_SUCCESS or appropriate error code. */
9653 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9654 _Bool outgoing_direction
,
9655 const struct timeval
*from_time
, const struct timeval
*to_time
,
9656 const long int *organization_unit_number
,
9657 const unsigned int status_filter
,
9658 const unsigned long int offset
, unsigned long int *number
,
9659 struct isds_list
**messages
) {
9661 isds_error err
= IE_SUCCESS
;
9663 xmlNsPtr isds_ns
= NULL
;
9664 xmlNodePtr request
= NULL
, node
;
9665 xmlDocPtr response
= NULL
;
9666 xmlChar
*code
= NULL
, *message
= NULL
;
9667 xmlXPathContextPtr xpath_ctx
= NULL
;
9668 xmlXPathObjectPtr result
= NULL
;
9669 xmlChar
*string
= NULL
;
9673 if (!context
) return IE_INVALID_CONTEXT
;
9674 zfree(context
->long_message
);
9676 /* Free former message list if any */
9677 if (messages
) isds_list_free(messages
);
9680 /* Check if connection is established
9681 * TODO: This check should be done downstairs. */
9682 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9684 /* Build GetListOf*Messages request */
9685 request
= xmlNewNode(NULL
,
9686 (outgoing_direction
) ?
9687 BAD_CAST
"GetListOfSentMessages" :
9688 BAD_CAST
"GetListOfReceivedMessages"
9691 isds_log_message(context
,
9692 (outgoing_direction
) ?
9693 _("Could not build GetListOfSentMessages request") :
9694 _("Could not build GetListOfReceivedMessages request")
9698 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9700 isds_log_message(context
, _("Could not create ISDS name space"));
9701 xmlFreeNode(request
);
9704 xmlSetNs(request
, isds_ns
);
9708 err
= timeval2timestring(from_time
, &string
);
9709 if (err
) goto leave
;
9711 INSERT_STRING(request
, "dmFromTime", string
);
9712 free(string
); string
= NULL
;
9715 err
= timeval2timestring(to_time
, &string
);
9716 if (err
) goto leave
;
9718 INSERT_STRING(request
, "dmToTime", string
);
9719 free(string
); string
= NULL
;
9721 if (outgoing_direction
) {
9722 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9723 organization_unit_number
, string
);
9725 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9726 organization_unit_number
, string
);
9729 if (status_filter
> MESSAGESTATE_ANY
) {
9730 isds_printf_message(context
,
9731 _("Invalid message state filter value: %ld"), status_filter
);
9735 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9738 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9740 INSERT_STRING(request
, "dmOffset", "1");
9743 /* number 0 means no limit */
9744 if (number
&& *number
== 0) {
9745 INSERT_STRING(request
, "dmLimit", NULL
);
9747 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9751 isds_log(ILF_ISDS
, ILL_DEBUG
,
9752 (outgoing_direction
) ?
9753 _("Sending GetListOfSentMessages request to ISDS\n") :
9754 _("Sending GetListOfReceivedMessages request to ISDS\n")
9758 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9759 xmlFreeNode(request
); request
= NULL
;
9762 isds_log(ILF_ISDS
, ILL_DEBUG
,
9763 (outgoing_direction
) ?
9764 _("Processing ISDS response on GetListOfSentMessages "
9765 "request failed\n") :
9766 _("Processing ISDS response on GetListOfReceivedMessages "
9772 /* Check for response status */
9773 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9774 &code
, &message
, NULL
);
9776 isds_log(ILF_ISDS
, ILL_DEBUG
,
9777 (outgoing_direction
) ?
9778 _("ISDS response on GetListOfSentMessages request "
9779 "is missing status\n") :
9780 _("ISDS response on GetListOfReceivedMessages request "
9781 "is missing status\n")
9786 /* Request processed, but nothing found */
9787 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9788 char *code_locale
= _isds_utf82locale((char*)code
);
9789 char *message_locale
= _isds_utf82locale((char*)message
);
9790 isds_log(ILF_ISDS
, ILL_DEBUG
,
9791 (outgoing_direction
) ?
9792 _("Server refused GetListOfSentMessages request "
9793 "(code=%s, message=%s)\n") :
9794 _("Server refused GetListOfReceivedMessages request "
9795 "(code=%s, message=%s)\n"),
9796 code_locale
, message_locale
);
9797 isds_log_message(context
, message_locale
);
9799 free(message_locale
);
9806 xpath_ctx
= xmlXPathNewContext(response
);
9811 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9815 result
= xmlXPathEvalExpression(
9816 (outgoing_direction
) ?
9817 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9818 "isds:dmRecords/isds:dmRecord" :
9819 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9820 "isds:dmRecords/isds:dmRecord",
9827 /* Fill output arguments in */
9828 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9829 struct isds_envelope
*envelope
;
9830 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9832 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9833 /* Create new message */
9834 item
= calloc(1, sizeof(*item
));
9839 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9840 item
->data
= calloc(1, sizeof(struct isds_message
));
9842 isds_list_free(&item
);
9847 /* Extract envelope data */
9848 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9850 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9852 isds_list_free(&item
);
9856 /* Attach extracted envelope */
9857 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9859 /* Append new message into the list */
9861 *messages
= last_item
= item
;
9863 last_item
->next
= item
;
9868 if (number
) *number
= count
;
9872 isds_list_free(messages
);
9876 xmlXPathFreeObject(result
);
9877 xmlXPathFreeContext(xpath_ctx
);
9881 xmlFreeDoc(response
);
9882 xmlFreeNode(request
);
9885 isds_log(ILF_ISDS
, ILL_DEBUG
,
9886 (outgoing_direction
) ?
9887 _("GetListOfSentMessages request processed by server "
9888 "successfully.\n") :
9889 _("GetListOfReceivedMessages request processed by server "
9892 #else /* not HAVE_LIBCURL */
9899 /* Get list of outgoing (already sent) messages.
9900 * Any criterion argument can be NULL, if you don't care about it.
9901 * @context is session context. Must not be NULL.
9902 * @from_time is minimal time and date of message sending inclusive.
9903 * @to_time is maximal time and date of message sending inclusive
9904 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9905 * @status_filter is bit field of isds_message_status values. Use special
9906 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9907 * all values, you can use bit-wise arithmetic if you want.)
9908 * @offset is index of first message we are interested in. First message is 1.
9909 * Set to 0 (or 1) if you don't care.
9910 * @number is maximal length of list you want to get as input value, outputs
9911 * number of messages matching these criteria. Can be NULL if you don't care
9912 * (applies to output value either).
9913 * @messages is automatically reallocated list of isds_message's. Be ware that
9914 * it returns only brief overview (envelope and some other fields) about each
9915 * message, not the complete message. FIXME: Specify exact fields.
9916 * The list is sorted by delivery time in ascending order.
9917 * Use NULL if you don't care about the meta data (useful if you want to know
9918 * only the @number). If you provide &NULL, list will be allocated on heap,
9919 * if you provide pointer to non-NULL, list will be freed automatically at
9920 * first. Also in case of error the list will be NULLed.
9921 * @return IE_SUCCESS or appropriate error code. */
9922 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9923 const struct timeval
*from_time
, const struct timeval
*to_time
,
9924 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9925 const unsigned long int offset
, unsigned long int *number
,
9926 struct isds_list
**messages
) {
9928 return isds_get_list_of_messages(
9930 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9936 /* Get list of incoming (addressed to you) messages.
9937 * Any criterion argument can be NULL, if you don't care about it.
9938 * @context is session context. Must not be NULL.
9939 * @from_time is minimal time and date of message sending inclusive.
9940 * @to_time is maximal time and date of message sending inclusive
9941 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9942 * @status_filter is bit field of isds_message_status values. Use special
9943 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9944 * all values, you can use bit-wise arithmetic if you want.)
9945 * @offset is index of first message we are interested in. First message is 1.
9946 * Set to 0 (or 1) if you don't care.
9947 * @number is maximal length of list you want to get as input value, outputs
9948 * number of messages matching these criteria. Can be NULL if you don't care
9949 * (applies to output value either).
9950 * @messages is automatically reallocated list of isds_message's. Be ware that
9951 * it returns only brief overview (envelope and some other fields) about each
9952 * message, not the complete message. FIXME: Specify exact fields.
9953 * Use NULL if you don't care about the meta data (useful if you want to know
9954 * only the @number). If you provide &NULL, list will be allocated on heap,
9955 * if you provide pointer to non-NULL, list will be freed automatically at
9956 * first. Also in case of error the list will be NULLed.
9957 * @return IE_SUCCESS or appropriate error code. */
9958 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9959 const struct timeval
*from_time
, const struct timeval
*to_time
,
9960 const long int *dmRecipientOrgUnitNum
,
9961 const unsigned int status_filter
,
9962 const unsigned long int offset
, unsigned long int *number
,
9963 struct isds_list
**messages
) {
9965 return isds_get_list_of_messages(
9967 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9973 /* Get list of sent message state changes.
9974 * Any criterion argument can be NULL, if you don't care about it.
9975 * @context is session context. Must not be NULL.
9976 * @from_time is minimal time and date of status changes inclusive
9977 * @to_time is maximal time and date of status changes inclusive
9978 * @changed_states is automatically reallocated list of
9979 * isds_message_status_change's. If you provide &NULL, list will be allocated
9980 * on heap, if you provide pointer to non-NULL, list will be freed
9981 * automatically at first. Also in case of error the list will be NULLed.
9982 * XXX: The list item ordering is not specified.
9983 * XXX: Server provides only `recent' changes.
9984 * @return IE_SUCCESS or appropriate error code. */
9985 isds_error
isds_get_list_of_sent_message_state_changes(
9986 struct isds_ctx
*context
,
9987 const struct timeval
*from_time
, const struct timeval
*to_time
,
9988 struct isds_list
**changed_states
) {
9990 isds_error err
= IE_SUCCESS
;
9992 xmlNsPtr isds_ns
= NULL
;
9993 xmlNodePtr request
= NULL
, node
;
9994 xmlDocPtr response
= NULL
;
9995 xmlXPathContextPtr xpath_ctx
= NULL
;
9996 xmlXPathObjectPtr result
= NULL
;
9997 xmlChar
*string
= NULL
;
10001 if (!context
) return IE_INVALID_CONTEXT
;
10002 zfree(context
->long_message
);
10004 /* Free former message list if any */
10005 isds_list_free(changed_states
);
10008 /* Check if connection is established
10009 * TODO: This check should be done downstairs. */
10010 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10012 /* Build GetMessageStateChanges request */
10013 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
10015 isds_log_message(context
,
10016 _("Could not build GetMessageStateChanges request"));
10019 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10021 isds_log_message(context
, _("Could not create ISDS name space"));
10022 xmlFreeNode(request
);
10025 xmlSetNs(request
, isds_ns
);
10029 err
= timeval2timestring(from_time
, &string
);
10030 if (err
) goto leave
;
10032 INSERT_STRING(request
, "dmFromTime", string
);
10036 err
= timeval2timestring(to_time
, &string
);
10037 if (err
) goto leave
;
10039 INSERT_STRING(request
, "dmToTime", string
);
10044 err
= send_destroy_request_check_response(context
,
10045 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
10046 &response
, NULL
, NULL
);
10047 if (err
) goto leave
;
10051 xpath_ctx
= xmlXPathNewContext(response
);
10056 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10060 result
= xmlXPathEvalExpression(
10061 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
10062 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
10068 /* Fill output arguments in */
10069 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10070 struct isds_list
*item
= NULL
, *last_item
= NULL
;
10072 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
10073 /* Create new status change */
10074 item
= calloc(1, sizeof(*item
));
10080 (void(*)(void**)) &isds_message_status_change_free
;
10082 /* Extract message status change */
10083 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
10084 err
= extract_StateChangesRecord(context
,
10085 (struct isds_message_status_change
**) &item
->data
,
10088 isds_list_free(&item
);
10092 /* Append new message status change into the list */
10093 if (!*changed_states
) {
10094 *changed_states
= last_item
= item
;
10096 last_item
->next
= item
;
10104 isds_list_free(changed_states
);
10108 xmlXPathFreeObject(result
);
10109 xmlXPathFreeContext(xpath_ctx
);
10110 xmlFreeDoc(response
);
10111 xmlFreeNode(request
);
10114 isds_log(ILF_ISDS
, ILL_DEBUG
,
10115 _("GetMessageStateChanges request processed by server "
10116 "successfully.\n"));
10117 #else /* not HAVE_LIBCURL */
10125 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10127 * @context is session context
10128 * @service is ISDS WS service handler
10129 * @service_name is name of SERVICE_DM_OPERATIONS
10130 * @message_id is message ID to send as service argument to ISDS
10131 * @response is reallocated server SOAP body response as XML document
10132 * @raw_response is reallocated bit stream with response body. Use
10133 * NULL if you don't care
10134 * @raw_response_length is size of @raw_response in bytes
10135 * @code is reallocated ISDS status code
10136 * @status_message is reallocated ISDS status message
10137 * @return error coded from lower layer, context message will be set up
10138 * appropriately. */
10139 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
10140 const isds_service service
, const xmlChar
*service_name
,
10141 const char *message_id
,
10142 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
10143 xmlChar
**code
, xmlChar
**status_message
) {
10145 isds_error err
= IE_SUCCESS
;
10146 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
10147 xmlNodePtr request
= NULL
, node
;
10148 xmlNsPtr isds_ns
= NULL
;
10150 if (!context
) return IE_INVALID_CONTEXT
;
10151 if (!service_name
|| !message_id
) return IE_INVAL
;
10152 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
10153 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
10155 /* Free output argument */
10156 xmlFreeDoc(*response
); *response
= NULL
;
10157 if (raw_response
) zfree(*raw_response
);
10159 zfree(*status_message
);
10162 /* Check if connection is established
10163 * TODO: This check should be done downstairs. */
10164 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10166 service_name_locale
= _isds_utf82locale((char*)service_name
);
10167 message_id_locale
= _isds_utf82locale(message_id
);
10168 if (!service_name_locale
|| !message_id_locale
) {
10173 /* Build request */
10174 request
= xmlNewNode(NULL
, service_name
);
10176 isds_printf_message(context
,
10177 _("Could not build %s request for %s message ID"),
10178 service_name_locale
, message_id_locale
);
10182 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10184 isds_log_message(context
, _("Could not create ISDS name space"));
10188 xmlSetNs(request
, isds_ns
);
10191 /* Add requested ID */
10192 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
10193 if (err
) goto leave
;
10194 INSERT_STRING(request
, "dmID", message_id
);
10197 isds_log(ILF_ISDS
, ILL_DEBUG
,
10198 _("Sending %s request for %s message ID to ISDS\n"),
10199 service_name_locale
, message_id_locale
);
10202 err
= _isds(context
, service
, request
, response
,
10203 raw_response
, raw_response_length
);
10204 xmlFreeNode(request
); request
= NULL
;
10207 isds_log(ILF_ISDS
, ILL_DEBUG
,
10208 _("Processing ISDS response on %s request failed\n"),
10209 service_name_locale
);
10213 /* Check for response status */
10214 err
= isds_response_status(context
, service
, *response
,
10215 code
, status_message
, NULL
);
10217 isds_log(ILF_ISDS
, ILL_DEBUG
,
10218 _("ISDS response on %s request is missing status\n"),
10219 service_name_locale
);
10223 /* Request processed, but nothing found */
10224 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
10225 char *code_locale
= _isds_utf82locale((char*) *code
);
10226 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
10227 isds_log(ILF_ISDS
, ILL_DEBUG
,
10228 _("Server refused %s request for %s message ID "
10229 "(code=%s, message=%s)\n"),
10230 service_name_locale
, message_id_locale
,
10231 code_locale
, status_message_locale
);
10232 isds_log_message(context
, status_message_locale
);
10234 free(status_message_locale
);
10240 free(message_id_locale
);
10241 free(service_name_locale
);
10242 xmlFreeNode(request
);
10247 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10248 * signed data and free ISDS response.
10249 * @context is session context
10250 * @message_id is UTF-8 encoded message ID for logging purpose
10251 * @response is parsed XML document. It will be freed and NULLed in the middle
10252 * of function run to save memory. This is not guaranteed in case of error.
10253 * @request_name is name of ISDS request used to construct response root
10254 * element name and for logging purpose.
10255 * @raw is reallocated output buffer with DER encoded CMS data
10256 * @raw_length is size of @raw buffer in bytes
10257 * @returns standard error codes, in case of error, @raw will be freed and
10258 * NULLed, @response sometimes. */
10259 static isds_error
find_extract_signed_data_free_response(
10260 struct isds_ctx
*context
, const xmlChar
*message_id
,
10261 xmlDocPtr
*response
, const xmlChar
*request_name
,
10262 void **raw
, size_t *raw_length
) {
10264 isds_error err
= IE_SUCCESS
;
10265 char *xpath_expression
= NULL
;
10266 xmlXPathContextPtr xpath_ctx
= NULL
;
10267 xmlXPathObjectPtr result
= NULL
;
10268 char *encoded_structure
= NULL
;
10270 if (!context
) return IE_INVALID_CONTEXT
;
10271 if (!raw
) return IE_INVAL
;
10273 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
10276 /* Build XPath expression */
10277 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
10278 "Response/isds:dmSignature");
10279 if (!xpath_expression
) return IE_NOMEM
;
10282 xpath_ctx
= xmlXPathNewContext(*response
);
10287 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10291 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
10296 /* Empty response */
10297 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10298 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10299 isds_printf_message(context
,
10300 _("Server did not return any signed data for message ID `%s' "
10302 message_id_locale
, request_name
);
10303 free(message_id_locale
);
10307 /* More responses */
10308 if (result
->nodesetval
->nodeNr
> 1) {
10309 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10310 isds_printf_message(context
,
10311 _("Server did return more signed data for message ID `%s' "
10313 message_id_locale
, request_name
);
10314 free(message_id_locale
);
10319 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10321 /* Extract PKCS#7 structure */
10322 EXTRACT_STRING(".", encoded_structure
);
10323 if (!encoded_structure
) {
10324 isds_log_message(context
, _("dmSignature element is empty"));
10327 /* Here we have delivery info as standalone CMS in encoded_structure.
10328 * We don't need any other data, free them: */
10329 xmlXPathFreeObject(result
); result
= NULL
;
10330 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
10331 xmlFreeDoc(*response
); *response
= NULL
;
10334 /* Decode PKCS#7 to DER format */
10335 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
10336 if (*raw_length
== (size_t) -1) {
10337 isds_log_message(context
,
10338 _("Error while Base64-decoding PKCS#7 structure"));
10349 free(encoded_structure
);
10350 xmlXPathFreeObject(result
);
10351 xmlXPathFreeContext(xpath_ctx
);
10352 free(xpath_expression
);
10356 #endif /* HAVE_LIBCURL */
10359 /* Download incoming message envelope identified by ID.
10360 * @context is session context
10361 * @message_id is message identifier (you can get them from
10362 * isds_get_list_of_received_messages())
10363 * @message is automatically reallocated message retrieved from ISDS.
10364 * It will miss documents per se. Use isds_get_received_message(), if you are
10365 * interested in documents (content) too.
10366 * Returned hash and timestamp require documents to be verifiable. */
10367 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
10368 const char *message_id
, struct isds_message
**message
) {
10370 isds_error err
= IE_SUCCESS
;
10372 xmlDocPtr response
= NULL
;
10373 xmlChar
*code
= NULL
, *status_message
= NULL
;
10374 xmlXPathContextPtr xpath_ctx
= NULL
;
10375 xmlXPathObjectPtr result
= NULL
;
10378 if (!context
) return IE_INVALID_CONTEXT
;
10379 zfree(context
->long_message
);
10381 /* Free former message if any */
10382 if (!message
) return IE_INVAL
;
10383 isds_message_free(message
);
10386 /* Do request and check for success */
10387 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10388 BAD_CAST
"MessageEnvelopeDownload", message_id
,
10389 &response
, NULL
, NULL
, &code
, &status_message
);
10390 if (err
) goto leave
;
10393 xpath_ctx
= xmlXPathNewContext(response
);
10398 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10402 result
= xmlXPathEvalExpression(
10403 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
10404 "isds:dmReturnedMessageEnvelope",
10410 /* Empty response */
10411 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10412 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10413 isds_printf_message(context
,
10414 _("Server did not return any envelope for ID `%s' "
10415 "on MessageEnvelopeDownload request"), message_id_locale
);
10416 free(message_id_locale
);
10420 /* More envelops */
10421 if (result
->nodesetval
->nodeNr
> 1) {
10422 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10423 isds_printf_message(context
,
10424 _("Server did return more envelopes for ID `%s' "
10425 "on MessageEnvelopeDownload request"), message_id_locale
);
10426 free(message_id_locale
);
10431 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10433 /* Extract the envelope (= message without documents, hence 0) */
10434 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10435 if (err
) goto leave
;
10437 /* Save XML blob */
10438 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10439 &(*message
)->raw_length
);
10443 isds_message_free(message
);
10446 xmlXPathFreeObject(result
);
10447 xmlXPathFreeContext(xpath_ctx
);
10450 free(status_message
);
10451 if (!*message
|| !(*message
)->xml
) {
10452 xmlFreeDoc(response
);
10456 isds_log(ILF_ISDS
, ILL_DEBUG
,
10457 _("MessageEnvelopeDownload request processed by server "
10460 #else /* not HAVE_LIBCURL */
10467 /* Load delivery info of any format from buffer.
10468 * @context is session context
10469 * @raw_type advertises format of @buffer content. Only delivery info types
10471 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10472 * retrieve such data from message->raw after calling
10473 * isds_get_signed_delivery_info().
10474 * @length is length of buffer in bytes.
10475 * @message is automatically reallocated message parsed from @buffer.
10476 * @strategy selects how buffer will be attached into raw isds_message member.
10478 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10479 const isds_raw_type raw_type
,
10480 const void *buffer
, const size_t length
,
10481 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10483 isds_error err
= IE_SUCCESS
;
10484 message_ns_type message_ns
;
10485 xmlDocPtr message_doc
= NULL
;
10486 xmlXPathContextPtr xpath_ctx
= NULL
;
10487 xmlXPathObjectPtr result
= NULL
;
10488 void *xml_stream
= NULL
;
10489 size_t xml_stream_length
= 0;
10491 if (!context
) return IE_INVALID_CONTEXT
;
10492 zfree(context
->long_message
);
10493 if (!message
) return IE_INVAL
;
10494 isds_message_free(message
);
10495 if (!buffer
) return IE_INVAL
;
10498 /* Select buffer format and extract XML from CMS*/
10499 switch (raw_type
) {
10500 case RAWTYPE_DELIVERYINFO
:
10501 message_ns
= MESSAGE_NS_UNSIGNED
;
10502 xml_stream
= (void *) buffer
;
10503 xml_stream_length
= length
;
10506 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10507 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10508 xml_stream
= (void *) buffer
;
10509 xml_stream_length
= length
;
10512 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10513 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10514 err
= _isds_extract_cms_data(context
, buffer
, length
,
10515 &xml_stream
, &xml_stream_length
);
10516 if (err
) goto leave
;
10520 isds_log_message(context
, _("Bad raw delivery representation type"));
10525 if (_isds_sizet2int(xml_stream_length
) >= 0) {
10526 isds_log(ILF_ISDS
, ILL_DEBUG
,
10527 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10528 _isds_sizet2int(xml_stream_length
), xml_stream
);
10531 /* Convert delivery info XML stream into XPath context */
10532 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10533 if (!message_doc
) {
10537 xpath_ctx
= xmlXPathNewContext(message_doc
);
10542 /* XXX: Name spaces mangled for signed delivery info:
10543 * http://isds.czechpoint.cz/v20/delivery:
10545 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10547 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10548 * <p:dmID>170272</p:dmID>
10551 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10553 * </q:dmEvents>...</q:dmEvents>
10555 * </q:GetDeliveryInfoResponse>
10557 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10561 result
= xmlXPathEvalExpression(
10562 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10568 /* Empty delivery info */
10569 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10570 isds_printf_message(context
,
10571 _("XML document is not sisds:dmDelivery document"));
10575 /* More delivery info's */
10576 if (result
->nodesetval
->nodeNr
> 1) {
10577 isds_printf_message(context
,
10578 _("XML document has more sisds:dmDelivery elements"));
10582 /* One delivery info */
10583 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10585 /* Extract the envelope (= message without documents, hence 0).
10586 * XXX: extract_TReturnedMessage() can obtain attachments size,
10587 * but delivery info carries none. It's coded as option elements,
10588 * so it should work. */
10589 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10590 if (err
) goto leave
;
10592 /* Extract events */
10593 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10594 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10595 if (err
) { err
= IE_ERROR
; goto leave
; }
10596 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10597 if (err
) goto leave
;
10599 /* Append raw CMS structure into message */
10600 (*message
)->raw_type
= raw_type
;
10601 switch (strategy
) {
10602 case BUFFER_DONT_STORE
:
10605 (*message
)->raw
= malloc(length
);
10606 if (!(*message
)->raw
) {
10610 memcpy((*message
)->raw
, buffer
, length
);
10611 (*message
)->raw_length
= length
;
10614 (*message
)->raw
= (void *) buffer
;
10615 (*message
)->raw_length
= length
;
10624 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10625 isds_message_free(message
);
10628 xmlXPathFreeObject(result
);
10629 xmlXPathFreeContext(xpath_ctx
);
10630 if (!*message
|| !(*message
)->xml
) {
10631 xmlFreeDoc(message_doc
);
10633 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10636 isds_log(ILF_ISDS
, ILL_DEBUG
,
10637 _("Delivery info loaded successfully.\n"));
10642 /* Download signed delivery info-sheet of given message identified by ID.
10643 * @context is session context
10644 * @message_id is message identifier (you can get them from
10645 * isds_get_list_of_{sent,received}_messages())
10646 * @message is automatically reallocated message retrieved from ISDS.
10647 * It will miss documents per se. Use isds_get_signed_received_message(),
10648 * if you are interested in documents (content). OTOH, only this function
10649 * can get list events message has gone through. */
10650 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10651 const char *message_id
, struct isds_message
**message
) {
10653 isds_error err
= IE_SUCCESS
;
10655 xmlDocPtr response
= NULL
;
10656 xmlChar
*code
= NULL
, *status_message
= NULL
;
10658 size_t raw_length
= 0;
10661 if (!context
) return IE_INVALID_CONTEXT
;
10662 zfree(context
->long_message
);
10664 /* Free former message if any */
10665 if (!message
) return IE_INVAL
;
10666 isds_message_free(message
);
10669 /* Do request and check for success */
10670 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10671 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10672 &response
, NULL
, NULL
, &code
, &status_message
);
10673 if (err
) goto leave
;
10675 /* Find signed delivery info, extract it into raw and maybe free
10677 err
= find_extract_signed_data_free_response(context
,
10678 (xmlChar
*)message_id
, &response
,
10679 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10680 if (err
) goto leave
;
10682 /* Parse delivery info */
10683 err
= isds_load_delivery_info(context
,
10684 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10685 message
, BUFFER_MOVE
);
10686 if (err
) goto leave
;
10692 isds_message_free(message
);
10697 free(status_message
);
10698 xmlFreeDoc(response
);
10701 isds_log(ILF_ISDS
, ILL_DEBUG
,
10702 _("GetSignedDeliveryInfo request processed by server "
10705 #else /* not HAVE_LIBCURL */
10712 /* Download delivery info-sheet of given message identified by ID.
10713 * @context is session context
10714 * @message_id is message identifier (you can get them from
10715 * isds_get_list_of_{sent,received}_messages())
10716 * @message is automatically reallocated message retrieved from ISDS.
10717 * It will miss documents per se. Use isds_get_received_message(), if you are
10718 * interested in documents (content). OTOH, only this function can get list
10719 * of events message has gone through. */
10720 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10721 const char *message_id
, struct isds_message
**message
) {
10723 isds_error err
= IE_SUCCESS
;
10725 xmlDocPtr response
= NULL
;
10726 xmlChar
*code
= NULL
, *status_message
= NULL
;
10727 xmlNodePtr delivery_node
= NULL
;
10729 size_t raw_length
= 0;
10732 if (!context
) return IE_INVALID_CONTEXT
;
10733 zfree(context
->long_message
);
10735 /* Free former message if any */
10736 if (!message
) return IE_INVAL
;
10737 isds_message_free(message
);
10740 /* Do request and check for success */
10741 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10742 BAD_CAST
"GetDeliveryInfo", message_id
,
10743 &response
, NULL
, NULL
, &code
, &status_message
);
10744 if (err
) goto leave
;
10747 /* Serialize delivery info */
10748 delivery_node
= xmlDocGetRootElement(response
);
10749 if (!delivery_node
) {
10750 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10751 isds_printf_message(context
,
10752 _("Server did not return any delivery info for ID `%s' "
10753 "on GetDeliveryInfo request"), message_id_locale
);
10754 free(message_id_locale
);
10758 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10759 if (err
) goto leave
;
10761 /* Parse delivery info */
10762 /* TODO: Here we parse the response second time. We could single delivery
10763 * parser from isds_load_delivery_info() to make things faster. */
10764 err
= isds_load_delivery_info(context
,
10765 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10766 message
, BUFFER_MOVE
);
10767 if (err
) goto leave
;
10774 isds_message_free(message
);
10779 free(status_message
);
10780 xmlFreeDoc(response
);
10783 isds_log(ILF_ISDS
, ILL_DEBUG
,
10784 _("GetDeliveryInfo request processed by server "
10787 #else /* not HAVE_LIBCURL */
10794 /* Download incoming message identified by ID.
10795 * @context is session context
10796 * @message_id is message identifier (you can get them from
10797 * isds_get_list_of_received_messages())
10798 * @message is automatically reallocated message retrieved from ISDS */
10799 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10800 const char *message_id
, struct isds_message
**message
) {
10802 isds_error err
= IE_SUCCESS
;
10804 xmlDocPtr response
= NULL
;
10805 void *xml_stream
= NULL
;
10806 size_t xml_stream_length
;
10807 xmlChar
*code
= NULL
, *status_message
= NULL
;
10808 xmlXPathContextPtr xpath_ctx
= NULL
;
10809 xmlXPathObjectPtr result
= NULL
;
10810 char *phys_path
= NULL
;
10811 size_t phys_start
, phys_end
;
10814 if (!context
) return IE_INVALID_CONTEXT
;
10815 zfree(context
->long_message
);
10817 /* Free former message if any */
10818 if (NULL
== message
) return IE_INVAL
;
10819 if (message
) isds_message_free(message
);
10822 /* Do request and check for success */
10823 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10824 BAD_CAST
"MessageDownload", message_id
,
10825 &response
, &xml_stream
, &xml_stream_length
,
10826 &code
, &status_message
);
10827 if (err
) goto leave
;
10830 xpath_ctx
= xmlXPathNewContext(response
);
10835 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10839 result
= xmlXPathEvalExpression(
10840 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10846 /* Empty response */
10847 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10848 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10849 isds_printf_message(context
,
10850 _("Server did not return any message for ID `%s' "
10851 "on MessageDownload request"), message_id_locale
);
10852 free(message_id_locale
);
10856 /* More messages */
10857 if (result
->nodesetval
->nodeNr
> 1) {
10858 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10859 isds_printf_message(context
,
10860 _("Server did return more messages for ID `%s' "
10861 "on MessageDownload request"), message_id_locale
);
10862 free(message_id_locale
);
10867 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10869 /* Extract the message */
10870 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10871 if (err
) goto leave
;
10873 /* Locate raw XML blob */
10874 phys_path
= strdup(
10875 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10876 PHYSXML_ELEMENT_SEPARATOR
10877 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10878 PHYSXML_ELEMENT_SEPARATOR
10879 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10885 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10886 phys_path
, &phys_start
, &phys_end
);
10889 isds_log_message(context
,
10890 _("Substring with isds:MessageDownloadResponse element "
10891 "could not be located in raw SOAP message"));
10894 /* Save XML blob */
10895 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10896 &(*message)->raw_length);*/
10897 /* TODO: Store name space declarations from ancestors */
10898 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10899 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10900 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10901 (*message
)->raw
= malloc((*message
)->raw_length
);
10902 if (!(*message
)->raw
) {
10906 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10911 isds_message_free(message
);
10916 xmlXPathFreeObject(result
);
10917 xmlXPathFreeContext(xpath_ctx
);
10920 free(status_message
);
10922 if (!*message
|| !(*message
)->xml
) {
10923 xmlFreeDoc(response
);
10927 isds_log(ILF_ISDS
, ILL_DEBUG
,
10928 _("MessageDownload request processed by server "
10931 #else /* not HAVE_LIBCURL */
10938 /* Load message of any type from buffer.
10939 * @context is session context
10940 * @raw_type defines content type of @buffer. Only message types are allowed.
10941 * @buffer is message raw representation. Format (CMS, plain signed,
10942 * message direction) is defined in @raw_type. You can retrieve such data
10943 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10944 * @length is length of buffer in bytes.
10945 * @message is automatically reallocated message parsed from @buffer.
10946 * @strategy selects how buffer will be attached into raw isds_message member.
10948 isds_error
isds_load_message(struct isds_ctx
*context
,
10949 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10950 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10952 isds_error err
= IE_SUCCESS
;
10953 void *xml_stream
= NULL
;
10954 size_t xml_stream_length
= 0;
10955 message_ns_type message_ns
;
10956 xmlDocPtr message_doc
= NULL
;
10957 xmlXPathContextPtr xpath_ctx
= NULL
;
10958 xmlXPathObjectPtr result
= NULL
;
10960 if (!context
) return IE_INVALID_CONTEXT
;
10961 zfree(context
->long_message
);
10962 if (!message
) return IE_INVAL
;
10963 isds_message_free(message
);
10964 if (!buffer
) return IE_INVAL
;
10967 /* Select buffer format and extract XML from CMS*/
10968 switch (raw_type
) {
10969 case RAWTYPE_INCOMING_MESSAGE
:
10970 message_ns
= MESSAGE_NS_UNSIGNED
;
10971 xml_stream
= (void *) buffer
;
10972 xml_stream_length
= length
;
10975 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10976 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10977 xml_stream
= (void *) buffer
;
10978 xml_stream_length
= length
;
10981 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10982 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10983 err
= _isds_extract_cms_data(context
, buffer
, length
,
10984 &xml_stream
, &xml_stream_length
);
10985 if (err
) goto leave
;
10988 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10989 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10990 xml_stream
= (void *) buffer
;
10991 xml_stream_length
= length
;
10994 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10995 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10996 err
= _isds_extract_cms_data(context
, buffer
, length
,
10997 &xml_stream
, &xml_stream_length
);
10998 if (err
) goto leave
;
11002 isds_log_message(context
, _("Bad raw message representation type"));
11007 if (_isds_sizet2int(xml_stream_length
) >= 0) {
11008 isds_log(ILF_ISDS
, ILL_DEBUG
,
11009 _("Loading message:\n%.*s\nEnd of message\n"),
11010 _isds_sizet2int(xml_stream_length
), xml_stream
);
11013 /* Convert messages XML stream into XPath context */
11014 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
11015 if (!message_doc
) {
11019 xpath_ctx
= xmlXPathNewContext(message_doc
);
11024 /* XXX: Standard name space for unsigned incoming direction:
11025 * http://isds.czechpoint.cz/v20/
11027 * XXX: Name spaces mangled for signed outgoing direction:
11028 * http://isds.czechpoint.cz/v20/SentMessage:
11030 * <q:MessageDownloadResponse
11031 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11032 * <q:dmReturnedMessage>
11033 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11034 * <p:dmID>151916</p:dmID>
11037 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11039 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11040 * </q:dmReturnedMessage>
11041 * </q:MessageDownloadResponse>
11043 * XXX: Name spaces mangled for signed incoming direction:
11044 * http://isds.czechpoint.cz/v20/message:
11046 * <q:MessageDownloadResponse
11047 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11048 * <q:dmReturnedMessage>
11049 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11050 * <p:dmID>151916</p:dmID>
11053 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11055 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11056 * </q:dmReturnedMessage>
11057 * </q:MessageDownloadResponse>
11059 * Stupidity of ISDS developers is unlimited */
11060 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
11064 result
= xmlXPathEvalExpression(
11065 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11071 /* Empty message */
11072 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11073 isds_printf_message(context
,
11074 _("XML document does not contain "
11075 "sisds:dmReturnedMessage element"));
11079 /* More messages */
11080 if (result
->nodesetval
->nodeNr
> 1) {
11081 isds_printf_message(context
,
11082 _("XML document has more sisds:dmReturnedMessage elements"));
11087 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11089 /* Extract the message */
11090 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
11091 if (err
) goto leave
;
11093 /* Append raw buffer into message */
11094 (*message
)->raw_type
= raw_type
;
11095 switch (strategy
) {
11096 case BUFFER_DONT_STORE
:
11099 (*message
)->raw
= malloc(length
);
11100 if (!(*message
)->raw
) {
11104 memcpy((*message
)->raw
, buffer
, length
);
11105 (*message
)->raw_length
= length
;
11108 (*message
)->raw
= (void *) buffer
;
11109 (*message
)->raw_length
= length
;
11119 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
11120 isds_message_free(message
);
11123 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11124 xmlXPathFreeObject(result
);
11125 xmlXPathFreeContext(xpath_ctx
);
11126 if (!*message
|| !(*message
)->xml
) {
11127 xmlFreeDoc(message_doc
);
11131 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
11136 /* Determine type of raw message or delivery info according some heuristics.
11137 * It does not validate the raw blob.
11138 * @context is session context
11139 * @raw_type returns content type of @buffer. Valid only if exit code of this
11140 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11141 * reallocated memory.
11142 * @buffer is message raw representation.
11143 * @length is length of buffer in bytes. */
11144 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
11145 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
11147 void *xml_stream
= NULL
;
11148 size_t xml_stream_length
= 0;
11149 xmlDocPtr document
= NULL
;
11150 xmlNodePtr root
= NULL
;
11152 if (!context
) return IE_INVALID_CONTEXT
;
11153 zfree(context
->long_message
);
11154 if (length
== 0 || !buffer
) return IE_INVAL
;
11155 if (!raw_type
) return IE_INVAL
;
11158 err
= _isds_extract_cms_data(context
, buffer
, length
,
11159 &xml_stream
, &xml_stream_length
);
11161 xml_stream
= (void *) buffer
;
11162 xml_stream_length
= (size_t) length
;
11167 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
11169 isds_printf_message(context
,
11170 _("Could not parse data as XML document"));
11175 /* Get root element */
11176 root
= xmlDocGetRootElement(document
);
11178 isds_printf_message(context
,
11179 _("XML document is missing root element"));
11184 if (!root
->ns
|| !root
->ns
->href
) {
11185 isds_printf_message(context
,
11186 _("Root element does not belong to any name space"));
11191 /* Test name space */
11192 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
11193 if (xml_stream
== buffer
)
11194 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
11196 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
11197 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
11198 if (xml_stream
== buffer
)
11199 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
11201 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
11202 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
11203 if (xml_stream
== buffer
)
11204 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
11206 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
11207 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
11208 if (xml_stream
!= buffer
) {
11209 isds_printf_message(context
,
11210 _("Document in ISDS name space is encapsulated into CMS" ));
11212 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
11213 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
11214 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
11215 *raw_type
= RAWTYPE_DELIVERYINFO
;
11217 isds_printf_message(context
,
11218 _("Unknown root element in ISDS name space"));
11222 isds_printf_message(context
,
11223 _("Unknown name space"));
11228 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11229 xmlFreeDoc(document
);
11234 /* Download signed incoming/outgoing message identified by ID.
11235 * @context is session context
11236 * @output is true for outgoing message, false for incoming message
11237 * @message_id is message identifier (you can get them from
11238 * isds_get_list_of_{sent,received}_messages())
11239 * @message is automatically reallocated message retrieved from ISDS. The raw
11240 * member will be filled with PKCS#7 structure in DER format. */
11241 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
11242 const _Bool outgoing
, const char *message_id
,
11243 struct isds_message
**message
) {
11245 isds_error err
= IE_SUCCESS
;
11247 xmlDocPtr response
= NULL
;
11248 xmlChar
*code
= NULL
, *status_message
= NULL
;
11249 xmlXPathContextPtr xpath_ctx
= NULL
;
11250 xmlXPathObjectPtr result
= NULL
;
11251 char *encoded_structure
= NULL
;
11253 size_t raw_length
= 0;
11256 if (!context
) return IE_INVALID_CONTEXT
;
11257 zfree(context
->long_message
);
11258 if (!message
) return IE_INVAL
;
11259 isds_message_free(message
);
11262 /* Do request and check for success */
11263 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
11264 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11265 BAD_CAST
"SignedMessageDownload",
11266 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11267 if (err
) goto leave
;
11269 /* Find signed message, extract it into raw and maybe free
11271 err
= find_extract_signed_data_free_response(context
,
11272 (xmlChar
*)message_id
, &response
,
11273 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11274 BAD_CAST
"SignedMessageDownload",
11275 &raw
, &raw_length
);
11276 if (err
) goto leave
;
11278 /* Parse message */
11279 err
= isds_load_message(context
,
11280 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11281 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
11282 raw
, raw_length
, message
, BUFFER_MOVE
);
11283 if (err
) goto leave
;
11289 isds_message_free(message
);
11292 free(encoded_structure
);
11293 xmlXPathFreeObject(result
);
11294 xmlXPathFreeContext(xpath_ctx
);
11298 free(status_message
);
11299 xmlFreeDoc(response
);
11302 isds_log(ILF_ISDS
, ILL_DEBUG
,
11304 _("SignedSentMessageDownload request processed by server "
11305 "successfully.\n") :
11306 _("SignedMessageDownload request processed by server "
11309 #else /* not HAVE_LIBCURL */
11316 /* Download signed incoming message identified by ID.
11317 * @context is session context
11318 * @message_id is message identifier (you can get them from
11319 * isds_get_list_of_received_messages())
11320 * @message is automatically reallocated message retrieved from ISDS. The raw
11321 * member will be filled with PKCS#7 structure in DER format. */
11322 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
11323 const char *message_id
, struct isds_message
**message
) {
11324 return isds_get_signed_message(context
, 0, message_id
, message
);
11328 /* Download signed outgoing message identified by ID.
11329 * @context is session context
11330 * @message_id is message identifier (you can get them from
11331 * isds_get_list_of_sent_messages())
11332 * @message is automatically reallocated message retrieved from ISDS. The raw
11333 * member will be filled with PKCS#7 structure in DER format. */
11334 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
11335 const char *message_id
, struct isds_message
**message
) {
11336 return isds_get_signed_message(context
, 1, message_id
, message
);
11340 /* Get type and name of user who sent a message identified by ID.
11341 * @context is session context
11342 * @message_id is message identifier
11343 * @sender_type is pointer to automatically allocated type of sender detected
11344 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11345 * library or to the server, NULL will be returned. Pass NULL if you don't
11347 * @raw_sender_type is automatically reallocated UTF-8 string describing
11348 * sender type or NULL if not known to server. Pass NULL if you don't care.
11349 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11350 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11351 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
11352 const char *message_id
, isds_sender_type
**sender_type
,
11353 char **raw_sender_type
, char **sender_name
) {
11354 isds_error err
= IE_SUCCESS
;
11356 xmlDocPtr response
= NULL
;
11357 xmlChar
*code
= NULL
, *status_message
= NULL
;
11358 xmlXPathContextPtr xpath_ctx
= NULL
;
11359 xmlXPathObjectPtr result
= NULL
;
11360 char *type_string
= NULL
;
11363 if (!context
) return IE_INVALID_CONTEXT
;
11364 zfree(context
->long_message
);
11365 if (sender_type
) zfree(*sender_type
);
11366 if (raw_sender_type
) zfree(*raw_sender_type
);
11367 if (sender_name
) zfree(*sender_name
);
11368 if (!message_id
) return IE_INVAL
;
11371 /* Do request and check for success */
11372 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11373 BAD_CAST
"GetMessageAuthor",
11374 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11375 if (err
) goto leave
;
11378 xpath_ctx
= xmlXPathNewContext(response
);
11383 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11387 result
= xmlXPathEvalExpression(
11388 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
11393 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11394 isds_log_message(context
,
11395 _("Missing GetMessageAuthorResponse element"));
11399 if (result
->nodesetval
->nodeNr
> 1) {
11400 isds_log_message(context
,
11401 _("Multiple GetMessageAuthorResponse element"));
11405 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11406 xmlXPathFreeObject(result
); result
= NULL
;
11408 /* Fill output arguments in */
11409 EXTRACT_STRING("isds:userType", type_string
);
11410 if (NULL
!= type_string
) {
11411 if (NULL
!= sender_type
) {
11412 *sender_type
= calloc(1, sizeof(**sender_type
));
11413 if (NULL
== *sender_type
) {
11418 err
= string2isds_sender_type((xmlChar
*)type_string
,
11421 zfree(*sender_type
);
11422 if (err
== IE_ENUM
) {
11424 char *type_string_locale
= _isds_utf82locale(type_string
);
11425 isds_log(ILF_ISDS
, ILL_WARNING
,
11426 _("Unknown isds:userType value: %s"),
11427 type_string_locale
);
11428 free(type_string_locale
);
11433 if (NULL
!= sender_name
)
11434 EXTRACT_STRING("isds:authorName", *sender_name
);
11438 if (NULL
!= sender_type
) zfree(*sender_type
);
11439 zfree(type_string
);
11440 if (NULL
!= sender_name
) zfree(*sender_name
);
11442 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11444 xmlXPathFreeObject(result
);
11445 xmlXPathFreeContext(xpath_ctx
);
11448 free(status_message
);
11449 xmlFreeDoc(response
);
11452 isds_log(ILF_ISDS
, ILL_DEBUG
,
11453 _("GetMessageAuthor request processed by server "
11454 "successfully.\n"));
11455 #else /* not HAVE_LIBCURL */
11462 /* Retrieve hash of message identified by ID stored in ISDS.
11463 * @context is session context
11464 * @message_id is message identifier
11465 * @hash is automatically reallocated message hash downloaded from ISDS.
11466 * Message must exist in system and must not be deleted. */
11467 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11468 const char *message_id
, struct isds_hash
**hash
) {
11470 isds_error err
= IE_SUCCESS
;
11472 xmlDocPtr response
= NULL
;
11473 xmlChar
*code
= NULL
, *status_message
= NULL
;
11474 xmlXPathContextPtr xpath_ctx
= NULL
;
11475 xmlXPathObjectPtr result
= NULL
;
11478 if (!context
) return IE_INVALID_CONTEXT
;
11479 zfree(context
->long_message
);
11481 isds_hash_free(hash
);
11484 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11485 BAD_CAST
"VerifyMessage", message_id
,
11486 &response
, NULL
, NULL
, &code
, &status_message
);
11487 if (err
) goto leave
;
11491 xpath_ctx
= xmlXPathNewContext(response
);
11496 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11500 result
= xmlXPathEvalExpression(
11501 BAD_CAST
"/isds:VerifyMessageResponse",
11507 /* Empty response */
11508 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11509 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11510 isds_printf_message(context
,
11511 _("Server did not return any response for ID `%s' "
11512 "on VerifyMessage request"), message_id_locale
);
11513 free(message_id_locale
);
11517 /* More responses */
11518 if (result
->nodesetval
->nodeNr
> 1) {
11519 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11520 isds_printf_message(context
,
11521 _("Server did return more responses for ID `%s' "
11522 "on VerifyMessage request"), message_id_locale
);
11523 free(message_id_locale
);
11528 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11530 /* Extract the hash */
11531 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11535 isds_hash_free(hash
);
11538 xmlXPathFreeObject(result
);
11539 xmlXPathFreeContext(xpath_ctx
);
11542 free(status_message
);
11543 xmlFreeDoc(response
);
11546 isds_log(ILF_ISDS
, ILL_DEBUG
,
11547 _("VerifyMessage request processed by server "
11550 #else /* not HAVE_LIBCURL */
11557 /* Erase message specified by @message_id from long term storage. Other
11558 * message cannot be erased on user request.
11559 * @context is session context
11560 * @message_id is message identifier.
11561 * @incoming is true for incoming message, false for outgoing message.
11563 * IE_SUCCESS if message has ben removed
11564 * IE_INVAL if message does not exist in long term storage or message
11565 * belongs to different box
11566 * TODO: IE_NOEPRM if user has no permission to erase a message */
11567 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11568 const char *message_id
, _Bool incoming
) {
11569 isds_error err
= IE_SUCCESS
;
11571 xmlNodePtr request
= NULL
, node
;
11572 xmlNsPtr isds_ns
= NULL
;
11573 xmlDocPtr response
= NULL
;
11574 xmlChar
*code
= NULL
, *status_message
= NULL
;
11577 if (!context
) return IE_INVALID_CONTEXT
;
11578 zfree(context
->long_message
);
11579 if (NULL
== message_id
) return IE_INVAL
;
11582 /* Check if connection is established
11583 * TODO: This check should be done downstairs. */
11584 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11586 /* Build request */
11587 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11589 isds_log_message(context
,
11590 _("Could build EraseMessage request"));
11593 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11595 isds_log_message(context
, _("Could not create ISDS name space"));
11596 xmlFreeNode(request
);
11599 xmlSetNs(request
, isds_ns
);
11601 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11602 if (err
) goto leave
;
11603 INSERT_STRING(request
, "dmID", message_id
);
11605 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11609 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11610 "message ID %s to ISDS\n"), message_id
);
11611 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11612 xmlFreeNode(request
); request
= NULL
;
11615 isds_log(ILF_ISDS
, ILL_DEBUG
,
11616 _("Processing ISDS response on EraseMessage request "
11621 /* Check for response status */
11622 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11623 &code
, &status_message
, NULL
);
11625 isds_log(ILF_ISDS
, ILL_DEBUG
,
11626 _("ISDS response on EraseMessage request is missing "
11631 /* Check server status code */
11632 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11633 isds_log_message(context
, _("Message to erase belongs to other box"));
11635 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11636 isds_log_message(context
, _("Message to erase is not saved in "
11637 "long term storage or the direction does not match"));
11639 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11640 char *code_locale
= _isds_utf82locale((char*) code
);
11641 char *message_locale
= _isds_utf82locale((char*) status_message
);
11642 isds_log(ILF_ISDS
, ILL_DEBUG
,
11643 _("Server refused EraseMessage request "
11644 "(code=%s, message=%s)\n"),
11645 code_locale
, message_locale
);
11646 isds_log_message(context
, message_locale
);
11648 free(message_locale
);
11655 free(status_message
);
11656 xmlFreeDoc(response
);
11657 xmlFreeNode(request
);
11660 isds_log(ILF_ISDS
, ILL_DEBUG
,
11661 _("EraseMessage request processed by server "
11664 #else /* not HAVE_LIBCURL */
11671 /* Mark message as read. This is a transactional commit function to acknowledge
11672 * to ISDS the message has been downloaded and processed by client properly.
11673 * @context is session context
11674 * @message_id is message identifier. */
11675 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11676 const char *message_id
) {
11678 isds_error err
= IE_SUCCESS
;
11680 xmlDocPtr response
= NULL
;
11681 xmlChar
*code
= NULL
, *status_message
= NULL
;
11684 if (!context
) return IE_INVALID_CONTEXT
;
11685 zfree(context
->long_message
);
11688 /* Do request and check for success */
11689 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11690 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11691 &response
, NULL
, NULL
, &code
, &status_message
);
11694 free(status_message
);
11695 xmlFreeDoc(response
);
11698 isds_log(ILF_ISDS
, ILL_DEBUG
,
11699 _("MarkMessageAsDownloaded request processed by server "
11702 #else /* not HAVE_LIBCURL */
11709 /* Mark message as received by recipient. This is applicable only to
11710 * commercial message. Use envelope->dmType message member to distinguish
11711 * commercial message from government message. Government message is
11712 * received automatically (by law), commercial message on recipient request.
11713 * @context is session context
11714 * @message_id is message identifier. */
11715 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11716 const char *message_id
) {
11718 isds_error err
= IE_SUCCESS
;
11720 xmlDocPtr response
= NULL
;
11721 xmlChar
*code
= NULL
, *status_message
= NULL
;
11724 if (!context
) return IE_INVALID_CONTEXT
;
11725 zfree(context
->long_message
);
11728 /* Do request and check for success */
11729 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11730 BAD_CAST
"ConfirmDelivery", message_id
,
11731 &response
, NULL
, NULL
, &code
, &status_message
);
11734 free(status_message
);
11735 xmlFreeDoc(response
);
11738 isds_log(ILF_ISDS
, ILL_DEBUG
,
11739 _("ConfirmDelivery request processed by server "
11742 #else /* not HAVE_LIBCURL */
11749 /* Send document for authorized conversion into Czech POINT system.
11750 * This is public anonymous service, no log-in necessary. Special context is
11751 * used to reuse keep-a-live HTTPS connection.
11752 * @context is Czech POINT session context. DO NOT use context connected to
11753 * ISDS server. Use new context or context used by this function previously.
11754 * @document is document to convert. Only data, data_length, dmFileDescr and
11755 * is_xml members are significant. Be ware that not all document formats can be
11756 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11757 * @id is reallocated identifier assigned by Czech POINT system to
11758 * your document on submit. Use is to tell it to Czech POINT officer.
11759 * @date is reallocated document submit date (submitted documents
11760 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11762 isds_error
czp_convert_document(struct isds_ctx
*context
,
11763 const struct isds_document
*document
,
11764 char **id
, struct tm
**date
) {
11765 isds_error err
= IE_SUCCESS
;
11767 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11768 xmlNodePtr request
= NULL
, node
;
11769 xmlDocPtr response
= NULL
;
11771 xmlXPathContextPtr xpath_ctx
= NULL
;
11772 xmlXPathObjectPtr result
= NULL
;
11773 long int status
= -1;
11774 long int *status_ptr
= &status
;
11775 char *string
= NULL
;
11779 if (!context
) return IE_INVALID_CONTEXT
;
11780 zfree(context
->long_message
);
11781 if (!document
|| !id
|| !date
) return IE_INVAL
;
11783 if (document
->is_xml
) {
11784 isds_log_message(context
,
11785 _("XML documents cannot be submitted to conversion"));
11789 /* Free output arguments */
11794 /* Store configuration */
11795 context
->type
= CTX_TYPE_CZP
;
11796 free(context
->url
);
11797 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11798 if (!(context
->url
))
11801 /* Prepare CURL handle if not yet connected */
11802 if (!context
->curl
) {
11803 context
->curl
= curl_easy_init();
11804 if (!(context
->curl
))
11808 /* Build conversion request */
11809 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11811 isds_log_message(context
,
11812 _("Could not build Czech POINT conversion request"));
11815 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11817 isds_log_message(context
,
11818 _("Could not create Czech POINT deposit name space"));
11819 xmlFreeNode(request
);
11822 xmlSetNs(request
, deposit_ns
);
11824 /* Insert children. They are in empty namespace! */
11825 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11827 isds_log_message(context
, _("Could not create empty name space"));
11831 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11832 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11833 document
->dmFileDescr
);
11835 /* Document encoded in Base64 */
11836 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11837 document
->data
, document
->data_length
);
11838 if (err
) goto leave
;
11840 isds_log(ILF_ISDS
, ILL_DEBUG
,
11841 _("Submitting document for conversion into Czech POINT deposit"));
11843 /* Send conversion request */
11844 err
= _czp_czpdeposit(context
, request
, &response
);
11845 xmlFreeNode(request
); request
= NULL
;
11848 czp_do_close_connection(context
);
11853 /* Extract response */
11854 xpath_ctx
= xmlXPathNewContext(response
);
11859 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11863 result
= xmlXPathEvalExpression(
11864 BAD_CAST
"/deposit:saveDocumentResponse/return",
11870 /* Empty response */
11871 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11872 isds_printf_message(context
,
11873 _("Missing `return' element in Czech POINT deposit response"));
11877 /* More responses */
11878 if (result
->nodesetval
->nodeNr
> 1) {
11879 isds_printf_message(context
,
11880 _("Multiple `return' element in Czech POINT deposit response"));
11885 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11888 EXTRACT_LONGINT("status", status_ptr
, 1);
11890 EXTRACT_STRING("statusMsg", string
);
11891 char *string_locale
= _isds_utf82locale(string
);
11892 isds_printf_message(context
,
11893 _("Czech POINT deposit refused document for conversion "
11894 "(code=%ld, message=%s)"),
11895 status
, string_locale
);
11896 free(string_locale
);
11901 /* Get document ID */
11902 EXTRACT_STRING("documentID", *id
);
11904 /* Get submit date */
11905 EXTRACT_STRING("dateInserted", string
);
11907 *date
= calloc(1, sizeof(**date
));
11912 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11914 if (err
== IE_NOTSUP
) {
11916 char *string_locale
= _isds_utf82locale(string
);
11917 isds_printf_message(context
,
11918 _("Invalid dateInserted value: %s"), string_locale
);
11919 free(string_locale
);
11927 xmlXPathFreeObject(result
);
11928 xmlXPathFreeContext(xpath_ctx
);
11930 xmlFreeDoc(response
);
11931 xmlFreeNode(request
);
11934 char *id_locale
= _isds_utf82locale((char *) *id
);
11935 isds_log(ILF_ISDS
, ILL_DEBUG
,
11936 _("Document %s has been submitted for conversion "
11937 "to server successfully\n"), id_locale
);
11940 #else /* not HAVE_LIBCURL */
11947 /* Close possibly opened connection to Czech POINT document deposit.
11948 * @context is Czech POINT session context. */
11949 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11950 if (!context
) return IE_INVALID_CONTEXT
;
11951 zfree(context
->long_message
);
11953 return czp_do_close_connection(context
);
11960 /* Send request for new box creation in testing ISDS instance.
11961 * It's not possible to request for a production box currently, as it
11962 * communicates via e-mail.
11963 * XXX: This function does not work either. Server complains about invalid
11965 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11967 * @context is special session context for box creation request. DO NOT use
11968 * standard context as it could reveal your password. Use fresh new context or
11969 * context previously used by this function.
11970 * @box is box description to create including single primary user (in case of
11971 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11972 * ignored. It outputs box ID assigned by ISDS in dbID element.
11973 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11974 * box, or contact address of PFO box owner). The email member is mandatory as
11975 * it will be used to deliver credentials.
11976 * @former_names is former name of box owner. Pass NULL if you don't care.
11977 * @approval is optional external approval of box manipulation
11978 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11979 * NULL, if you don't care.*/
11980 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11981 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11982 const char *former_names
, const struct isds_approval
*approval
,
11983 char **refnumber
) {
11984 isds_error err
= IE_SUCCESS
;
11986 xmlNodePtr request
= NULL
;
11987 xmlDocPtr response
= NULL
;
11988 xmlXPathContextPtr xpath_ctx
= NULL
;
11989 xmlXPathObjectPtr result
= NULL
;
11993 if (!context
) return IE_INVALID_CONTEXT
;
11994 zfree(context
->long_message
);
11995 if (!box
) return IE_INVAL
;
11998 if (!box
->email
|| box
->email
[0] == '\0') {
11999 isds_log_message(context
, _("E-mail field is mandatory"));
12003 /* Scratch box ID */
12006 /* Store configuration */
12007 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
12008 free(context
->url
);
12009 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
12010 if (!(context
->url
))
12013 /* Prepare CURL handle if not yet connected */
12014 if (!context
->curl
) {
12015 context
->curl
= curl_easy_init();
12016 if (!(context
->curl
))
12020 /* Build CreateDataBox request */
12021 err
= build_CreateDBInput_request(context
,
12022 &request
, BAD_CAST
"CreateDataBox",
12023 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
12024 if (err
) goto leave
;
12026 /* Send it to server and process response */
12027 err
= send_destroy_request_check_response(context
,
12028 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
12029 &response
, (xmlChar
**) refnumber
, NULL
);
12030 if (err
) goto leave
;
12032 /* Extract box ID */
12033 xpath_ctx
= xmlXPathNewContext(response
);
12038 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12042 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
12045 xmlXPathFreeObject(result
);
12046 xmlXPathFreeContext(xpath_ctx
);
12047 xmlFreeDoc(response
);
12048 xmlFreeNode(request
);
12051 isds_log(ILF_ISDS
, ILL_DEBUG
,
12052 _("CreateDataBox request processed by server successfully.\n"));
12054 #else /* not HAVE_LIBCURL */
12062 /* Submit CMS signed message to ISDS to verify its originality. This is
12063 * stronger form of isds_verify_message_hash() because ISDS does more checks
12064 * than simple one (potentialy old weak) hash comparison.
12065 * @context is session context
12066 * @message is memory with raw CMS signed message bit stream
12067 * @length is @message size in bytes
12069 * IE_SUCCESS if message originates in ISDS
12070 * IE_NOTEQUAL if message is unknown to ISDS
12071 * other code for other errors */
12072 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
12073 const void *message
, size_t length
) {
12074 isds_error err
= IE_SUCCESS
;
12076 xmlNsPtr isds_ns
= NULL
;
12077 xmlNodePtr request
= NULL
;
12078 xmlDocPtr response
= NULL
;
12079 xmlXPathContextPtr xpath_ctx
= NULL
;
12080 xmlXPathObjectPtr result
= NULL
;
12081 _Bool
*authentic
= NULL
;
12084 if (!context
) return IE_INVALID_CONTEXT
;
12085 zfree(context
->long_message
);
12086 if (!message
|| length
== 0) return IE_INVAL
;
12089 /* Check if connection is established
12090 * TODO: This check should be done downstairs. */
12091 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12094 /* Build AuthenticateMessage request */
12095 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
12097 isds_log_message(context
,
12098 _("Could not build AuthenticateMessage request"));
12101 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12103 isds_log_message(context
, _("Could not create ISDS name space"));
12104 xmlFreeNode(request
);
12107 xmlSetNs(request
, isds_ns
);
12109 /* Insert Base64 encoded message */
12110 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
12112 if (err
) goto leave
;
12114 /* Send request to server and process response */
12115 err
= send_destroy_request_check_response(context
,
12116 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
12117 &response
, NULL
, NULL
);
12118 if (err
) goto leave
;
12121 /* ISDS has decided */
12122 xpath_ctx
= xmlXPathNewContext(response
);
12127 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12132 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
12135 isds_log_message(context
,
12136 _("Server did not return any response on "
12137 "AuthenticateMessage request"));
12142 isds_log(ILF_ISDS
, ILL_DEBUG
,
12143 _("ISDS authenticated the message successfully\n"));
12145 isds_log_message(context
, _("ISDS does not know the message"));
12152 xmlXPathFreeObject(result
);
12153 xmlXPathFreeContext(xpath_ctx
);
12155 xmlFreeDoc(response
);
12156 xmlFreeNode(request
);
12157 #else /* not HAVE_LIBCURL */
12165 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12166 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12168 * @context is session context
12169 * @input_data is memory with raw CMS signed message or delivery info bit
12170 * stream to re-sign
12171 * @input_length is @input_data size in bytes
12172 * @output_data is pointer to auto-allocated memory where to store re-signed
12173 * input data blob. Caller must free it.
12174 * @output_data is pointer where to store @output_data size in bytes
12175 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12176 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12178 * IE_SUCCESS if CMS blob has been re-signed successfully
12179 * other code for other errors */
12180 isds_error
isds_resign_message(struct isds_ctx
*context
,
12181 const void *input_data
, size_t input_length
,
12182 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
12183 isds_error err
= IE_SUCCESS
;
12185 xmlNsPtr isds_ns
= NULL
;
12186 xmlNodePtr request
= NULL
;
12187 xmlDocPtr response
= NULL
;
12188 xmlXPathContextPtr xpath_ctx
= NULL
;
12189 xmlXPathObjectPtr result
= NULL
;
12190 char *string
= NULL
;
12191 const xmlChar
*codes
[] = {
12198 const char *meanings
[] = {
12200 "Message is not original",
12201 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12202 "Time stamp could not been generated in time"
12204 const isds_error errors
[] = {
12210 struct code_map_isds_error map
= {
12212 .meanings
= meanings
,
12217 if (NULL
!= output_data
) *output_data
= NULL
;
12218 if (NULL
!= output_length
) *output_length
= 0;
12219 if (NULL
!= valid_to
) *valid_to
= NULL
;
12221 if (NULL
== context
) return IE_INVALID_CONTEXT
;
12222 zfree(context
->long_message
);
12223 if (NULL
== input_data
|| 0 == input_length
) {
12224 isds_log_message(context
, _("Empty CMS blob on input"));
12227 if (NULL
== output_data
|| NULL
== output_length
) {
12228 isds_log_message(context
,
12229 _("NULL pointer provided for output CMS blob"));
12234 /* Check if connection is established
12235 * TODO: This check should be done downstairs. */
12236 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12239 /* Build Re-signISDSDocument request */
12240 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
12242 isds_log_message(context
,
12243 _("Could not build Re-signISDSDocument request"));
12246 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12248 isds_log_message(context
, _("Could not create ISDS name space"));
12249 xmlFreeNode(request
);
12252 xmlSetNs(request
, isds_ns
);
12254 /* Insert Base64 encoded CMS blob */
12255 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
12256 input_data
, input_length
);
12257 if (err
) goto leave
;
12259 /* Send request to server and process response */
12260 err
= send_destroy_request_check_response(context
,
12261 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
12262 &response
, NULL
, &map
);
12263 if (err
) goto leave
;
12266 /* Extract re-signed data */
12267 xpath_ctx
= xmlXPathNewContext(response
);
12272 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12276 result
= xmlXPathEvalExpression(
12277 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
12282 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
12283 isds_log_message(context
,
12284 _("Missing Re-signISDSDocumentResponse element"));
12288 if (result
->nodesetval
->nodeNr
> 1) {
12289 isds_log_message(context
,
12290 _("Multiple Re-signISDSDocumentResponse element"));
12294 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
12295 xmlXPathFreeObject(result
); result
= NULL
;
12297 EXTRACT_STRING("isds:dmResultDoc", string
);
12298 /* Decode non-empty data */
12299 if (NULL
!= string
&& string
[0] != '\0') {
12300 *output_length
= _isds_b64decode(string
, output_data
);
12301 if (*output_length
== (size_t) -1) {
12302 isds_log_message(context
,
12303 _("Error while Base64-decoding re-signed data"));
12308 isds_log_message(context
, _("Server did not send re-signed data"));
12314 if (NULL
!= valid_to
) {
12315 /* Get time stamp expiration date */
12316 EXTRACT_STRING("isds:dmValidTo", string
);
12317 if (NULL
!= string
) {
12318 *valid_to
= calloc(1, sizeof(**valid_to
));
12323 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
12325 if (err
== IE_NOTSUP
) {
12327 char *string_locale
= _isds_utf82locale(string
);
12328 isds_printf_message(context
,
12329 _("Invalid dmValidTo value: %s"), string_locale
);
12330 free(string_locale
);
12340 xmlXPathFreeObject(result
);
12341 xmlXPathFreeContext(xpath_ctx
);
12343 xmlFreeDoc(response
);
12344 xmlFreeNode(request
);
12345 #else /* not HAVE_LIBCURL */
12352 #undef INSERT_ELEMENT
12353 #undef CHECK_FOR_STRING_LENGTH
12354 #undef INSERT_STRING_ATTRIBUTE
12355 #undef INSERT_ULONGINTNOPTR
12356 #undef INSERT_ULONGINT
12357 #undef INSERT_LONGINT
12358 #undef INSERT_BOOLEAN
12359 #undef INSERT_SCALAR_BOOLEAN
12360 #undef INSERT_STRING
12361 #undef INSERT_STRING_WITH_NS
12362 #undef EXTRACT_STRING_ATTRIBUTE
12363 #undef EXTRACT_ULONGINT
12364 #undef EXTRACT_LONGINT
12365 #undef EXTRACT_BOOLEAN
12366 #undef EXTRACT_STRING
12369 /* Compute hash of message from raw representation and store it into envelope.
12370 * Original hash structure will be destroyed in envelope.
12371 * @context is session context
12372 * @message is message carrying raw XML message blob
12373 * @algorithm is desired hash algorithm to use */
12374 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
12375 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
12376 isds_error err
= IE_SUCCESS
;
12378 void *xml_stream
= NULL
;
12379 size_t xml_stream_length
;
12380 size_t phys_start
, phys_end
;
12381 char *phys_path
= NULL
;
12382 struct isds_hash
*new_hash
= NULL
;
12385 if (!context
) return IE_INVALID_CONTEXT
;
12386 zfree(context
->long_message
);
12387 if (!message
) return IE_INVAL
;
12389 if (!message
->raw
) {
12390 isds_log_message(context
,
12391 _("Message does not carry raw representation"));
12395 switch (message
->raw_type
) {
12396 case RAWTYPE_INCOMING_MESSAGE
:
12398 xml_stream
= message
->raw
;
12399 xml_stream_length
= message
->raw_length
;
12402 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
12403 nsuri
= SISDS_INCOMING_NS
;
12404 xml_stream
= message
->raw
;
12405 xml_stream_length
= message
->raw_length
;
12408 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
12409 nsuri
= SISDS_INCOMING_NS
;
12410 err
= _isds_extract_cms_data(context
,
12411 message
->raw
, message
->raw_length
,
12412 &xml_stream
, &xml_stream_length
);
12413 if (err
) goto leave
;
12416 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
12417 nsuri
= SISDS_OUTGOING_NS
;
12418 xml_stream
= message
->raw
;
12419 xml_stream_length
= message
->raw_length
;
12422 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
12423 nsuri
= SISDS_OUTGOING_NS
;
12424 err
= _isds_extract_cms_data(context
,
12425 message
->raw
, message
->raw_length
,
12426 &xml_stream
, &xml_stream_length
);
12427 if (err
) goto leave
;
12431 isds_log_message(context
, _("Bad raw representation type"));
12437 /* XXX: Hash is computed from original string representing isds:dmDm
12438 * subtree. That means no encoding, white space, xmlns attributes changes.
12439 * In other words, input for hash can be invalid XML stream. */
12440 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12441 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12442 PHYSXML_ELEMENT_SEPARATOR
,
12443 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12444 PHYSXML_ELEMENT_SEPARATOR
12445 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12449 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12450 phys_path
, &phys_start
, &phys_end
);
12453 isds_log_message(context
,
12454 _("Substring with isds:dmDM element could not be located "
12455 "in raw message"));
12461 new_hash
= calloc(1, sizeof(*new_hash
));
12466 new_hash
->algorithm
= algorithm
;
12467 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12470 isds_log_message(context
, _("Could not compute message hash"));
12474 /* Save computed hash */
12475 if (!message
->envelope
) {
12476 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12477 if (!message
->envelope
) {
12482 isds_hash_free(&message
->envelope
->hash
);
12483 message
->envelope
->hash
= new_hash
;
12487 isds_hash_free(&new_hash
);
12491 if (xml_stream
!= message
->raw
) free(xml_stream
);
12496 /* Compare two hashes.
12497 * @h1 is first hash
12498 * @h2 is another hash
12500 * IE_SUCCESS if hashes equal
12501 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12502 * IE_ENUM if not comparable, but both structures defined
12503 * IE_INVAL if some of the structures are undefined (NULL)
12504 * IE_ERROR if internal error occurs */
12505 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12506 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12507 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12508 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12509 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12510 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12512 for (size_t i
= 0; i
< h1
->length
; i
++) {
12513 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12514 return IE_NOTEQUAL
;
12520 /* Check message has gone through ISDS by comparing message hash stored in
12521 * ISDS and locally computed hash. You must provide message with valid raw
12522 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12523 * This is convenient wrapper for isds_download_message_hash(),
12524 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12525 * @context is session context
12526 * @message is message with valid raw and envelope member; envelope->hash
12527 * member will be changed during function run. Use envelope on heap only.
12529 * IE_SUCCESS if message originates in ISDS
12530 * IE_NOTEQUAL if message is unknown to ISDS
12531 * other code for other errors */
12532 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12533 struct isds_message
*message
) {
12534 isds_error err
= IE_SUCCESS
;
12535 struct isds_hash
*downloaded_hash
= NULL
;
12537 if (!context
) return IE_INVALID_CONTEXT
;
12538 zfree(context
->long_message
);
12539 if (!message
) return IE_INVAL
;
12541 if (!message
->envelope
) {
12542 isds_log_message(context
,
12543 _("Given message structure is missing envelope"));
12546 if (!message
->raw
) {
12547 isds_log_message(context
,
12548 _("Given message structure is missing raw representation"));
12552 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12554 if (err
) goto leave
;
12556 err
= isds_compute_message_hash(context
, message
,
12557 downloaded_hash
->algorithm
);
12558 if (err
) goto leave
;
12560 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12563 isds_hash_free(&downloaded_hash
);
12568 /* Search for document by document ID in list of documents. IDs are compared
12570 * @documents is list of isds_documents
12571 * @id is document identifier
12572 * @return first matching document or NULL. */
12573 const struct isds_document
*isds_find_document_by_id(
12574 const struct isds_list
*documents
, const char *id
) {
12575 const struct isds_list
*item
;
12576 const struct isds_document
*document
;
12578 for (item
= documents
; item
; item
= item
->next
) {
12579 document
= (struct isds_document
*) item
->data
;
12580 if (!document
) continue;
12582 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12590 /* Normalize @mime_type to be proper MIME type.
12591 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12592 * guess regular MIME type (e.g. "application/pdf").
12593 * @mime_type is UTF-8 encoded MIME type to fix
12594 * @return original @mime_type if no better interpretation exists, or
12595 * constant static UTF-8 encoded string with proper MIME type. */
12596 const char *isds_normalize_mime_type(const char *mime_type
) {
12597 if (!mime_type
) return NULL
;
12599 for (size_t offset
= 0;
12600 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12602 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12603 extension_map_mime
[offset
]))
12604 return (const char *) extension_map_mime
[offset
+ 1];
12611 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12612 struct isds_message **message);
12613 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12614 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12615 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12616 struct isds_address **address);
12618 int isds_message_free(struct isds_message **message);
12619 int isds_address_free(struct isds_address **address);
12623 /* Makes known all relevant namespaces to given XPath context
12624 * @xpath_ctx is XPath context
12625 * @message_ns selects proper message name space. Unsigned and signed
12626 * messages and delivery info's differ in prefix and URI. */
12627 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12628 const message_ns_type message_ns
) {
12629 const xmlChar
*message_namespace
= NULL
;
12631 if (!xpath_ctx
) return IE_ERROR
;
12633 switch(message_ns
) {
12635 message_namespace
= BAD_CAST ISDS1_NS
; break;
12636 case MESSAGE_NS_UNSIGNED
:
12637 message_namespace
= BAD_CAST ISDS_NS
; break;
12638 case MESSAGE_NS_SIGNED_INCOMING
:
12639 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12640 case MESSAGE_NS_SIGNED_OUTGOING
:
12641 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12642 case MESSAGE_NS_SIGNED_DELIVERY
:
12643 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12648 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12650 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12652 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12654 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12656 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12658 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))