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
"PFO_AUDITOR"))
2018 *type
= DBTYPE_PFO_AUDITOR
;
2019 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
2021 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
2022 *type
= DBTYPE_PO_ZAK
;
2023 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
2024 *type
= DBTYPE_PO_REQ
;
2025 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
2027 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
2028 *type
= DBTYPE_OVM_NOTAR
;
2029 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
2030 *type
= DBTYPE_OVM_EXEKUT
;
2031 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
2032 *type
= DBTYPE_OVM_REQ
;
2033 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_FO"))
2034 *type
= DBTYPE_OVM_FO
;
2035 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_PFO"))
2036 *type
= DBTYPE_OVM_PFO
;
2037 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_PO"))
2038 *type
= DBTYPE_OVM_PO
;
2045 /* Convert ISDS dbType enum @type to UTF-8 string.
2046 * @Return pointer to static string, or NULL if unknown enum value */
2047 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2049 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2050 * of view of generic public SOAP interface. */
2051 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2052 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2053 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2054 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2055 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2056 case DBTYPE_PFO_AUDITOR
: return(BAD_CAST
"PFO_AUDITOR"); break;
2057 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2058 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2059 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2060 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2061 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2062 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2063 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2064 case DBTYPE_OVM_FO
: return(BAD_CAST
"OVM_FO"); break;
2065 case DBTYPE_OVM_PFO
: return(BAD_CAST
"OVM_PFO"); break;
2066 case DBTYPE_OVM_PO
: return(BAD_CAST
"OVM_PO"); break;
2067 default: return NULL
; break;
2072 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2073 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2074 if (!string
|| !type
) return IE_INVAL
;
2076 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2077 *type
= USERTYPE_PRIMARY
;
2078 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2079 *type
= USERTYPE_ENTRUSTED
;
2080 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2081 *type
= USERTYPE_ADMINISTRATOR
;
2082 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2083 *type
= USERTYPE_OFFICIAL
;
2084 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2085 *type
= USERTYPE_OFFICIAL_CERT
;
2086 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2087 *type
= USERTYPE_LIQUIDATOR
;
2088 else if (!xmlStrcmp(string
, BAD_CAST
"RECEIVER"))
2089 *type
= USERTYPE_RECEIVER
;
2090 else if (!xmlStrcmp(string
, BAD_CAST
"GUARDIAN"))
2091 *type
= USERTYPE_GUARDIAN
;
2098 /* Convert ISDS userType enum @type to UTF-8 string.
2099 * @Return pointer to static string, or NULL if unknown enum value */
2100 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2102 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2103 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2104 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2105 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2106 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2107 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2108 case USERTYPE_RECEIVER
: return(BAD_CAST
"RECEIVER"); break;
2109 case USERTYPE_GUARDIAN
: return(BAD_CAST
"GUARDIAN"); break;
2110 default: return NULL
; break;
2115 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2116 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2117 isds_sender_type
*type
) {
2118 if (!string
|| !type
) return IE_INVAL
;
2120 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2121 *type
= SENDERTYPE_PRIMARY
;
2122 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2123 *type
= SENDERTYPE_ENTRUSTED
;
2124 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2125 *type
= SENDERTYPE_ADMINISTRATOR
;
2126 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2127 *type
= SENDERTYPE_OFFICIAL
;
2128 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2129 *type
= SENDERTYPE_VIRTUAL
;
2130 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2131 *type
= SENDERTYPE_OFFICIAL_CERT
;
2132 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2133 *type
= SENDERTYPE_LIQUIDATOR
;
2134 else if (!xmlStrcmp(string
, BAD_CAST
"RECEIVER"))
2135 *type
= SENDERTYPE_RECEIVER
;
2136 else if (!xmlStrcmp(string
, BAD_CAST
"GUARDIAN"))
2137 *type
= SENDERTYPE_GUARDIAN
;
2144 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2145 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2146 isds_payment_type
*type
) {
2147 if (!string
|| !type
) return IE_INVAL
;
2149 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2150 *type
= PAYMENT_SENDER
;
2151 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2152 *type
= PAYMENT_RESPONSE
;
2153 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2154 *type
= PAYMENT_SPONSOR
;
2155 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2156 *type
= PAYMENT_SPONSOR_LIMITED
;
2157 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2158 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2159 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2160 *type
= PAYMENT_STAMP
;
2167 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2168 * ciEventType is integer but we convert it from string representation
2170 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2171 isds_credit_event_type
*type
) {
2172 if (!string
|| !type
) return IE_INVAL
;
2174 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2175 *type
= ISDS_CREDIT_CHARGED
;
2176 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2177 *type
= ISDS_CREDIT_DISCHARGED
;
2178 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2179 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2180 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2181 *type
= ISDS_CREDIT_STORAGE_SET
;
2182 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2183 *type
= ISDS_CREDIT_EXPIRED
;
2190 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2191 * @Return pointer to static string, or NULL if unknown enum value */
2192 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2194 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2195 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2196 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2197 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2198 default: return NULL
; break;
2203 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2204 * ISDSSearch2/searchType value.
2205 * @Return pointer to static string, or NULL if unknown enum value */
2206 static const xmlChar
*isds_fulltext_target2string(
2207 const isds_fulltext_target type
) {
2209 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2210 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2211 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2212 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2213 default: return NULL
; break;
2216 #endif /* HAVE_LIBCURL */
2219 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2220 * @Return IE_ENUM if @string is not valid enum member */
2221 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2222 isds_FileMetaType
*type
) {
2223 if (!string
|| !type
) return IE_INVAL
;
2225 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2226 *type
= FILEMETATYPE_MAIN
;
2227 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2228 *type
= FILEMETATYPE_ENCLOSURE
;
2229 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2230 *type
= FILEMETATYPE_SIGNATURE
;
2231 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2232 *type
= FILEMETATYPE_META
;
2239 /* Convert UTF-8 @string to ISDS hash @algorithm.
2240 * @Return IE_ENUM if @string is not valid enum member */
2241 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2242 isds_hash_algorithm
*algorithm
) {
2243 if (!string
|| !algorithm
) return IE_INVAL
;
2245 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2246 *algorithm
= HASH_ALGORITHM_MD5
;
2247 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2248 *algorithm
= HASH_ALGORITHM_SHA_1
;
2249 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2250 *algorithm
= HASH_ALGORITHM_SHA_224
;
2251 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2252 *algorithm
= HASH_ALGORITHM_SHA_256
;
2253 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2254 *algorithm
= HASH_ALGORITHM_SHA_384
;
2255 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2256 *algorithm
= HASH_ALGORITHM_SHA_512
;
2264 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2265 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2266 if (!time
|| !string
) return IE_INVAL
;
2268 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2269 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2276 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2277 * respects the @time microseconds too. */
2278 static isds_error
timeval2timestring(const struct timeval
*time
,
2281 time_t seconds_as_time_t
;
2283 if (!time
|| !string
) return IE_INVAL
;
2285 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2286 * 32-bit long in Microsoft API. Convert value to the type expected by
2288 seconds_as_time_t
= time
->tv_sec
;
2289 if (!gmtime_r(&seconds_as_time_t
, &broken
)) return IE_DATE
;
2290 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2292 /* TODO: small negative year should be formatted as "-0012". This is not
2293 * true for glibc "%04d". We should implement it.
2294 * time->tv_usec type is su_seconds_t which is required to be signed
2295 * integer to accomodate values from range [-1, 1000000].
2296 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2297 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2298 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2299 * of the range check above. */
2300 if (-1 == isds_asprintf((char **) string
,
2301 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32
,
2302 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2303 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2304 (int32_t)time
->tv_usec
))
2309 #endif /* HAVE_LIBCURL */
2312 /* Convert UTF-8 ISO 8601 date-time @string to static struct timeval.
2313 * It respects microseconds too. Microseconds are rounded half up.
2314 * In case of error, @time will be undefined. */
2315 static isds_error
timestring2static_timeval(const xmlChar
*string
,
2316 struct timeval
*time
) {
2318 char *offset
, *delim
, *endptr
;
2319 const int subsecond_resolution
= 6;
2320 char subseconds
[subsecond_resolution
+ 1];
2322 int offset_hours
, offset_minutes
;
2324 long int long_number
;
2329 if (!time
) return IE_INVAL
;
2334 memset(&broken
, 0, sizeof(broken
));
2335 memset(time
, 0, sizeof(*time
));
2338 /* xsd:date is ISO 8601 string, thus ASCII */
2339 /*TODO: negative year */
2343 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2344 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2345 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2350 broken
.tm_year
-= 1900;
2352 broken
.tm_isdst
= -1;
2353 offset
= (char*)string
+ i
;
2355 /* Parse date and time without subseconds and offset */
2356 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2362 /* Get subseconds */
2363 if (*offset
== '.' ) {
2366 /* Copy first 6 digits, pad it with zeros.
2367 * Current server implementation uses only millisecond resolution. */
2368 /* TODO: isdigit() is locale sensitive */
2370 i
< subsecond_resolution
&& isdigit(*offset
);
2372 subseconds
[i
] = *offset
;
2374 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2375 /* Check 7th digit for rounding */
2376 if (*offset
>= '5') round_up
= 1;
2379 for (; i
< subsecond_resolution
; i
++) {
2380 subseconds
[i
] = '0';
2382 subseconds
[subsecond_resolution
] = '\0';
2384 /* Convert it into integer */
2385 long_number
= strtol(subseconds
, &endptr
, 10);
2386 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2387 long_number
== LONG_MAX
) {
2390 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2391 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2392 * microseconds" and "the type shall be a signed integer capable of
2393 * storing values at least in the range [-1, 1000000]. */
2394 if (long_number
< -1 || long_number
>= 1000000) {
2397 time
->tv_usec
= long_number
;
2399 /* Round the subseconds */
2401 if (999999 == time
->tv_usec
) {
2409 /* move to the zone offset delimiter or signal NULL*/
2410 delim
= strchr(offset
, '-');
2412 delim
= strchr(offset
, '+');
2414 delim
= strchr(offset
, 'Z');
2418 /* Get zone offset */
2419 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2420 * "" equals to "Z" and it means UTC zone. */
2421 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2422 * colon separator */
2423 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2424 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2427 if (*offset
== '+') {
2428 broken
.tm_hour
-= offset_hours
;
2429 broken
.tm_min
-= offset_minutes
;
2431 broken
.tm_hour
+= offset_hours
;
2432 broken
.tm_min
+= offset_minutes
;
2436 /* Convert to time_t */
2437 time
->tv_sec
= _isds_timegm(&broken
);
2438 if (time
->tv_sec
== (time_t) -1) {
2446 /* Convert UTF-8 ISO 8601 date-time @string to reallocated struct timeval.
2447 * It respects microseconds too. Microseconds are rounded half up.
2448 * In case of error, @time will be freed. */
2449 static isds_error
timestring2timeval(const xmlChar
*string
,
2450 struct timeval
**time
) {
2453 if (!time
) return IE_INVAL
;
2460 *time
= calloc(1, sizeof(**time
));
2461 if (!*time
) return IE_NOMEM
;
2463 memset(*time
, 0, sizeof(**time
));
2466 error
= timestring2static_timeval(string
, *time
);
2475 /* Convert unsigned int into isds_message_status.
2476 * @context is session context
2477 * @number is pointer to number value. NULL will be treated as invalid value.
2478 * @status is automatically reallocated status
2479 * @return IE_SUCCESS, or error code and free status */
2480 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2481 const unsigned long int *number
, isds_message_status
**status
) {
2482 if (!context
) return IE_INVALID_CONTEXT
;
2483 if (!status
) return IE_INVAL
;
2485 free(*status
); *status
= NULL
;
2486 if (!number
) return IE_INVAL
;
2488 if (*number
< 1 || *number
> 10) {
2489 isds_printf_message(context
, _("Invalid message status value: %lu"),
2494 *status
= malloc(sizeof(**status
));
2495 if (!*status
) return IE_NOMEM
;
2497 **status
= 1 << *number
;
2502 /* Convert event description string into isds_event members type and
2504 * @string is raw event description starting with event prefix
2505 * @event is structure where to store type and stripped description to
2506 * @return standard error code, unknown prefix is not classified as an error.
2508 static isds_error
eventstring2event(const xmlChar
*string
,
2509 struct isds_event
* event
) {
2510 const xmlChar
*known_prefixes
[] = {
2521 const isds_event_type types
[] = {
2522 EVENT_ENTERED_SYSTEM
,
2523 EVENT_ACCEPTED_BY_RECIPIENT
,
2524 EVENT_ACCEPTED_BY_FICTION
,
2525 EVENT_UNDELIVERABLE
,
2526 EVENT_COMMERCIAL_ACCEPTED
,
2528 EVENT_PRIMARY_LOGIN
,
2529 EVENT_ENTRUSTED_LOGIN
,
2535 if (!string
|| !event
) return IE_INVAL
;
2538 event
->type
= malloc(sizeof(*event
->type
));
2539 if (!(event
->type
)) return IE_NOMEM
;
2541 zfree(event
->description
);
2543 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2545 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2547 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2548 /* Prefix is known */
2549 *event
->type
= types
[index
];
2551 /* Strip prefix from description and spaces */
2552 /* TODO: Recognize all white spaces from UCS blank class and
2553 * operate on UTF-8 chars. */
2554 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2555 event
->description
= strdup((char *) (string
+ length
));
2556 if (!(event
->description
)) return IE_NOMEM
;
2562 /* Unknown event prefix.
2563 * XSD allows any string */
2564 char *string_locale
= _isds_utf82locale((char *) string
);
2565 isds_log(ILF_ISDS
, ILL_WARNING
,
2566 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2567 free(string_locale
);
2569 *event
->type
= EVENT_UKNOWN
;
2570 event
->description
= strdup((char *) string
);
2571 if (!(event
->description
)) return IE_NOMEM
;
2577 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2578 * and leave label */
2579 #define EXTRACT_STRING(element, string) { \
2580 xmlXPathFreeObject(result); \
2581 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2582 if (NULL == (result)) { \
2586 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2587 if (result->nodesetval->nodeNr > 1) { \
2588 isds_printf_message(context, _("Multiple %s element"), element); \
2592 (string) = (char *) \
2593 xmlXPathCastNodeSetToString(result->nodesetval); \
2594 if (NULL == (string)) { \
2601 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2603 char *string = NULL; \
2604 EXTRACT_STRING(element, string); \
2607 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2608 if (!(booleanPtr)) { \
2614 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2615 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2616 *(booleanPtr) = 1; \
2617 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2618 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2619 *(booleanPtr) = 0; \
2621 char *string_locale = _isds_utf82locale((char*)string); \
2622 isds_printf_message(context, \
2623 _("%s value is not valid boolean: %s"), \
2624 element, string_locale); \
2625 free(string_locale); \
2635 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2637 char *string = NULL; \
2638 EXTRACT_STRING(element, string); \
2640 if (NULL == string) { \
2641 isds_printf_message(context, _("%s element is empty"), element); \
2645 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2646 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2648 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2649 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2652 char *string_locale = _isds_utf82locale((char*)string); \
2653 isds_printf_message(context, \
2654 _("%s value is not valid boolean: %s"), \
2655 element, string_locale); \
2656 free(string_locale); \
2665 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2667 char *string = NULL; \
2668 EXTRACT_STRING(element, string); \
2673 number = strtol((char*)string, &endptr, 10); \
2675 if (*endptr != '\0') { \
2676 char *string_locale = _isds_utf82locale((char *)string); \
2677 isds_printf_message(context, \
2678 _("%s is not valid integer: %s"), \
2679 element, string_locale); \
2680 free(string_locale); \
2686 if (number == LONG_MIN || number == LONG_MAX) { \
2687 char *string_locale = _isds_utf82locale((char *)string); \
2688 isds_printf_message(context, \
2689 _("%s value out of range of long int: %s"), \
2690 element, string_locale); \
2691 free(string_locale); \
2697 free(string); string = NULL; \
2699 if (!(preallocated)) { \
2700 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2701 if (!(longintPtr)) { \
2706 *(longintPtr) = number; \
2710 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2712 char *string = NULL; \
2713 EXTRACT_STRING(element, string); \
2718 number = strtol((char*)string, &endptr, 10); \
2720 if (*endptr != '\0') { \
2721 char *string_locale = _isds_utf82locale((char *)string); \
2722 isds_printf_message(context, \
2723 _("%s is not valid integer: %s"), \
2724 element, string_locale); \
2725 free(string_locale); \
2731 if (number == LONG_MIN || number == LONG_MAX) { \
2732 char *string_locale = _isds_utf82locale((char *)string); \
2733 isds_printf_message(context, \
2734 _("%s value out of range of long int: %s"), \
2735 element, string_locale); \
2736 free(string_locale); \
2742 free(string); string = NULL; \
2744 isds_printf_message(context, \
2745 _("%s value is negative: %ld"), element, number); \
2750 if (!(preallocated)) { \
2751 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2752 if (!(ulongintPtr)) { \
2757 *(ulongintPtr) = number; \
2761 #define EXTRACT_DATE(element, tmPtr) { \
2762 char *string = NULL; \
2763 EXTRACT_STRING(element, string); \
2764 if (NULL != string) { \
2765 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2766 if (NULL == (tmPtr)) { \
2771 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2773 if (err == IE_NOTSUP) { \
2775 char *string_locale = _isds_utf82locale(string); \
2776 char *element_locale = _isds_utf82locale(element); \
2777 isds_printf_message(context, _("Invalid %s value: %s"), \
2778 element_locale, string_locale); \
2779 free(string_locale); \
2780 free(element_locale); \
2789 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2790 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2792 if ((required) && (!string)) { \
2793 char *attribute_locale = _isds_utf82locale(attribute); \
2794 char *element_locale = \
2795 _isds_utf82locale((char *)xpath_ctx->node->name); \
2796 isds_printf_message(context, \
2797 _("Could not extract required %s attribute value from " \
2798 "%s element"), attribute_locale, element_locale); \
2799 free(element_locale); \
2800 free(attribute_locale); \
2807 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2809 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2810 (xmlChar *) (string)); \
2812 isds_printf_message(context, \
2813 _("Could not add %s child to %s element"), \
2814 element, (parent)->name); \
2820 #define INSERT_STRING(parent, element, string) \
2821 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2823 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2825 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2826 else { INSERT_STRING(parent, element, "false"); } \
2829 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2832 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2834 INSERT_STRING(parent, element, NULL); \
2838 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2839 if ((longintPtr)) { \
2840 /* FIXME: locale sensitive */ \
2841 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2845 INSERT_STRING(parent, element, buffer) \
2846 free(buffer); (buffer) = NULL; \
2847 } else { INSERT_STRING(parent, element, NULL) } \
2850 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2851 if ((ulongintPtr)) { \
2852 /* FIXME: locale sensitive */ \
2853 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2857 INSERT_STRING(parent, element, buffer) \
2858 free(buffer); (buffer) = NULL; \
2859 } else { INSERT_STRING(parent, element, NULL) } \
2862 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2864 /* FIXME: locale sensitive */ \
2865 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2869 INSERT_STRING(parent, element, buffer) \
2870 free(buffer); (buffer) = NULL; \
2873 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2875 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2877 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2878 (xmlChar *) (string)); \
2879 if (!attribute_node) { \
2880 isds_printf_message(context, _("Could not add %s " \
2881 "attribute to %s element"), \
2882 (attribute), (parent)->name); \
2888 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2890 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2891 if (length > (maximum)) { \
2892 isds_printf_message(context, \
2893 ngettext("%s has more than %d characters", \
2894 "%s has more than %d characters", (maximum)), \
2895 (name), (maximum)); \
2899 if (length < (minimum)) { \
2900 isds_printf_message(context, \
2901 ngettext("%s has less than %d characters", \
2902 "%s has less than %d characters", (minimum)), \
2903 (name), (minimum)); \
2910 #define INSERT_ELEMENT(child, parent, element) \
2912 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2914 isds_printf_message(context, \
2915 _("Could not add %s child to %s element"), \
2916 (element), (parent)->name); \
2923 /* Find child element by name in given XPath context and switch context onto
2924 * it. The child must be uniq and must exist. Otherwise fails.
2925 * @context is ISDS context
2926 * @child is child element name
2927 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2928 * into it child. In error case, the @xpath_ctx keeps original value. */
2929 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2930 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2931 isds_error err
= IE_SUCCESS
;
2932 xmlXPathObjectPtr result
= NULL
;
2934 if (!context
) return IE_INVALID_CONTEXT
;
2935 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2938 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2945 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2946 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2947 char *child_locale
= _isds_utf82locale((char*) child
);
2948 isds_printf_message(context
,
2949 _("%s element does not contain %s child"),
2950 parent_locale
, child_locale
);
2952 free(parent_locale
);
2958 if (result
->nodesetval
->nodeNr
> 1) {
2959 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2960 char *child_locale
= _isds_utf82locale((char*) child
);
2961 isds_printf_message(context
,
2962 _("%s element contains multiple %s children"),
2963 parent_locale
, child_locale
);
2965 free(parent_locale
);
2970 /* Switch context */
2971 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2974 xmlXPathFreeObject(result
);
2981 /* Find and convert XSD:gPersonName group in current node into structure
2982 * @context is ISDS context
2983 * @personName is automatically reallocated person name structure. If no member
2984 * value is found, will be freed.
2985 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2987 * In case of error @personName will be freed. */
2988 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2989 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2990 isds_error err
= IE_SUCCESS
;
2991 xmlXPathObjectPtr result
= NULL
;
2993 if (!context
) return IE_INVALID_CONTEXT
;
2994 if (!personName
) return IE_INVAL
;
2995 isds_PersonName_free(personName
);
2996 if (!xpath_ctx
) return IE_INVAL
;
2999 *personName
= calloc(1, sizeof(**personName
));
3005 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
3006 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
3007 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
3008 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
3010 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
3011 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
3012 isds_PersonName_free(personName
);
3015 if (err
) isds_PersonName_free(personName
);
3016 xmlXPathFreeObject(result
);
3021 /* Find and convert XSD:gAddress group extended with relevant
3022 * tdbPersonalOwnerinfo members in current node into structure
3023 * @context is ISDS context
3024 * @address is automatically reallocated address structure. If no member
3025 * value is found, will be freed.
3026 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
3028 * In case of error @address will be freed. */
3029 static isds_error
extract_gAddress(struct isds_ctx
*context
,
3030 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
3031 isds_error err
= IE_SUCCESS
;
3032 xmlXPathObjectPtr result
= NULL
;
3034 if (!context
) return IE_INVALID_CONTEXT
;
3035 if (!address
) return IE_INVAL
;
3036 isds_Address_free(address
);
3037 if (!xpath_ctx
) return IE_INVAL
;
3040 *address
= calloc(1, sizeof(**address
));
3046 EXTRACT_LONGINT("isds:adCode", (*address
)->adCode
, 0);
3047 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
3048 EXTRACT_STRING("isds:adDistrict", (*address
)->adDistrict
);
3049 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
3050 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
3051 EXTRACT_STRING("isds:adNumberInMunicipality",
3052 (*address
)->adNumberInMunicipality
);
3053 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
3054 EXTRACT_STRING("isds:adState", (*address
)->adState
);
3056 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
3057 !(*address
)->adNumberInStreet
&&
3058 !(*address
)->adNumberInMunicipality
&&
3059 !(*address
)->adZipCode
&& !(*address
)->adState
)
3060 isds_Address_free(address
);
3063 if (err
) isds_Address_free(address
);
3064 xmlXPathFreeObject(result
);
3069 /* Find and convert isds:biDate element in current node into structure
3070 * @context is ISDS context
3071 * @biDate is automatically reallocated birth date structure. If no member
3072 * value is found, will be freed.
3073 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3075 * In case of error @biDate will be freed. */
3076 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3077 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3078 isds_error err
= IE_SUCCESS
;
3079 xmlXPathObjectPtr result
= NULL
;
3080 char *string
= NULL
;
3082 if (!context
) return IE_INVALID_CONTEXT
;
3083 if (!biDate
) return IE_INVAL
;
3085 if (!xpath_ctx
) return IE_INVAL
;
3087 EXTRACT_STRING("isds:biDate", string
);
3089 *biDate
= calloc(1, sizeof(**biDate
));
3094 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3096 if (err
== IE_NOTSUP
) {
3098 char *string_locale
= _isds_utf82locale(string
);
3099 isds_printf_message(context
,
3100 _("Invalid isds:biDate value: %s"), string_locale
);
3101 free(string_locale
);
3108 if (err
) zfree(*biDate
);
3110 xmlXPathFreeObject(result
);
3115 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3116 * @context is ISDS context
3117 * @db_owner_info is automatically reallocated box owner info structure
3118 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3119 * XSD:tdbPersonalOwenerInfo element
3120 * In case of error @db_owner_info will be freed. */
3121 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3122 struct isds_DbOwnerInfo
**db_owner_info
,
3123 xmlXPathContextPtr xpath_ctx
) {
3124 isds_error err
= IE_SUCCESS
;
3125 xmlXPathObjectPtr result
= NULL
;
3126 char *string
= NULL
;
3128 if (!context
) return IE_INVALID_CONTEXT
;
3129 if (!db_owner_info
) return IE_INVAL
;
3130 isds_DbOwnerInfo_free(db_owner_info
);
3131 if (!xpath_ctx
) return IE_INVAL
;
3134 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3135 if (!*db_owner_info
) {
3140 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3142 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info
)->aifoIsds
);
3144 EXTRACT_STRING("isds:dbType", string
);
3146 (*db_owner_info
)->dbType
=
3147 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3148 if (!(*db_owner_info
)->dbType
) {
3152 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3154 zfree((*db_owner_info
)->dbType
);
3155 if (err
== IE_ENUM
) {
3157 char *string_locale
= _isds_utf82locale(string
);
3158 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3160 free(string_locale
);
3167 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3169 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3171 if (err
) goto leave
;
3173 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3175 (*db_owner_info
)->birthInfo
=
3176 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3177 if (!(*db_owner_info
)->birthInfo
) {
3181 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3183 if (err
) goto leave
;
3184 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3185 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3186 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3187 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3188 !(*db_owner_info
)->birthInfo
->biCity
&&
3189 !(*db_owner_info
)->birthInfo
->biCounty
&&
3190 !(*db_owner_info
)->birthInfo
->biState
)
3191 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3193 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3194 if (err
) goto leave
;
3196 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3197 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3198 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3199 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3200 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3202 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3204 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3205 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3206 (*db_owner_info
)->dbOpenAddressing
);
3209 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3211 xmlXPathFreeObject(result
);
3216 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3217 * @context is session context
3218 * @owner is libisds structure with box description.
3219 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3220 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3221 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3222 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3224 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3225 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3226 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3227 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3229 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3230 const struct isds_DbOwnerInfo
*owner
, _Bool pfo_subtype
,
3231 xmlNodePtr db_owner_info
) {
3233 isds_error err
= IE_SUCCESS
;
3235 xmlChar
*string
= NULL
;
3236 const xmlChar
*type_string
= NULL
;
3238 if (!context
) return IE_INVALID_CONTEXT
;
3239 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3242 /* XXX: All the elements except email and telNumber are mandatory. */
3243 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3244 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3247 INSERT_BOOLEAN(db_owner_info
, "aifoIsds", owner
->aifoIsds
);
3252 if (owner
->dbType
) {
3253 type_string
= isds_DbType2string(*(owner
->dbType
));
3255 isds_printf_message(context
, _("Invalid dbType value: %d"),
3261 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3263 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3266 INSERT_STRING(db_owner_info
, "pnFirstName",
3267 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnFirstName
);
3268 INSERT_STRING(db_owner_info
, "pnMiddleName",
3269 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnMiddleName
);
3270 INSERT_STRING(db_owner_info
, "pnLastName",
3271 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnLastName
);
3273 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3274 (NULL
== owner
->personName
) ? NULL
:
3275 owner
->personName
->pnLastNameAtBirth
);
3277 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3280 if (NULL
!= owner
->birthInfo
&& NULL
!= owner
->birthInfo
->biDate
) {
3281 err
= tm2datestring(owner
->birthInfo
->biDate
, &string
);
3282 if (err
) goto leave
;
3284 INSERT_STRING(db_owner_info
, "biDate", string
);
3287 INSERT_STRING(db_owner_info
, "biCity",
3288 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCity
);
3289 INSERT_STRING(db_owner_info
, "biCounty",
3290 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCounty
);
3291 INSERT_STRING(db_owner_info
, "biState",
3292 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biState
);
3295 INSERT_LONGINT(db_owner_info
, "adCode",
3296 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCode
,
3299 INSERT_STRING(db_owner_info
, "adCity",
3300 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCity
);
3302 INSERT_STRING(db_owner_info
, "adDistrict",
3303 (NULL
== owner
->address
) ? NULL
: owner
->address
->adDistrict
);
3305 INSERT_STRING(db_owner_info
, "adStreet",
3306 (NULL
== owner
->address
) ? NULL
: owner
->address
->adStreet
);
3307 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3308 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInStreet
);
3309 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3310 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInMunicipality
);
3311 INSERT_STRING(db_owner_info
, "adZipCode",
3312 (NULL
== owner
->address
) ? NULL
: owner
->address
->adZipCode
);
3313 INSERT_STRING(db_owner_info
, "adState",
3314 (NULL
== owner
->address
) ? NULL
: owner
->address
->adState
);
3316 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3319 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3320 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3322 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3323 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3325 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3326 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3328 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3330 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3331 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3332 owner
->dbOpenAddressing
);
3341 /* Convert XSD:tDbUserInfo XML tree into structure
3342 * @context is ISDS context
3343 * @db_user_info is automatically reallocated user info structure
3344 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3345 * In case of error @db_user_info will be freed. */
3346 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3347 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3348 isds_error err
= IE_SUCCESS
;
3349 xmlXPathObjectPtr result
= NULL
;
3350 char *string
= NULL
;
3352 if (!context
) return IE_INVALID_CONTEXT
;
3353 if (!db_user_info
) return IE_INVAL
;
3354 isds_DbUserInfo_free(db_user_info
);
3355 if (!xpath_ctx
) return IE_INVAL
;
3358 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3359 if (!*db_user_info
) {
3364 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info
)->aifo_ticket
, 0);
3366 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3368 EXTRACT_STRING("isds:userType", string
);
3370 (*db_user_info
)->userType
=
3371 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3372 if (!(*db_user_info
)->userType
) {
3376 err
= string2isds_UserType((xmlChar
*)string
,
3377 (*db_user_info
)->userType
);
3379 zfree((*db_user_info
)->userType
);
3380 if (err
== IE_ENUM
) {
3382 char *string_locale
= _isds_utf82locale(string
);
3383 isds_printf_message(context
,
3384 _("Unknown isds:userType value: %s"), string_locale
);
3385 free(string_locale
);
3392 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3394 (*db_user_info
)->personName
=
3395 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3396 if (!(*db_user_info
)->personName
) {
3401 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3403 if (err
) goto leave
;
3405 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3406 if (err
) goto leave
;
3408 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3409 if (err
) goto leave
;
3411 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3412 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3414 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3415 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3416 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3418 /* ???: Default value is "CZ" according specification. Should we provide
3420 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3423 if (err
) isds_DbUserInfo_free(db_user_info
);
3425 xmlXPathFreeObject(result
);
3430 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3431 * @context is session context
3432 * @user is libisds structure with user description
3433 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3434 * @db_user_info is XML element of XSD:tDbUserInfo */
3435 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3436 const struct isds_DbUserInfo
*user
, _Bool honor_aifo_ticket
,
3437 xmlNodePtr db_user_info
) {
3439 isds_error err
= IE_SUCCESS
;
3441 xmlAttrPtr attribute_node
;
3442 xmlChar
*string
= NULL
;
3444 if (!context
) return IE_INVALID_CONTEXT
;
3445 if (!user
|| !db_user_info
) return IE_INVAL
;
3447 /* Build XSD:tDbUserInfo */
3449 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3450 * allow it everywhere. */
3451 if (honor_aifo_ticket
&& user
->aifo_ticket
) {
3452 INSERT_STRING_ATTRIBUTE(db_user_info
, "AIFOTicket", user
->aifo_ticket
);
3455 if (user
->personName
) {
3456 INSERT_STRING(db_user_info
, "pnFirstName",
3457 user
->personName
->pnFirstName
);
3458 INSERT_STRING(db_user_info
, "pnMiddleName",
3459 user
->personName
->pnMiddleName
);
3460 INSERT_STRING(db_user_info
, "pnLastName",
3461 user
->personName
->pnLastName
);
3462 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3463 user
->personName
->pnLastNameAtBirth
);
3465 if (user
->address
) {
3466 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3467 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3468 INSERT_STRING(db_user_info
, "adNumberInStreet",
3469 user
->address
->adNumberInStreet
);
3470 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3471 user
->address
->adNumberInMunicipality
);
3472 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3473 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3476 if (!tm2datestring(user
->biDate
, &string
))
3477 INSERT_STRING(db_user_info
, "biDate", string
);
3480 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3481 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3484 if (user
->userType
) {
3485 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3487 isds_printf_message(context
, _("Invalid userType value: %d"),
3492 INSERT_STRING(db_user_info
, "userType", type_string
);
3495 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3496 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3497 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3498 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3499 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3500 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3501 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3502 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3503 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3511 /* Convert XSD:tPDZRec XML tree into structure
3512 * @context is ISDS context
3513 * @permission is automatically reallocated commercial permission structure
3514 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3515 * In case of error @permission will be freed. */
3516 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3517 struct isds_commercial_permission
**permission
,
3518 xmlXPathContextPtr xpath_ctx
) {
3519 isds_error err
= IE_SUCCESS
;
3520 xmlXPathObjectPtr result
= NULL
;
3521 char *string
= NULL
;
3523 if (!context
) return IE_INVALID_CONTEXT
;
3524 if (!permission
) return IE_INVAL
;
3525 isds_commercial_permission_free(permission
);
3526 if (!xpath_ctx
) return IE_INVAL
;
3529 *permission
= calloc(1, sizeof(**permission
));
3535 EXTRACT_STRING("isds:PDZType", string
);
3537 err
= string2isds_payment_type((xmlChar
*)string
,
3538 &(*permission
)->type
);
3540 if (err
== IE_ENUM
) {
3542 char *string_locale
= _isds_utf82locale(string
);
3543 isds_printf_message(context
,
3544 _("Unknown isds:PDZType value: %s"), string_locale
);
3545 free(string_locale
);
3552 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3553 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3555 EXTRACT_STRING("isds:PDZExpire", string
);
3557 err
= timestring2timeval((xmlChar
*) string
,
3558 &((*permission
)->expiration
));
3560 char *string_locale
= _isds_utf82locale(string
);
3561 if (err
== IE_DATE
) err
= IE_ISDS
;
3562 isds_printf_message(context
,
3563 _("Could not convert PDZExpire as ISO time: %s"),
3565 free(string_locale
);
3571 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3572 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3575 if (err
) isds_commercial_permission_free(permission
);
3577 xmlXPathFreeObject(result
);
3582 /* Convert XSD:tCiRecord XML tree into structure
3583 * @context is ISDS context
3584 * @event is automatically reallocated commercial credit event structure
3585 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3586 * In case of error @event will be freed. */
3587 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3588 struct isds_credit_event
**event
,
3589 xmlXPathContextPtr xpath_ctx
) {
3590 isds_error err
= IE_SUCCESS
;
3591 xmlXPathObjectPtr result
= NULL
;
3592 char *string
= NULL
;
3593 long int *number_ptr
;
3595 if (!context
) return IE_INVALID_CONTEXT
;
3596 if (!event
) return IE_INVAL
;
3597 isds_credit_event_free(event
);
3598 if (!xpath_ctx
) return IE_INVAL
;
3601 *event
= calloc(1, sizeof(**event
));
3607 EXTRACT_STRING("isds:ciEventTime", string
);
3609 err
= timestring2timeval((xmlChar
*) string
,
3612 char *string_locale
= _isds_utf82locale(string
);
3613 if (err
== IE_DATE
) err
= IE_ISDS
;
3614 isds_printf_message(context
,
3615 _("Could not convert ciEventTime as ISO time: %s"),
3617 free(string_locale
);
3623 EXTRACT_STRING("isds:ciEventType", string
);
3625 err
= string2isds_credit_event_type((xmlChar
*)string
,
3628 if (err
== IE_ENUM
) {
3630 char *string_locale
= _isds_utf82locale(string
);
3631 isds_printf_message(context
,
3632 _("Unknown isds:ciEventType value: %s"), string_locale
);
3633 free(string_locale
);
3640 number_ptr
= &((*event
)->credit_change
);
3641 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3642 number_ptr
= &(*event
)->new_credit
;
3643 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3645 switch((*event
)->type
) {
3646 case ISDS_CREDIT_CHARGED
:
3647 EXTRACT_STRING("isds:ciTransID",
3648 (*event
)->details
.charged
.transaction
);
3650 case ISDS_CREDIT_DISCHARGED
:
3651 EXTRACT_STRING("isds:ciTransID",
3652 (*event
)->details
.discharged
.transaction
);
3654 case ISDS_CREDIT_MESSAGE_SENT
:
3655 EXTRACT_STRING("isds:ciRecipientID",
3656 (*event
)->details
.message_sent
.recipient
);
3657 EXTRACT_STRING("isds:ciPDZID",
3658 (*event
)->details
.message_sent
.message_id
);
3660 case ISDS_CREDIT_STORAGE_SET
:
3661 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3662 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3663 EXTRACT_DATE("isds:ciNewFrom",
3664 (*event
)->details
.storage_set
.new_valid_from
);
3665 EXTRACT_DATE("isds:ciNewTo",
3666 (*event
)->details
.storage_set
.new_valid_to
);
3667 EXTRACT_LONGINT("isds:ciOldCapacity",
3668 (*event
)->details
.storage_set
.old_capacity
, 0);
3669 EXTRACT_DATE("isds:ciOldFrom",
3670 (*event
)->details
.storage_set
.old_valid_from
);
3671 EXTRACT_DATE("isds:ciOldTo",
3672 (*event
)->details
.storage_set
.old_valid_to
);
3673 EXTRACT_STRING("isds:ciDoneBy",
3674 (*event
)->details
.storage_set
.initiator
);
3676 case ISDS_CREDIT_EXPIRED
:
3681 if (err
) isds_credit_event_free(event
);
3683 xmlXPathFreeObject(result
);
3688 #endif /* HAVE_LIBCURL */
3691 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3692 * isds_envelope structure. The envelope is automatically allocated but not
3693 * reallocated. The date are just appended into envelope structure.
3694 * @context is ISDS context
3695 * @envelope is automatically allocated message envelope structure
3696 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3697 * In case of error @envelope will be freed. */
3698 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3699 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3700 isds_error err
= IE_SUCCESS
;
3701 xmlXPathObjectPtr result
= NULL
;
3703 if (!context
) return IE_INVALID_CONTEXT
;
3704 if (!envelope
) return IE_INVAL
;
3705 if (!xpath_ctx
) return IE_INVAL
;
3709 /* Allocate envelope */
3710 *envelope
= calloc(1, sizeof(**envelope
));
3716 /* Else free former data */
3717 zfree((*envelope
)->dmSenderOrgUnit
);
3718 zfree((*envelope
)->dmSenderOrgUnitNum
);
3719 zfree((*envelope
)->dbIDRecipient
);
3720 zfree((*envelope
)->dmRecipientOrgUnit
);
3721 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3722 zfree((*envelope
)->dmToHands
);
3723 zfree((*envelope
)->dmAnnotation
);
3724 zfree((*envelope
)->dmRecipientRefNumber
);
3725 zfree((*envelope
)->dmSenderRefNumber
);
3726 zfree((*envelope
)->dmRecipientIdent
);
3727 zfree((*envelope
)->dmSenderIdent
);
3728 zfree((*envelope
)->dmLegalTitleLaw
);
3729 zfree((*envelope
)->dmLegalTitleYear
);
3730 zfree((*envelope
)->dmLegalTitleSect
);
3731 zfree((*envelope
)->dmLegalTitlePar
);
3732 zfree((*envelope
)->dmLegalTitlePoint
);
3733 zfree((*envelope
)->dmPersonalDelivery
);
3734 zfree((*envelope
)->dmAllowSubstDelivery
);
3737 /* Extract envelope elements added by sender or ISDS
3738 * (XSD: gMessageEnvelopeSub type) */
3739 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3740 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3741 (*envelope
)->dmSenderOrgUnitNum
, 0);
3742 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3743 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3744 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3745 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3746 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3747 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3748 EXTRACT_STRING("isds:dmRecipientRefNumber",
3749 (*envelope
)->dmRecipientRefNumber
);
3750 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3751 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3752 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3754 /* Extract envelope elements regarding law reference */
3755 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3756 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3757 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3758 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3759 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3761 /* Extract envelope other elements */
3762 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3763 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3764 (*envelope
)->dmAllowSubstDelivery
);
3767 if (err
) isds_envelope_free(envelope
);
3768 xmlXPathFreeObject(result
);
3774 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3775 * isds_envelope structure. The envelope is automatically allocated but not
3776 * reallocated. The date are just appended into envelope structure.
3777 * @context is ISDS context
3778 * @envelope is automatically allocated message envelope structure
3779 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3780 * In case of error @envelope will be freed. */
3781 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3782 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3783 isds_error err
= IE_SUCCESS
;
3784 xmlXPathObjectPtr result
= NULL
;
3786 if (!context
) return IE_INVALID_CONTEXT
;
3787 if (!envelope
) return IE_INVAL
;
3788 if (!xpath_ctx
) return IE_INVAL
;
3792 /* Allocate envelope */
3793 *envelope
= calloc(1, sizeof(**envelope
));
3799 /* Else free former data */
3800 zfree((*envelope
)->dmID
);
3801 zfree((*envelope
)->dbIDSender
);
3802 zfree((*envelope
)->dmSender
);
3803 zfree((*envelope
)->dmSenderAddress
);
3804 zfree((*envelope
)->dmSenderType
);
3805 zfree((*envelope
)->dmRecipient
);
3806 zfree((*envelope
)->dmRecipientAddress
);
3807 zfree((*envelope
)->dmAmbiguousRecipient
);
3810 /* Extract envelope elements added by ISDS
3811 * (XSD: gMessageEnvelope type) */
3812 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3813 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3814 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3815 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3816 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3817 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3818 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3819 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3820 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3821 (*envelope
)->dmAmbiguousRecipient
);
3823 /* Extract envelope elements added by sender and ISDS
3824 * (XSD: gMessageEnvelope type) */
3825 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3826 if (err
) goto leave
;
3829 if (err
) isds_envelope_free(envelope
);
3830 xmlXPathFreeObject(result
);
3835 /* Convert other envelope elements from XML tree into isds_envelope structure:
3836 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3837 * The envelope is automatically allocated but not reallocated.
3838 * The data are just appended into envelope structure.
3839 * @context is ISDS context
3840 * @envelope is automatically allocated message envelope structure
3841 * @xpath_ctx is XPath context with current node as parent desired elements
3842 * In case of error @envelope will be freed. */
3843 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3844 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3845 isds_error err
= IE_SUCCESS
;
3846 xmlXPathObjectPtr result
= NULL
;
3847 char *string
= NULL
;
3848 unsigned long int *unumber
= NULL
;
3850 if (!context
) return IE_INVALID_CONTEXT
;
3851 if (!envelope
) return IE_INVAL
;
3852 if (!xpath_ctx
) return IE_INVAL
;
3857 *envelope
= calloc(1, sizeof(**envelope
));
3864 zfree((*envelope
)->dmMessageStatus
);
3865 zfree((*envelope
)->dmAttachmentSize
);
3866 zfree((*envelope
)->dmDeliveryTime
);
3867 zfree((*envelope
)->dmAcceptanceTime
);
3871 /* dmMessageStatus element is mandatory */
3872 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3874 isds_log_message(context
,
3875 _("Missing mandatory sisds:dmMessageStatus integer"));
3879 err
= uint2isds_message_status(context
, unumber
,
3880 &((*envelope
)->dmMessageStatus
));
3882 if (err
== IE_ENUM
) err
= IE_ISDS
;
3885 free(unumber
); unumber
= NULL
;
3887 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3890 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3892 err
= timestring2timeval((xmlChar
*) string
,
3893 &((*envelope
)->dmDeliveryTime
));
3895 char *string_locale
= _isds_utf82locale(string
);
3896 if (err
== IE_DATE
) err
= IE_ISDS
;
3897 isds_printf_message(context
,
3898 _("Could not convert dmDeliveryTime as ISO time: %s"),
3900 free(string_locale
);
3906 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3908 err
= timestring2timeval((xmlChar
*) string
,
3909 &((*envelope
)->dmAcceptanceTime
));
3911 char *string_locale
= _isds_utf82locale(string
);
3912 if (err
== IE_DATE
) err
= IE_ISDS
;
3913 isds_printf_message(context
,
3914 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3916 free(string_locale
);
3923 if (err
) isds_envelope_free(envelope
);
3926 xmlXPathFreeObject(result
);
3931 /* Convert message type attribute of current element into isds_envelope
3933 * TODO: This function can be incorporated into append_status_size_times() as
3934 * they are called always together.
3935 * The envelope is automatically allocated but not reallocated.
3936 * The data are just appended into envelope structure.
3937 * @context is ISDS context
3938 * @envelope is automatically allocated message envelope structure
3939 * @xpath_ctx is XPath context with current node as parent of attribute
3940 * carrying message type
3941 * In case of error @envelope will be freed. */
3942 static isds_error
append_message_type(struct isds_ctx
*context
,
3943 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3944 isds_error err
= IE_SUCCESS
;
3946 if (!context
) return IE_INVALID_CONTEXT
;
3947 if (!envelope
) return IE_INVAL
;
3948 if (!xpath_ctx
) return IE_INVAL
;
3953 *envelope
= calloc(1, sizeof(**envelope
));
3960 zfree((*envelope
)->dmType
);
3964 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3966 if (!(*envelope
)->dmType
) {
3967 /* Use default value */
3968 (*envelope
)->dmType
= strdup("V");
3969 if (!(*envelope
)->dmType
) {
3973 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3974 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3975 isds_printf_message(context
,
3976 _("Message type in dmType attribute is not 1 character long: "
3985 if (err
) isds_envelope_free(envelope
);
3991 /* Convert dmType isds_envelope member into XML attribute and append it to
3993 * @context is ISDS context
3994 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3995 * @dm_envelope is XML element the resulting attribute will be appended to.
3996 * @return error code, in case of error context' message is filled. */
3997 static isds_error
insert_message_type(struct isds_ctx
*context
,
3998 const char *type
, xmlNodePtr dm_envelope
) {
3999 isds_error err
= IE_SUCCESS
;
4000 xmlAttrPtr attribute_node
;
4002 if (!context
) return IE_INVALID_CONTEXT
;
4003 if (!dm_envelope
) return IE_INVAL
;
4005 /* Insert optional message type */
4007 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
4008 char *type_locale
= _isds_utf82locale(type
);
4009 isds_printf_message(context
,
4010 _("Message type in envelope is not 1 character long: %s"),
4016 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
4022 #endif /* HAVE_LIBCURL */
4025 /* Extract message document into reallocated document structure
4026 * @context is ISDS context
4027 * @document is automatically reallocated message documents structure
4028 * @xpath_ctx is XPath context with current node as isds:dmFile
4029 * In case of error @document will be freed. */
4030 static isds_error
extract_document(struct isds_ctx
*context
,
4031 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
4032 isds_error err
= IE_SUCCESS
;
4033 xmlXPathObjectPtr result
= NULL
;
4034 xmlNodePtr file_node
;
4035 char *string
= NULL
;
4037 if (!context
) return IE_INVALID_CONTEXT
;
4038 if (!document
) return IE_INVAL
;
4039 isds_document_free(document
);
4040 if (!xpath_ctx
) return IE_INVAL
;
4041 file_node
= xpath_ctx
->node
;
4043 *document
= calloc(1, sizeof(**document
));
4049 /* Extract document meta data */
4050 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
4051 if (context
->normalize_mime_type
) {
4052 const char *normalized_type
=
4053 isds_normalize_mime_type((*document
)->dmMimeType
);
4054 if (NULL
!= normalized_type
&&
4055 normalized_type
!= (*document
)->dmMimeType
) {
4056 char *new_type
= strdup(normalized_type
);
4057 if (NULL
== new_type
) {
4058 isds_printf_message(context
,
4059 _("Not enough memory to normalize document MIME type"));
4063 free((*document
)->dmMimeType
);
4064 (*document
)->dmMimeType
= new_type
;
4068 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
4069 err
= string2isds_FileMetaType((xmlChar
*)string
,
4070 &((*document
)->dmFileMetaType
));
4072 char *meta_type_locale
= _isds_utf82locale(string
);
4073 isds_printf_message(context
,
4074 _("Document has invalid dmFileMetaType attribute value: %s"),
4076 free(meta_type_locale
);
4082 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
4083 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
4084 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
4085 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
4088 /* Extract document data.
4089 * Base64 encoded blob or XML subtree must be presented. */
4091 /* Check for dmEncodedContent */
4092 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
4099 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4100 /* Here we have Base64 blob */
4101 (*document
)->is_xml
= 0;
4103 if (result
->nodesetval
->nodeNr
> 1) {
4104 isds_printf_message(context
,
4105 _("Document has more dmEncodedContent elements"));
4110 xmlXPathFreeObject(result
); result
= NULL
;
4111 EXTRACT_STRING("isds:dmEncodedContent", string
);
4113 /* Decode non-empty document */
4114 if (string
&& string
[0] != '\0') {
4115 (*document
)->data_length
=
4116 _isds_b64decode(string
, &((*document
)->data
));
4117 if ((*document
)->data_length
== (size_t) -1) {
4118 isds_printf_message(context
,
4119 _("Error while Base64-decoding document content"));
4125 /* No Base64 blob, try XML document */
4126 xmlXPathFreeObject(result
); result
= NULL
;
4127 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4134 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4135 /* Here we have XML document */
4136 (*document
)->is_xml
= 1;
4138 if (result
->nodesetval
->nodeNr
> 1) {
4139 isds_printf_message(context
,
4140 _("Document has more dmXMLContent elements"));
4145 /* XXX: We cannot serialize the content simply because:
4146 * - XML document may point out of its scope (e.g. to message
4148 * - isds:dmXMLContent can contain more elements, no element,
4150 * - it's not the XML way
4151 * Thus we provide the only right solution: XML DOM. Let's
4152 * application to cope with this hot potato :) */
4153 (*document
)->xml_node_list
=
4154 result
->nodesetval
->nodeTab
[0]->children
;
4156 /* No base64 blob, nor XML document */
4157 isds_printf_message(context
,
4158 _("Document has no dmEncodedContent, nor dmXMLContent "
4167 if (err
) isds_document_free(document
);
4169 xmlXPathFreeObject(result
);
4170 xpath_ctx
->node
= file_node
;
4176 /* Extract message documents into reallocated list of documents
4177 * @context is ISDS context
4178 * @documents is automatically reallocated message documents list structure
4179 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4180 * In case of error @documents will be freed. */
4181 static isds_error
extract_documents(struct isds_ctx
*context
,
4182 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4183 isds_error err
= IE_SUCCESS
;
4184 xmlXPathObjectPtr result
= NULL
;
4185 xmlNodePtr files_node
;
4186 struct isds_list
*document
, *prev_document
= NULL
;
4188 if (!context
) return IE_INVALID_CONTEXT
;
4189 if (!documents
) return IE_INVAL
;
4190 isds_list_free(documents
);
4191 if (!xpath_ctx
) return IE_INVAL
;
4192 files_node
= xpath_ctx
->node
;
4194 /* Find documents */
4195 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4202 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4203 isds_printf_message(context
,
4204 _("Message does not contain any document"));
4210 /* Iterate over documents */
4211 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4213 /* Allocate and append list item */
4214 document
= calloc(1, sizeof(*document
));
4219 document
->destructor
= (void (*)(void **))isds_document_free
;
4220 if (i
== 0) *documents
= document
;
4221 else prev_document
->next
= document
;
4222 prev_document
= document
;
4224 /* Extract document */
4225 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4226 err
= extract_document(context
,
4227 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4228 if (err
) goto leave
;
4233 if (err
) isds_list_free(documents
);
4234 xmlXPathFreeObject(result
);
4235 xpath_ctx
->node
= files_node
;
4241 /* Convert isds:dmRecord XML tree into structure
4242 * @context is ISDS context
4243 * @envelope is automatically reallocated message envelope structure
4244 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4245 * In case of error @envelope will be freed. */
4246 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4247 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4248 isds_error err
= IE_SUCCESS
;
4249 xmlXPathObjectPtr result
= NULL
;
4251 if (!context
) return IE_INVALID_CONTEXT
;
4252 if (!envelope
) return IE_INVAL
;
4253 isds_envelope_free(envelope
);
4254 if (!xpath_ctx
) return IE_INVAL
;
4257 *envelope
= calloc(1, sizeof(**envelope
));
4264 /* Extract tRecord data */
4265 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4267 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4268 * dmAcceptanceTime. */
4269 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4270 if (err
) goto leave
;
4272 /* Extract envelope elements added by sender and ISDS
4273 * (XSD: gMessageEnvelope type) */
4274 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4275 if (err
) goto leave
;
4277 /* Get message type */
4278 err
= append_message_type(context
, envelope
, xpath_ctx
);
4279 if (err
) goto leave
;
4283 if (err
) isds_envelope_free(envelope
);
4284 xmlXPathFreeObject(result
);
4289 /* Convert XSD:tStateChangesRecord type XML tree into structure
4290 * @context is ISDS context
4291 * @changed_status is automatically reallocated message state change structure
4292 * @xpath_ctx is XPath context with current node as element of
4293 * XSD:tStateChangesRecord type
4294 * In case of error @changed_status will be freed. */
4295 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4296 struct isds_message_status_change
**changed_status
,
4297 xmlXPathContextPtr xpath_ctx
) {
4298 isds_error err
= IE_SUCCESS
;
4299 xmlXPathObjectPtr result
= NULL
;
4300 unsigned long int *unumber
= NULL
;
4301 char *string
= NULL
;
4303 if (!context
) return IE_INVALID_CONTEXT
;
4304 if (!changed_status
) return IE_INVAL
;
4305 isds_message_status_change_free(changed_status
);
4306 if (!xpath_ctx
) return IE_INVAL
;
4309 *changed_status
= calloc(1, sizeof(**changed_status
));
4310 if (!*changed_status
) {
4316 /* Extract tGetStateChangesInput data */
4317 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4319 /* dmEventTime is mandatory */
4320 EXTRACT_STRING("isds:dmEventTime", string
);
4322 err
= timestring2timeval((xmlChar
*) string
,
4323 &((*changed_status
)->time
));
4325 char *string_locale
= _isds_utf82locale(string
);
4326 if (err
== IE_DATE
) err
= IE_ISDS
;
4327 isds_printf_message(context
,
4328 _("Could not convert dmEventTime as ISO time: %s"),
4330 free(string_locale
);
4336 /* dmMessageStatus element is mandatory */
4337 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4339 isds_log_message(context
,
4340 _("Missing mandatory isds:dmMessageStatus integer"));
4344 err
= uint2isds_message_status(context
, unumber
,
4345 &((*changed_status
)->dmMessageStatus
));
4347 if (err
== IE_ENUM
) err
= IE_ISDS
;
4356 if (err
) isds_message_status_change_free(changed_status
);
4357 xmlXPathFreeObject(result
);
4360 #endif /* HAVE_LIBCURL */
4363 /* Find and convert isds:dmHash XML tree into structure
4364 * @context is ISDS context
4365 * @envelope is automatically reallocated message hash structure
4366 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4367 * In case of error @hash will be freed. */
4368 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4369 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4370 isds_error err
= IE_SUCCESS
;
4371 xmlNodePtr old_ctx_node
;
4372 xmlXPathObjectPtr result
= NULL
;
4373 char *string
= NULL
;
4375 if (!context
) return IE_INVALID_CONTEXT
;
4376 if (!hash
) return IE_INVAL
;
4377 isds_hash_free(hash
);
4378 if (!xpath_ctx
) return IE_INVAL
;
4380 old_ctx_node
= xpath_ctx
->node
;
4382 *hash
= calloc(1, sizeof(**hash
));
4389 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4390 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4399 /* Get hash algorithm */
4400 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4401 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4403 if (err
== IE_ENUM
) {
4404 char *string_locale
= _isds_utf82locale(string
);
4405 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4407 free(string_locale
);
4413 /* Get hash value */
4414 EXTRACT_STRING(".", string
);
4416 isds_printf_message(context
,
4417 _("sisds:dmHash element is missing hash value"));
4421 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4422 if ((*hash
)->length
== (size_t) -1) {
4423 isds_printf_message(context
,
4424 _("Error while Base64-decoding hash value"));
4430 if (err
) isds_hash_free(hash
);
4432 xmlXPathFreeObject(result
);
4433 xpath_ctx
->node
= old_ctx_node
;
4438 /* Find and append isds:dmQTimestamp XML tree into envelope.
4439 * Because one service is allowed to miss time-stamp content, and we think
4440 * other could too (flaw in specification), this function is deliberated and
4441 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4442 * @context is ISDS context
4443 * @envelope is automatically allocated envelope structure
4444 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4446 * In case of error @envelope will be freed. */
4447 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4448 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4449 isds_error err
= IE_SUCCESS
;
4450 xmlXPathObjectPtr result
= NULL
;
4451 char *string
= NULL
;
4453 if (!context
) return IE_INVALID_CONTEXT
;
4454 if (!envelope
) return IE_INVAL
;
4456 isds_envelope_free(envelope
);
4461 *envelope
= calloc(1, sizeof(**envelope
));
4467 zfree((*envelope
)->timestamp
);
4468 (*envelope
)->timestamp_length
= 0;
4471 /* Get dmQTimestamp */
4472 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4474 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4477 (*envelope
)->timestamp_length
=
4478 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4479 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4480 isds_printf_message(context
,
4481 _("Error while Base64-decoding time stamp value"));
4487 if (err
) isds_envelope_free(envelope
);
4489 xmlXPathFreeObject(result
);
4494 /* Convert XSD tReturnedMessage XML tree into message structure.
4495 * It does not store serialized XML tree into message->raw.
4496 * It does store (pointer to) parsed XML tree into message->xml if needed.
4497 * @context is ISDS context
4498 * @include_documents Use true if documents must be extracted
4499 * (tReturnedMessage XSD type), use false if documents shall be omitted
4500 * (tReturnedMessageEnvelope).
4501 * @message is automatically reallocated message structure
4502 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4504 * In case of error @message will be freed. */
4505 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4506 const _Bool include_documents
, struct isds_message
**message
,
4507 xmlXPathContextPtr xpath_ctx
) {
4508 isds_error err
= IE_SUCCESS
;
4509 xmlNodePtr message_node
;
4511 if (!context
) return IE_INVALID_CONTEXT
;
4512 if (!message
) return IE_INVAL
;
4513 isds_message_free(message
);
4514 if (!xpath_ctx
) return IE_INVAL
;
4517 *message
= calloc(1, sizeof(**message
));
4523 /* Save message XPATH context node */
4524 message_node
= xpath_ctx
->node
;
4528 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4529 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4530 if (err
) { err
= IE_ERROR
; goto leave
; }
4531 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4532 if (err
) goto leave
;
4534 if (include_documents
) {
4535 struct isds_list
*item
;
4537 /* Extract dmFiles */
4538 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4540 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4541 err
= IE_ISDS
; goto leave
;
4543 if (err
) { err
= IE_ERROR
; goto leave
; }
4544 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4545 if (err
) goto leave
;
4547 /* Store xmlDoc of this message if needed */
4548 /* Only if we got a XML document in all the documents. */
4549 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4550 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4551 (*message
)->xml
= xpath_ctx
->doc
;
4558 /* Restore context to message */
4559 xpath_ctx
->node
= message_node
;
4561 /* Extract dmHash */
4562 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4564 if (err
) goto leave
;
4566 /* Extract dmQTimestamp, */
4567 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4569 if (err
) goto leave
;
4571 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4572 * dmAcceptanceTime. */
4573 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4574 if (err
) goto leave
;
4576 /* Get message type */
4577 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4578 if (err
) goto leave
;
4581 if (err
) isds_message_free(message
);
4586 /* Extract message event into reallocated isds_event structure
4587 * @context is ISDS context
4588 * @event is automatically reallocated message event structure
4589 * @xpath_ctx is XPath context with current node as isds:dmEvent
4590 * In case of error @event will be freed. */
4591 static isds_error
extract_event(struct isds_ctx
*context
,
4592 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4593 isds_error err
= IE_SUCCESS
;
4594 xmlXPathObjectPtr result
= NULL
;
4595 xmlNodePtr event_node
;
4596 char *string
= NULL
;
4598 if (!context
) return IE_INVALID_CONTEXT
;
4599 if (!event
) return IE_INVAL
;
4600 isds_event_free(event
);
4601 if (!xpath_ctx
) return IE_INVAL
;
4602 event_node
= xpath_ctx
->node
;
4604 *event
= calloc(1, sizeof(**event
));
4610 /* Extract event data.
4611 * All elements are optional according XSD. That's funny. */
4612 EXTRACT_STRING("sisds:dmEventTime", string
);
4614 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4616 char *string_locale
= _isds_utf82locale(string
);
4617 if (err
== IE_DATE
) err
= IE_ISDS
;
4618 isds_printf_message(context
,
4619 _("Could not convert dmEventTime as ISO time: %s"),
4621 free(string_locale
);
4627 /* dmEventDescr element has prefix and the rest */
4628 EXTRACT_STRING("sisds:dmEventDescr", string
);
4630 err
= eventstring2event((xmlChar
*) string
, *event
);
4631 if (err
) goto leave
;
4636 if (err
) isds_event_free(event
);
4638 xmlXPathFreeObject(result
);
4639 xpath_ctx
->node
= event_node
;
4644 /* Convert element of XSD tEventsArray type from XML tree into
4645 * isds_list of isds_event's structure. The list is automatically reallocated.
4646 * @context is ISDS context
4647 * @events is automatically reallocated list of event structures
4648 * @xpath_ctx is XPath context with current node as tEventsArray
4649 * In case of error @events will be freed. */
4650 static isds_error
extract_events(struct isds_ctx
*context
,
4651 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4652 isds_error err
= IE_SUCCESS
;
4653 xmlXPathObjectPtr result
= NULL
;
4654 xmlNodePtr events_node
;
4655 struct isds_list
*event
, *prev_event
= NULL
;
4657 if (!context
) return IE_INVALID_CONTEXT
;
4658 if (!events
) return IE_INVAL
;
4659 if (!xpath_ctx
) return IE_INVAL
;
4660 events_node
= xpath_ctx
->node
;
4663 isds_list_free(events
);
4666 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4673 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4674 isds_printf_message(context
,
4675 _("Delivery info does not contain any event"));
4681 /* Iterate over events */
4682 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4684 /* Allocate and append list item */
4685 event
= calloc(1, sizeof(*event
));
4690 event
->destructor
= (void (*)(void **))isds_event_free
;
4691 if (i
== 0) *events
= event
;
4692 else prev_event
->next
= event
;
4696 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4697 err
= extract_event(context
,
4698 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4699 if (err
) goto leave
;
4704 if (err
) isds_list_free(events
);
4705 xmlXPathFreeObject(result
);
4706 xpath_ctx
->node
= events_node
;
4712 /* Insert Base64 encoded data as element with text child.
4713 * @context is session context
4714 * @parent is XML node to append @element with @data as child
4715 * @ns is XML namespace of @element, use NULL to inherit from @parent
4716 * @element is UTF-8 encoded name of new element
4717 * @data is bit stream to encode into @element
4718 * @length is size of @data in bytes
4719 * @return standard error code and fill long error message if needed */
4720 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4721 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4722 const void *data
, size_t length
) {
4723 isds_error err
= IE_SUCCESS
;
4726 if (!context
) return IE_INVALID_CONTEXT
;
4727 if (!data
&& length
> 0) return IE_INVAL
;
4728 if (!parent
|| !element
) return IE_INVAL
;
4730 xmlChar
*base64data
= NULL
;
4731 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4733 isds_printf_message(context
,
4734 ngettext("Not enough memory to encode %zd byte into Base64",
4735 "Not enough memory to encode %zd bytes into Base64",
4741 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4749 /* Convert isds_document structure into XML tree and append to dmFiles node.
4750 * @context is session context
4751 * @document is ISDS document
4752 * @dm_files is XML element the resulting tree will be appended to as a child.
4753 * @return error code, in case of error context' message is filled. */
4754 static isds_error
insert_document(struct isds_ctx
*context
,
4755 struct isds_document
*document
, xmlNodePtr dm_files
) {
4756 isds_error err
= IE_SUCCESS
;
4757 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4758 xmlAttrPtr attribute_node
;
4760 if (!context
) return IE_INVALID_CONTEXT
;
4761 if (!document
|| !dm_files
) return IE_INVAL
;
4763 /* Allocate new dmFile */
4764 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4766 isds_printf_message(context
, _("Could not allocate main dmFile"));
4770 /* Append the new dmFile.
4771 * XXX: Main document must go first */
4772 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4773 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4775 file
= xmlAddChild(dm_files
, new_file
);
4778 xmlFreeNode(new_file
); new_file
= NULL
;
4779 isds_printf_message(context
, _("Could not add dmFile child to "
4780 "%s element"), dm_files
->name
);
4785 /* @dmMimeType is required */
4786 if (!document
->dmMimeType
) {
4787 isds_log_message(context
,
4788 _("Document is missing mandatory MIME type definition"));
4792 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4794 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4796 isds_printf_message(context
,
4797 _("Document has unknown dmFileMetaType: %ld"),
4798 document
->dmFileMetaType
);
4802 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4804 if (document
->dmFileGuid
) {
4805 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4807 if (document
->dmUpFileGuid
) {
4808 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4811 /* @dmFileDescr is required */
4812 if (!document
->dmFileDescr
) {
4813 isds_log_message(context
,
4814 _("Document is missing mandatory description (title)"));
4818 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4820 if (document
->dmFormat
) {
4821 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4825 /* Insert content (body) of the document. */
4826 if (document
->is_xml
) {
4827 /* XML document requested */
4829 /* Allocate new dmXMLContent */
4830 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4832 isds_printf_message(context
,
4833 _("Could not allocate dmXMLContent element"));
4838 node
= xmlAddChild(file
, xmlcontent
);
4840 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4841 isds_printf_message(context
,
4842 _("Could not add dmXMLContent child to %s element"),
4848 /* Copy non-empty node list */
4849 if (document
->xml_node_list
) {
4850 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4851 document
->xml_node_list
);
4853 isds_printf_message(context
,
4854 _("Not enough memory to copy XML document"));
4859 if (!xmlAddChildList(node
, content
)) {
4860 xmlFreeNodeList(content
);
4861 isds_printf_message(context
,
4862 _("Error while adding XML document into dmXMLContent"));
4866 /* XXX: We cannot free the content here because it's part of node's
4867 * document since now. It will be freed with it automatically. */
4870 /* Binary document requested */
4871 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4872 document
->data
, document
->data_length
);
4873 if (err
) goto leave
;
4881 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4882 * The copy must be preallocated, the date are just appended into structure.
4883 * @context is ISDS context
4884 * @copy is message copy structure
4885 * @xpath_ctx is XPath context with current node as tMStatus */
4886 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4887 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4888 isds_error err
= IE_SUCCESS
;
4889 xmlXPathObjectPtr result
= NULL
;
4890 char *code
= NULL
, *message
= NULL
;
4892 if (!context
) return IE_INVALID_CONTEXT
;
4893 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4895 /* Free old values */
4896 zfree(copy
->dmStatus
);
4899 /* Get error specific to this copy */
4900 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4902 isds_log_message(context
,
4903 _("Missing isds:dmStatusCode under "
4904 "XSD:tMStatus type element"));
4909 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4910 /* This copy failed */
4911 copy
->error
= IE_ISDS
;
4912 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4914 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4915 if (!copy
->dmStatus
) {
4916 copy
->dmStatus
= code
;
4920 copy
->dmStatus
= code
;
4924 /* This copy succeeded. In this case only, message ID is valid */
4925 copy
->error
= IE_SUCCESS
;
4927 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4929 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4930 "but did not returned assigned message ID\n"));
4938 xmlXPathFreeObject(result
);
4943 /* Insert struct isds_approval data (box approval) into XML tree
4944 * @context is session context
4945 * @approval is libisds structure with approval description. NULL is
4947 * @parent is XML element to append @approval to */
4948 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4949 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4951 isds_error err
= IE_SUCCESS
;
4954 if (!context
) return IE_INVALID_CONTEXT
;
4955 if (!parent
) return IE_INVAL
;
4957 if (!approval
) return IE_SUCCESS
;
4959 /* Build XSD:gExtApproval */
4960 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4961 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4968 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4970 * @context is session context
4971 * @service_name is name of SERVICE_DB_ACCESS
4972 * @response is reallocated server SOAP body response as XML document
4973 * @raw_response is reallocated bit stream with response body. Use
4974 * NULL if you don't care
4975 * @raw_response_length is size of @raw_response in bytes
4976 * @code is reallocated ISDS status code
4977 * @status_message is reallocated ISDS status message
4978 * @return error coded from lower layer, context message will be set up
4980 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4981 const xmlChar
*service_name
,
4982 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4983 xmlChar
**code
, xmlChar
**status_message
) {
4985 isds_error err
= IE_SUCCESS
;
4986 char *service_name_locale
= NULL
;
4987 xmlNodePtr request
= NULL
, node
;
4988 xmlNsPtr isds_ns
= NULL
;
4990 if (!context
) return IE_INVALID_CONTEXT
;
4991 if (!service_name
) return IE_INVAL
;
4992 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4993 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4995 /* Free output argument */
4996 xmlFreeDoc(*response
); *response
= NULL
;
4997 if (raw_response
) zfree(*raw_response
);
4999 zfree(*status_message
);
5002 /* Check if connection is established
5003 * TODO: This check should be done downstairs. */
5004 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5006 service_name_locale
= _isds_utf82locale((char*)service_name
);
5007 if (!service_name_locale
) {
5013 request
= xmlNewNode(NULL
, service_name
);
5015 isds_printf_message(context
,
5016 _("Could not build %s request"), service_name_locale
);
5020 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5022 isds_log_message(context
, _("Could not create ISDS name space"));
5026 xmlSetNs(request
, isds_ns
);
5029 /* Add XSD:tDummyInput child */
5030 INSERT_STRING(request
, "dbDummy", NULL
);
5033 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5034 service_name_locale
);
5037 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
5038 raw_response
, raw_response_length
);
5039 xmlFreeNode(request
); request
= NULL
;
5042 isds_log(ILF_ISDS
, ILL_DEBUG
,
5043 _("Processing ISDS response on %s request failed\n"),
5044 service_name_locale
);
5048 /* Check for response status */
5049 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
5050 code
, status_message
, NULL
);
5052 isds_log(ILF_ISDS
, ILL_DEBUG
,
5053 _("ISDS response on %s request is missing status\n"),
5054 service_name_locale
);
5058 /* Request processed, but nothing found */
5059 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
5060 char *code_locale
= _isds_utf82locale((char*) *code
);
5061 char *status_message_locale
=
5062 _isds_utf82locale((char*) *status_message
);
5063 isds_log(ILF_ISDS
, ILL_DEBUG
,
5064 _("Server refused %s request (code=%s, message=%s)\n"),
5065 service_name_locale
, code_locale
, status_message_locale
);
5066 isds_log_message(context
, status_message_locale
);
5068 free(status_message_locale
);
5074 free(service_name_locale
);
5075 xmlFreeNode(request
);
5081 /* Get data about logged in user and his box.
5082 * @context is session context
5083 * @db_owner_info is reallocated box owner description. It will be freed on
5085 * @return error code from lower layer, context message will be set up
5087 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
5088 struct isds_DbOwnerInfo
**db_owner_info
) {
5089 isds_error err
= IE_SUCCESS
;
5091 xmlDocPtr response
= NULL
;
5092 xmlChar
*code
= NULL
, *message
= NULL
;
5093 xmlXPathContextPtr xpath_ctx
= NULL
;
5094 xmlXPathObjectPtr result
= NULL
;
5095 char *string
= NULL
;
5098 if (!context
) return IE_INVALID_CONTEXT
;
5099 zfree(context
->long_message
);
5100 if (!db_owner_info
) return IE_INVAL
;
5101 isds_DbOwnerInfo_free(db_owner_info
);
5104 /* Check if connection is established */
5105 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5108 /* Do request and check for success */
5109 err
= build_send_check_dbdummy_request(context
,
5110 BAD_CAST
"GetOwnerInfoFromLogin",
5111 &response
, NULL
, NULL
, &code
, &message
);
5112 if (err
) goto leave
;
5116 /* Prepare structure */
5117 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5118 if (!*db_owner_info
) {
5122 xpath_ctx
= xmlXPathNewContext(response
);
5127 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5132 /* Set context node */
5133 result
= xmlXPathEvalExpression(BAD_CAST
5134 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5139 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5140 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5144 if (result
->nodesetval
->nodeNr
> 1) {
5145 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5149 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5150 xmlXPathFreeObject(result
); result
= NULL
;
5153 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5158 isds_DbOwnerInfo_free(db_owner_info
);
5162 xmlXPathFreeObject(result
);
5163 xmlXPathFreeContext(xpath_ctx
);
5167 xmlFreeDoc(response
);
5170 isds_log(ILF_ISDS
, ILL_DEBUG
,
5171 _("GetOwnerInfoFromLogin request processed by server "
5172 "successfully.\n"));
5173 #else /* not HAVE_LIBCURL */
5181 /* Get data about logged in user. */
5182 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5183 struct isds_DbUserInfo
**db_user_info
) {
5184 isds_error err
= IE_SUCCESS
;
5186 xmlDocPtr response
= NULL
;
5187 xmlChar
*code
= NULL
, *message
= NULL
;
5188 xmlXPathContextPtr xpath_ctx
= NULL
;
5189 xmlXPathObjectPtr result
= NULL
;
5192 if (!context
) return IE_INVALID_CONTEXT
;
5193 zfree(context
->long_message
);
5194 if (!db_user_info
) return IE_INVAL
;
5195 isds_DbUserInfo_free(db_user_info
);
5198 /* Check if connection is established */
5199 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5202 /* Do request and check for success */
5203 err
= build_send_check_dbdummy_request(context
,
5204 BAD_CAST
"GetUserInfoFromLogin",
5205 &response
, NULL
, NULL
, &code
, &message
);
5206 if (err
) goto leave
;
5210 /* Prepare structure */
5211 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5212 if (!*db_user_info
) {
5216 xpath_ctx
= xmlXPathNewContext(response
);
5221 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5226 /* Set context node */
5227 result
= xmlXPathEvalExpression(BAD_CAST
5228 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5233 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5234 isds_log_message(context
, _("Missing dbUserInfo element"));
5238 if (result
->nodesetval
->nodeNr
> 1) {
5239 isds_log_message(context
, _("Multiple dbUserInfo element"));
5243 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5244 xmlXPathFreeObject(result
); result
= NULL
;
5247 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5251 isds_DbUserInfo_free(db_user_info
);
5254 xmlXPathFreeObject(result
);
5255 xmlXPathFreeContext(xpath_ctx
);
5259 xmlFreeDoc(response
);
5262 isds_log(ILF_ISDS
, ILL_DEBUG
,
5263 _("GetUserInfoFromLogin request processed by server "
5264 "successfully.\n"));
5265 #else /* not HAVE_LIBCURL */
5273 /* Get expiration time of current password
5274 * @context is session context
5275 * @expiration is automatically reallocated time when password expires. If
5276 * password expiration is disabled, NULL will be returned. In case of error
5277 * it will be nulled too. */
5278 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5279 struct timeval
**expiration
) {
5280 isds_error err
= IE_SUCCESS
;
5282 xmlDocPtr response
= NULL
;
5283 xmlChar
*code
= NULL
, *message
= NULL
;
5284 xmlXPathContextPtr xpath_ctx
= NULL
;
5285 xmlXPathObjectPtr result
= NULL
;
5286 char *string
= NULL
;
5289 if (!context
) return IE_INVALID_CONTEXT
;
5290 zfree(context
->long_message
);
5291 if (!expiration
) return IE_INVAL
;
5295 /* Check if connection is established */
5296 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5299 /* Do request and check for success */
5300 err
= build_send_check_dbdummy_request(context
,
5301 BAD_CAST
"GetPasswordInfo",
5302 &response
, NULL
, NULL
, &code
, &message
);
5303 if (err
) goto leave
;
5307 xpath_ctx
= xmlXPathNewContext(response
);
5312 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5317 /* Set context node */
5318 result
= xmlXPathEvalExpression(BAD_CAST
5319 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5324 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5325 isds_log_message(context
,
5326 _("Missing GetPasswordInfoResponse element"));
5330 if (result
->nodesetval
->nodeNr
> 1) {
5331 isds_log_message(context
,
5332 _("Multiple GetPasswordInfoResponse element"));
5336 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5337 xmlXPathFreeObject(result
); result
= NULL
;
5339 /* Extract expiration date */
5340 EXTRACT_STRING("isds:pswExpDate", string
);
5342 /* And convert it if any returned. Otherwise expiration is disabled. */
5343 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5345 char *string_locale
= _isds_utf82locale(string
);
5346 if (err
== IE_DATE
) err
= IE_ISDS
;
5347 isds_printf_message(context
,
5348 _("Could not convert pswExpDate as ISO time: %s"),
5350 free(string_locale
);
5363 xmlXPathFreeObject(result
);
5364 xmlXPathFreeContext(xpath_ctx
);
5368 xmlFreeDoc(response
);
5371 isds_log(ILF_ISDS
, ILL_DEBUG
,
5372 _("GetPasswordInfo request processed by server "
5373 "successfully.\n"));
5374 #else /* not HAVE_LIBCURL */
5383 /* Request delivering new TOTP code from ISDS through side channel before
5384 * changing password.
5385 * @context is session context
5386 * @password is current password.
5387 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5388 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5389 * function for more details.
5390 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5391 * NULL, if you don't care.
5392 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5394 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5395 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5396 isds_error err
= IE_SUCCESS
;
5397 char *saved_url
= NULL
; /* No copy */
5398 #if HAVE_CURL_REAUTHORIZATION_BUG
5399 CURL
*saved_curl
= NULL
; /* No copy */
5401 xmlNsPtr isds_ns
= NULL
;
5402 xmlNodePtr request
= NULL
;
5403 xmlDocPtr response
= NULL
;
5404 xmlChar
*code
= NULL
, *message
= NULL
;
5405 const xmlChar
*codes
[] = {
5410 const char *meanings
[] = {
5411 N_("Unexpected error"),
5412 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5413 N_("One-time code could not been sent. Try later again.")
5415 const isds_otp_resolution resolutions
[] = {
5416 OTP_RESOLUTION_UNKNOWN
,
5417 OTP_RESOLUTION_TO_FAST
,
5418 OTP_RESOLUTION_TOTP_NOT_SENT
5421 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5422 zfree(context
->long_message
);
5423 if (NULL
== password
) {
5424 isds_log_message(context
,
5425 _("Second argument (password) of isds_change_password() "
5430 /* Check if connection is established
5431 * TODO: This check should be done downstairs. */
5432 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5434 if (!context
->otp
) {
5435 isds_log_message(context
, _("This function requires OTP-authenticated "
5437 return IE_INVALID_CONTEXT
;
5440 isds_log_message(context
, _("If one-time password authentication "
5441 "method is in use, requesting new OTP code requires "
5442 "one-time credentials argument either"));
5445 if (otp
->method
!= OTP_TIME
) {
5446 isds_log_message(context
, _("Requesting new time-based OTP code from "
5447 "server requires one-time password authentication "
5451 if (otp
->otp_code
!= NULL
) {
5452 isds_log_message(context
, _("Requesting new time-based OTP code from "
5453 "server requires undefined OTP code member in "
5454 "one-time credentials argument"));
5460 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5462 isds_log_message(context
, _("Could not build SendSMSCode request"));
5465 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5467 isds_log_message(context
, _("Could not create ISDS name space"));
5468 xmlFreeNode(request
);
5471 xmlSetNs(request
, isds_ns
);
5473 /* Change URL temporarily for sending this request only */
5475 char *new_url
= NULL
;
5476 if ((err
= _isds_build_url_from_context(context
,
5477 "%.*sasws/changePassword", &new_url
))) {
5480 saved_url
= context
->url
;
5481 context
->url
= new_url
;
5484 /* Store credentials for sending this request only */
5485 context
->otp_credentials
= otp
;
5486 _isds_discard_credentials(context
, 0);
5487 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5489 _isds_discard_credentials(context
, 0);
5492 #if HAVE_CURL_REAUTHORIZATION_BUG
5493 saved_curl
= context
->curl
;
5494 context
->curl
= curl_easy_init();
5495 if (NULL
== context
->curl
) {
5499 if (context
->timeout
) {
5500 err
= isds_set_timeout(context
, context
->timeout
);
5501 if (err
) goto leave
;
5505 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5508 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5510 /* Remove temporal credentials */
5511 _isds_discard_credentials(context
, 0);
5512 /* Detach pointer to OTP credentials from context */
5513 context
->otp_credentials
= NULL
;
5514 /* Keep context->otp true to keep signaling this is OTP session */
5516 /* Destroy request */
5517 xmlFreeNode(request
); request
= NULL
;
5520 isds_log(ILF_ISDS
, ILL_DEBUG
,
5521 _("Processing ISDS response on SendSMSCode request failed\n"));
5525 /* Check for response status */
5526 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5527 &code
, &message
, (xmlChar
**)refnumber
);
5529 isds_log(ILF_ISDS
, ILL_DEBUG
,
5530 _("ISDS response on SendSMSCode request is missing "
5535 /* Check for error */
5536 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5537 char *code_locale
= _isds_utf82locale((char*)code
);
5538 char *message_locale
= _isds_utf82locale((char*)message
);
5540 isds_log(ILF_ISDS
, ILL_DEBUG
,
5541 _("Server refused to send new code on SendSMSCode "
5542 "request (code=%s, message=%s)\n"),
5543 code_locale
, message_locale
);
5545 /* Check for known error codes */
5546 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5547 if (!xmlStrcmp(code
, codes
[i
])) break;
5549 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5550 isds_log_message(context
, _(meanings
[i
]));
5551 /* Mimic otp->resolution according to the code, specification does
5552 * prescribe OTP header to be available. */
5553 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5554 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5555 otp
->resolution
= resolutions
[i
];
5557 isds_log_message(context
, message_locale
);
5560 free(message_locale
);
5566 /* Otherwise new code sent successfully */
5567 /* Mimic otp->resolution according to the code, specification does
5568 * prescribe OTP header to be available. */
5569 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5570 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5573 if (NULL
!= saved_url
) {
5574 /* Revert URL to original one */
5575 zfree(context
->url
);
5576 context
->url
= saved_url
;
5578 #if HAVE_CURL_REAUTHORIZATION_BUG
5579 if (NULL
!= saved_curl
) {
5580 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5581 context
->curl
= saved_curl
;
5587 xmlFreeDoc(response
);
5588 xmlFreeNode(request
);
5591 isds_log(ILF_ISDS
, ILL_DEBUG
,
5592 _("New OTP code has been sent successfully on SendSMSCode "
5598 /* Convert response status code to isds_error code and set long message
5599 * @context is context to save long message to
5600 * @map is mapping from codes to errors and messages. Pass NULL for generic
5602 * @code is status code to translate
5603 * @message is non-localized status message to put into long message in case
5604 * of uknown error. It can be NULL if server did not provide any.
5605 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5606 * invalid invocation. */
5607 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5608 const struct code_map_isds_error
*map
,
5609 const xmlChar
*code
, const xmlChar
*message
) {
5611 isds_log_message(context
,
5612 _("NULL status code passed to statuscode2isds_error()"));
5617 /* Check for known error codes */
5618 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5619 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5620 isds_log_message(context
, _(map
->meanings
[i
]));
5621 return map
->errors
[i
];
5627 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5628 char *message_locale
= _isds_utf82locale((char*)message
);
5629 if (NULL
== message_locale
)
5630 isds_log_message(context
, _("ISDS server returned unknown error"));
5632 isds_log_message(context
, message_locale
);
5633 free(message_locale
);
5642 /* Change user password in ISDS.
5643 * User must supply old password, new password will takes effect after some
5644 * time, current session can continue. Password must fulfill some constraints.
5645 * @context is session context
5646 * @old_password is current password.
5647 * @new_password is requested new password
5648 * @otp auxiliary data required if one-time password authentication is in use,
5649 * defines OTP code (if known) and returns fine grade resolution of OTP
5650 * procedure. Pass NULL, if one-time password authentication is not needed.
5651 * Please note the @otp argument must match OTP method used at log-in time. See
5652 * isds_login() function for more details.
5653 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5654 * NULL, if you don't care.
5655 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5656 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5657 * awaiting OTP code that has been delivered by side channel to the user. */
5658 isds_error
isds_change_password(struct isds_ctx
*context
,
5659 const char *old_password
, const char *new_password
,
5660 struct isds_otp
*otp
, char **refnumber
) {
5661 isds_error err
= IE_SUCCESS
;
5663 char *saved_url
= NULL
; /* No copy */
5664 #if HAVE_CURL_REAUTHORIZATION_BUG
5665 CURL
*saved_curl
= NULL
; /* No copy */
5667 xmlNsPtr isds_ns
= NULL
;
5668 xmlNodePtr request
= NULL
, node
;
5669 xmlDocPtr response
= NULL
;
5670 xmlChar
*code
= NULL
, *message
= NULL
;
5671 const xmlChar
*codes
[] = {
5684 const char *meanings
[] = {
5685 N_("Password length must be between 8 and 32 characters"),
5686 N_("Password cannot be reused"), /* Server does not distinguish 1067
5687 and 1091 on ChangePasswordOTP */
5688 N_("Password contains forbidden character"),
5689 N_("Password must contain at least one upper-case letter, "
5690 "one lower-case, and one digit"),
5691 N_("Password cannot contain sequence of three identical characters"),
5692 N_("Password cannot contain user identifier"),
5693 N_("Password is too simmple"),
5694 N_("Old password is not valid"),
5695 N_("Password cannot be reused"),
5696 N_("Unexpected error"),
5697 N_("LDAP update error")
5701 if (!context
) return IE_INVALID_CONTEXT
;
5702 zfree(context
->long_message
);
5703 if (NULL
!= refnumber
)
5705 if (NULL
== old_password
) {
5706 isds_log_message(context
,
5707 _("Second argument (old password) of isds_change_password() "
5711 if (NULL
== otp
&& NULL
== new_password
) {
5712 isds_log_message(context
,
5713 _("Third argument (new password) of isds_change_password() "
5719 /* Check if connection is established
5720 * TODO: This check should be done downstairs. */
5721 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5723 if (context
->otp
&& NULL
== otp
) {
5724 isds_log_message(context
, _("If one-time password authentication "
5725 "method is in use, changing password requires one-time "
5726 "credentials either"));
5730 /* Build ChangeISDSPassword request */
5731 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5732 BAD_CAST
"ChangePasswordOTP");
5734 isds_log_message(context
, (NULL
== otp
) ?
5735 _("Could not build ChangeISDSPassword request") :
5736 _("Could not build ChangePasswordOTP request"));
5739 isds_ns
= xmlNewNs(request
,
5740 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5743 isds_log_message(context
, _("Could not create ISDS name space"));
5744 xmlFreeNode(request
);
5747 xmlSetNs(request
, isds_ns
);
5749 INSERT_STRING(request
, "dbOldPassword", old_password
);
5750 INSERT_STRING(request
, "dbNewPassword", new_password
);
5753 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5754 switch (otp
->method
) {
5756 isds_log(ILF_SEC
, ILL_INFO
,
5757 _("Selected authentication method: "
5758 "HMAC-based one-time password\n"));
5759 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5762 isds_log(ILF_SEC
, ILL_INFO
,
5763 _("Selected authentication method: "
5764 "Time-based one-time password\n"));
5765 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5766 if (otp
->otp_code
== NULL
) {
5767 isds_log(ILF_SEC
, ILL_INFO
,
5768 _("OTP code has not been provided by "
5769 "application, requesting server for "
5771 err
= _isds_request_totp_code(context
, old_password
, otp
,
5773 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5777 isds_log(ILF_SEC
, ILL_INFO
,
5778 _("OTP code has been provided by "
5779 "application, not requesting server "
5784 isds_log_message(context
,
5785 _("Unknown one-time password authentication "
5786 "method requested by application"));
5791 /* Change URL temporarily for sending this request only */
5793 char *new_url
= NULL
;
5794 if ((err
= _isds_build_url_from_context(context
,
5795 "%.*sasws/changePassword", &new_url
))) {
5798 saved_url
= context
->url
;
5799 context
->url
= new_url
;
5802 /* Store credentials for sending this request only */
5803 context
->otp_credentials
= otp
;
5804 _isds_discard_credentials(context
, 0);
5805 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5806 old_password
, NULL
))) {
5807 _isds_discard_credentials(context
, 0);
5810 #if HAVE_CURL_REAUTHORIZATION_BUG
5811 saved_curl
= context
->curl
;
5812 context
->curl
= curl_easy_init();
5813 if (NULL
== context
->curl
) {
5817 if (context
->timeout
) {
5818 err
= isds_set_timeout(context
, context
->timeout
);
5819 if (err
) goto leave
;
5824 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5825 _("Sending ChangeISDSPassword request to ISDS\n") :
5826 _("Sending ChangePasswordOTP request to ISDS\n"));
5829 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5830 request
, &response
, NULL
, NULL
);
5833 /* Remove temporal credentials */
5834 _isds_discard_credentials(context
, 0);
5835 /* Detach pointer to OTP credentials from context */
5836 context
->otp_credentials
= NULL
;
5837 /* Keep context->otp true to keep signaling this is OTP session */
5840 /* Destroy request */
5841 xmlFreeNode(request
); request
= NULL
;
5844 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5845 _("Processing ISDS response on ChangeISDSPassword "
5846 "request failed\n") :
5847 _("Processing ISDS response on ChangePasswordOTP "
5848 "request failed\n"));
5852 /* Check for response status */
5853 err
= isds_response_status(context
,
5854 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5855 &code
, &message
, (xmlChar
**)refnumber
);
5857 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5858 _("ISDS response on ChangeISDSPassword request is missing "
5860 _("ISDS response on ChangePasswordOTP request is missing "
5865 /* Check for known error codes */
5866 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5867 if (!xmlStrcmp(code
, codes
[i
])) {
5868 char *code_locale
= _isds_utf82locale((char*)code
);
5869 char *message_locale
= _isds_utf82locale((char*)message
);
5870 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5871 _("Server refused to change password on ChangeISDSPassword "
5872 "request (code=%s, message=%s)\n") :
5873 _("Server refused to change password on ChangePasswordOTP "
5874 "request (code=%s, message=%s)\n"),
5875 code_locale
, message_locale
);
5877 free(message_locale
);
5878 isds_log_message(context
, _(meanings
[i
]));
5885 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5886 char *code_locale
= _isds_utf82locale((char*)code
);
5887 char *message_locale
= _isds_utf82locale((char*)message
);
5888 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5889 _("Server refused to change password on ChangeISDSPassword "
5890 "request (code=%s, message=%s)\n") :
5891 _("Server refused to change password on ChangePasswordOTP "
5892 "request (code=%s, message=%s)\n"),
5893 code_locale
, message_locale
);
5894 isds_log_message(context
, message_locale
);
5896 free(message_locale
);
5901 /* Otherwise password changed successfully */
5904 if (NULL
!= saved_url
) {
5905 /* Revert URL to original one */
5906 zfree(context
->url
);
5907 context
->url
= saved_url
;
5909 #if HAVE_CURL_REAUTHORIZATION_BUG
5910 if (NULL
!= saved_curl
) {
5911 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5912 context
->curl
= saved_curl
;
5918 xmlFreeDoc(response
);
5919 xmlFreeNode(request
);
5922 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5923 _("Password changed successfully on ChangeISDSPassword "
5925 _("Password changed successfully on ChangePasswordOTP "
5927 #else /* not HAVE_LIBCURL */
5936 /* Generic middle part with request sending and response check.
5937 * It sends prepared request and checks for error code.
5938 * @context is ISDS session context.
5939 * @service is ISDS service handler
5940 * @service_name is name in scope of given @service
5941 * @request is XML tree with request. Will be freed to save memory.
5942 * @response is XML document outputting ISDS response.
5943 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5944 * @map is mapping from status code to library error. Pass NULL if no special
5945 * handling is requested.
5946 * NULL, if you don't care. */
5947 static isds_error
send_destroy_request_check_response(
5948 struct isds_ctx
*context
,
5949 const isds_service service
, const xmlChar
*service_name
,
5950 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5951 const struct code_map_isds_error
*map
) {
5952 isds_error err
= IE_SUCCESS
;
5953 char *service_name_locale
= NULL
;
5954 xmlChar
*code
= NULL
, *message
= NULL
;
5957 if (!context
) return IE_INVALID_CONTEXT
;
5958 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5962 /* Check if connection is established
5963 * TODO: This check should be done downstairs. */
5964 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5966 service_name_locale
= _isds_utf82locale((char*) service_name
);
5967 if (!service_name_locale
) {
5972 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5973 service_name_locale
);
5976 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5977 xmlFreeNode(*request
); *request
= NULL
;
5980 isds_log(ILF_ISDS
, ILL_DEBUG
,
5981 _("Processing ISDS response on %s request failed\n"),
5982 service_name_locale
);
5986 /* Check for response status */
5987 err
= isds_response_status(context
, service
, *response
,
5988 &code
, &message
, refnumber
);
5990 isds_log(ILF_ISDS
, ILL_DEBUG
,
5991 _("ISDS response on %s request is missing status\n"),
5992 service_name_locale
);
5996 err
= statuscode2isds_error(context
, map
, code
, message
);
5998 /* Request processed, but server failed */
5999 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6000 char *code_locale
= _isds_utf82locale((char*) code
);
6001 char *message_locale
= _isds_utf82locale((char*) message
);
6002 isds_log(ILF_ISDS
, ILL_DEBUG
,
6003 _("Server refused %s request (code=%s, message=%s)\n"),
6004 service_name_locale
, code_locale
, message_locale
);
6006 free(message_locale
);
6014 if (err
&& *response
) {
6015 xmlFreeDoc(*response
);
6019 xmlFreeNode(*request
);
6022 free(service_name_locale
);
6028 /* Generic bottom half with request sending.
6029 * It sends prepared request, checks for error code, destroys response and
6030 * request and log success or failure.
6031 * @context is ISDS session context.
6032 * @service is ISDS service handler
6033 * @service_name is name in scope of given @service
6034 * @request is XML tree with request. Will be freed to save memory.
6035 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6036 * NULL, if you don't care. */
6037 static isds_error
send_request_check_drop_response(
6038 struct isds_ctx
*context
,
6039 const isds_service service
, const xmlChar
*service_name
,
6040 xmlNodePtr
*request
, xmlChar
**refnumber
) {
6041 isds_error err
= IE_SUCCESS
;
6042 xmlDocPtr response
= NULL
;
6045 if (!context
) return IE_INVALID_CONTEXT
;
6046 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
6049 /* Send request and check response*/
6050 err
= send_destroy_request_check_response(context
,
6051 service
, service_name
, request
, &response
, refnumber
, NULL
);
6053 xmlFreeDoc(response
);
6056 xmlFreeNode(*request
);
6061 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6062 isds_log(ILF_ISDS
, ILL_DEBUG
,
6063 _("%s request processed by server successfully.\n"),
6064 service_name_locale
);
6065 free(service_name_locale
);
6072 /* Insert isds_credentials_delivery structure into XML request if not NULL
6073 * @context is session context
6074 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6075 * credentials delivery. The email field is passed.
6076 * @parent is XML element where to insert */
6077 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
6078 const struct isds_credentials_delivery
*credentials_delivery
,
6079 xmlNodePtr parent
) {
6080 isds_error err
= IE_SUCCESS
;
6083 if (!context
) return IE_INVALID_CONTEXT
;
6084 if (!parent
) return IE_INVAL
;
6086 if (credentials_delivery
) {
6087 /* Following elements are valid only for services:
6088 * NewAccessData, AddDataBoxUser, CreateDataBox */
6089 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
6090 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
6098 /* Extract credentials delivery from ISDS response.
6099 * @context is session context
6100 * @credentials_delivery is pointer to valid structure to fill in returned
6101 * user's password (and new log-in name). If NULL, do not extract the data.
6102 * @response is pointer to XML document with ISDS response
6103 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6104 * @return IE_SUCCESS even if new user name has not been found because it's not
6105 * clear whether it's returned always. */
6106 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
6107 struct isds_credentials_delivery
*credentials_delivery
,
6108 xmlDocPtr response
, const char *request_name
) {
6109 isds_error err
= IE_SUCCESS
;
6110 xmlXPathContextPtr xpath_ctx
= NULL
;
6111 xmlXPathObjectPtr result
= NULL
;
6112 char *xpath_query
= NULL
;
6114 if (!context
) return IE_INVALID_CONTEXT
;
6115 if (credentials_delivery
) {
6116 zfree(credentials_delivery
->token
);
6117 zfree(credentials_delivery
->new_user_name
);
6119 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6122 /* Extract optional token */
6123 if (credentials_delivery
) {
6124 xpath_ctx
= xmlXPathNewContext(response
);
6129 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6134 /* Verify root element */
6135 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6140 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6145 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6146 char *request_name_locale
= _isds_utf82locale(request_name
);
6147 isds_log(ILF_ISDS
, ILL_WARNING
,
6148 _("Wrong element in ISDS response for %s request "
6149 "while extracting credentials delivery details\n"),
6150 request_name_locale
);
6151 free(request_name_locale
);
6155 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6158 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6160 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6162 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6163 if (!credentials_delivery
->token
) {
6164 char *request_name_locale
= _isds_utf82locale(request_name
);
6165 isds_log(ILF_ISDS
, ILL_ERR
,
6166 _("ISDS did not return token on %s request "
6167 "even if requested\n"), request_name_locale
);
6168 free(request_name_locale
);
6175 xmlXPathFreeObject(result
);
6176 xmlXPathFreeContext(xpath_ctx
);
6182 /* Build XSD:tCreateDBInput request type for box creating.
6183 * @context is session context
6184 * @request outputs built XML tree
6185 * @service_name is request name of SERVICE_DB_MANIPULATION service
6186 * @box is box description to create including single primary user (in case of
6187 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6189 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6190 * box, or contact address of PFO box owner)
6191 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6192 * @upper_box_id is optional ID of supper box if currently created box is
6194 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6196 * @credentials_delivery is valid pointer if ISDS should return token that box
6197 * owner can use to obtain his new credentials in on-line way. Then valid email
6198 * member value should be supplied.
6199 * @approval is optional external approval of box manipulation */
6200 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6201 xmlNodePtr
*request
, const xmlChar
*service_name
,
6202 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6203 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6204 const xmlChar
*ceo_label
,
6205 const struct isds_credentials_delivery
*credentials_delivery
,
6206 const struct isds_approval
*approval
) {
6207 isds_error err
= IE_SUCCESS
;
6208 xmlNsPtr isds_ns
= NULL
;
6209 xmlNodePtr node
, dbPrimaryUsers
;
6210 xmlChar
*string
= NULL
;
6211 const struct isds_list
*item
;
6214 if (!context
) return IE_INVALID_CONTEXT
;
6215 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6219 /* Build CreateDataBox-similar request */
6220 *request
= xmlNewNode(NULL
, service_name
);
6222 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6223 isds_printf_message(context
, _("Could build %s request"),
6224 service_name_locale
);
6225 free(service_name_locale
);
6228 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6229 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6231 isds_log_message(context
, _("Could not create ISDS1 name space"));
6232 xmlFreeNode(*request
);
6236 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6238 isds_log_message(context
, _("Could not create ISDS name space"));
6239 xmlFreeNode(*request
);
6243 xmlSetNs(*request
, isds_ns
);
6245 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6246 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6247 if (err
) goto leave
;
6250 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6251 * verbose documentation allows none dbUserInfo */
6252 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6253 for (item
= users
; item
; item
= item
->next
) {
6255 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6256 err
= insert_DbUserInfo(context
,
6257 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6258 if (err
) goto leave
;
6262 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6263 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6264 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6266 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6267 if (err
) goto leave
;
6269 err
= insert_GExtApproval(context
, approval
, *request
);
6270 if (err
) goto leave
;
6274 xmlFreeNode(*request
);
6280 #endif /* HAVE_LIBCURL */
6284 * @context is session context
6285 * @box is box description to create including single primary user (in case of
6286 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6287 * ignored. It outputs box ID assigned by ISDS in dbID element.
6288 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6289 * box, or contact address of PFO box owner)
6290 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6291 * @upper_box_id is optional ID of supper box if currently created box is
6293 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6294 * @credentials_delivery is NULL if new password should be delivered off-line
6295 * to box owner. It is valid pointer if owner should obtain new password on-line
6296 * on dedicated web server. Then input @credentials_delivery.email value is
6297 * his e-mail address he must provide to dedicated web server together
6298 * with output reallocated @credentials_delivery.token member. Output
6299 * member @credentials_delivery.new_user_name is unused up on this call.
6300 * @approval is optional external approval of box manipulation
6301 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6302 * NULL, if you don't care.*/
6303 isds_error
isds_add_box(struct isds_ctx
*context
,
6304 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6305 const char *former_names
, const char *upper_box_id
,
6306 const char *ceo_label
,
6307 struct isds_credentials_delivery
*credentials_delivery
,
6308 const struct isds_approval
*approval
, char **refnumber
) {
6309 isds_error err
= IE_SUCCESS
;
6311 xmlNodePtr request
= NULL
;
6312 xmlDocPtr response
= NULL
;
6313 xmlXPathContextPtr xpath_ctx
= NULL
;
6314 xmlXPathObjectPtr result
= NULL
;
6318 if (!context
) return IE_INVALID_CONTEXT
;
6319 zfree(context
->long_message
);
6320 if (credentials_delivery
) {
6321 zfree(credentials_delivery
->token
);
6322 zfree(credentials_delivery
->new_user_name
);
6324 if (!box
) return IE_INVAL
;
6327 /* Scratch box ID */
6330 /* Build CreateDataBox request */
6331 err
= build_CreateDBInput_request(context
,
6332 &request
, BAD_CAST
"CreateDataBox",
6333 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6334 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6335 if (err
) goto leave
;
6337 /* Send it to server and process response */
6338 err
= send_destroy_request_check_response(context
,
6339 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6340 &response
, (xmlChar
**) refnumber
, NULL
);
6342 /* Extract box ID */
6343 xpath_ctx
= xmlXPathNewContext(response
);
6348 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6352 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6354 /* Extract optional token */
6355 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6359 xmlXPathFreeObject(result
);
6360 xmlXPathFreeContext(xpath_ctx
);
6361 xmlFreeDoc(response
);
6362 xmlFreeNode(request
);
6365 isds_log(ILF_ISDS
, ILL_DEBUG
,
6366 _("CreateDataBox request processed by server successfully.\n"));
6368 #else /* not HAVE_LIBCURL */
6376 /* Notify ISDS about new PFO entity.
6377 * This function has no real effect.
6378 * @context is session context
6379 * @box is PFO description including single primary user. aifoIsds,
6380 * address->adCode, address->adDistrict members are ignored.
6381 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6382 * @former_names is optional undocumented string. Pass NULL if you don't care.
6383 * @upper_box_id is optional ID of supper box if currently created box is
6385 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6386 * @approval is optional external approval of box manipulation
6387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6388 * NULL, if you don't care.*/
6389 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6390 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6391 const char *former_names
, const char *upper_box_id
,
6392 const char *ceo_label
, const struct isds_approval
*approval
,
6394 isds_error err
= IE_SUCCESS
;
6396 xmlNodePtr request
= NULL
;
6399 if (!context
) return IE_INVALID_CONTEXT
;
6400 zfree(context
->long_message
);
6401 if (!box
) return IE_INVAL
;
6404 /* Build CreateDataBoxPFOInfo request */
6405 err
= build_CreateDBInput_request(context
,
6406 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6407 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6408 (xmlChar
*) ceo_label
, NULL
, approval
);
6409 if (err
) goto leave
;
6411 /* Send it to server and process response */
6412 err
= send_request_check_drop_response(context
,
6413 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6414 (xmlChar
**) refnumber
);
6415 /* XXX: XML Schema names output dbID element but textual documentation
6416 * states no box identifier is returned. */
6418 xmlFreeNode(request
);
6419 #else /* not HAVE_LIBCURL */
6426 /* Common implementation for removing given box.
6427 * @context is session context
6428 * @service_name is UTF-8 encoded name fo ISDS service
6429 * @box is box description to delete. aifoIsds, address->adCode,
6430 * address->adDistrict members are ignored.
6431 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6432 * carry sane value. If NULL, do not inject this information into request.
6433 * @approval is optional external approval of box manipulation
6434 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6435 * NULL, if you don't care.*/
6436 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6437 const xmlChar
*service_name
,
6438 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6439 const struct isds_approval
*approval
, char **refnumber
) {
6440 isds_error err
= IE_SUCCESS
;
6442 xmlNsPtr isds_ns
= NULL
;
6443 xmlNodePtr request
= NULL
;
6445 xmlChar
*string
= NULL
;
6449 if (!context
) return IE_INVALID_CONTEXT
;
6450 zfree(context
->long_message
);
6451 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6455 /* Build DeleteDataBox(Promptly) request */
6456 request
= xmlNewNode(NULL
, service_name
);
6458 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6459 isds_printf_message(context
,
6460 _("Could build %s request"), service_name_locale
);
6461 free(service_name_locale
);
6464 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6466 isds_log_message(context
, _("Could not create ISDS name space"));
6467 xmlFreeNode(request
);
6470 xmlSetNs(request
, isds_ns
);
6472 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6473 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6474 if (err
) goto leave
;
6477 err
= tm2datestring(since
, &string
);
6479 isds_log_message(context
,
6480 _("Could not convert `since' argument to ISO date string"));
6483 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6487 err
= insert_GExtApproval(context
, approval
, request
);
6488 if (err
) goto leave
;
6491 /* Send it to server and process response */
6492 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6493 service_name
, &request
, (xmlChar
**) refnumber
);
6496 xmlFreeNode(request
);
6498 #else /* not HAVE_LIBCURL */
6505 /* Remove given box permanently.
6506 * @context is session context
6507 * @box is box description to delete. aifoIsds, address->adCode,
6508 * address->adDistrict members are ignored.
6509 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6511 * @approval is optional external approval of box manipulation
6512 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6513 * NULL, if you don't care.*/
6514 isds_error
isds_delete_box(struct isds_ctx
*context
,
6515 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6516 const struct isds_approval
*approval
, char **refnumber
) {
6517 if (!context
) return IE_INVALID_CONTEXT
;
6518 zfree(context
->long_message
);
6519 if (!box
|| !since
) return IE_INVAL
;
6521 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6522 box
, since
, approval
, refnumber
);
6526 /* Undocumented function.
6527 * @context is session context
6528 * @box is box description to delete. aifoIsds, address->adCode,
6529 * address->adDistrict members are ignored.
6530 * @approval is optional external approval of box manipulation
6531 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6532 * NULL, if you don't care.*/
6533 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6534 const struct isds_DbOwnerInfo
*box
,
6535 const struct isds_approval
*approval
, char **refnumber
) {
6536 if (!context
) return IE_INVALID_CONTEXT
;
6537 zfree(context
->long_message
);
6538 if (!box
) return IE_INVAL
;
6540 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6541 box
, NULL
, approval
, refnumber
);
6545 /* Update data about given box.
6546 * @context is session context
6547 * @old_box current box description. aifoIsds, address->adCode,
6548 * address->adDistrict members are ignored.
6549 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6550 * address->adDistrict members are ignored.
6551 * @approval is optional external approval of box manipulation
6552 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6553 * NULL, if you don't care.*/
6554 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6555 const struct isds_DbOwnerInfo
*old_box
,
6556 const struct isds_DbOwnerInfo
*new_box
,
6557 const struct isds_approval
*approval
, char **refnumber
) {
6558 isds_error err
= IE_SUCCESS
;
6560 xmlNsPtr isds_ns
= NULL
;
6561 xmlNodePtr request
= NULL
;
6566 if (!context
) return IE_INVALID_CONTEXT
;
6567 zfree(context
->long_message
);
6568 if (!old_box
|| !new_box
) return IE_INVAL
;
6572 /* Build UpdateDataBoxDescr request */
6573 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6575 isds_log_message(context
,
6576 _("Could build UpdateDataBoxDescr request"));
6579 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6581 isds_log_message(context
, _("Could not create ISDS name space"));
6582 xmlFreeNode(request
);
6585 xmlSetNs(request
, isds_ns
);
6587 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6588 err
= insert_DbOwnerInfo(context
, old_box
, 0, node
);
6589 if (err
) goto leave
;
6591 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6592 err
= insert_DbOwnerInfo(context
, new_box
, 0, node
);
6593 if (err
) goto leave
;
6595 err
= insert_GExtApproval(context
, approval
, request
);
6596 if (err
) goto leave
;
6599 /* Send it to server and process response */
6600 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6601 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6604 xmlFreeNode(request
);
6605 #else /* not HAVE_LIBCURL */
6614 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6616 * @context is session context
6617 * @service is SOAP service
6618 * @service_name is name of request in @service
6619 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6620 * @box_id is box ID of interest
6621 * @approval is optional external approval of box manipulation
6622 * @response is server SOAP body response as XML document
6623 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6624 * NULL, if you don't care.
6625 * @return error coded from lower layer, context message will be set up
6627 static isds_error
build_send_dbid_request_check_response(
6628 struct isds_ctx
*context
, const isds_service service
,
6629 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6630 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6631 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6633 isds_error err
= IE_SUCCESS
;
6634 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6635 xmlNodePtr request
= NULL
, node
;
6636 xmlNsPtr isds_ns
= NULL
;
6638 if (!context
) return IE_INVALID_CONTEXT
;
6639 if (!service_name
|| !box_id
) return IE_INVAL
;
6640 if (!response
) return IE_INVAL
;
6642 /* Free output argument */
6643 xmlFreeDoc(*response
); *response
= NULL
;
6645 /* Prepare strings */
6646 service_name_locale
= _isds_utf82locale((char*)service_name
);
6647 if (!service_name_locale
) {
6651 box_id_locale
= _isds_utf82locale((char*)box_id
);
6652 if (!box_id_locale
) {
6658 request
= xmlNewNode(NULL
, service_name
);
6660 isds_printf_message(context
,
6661 _("Could not build %s request for %s box"), service_name_locale
,
6666 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6668 isds_log_message(context
, _("Could not create ISDS name space"));
6672 xmlSetNs(request
, isds_ns
);
6674 /* Add XSD:tIdDbInput children */
6675 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6676 INSERT_STRING(request
, box_id_element
, box_id
);
6677 err
= insert_GExtApproval(context
, approval
, request
);
6678 if (err
) goto leave
;
6680 /* Send request and check response*/
6681 err
= send_destroy_request_check_response(context
,
6682 service
, service_name
, &request
, response
, refnumber
, NULL
);
6685 free(service_name_locale
);
6686 free(box_id_locale
);
6687 xmlFreeNode(request
);
6690 #endif /* HAVE_LIBCURL */
6693 /* Get data about all users assigned to given box.
6694 * @context is session context
6696 * @users is automatically reallocated list of struct isds_DbUserInfo */
6697 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6698 struct isds_list
**users
) {
6699 isds_error err
= IE_SUCCESS
;
6701 xmlDocPtr response
= NULL
;
6702 xmlXPathContextPtr xpath_ctx
= NULL
;
6703 xmlXPathObjectPtr result
= NULL
;
6705 struct isds_list
*item
, *prev_item
= NULL
;
6708 if (!context
) return IE_INVALID_CONTEXT
;
6709 zfree(context
->long_message
);
6710 if (!users
|| !box_id
) return IE_INVAL
;
6711 isds_list_free(users
);
6715 /* Do request and check for success */
6716 err
= build_send_dbid_request_check_response(context
,
6717 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6718 BAD_CAST box_id
, NULL
, &response
, NULL
);
6719 if (err
) goto leave
;
6723 /* Prepare structure */
6724 xpath_ctx
= xmlXPathNewContext(response
);
6729 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6734 /* Set context node */
6735 result
= xmlXPathEvalExpression(BAD_CAST
6736 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6742 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6743 /* Iterate over all users */
6744 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6746 /* Prepare structure */
6747 item
= calloc(1, sizeof(*item
));
6752 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6753 if (i
== 0) *users
= item
;
6754 else prev_item
->next
= item
;
6758 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6759 err
= extract_DbUserInfo(context
,
6760 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6761 if (err
) goto leave
;
6767 isds_list_free(users
);
6770 xmlXPathFreeObject(result
);
6771 xmlXPathFreeContext(xpath_ctx
);
6772 xmlFreeDoc(response
);
6775 isds_log(ILF_ISDS
, ILL_DEBUG
,
6776 _("GetDataBoxUsers request processed by server "
6777 "successfully.\n"));
6778 #else /* not HAVE_LIBCURL */
6786 /* Update data about user assigned to given box.
6787 * @context is session context
6788 * @box is box identification. aifoIsds, address->adCode,
6789 * address->adDistrict members are ignored.
6790 * @old_user identifies user to update, aifo_ticket member is ignored
6791 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6792 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6793 * NULL, if you don't care.*/
6794 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6795 const struct isds_DbOwnerInfo
*box
,
6796 const struct isds_DbUserInfo
*old_user
,
6797 const struct isds_DbUserInfo
*new_user
,
6799 isds_error err
= IE_SUCCESS
;
6801 xmlNsPtr isds_ns
= NULL
;
6802 xmlNodePtr request
= NULL
;
6807 if (!context
) return IE_INVALID_CONTEXT
;
6808 zfree(context
->long_message
);
6809 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6813 /* Build UpdateDataBoxUser request */
6814 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6816 isds_log_message(context
,
6817 _("Could build UpdateDataBoxUser request"));
6820 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6822 isds_log_message(context
, _("Could not create ISDS name space"));
6823 xmlFreeNode(request
);
6826 xmlSetNs(request
, isds_ns
);
6828 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6829 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6830 if (err
) goto leave
;
6832 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6833 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
6834 if (err
) goto leave
;
6836 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6837 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
6838 if (err
) goto leave
;
6840 /* Send it to server and process response */
6841 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6842 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6845 xmlFreeNode(request
);
6846 #else /* not HAVE_LIBCURL */
6854 /* Undocumented function.
6855 * @context is session context
6856 * @box_id is UTF-8 encoded box identifier
6857 * @token is UTF-8 encoded temporary password
6858 * @user_id outputs UTF-8 encoded reallocated user identifier
6859 * @password outpus UTF-8 encoded reallocated user password
6860 * Output arguments will be nulled in case of error */
6861 isds_error
isds_activate(struct isds_ctx
*context
,
6862 const char *box_id
, const char *token
,
6863 char **user_id
, char **password
) {
6864 isds_error err
= IE_SUCCESS
;
6866 xmlNsPtr isds_ns
= NULL
;
6867 xmlNodePtr request
= NULL
, node
;
6868 xmlDocPtr response
= NULL
;
6869 xmlXPathContextPtr xpath_ctx
= NULL
;
6870 xmlXPathObjectPtr result
= NULL
;
6874 if (!context
) return IE_INVALID_CONTEXT
;
6875 zfree(context
->long_message
);
6877 if (user_id
) zfree(*user_id
);
6878 if (password
) zfree(*password
);
6880 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6884 /* Build Activate request */
6885 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6887 isds_log_message(context
, _("Could build Activate request"));
6890 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6892 isds_log_message(context
, _("Could not create ISDS name space"));
6893 xmlFreeNode(request
);
6896 xmlSetNs(request
, isds_ns
);
6898 INSERT_STRING(request
, "dbAccessDataId", token
);
6899 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6900 INSERT_STRING(request
, "dbID", box_id
);
6903 /* Send request and check response*/
6904 err
= send_destroy_request_check_response(context
,
6905 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6906 &response
, NULL
, NULL
);
6907 if (err
) goto leave
;
6911 xpath_ctx
= xmlXPathNewContext(response
);
6916 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6920 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6926 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6927 isds_log_message(context
, _("Missing ActivateResponse element"));
6931 if (result
->nodesetval
->nodeNr
> 1) {
6932 isds_log_message(context
, _("Multiple ActivateResponse element"));
6936 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6937 xmlXPathFreeObject(result
); result
= NULL
;
6939 EXTRACT_STRING("isds:userId", *user_id
);
6941 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6942 "but did not return `userId' element.\n"));
6944 EXTRACT_STRING("isds:password", *password
);
6946 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6947 "but did not return `password' element.\n"));
6950 xmlXPathFreeObject(result
);
6951 xmlXPathFreeContext(xpath_ctx
);
6952 xmlFreeDoc(response
);
6953 xmlFreeNode(request
);
6956 isds_log(ILF_ISDS
, ILL_DEBUG
,
6957 _("Activate request processed by server successfully.\n"));
6958 #else /* not HAVE_LIBCURL */
6966 /* Reset credentials of user assigned to given box.
6967 * @context is session context
6968 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6969 * members are ignored.
6970 * @user identifies user to reset password, aifo_ticket member is ignored
6971 * @fee_paid is true if fee has been paid, false otherwise
6972 * @approval is optional external approval of box manipulation
6973 * @credentials_delivery is NULL if new password should be delivered off-line
6974 * to the user. It is valid pointer if user should obtain new password on-line
6975 * on dedicated web server. Then input @credentials_delivery.email value is
6976 * user's e-mail address user must provide to dedicated web server together
6977 * with @credentials_delivery.token. The output reallocated token user needs
6978 * to use to authorize on the web server to view his new password. Output
6979 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6980 * ISDS changed up on this call. (No reason why server could change the name
6982 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6983 * NULL, if you don't care.*/
6984 isds_error
isds_reset_password(struct isds_ctx
*context
,
6985 const struct isds_DbOwnerInfo
*box
,
6986 const struct isds_DbUserInfo
*user
,
6987 const _Bool fee_paid
, const struct isds_approval
*approval
,
6988 struct isds_credentials_delivery
*credentials_delivery
,
6990 isds_error err
= IE_SUCCESS
;
6992 xmlNsPtr isds_ns
= NULL
;
6993 xmlNodePtr request
= NULL
, node
;
6994 xmlDocPtr response
= NULL
;
6998 if (!context
) return IE_INVALID_CONTEXT
;
6999 zfree(context
->long_message
);
7001 if (credentials_delivery
) {
7002 zfree(credentials_delivery
->token
);
7003 zfree(credentials_delivery
->new_user_name
);
7005 if (!box
|| !user
) return IE_INVAL
;
7009 /* Build NewAccessData request */
7010 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
7012 isds_log_message(context
,
7013 _("Could build NewAccessData request"));
7016 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7018 isds_log_message(context
, _("Could not create ISDS name space"));
7019 xmlFreeNode(request
);
7022 xmlSetNs(request
, isds_ns
);
7024 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7025 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7026 if (err
) goto leave
;
7028 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7029 err
= insert_DbUserInfo(context
, user
, 0, node
);
7030 if (err
) goto leave
;
7032 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
7034 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7035 if (err
) goto leave
;
7037 err
= insert_GExtApproval(context
, approval
, request
);
7038 if (err
) goto leave
;
7040 /* Send request and check response*/
7041 err
= send_destroy_request_check_response(context
,
7042 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
7043 &response
, (xmlChar
**) refnumber
, NULL
);
7044 if (err
) goto leave
;
7047 /* Extract optional token */
7048 err
= extract_credentials_delivery(context
, credentials_delivery
,
7049 response
, "NewAccessData");
7052 xmlFreeDoc(response
);
7053 xmlFreeNode(request
);
7056 isds_log(ILF_ISDS
, ILL_DEBUG
,
7057 _("NewAccessData request processed by server "
7058 "successfully.\n"));
7059 #else /* not HAVE_LIBCURL */
7067 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7068 * code, destroy response and log success.
7069 * @context is ISDS session context.
7070 * @service_name is name of SERVICE_DB_MANIPULATION service
7071 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7072 * members are ignored.
7073 * @user identifies user to remove
7074 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7075 * @credentials_delivery is NULL if new user's password should be delivered
7076 * off-line to the user. It is valid pointer if user should obtain new
7077 * password on-line on dedicated web server. Then input
7078 * @credentials_delivery.email value is user's e-mail address user must
7079 * provide to dedicated web server together with @credentials_delivery.token.
7080 * The output reallocated token user needs to use to authorize on the web
7081 * server to view his new password. Output reallocated
7082 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7083 * assingned or changed up on this call.
7084 * @approval is optional external approval of box manipulation
7085 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7086 * NULL, if you don't care. */
7087 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
7088 struct isds_ctx
*context
, const xmlChar
*service_name
,
7089 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7090 _Bool honor_aifo_ticket
,
7091 struct isds_credentials_delivery
*credentials_delivery
,
7092 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7093 isds_error err
= IE_SUCCESS
;
7095 xmlNsPtr isds_ns
= NULL
;
7096 xmlNodePtr request
= NULL
, node
;
7097 xmlDocPtr response
= NULL
;
7101 if (!context
) return IE_INVALID_CONTEXT
;
7102 zfree(context
->long_message
);
7103 if (credentials_delivery
) {
7104 zfree(credentials_delivery
->token
);
7105 zfree(credentials_delivery
->new_user_name
);
7107 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7112 /* Build NewAccessData or similar request */
7113 request
= xmlNewNode(NULL
, service_name
);
7115 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7116 isds_printf_message(context
, _("Could not build %s request"),
7117 service_name_locale
);
7118 free(service_name_locale
);
7121 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7123 isds_log_message(context
, _("Could not create ISDS name space"));
7124 xmlFreeNode(request
);
7127 xmlSetNs(request
, isds_ns
);
7129 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7130 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7131 if (err
) goto leave
;
7133 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7134 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7135 if (err
) goto leave
;
7137 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7138 if (err
) goto leave
;
7140 err
= insert_GExtApproval(context
, approval
, request
);
7141 if (err
) goto leave
;
7144 /* Send request and check response*/
7145 err
= send_destroy_request_check_response(context
,
7146 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7149 xmlFreeNode(request
);
7152 /* Pick up credentials_delivery if requested */
7153 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7154 (char *)service_name
);
7157 xmlFreeDoc(response
);
7158 if (request
) xmlFreeNode(request
);
7161 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7162 isds_log(ILF_ISDS
, ILL_DEBUG
,
7163 _("%s request processed by server successfully.\n"),
7164 service_name_locale
);
7165 free(service_name_locale
);
7167 #else /* not HAVE_LIBCURL */
7175 /* Assign new user to given box.
7176 * @context is session context
7177 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7178 * members are ignored.
7179 * @user defines new user to add
7180 * @credentials_delivery is NULL if new user's password should be delivered
7181 * off-line to the user. It is valid pointer if user should obtain new
7182 * password on-line on dedicated web server. Then input
7183 * @credentials_delivery.email value is user's e-mail address user must
7184 * provide to dedicated web server together with @credentials_delivery.token.
7185 * The output reallocated token user needs to use to authorize on the web
7186 * server to view his new password. Output reallocated
7187 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7188 * assingned up on this call.
7189 * @approval is optional external approval of box manipulation
7190 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7191 * NULL, if you don't care.*/
7192 isds_error
isds_add_user(struct isds_ctx
*context
,
7193 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7194 struct isds_credentials_delivery
*credentials_delivery
,
7195 const struct isds_approval
*approval
, char **refnumber
) {
7196 return build_send_manipulationboxuser_request_check_drop_response(context
,
7197 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7198 approval
, (xmlChar
**) refnumber
);
7202 /* Remove user assigned to given box.
7203 * @context is session context
7204 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7205 * members are ignored.
7206 * @user identifies user to remove, aifo_ticket member is ignored
7207 * @approval is optional external approval of box manipulation
7208 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7209 * NULL, if you don't care.*/
7210 isds_error
isds_delete_user(struct isds_ctx
*context
,
7211 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7212 const struct isds_approval
*approval
, char **refnumber
) {
7213 return build_send_manipulationboxuser_request_check_drop_response(context
,
7214 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7215 (xmlChar
**) refnumber
);
7219 /* Get list of boxes in ZIP archive.
7220 * @context is session context
7221 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7222 * System recognizes following values currently: ALL (all boxes), UPG
7223 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7224 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7225 * commercial messages). This argument is a string because specification
7226 * states new values can appear in the future. Not all list types are
7227 * available to all users.
7228 * @buffer is automatically reallocated memory to store the list of boxes. The
7229 * list is zipped CSV file.
7230 * @buffer_length is size of @buffer data in bytes.
7231 * In case of error @buffer will be freed and @buffer_length will be
7233 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7234 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7235 isds_error err
= IE_SUCCESS
;
7237 xmlNsPtr isds_ns
= NULL
;
7238 xmlNodePtr request
= NULL
, node
;
7239 xmlDocPtr response
= NULL
;
7240 xmlXPathContextPtr xpath_ctx
= NULL
;
7241 xmlXPathObjectPtr result
= NULL
;
7242 char *string
= NULL
;
7246 if (!context
) return IE_INVALID_CONTEXT
;
7247 zfree(context
->long_message
);
7248 if (buffer
) zfree(*buffer
);
7249 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7253 /* Check if connection is established
7254 * TODO: This check should be done downstairs. */
7255 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7258 /* Build AuthenticateMessage request */
7259 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7261 isds_log_message(context
,
7262 _("Could not build GetDataBoxList request"));
7265 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7267 isds_log_message(context
, _("Could not create ISDS name space"));
7268 xmlFreeNode(request
);
7271 xmlSetNs(request
, isds_ns
);
7272 INSERT_STRING(request
, "dblType", list_identifier
);
7274 /* Send request to server and process response */
7275 err
= send_destroy_request_check_response(context
,
7276 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7277 &response
, NULL
, NULL
);
7278 if (err
) goto leave
;
7281 /* Extract Base-64 encoded ZIP file */
7282 xpath_ctx
= xmlXPathNewContext(response
);
7287 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7291 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7293 /* Decode non-empty archive */
7294 if (string
&& string
[0] != '\0') {
7295 *buffer_length
= _isds_b64decode(string
, buffer
);
7296 if (*buffer_length
== (size_t) -1) {
7297 isds_printf_message(context
,
7298 _("Error while Base64-decoding box list archive"));
7307 xmlXPathFreeObject(result
);
7308 xmlXPathFreeContext(xpath_ctx
);
7309 xmlFreeDoc(response
);
7310 xmlFreeNode(request
);
7313 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7314 "processed by server successfully.\n"));
7316 #else /* not HAVE_LIBCURL */
7324 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7325 * send it, check for error code, extract list of results, destroy response
7327 * @context is ISDS session context.
7328 * @service_name is name of SERVICE_DB_SEARCH service
7329 * @pfo_service is false if tDbOwnerInfo request should be built from
7330 * @criteria and corresponding result extracted. It is true if
7331 * tDbPersonalOwnerInfoRequest request should be built. The request and
7332 * response differ subset of significant isds_DbOwnerInfo structure members.
7333 * @criteria is filter. You should fill in at least some members.
7334 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7335 * members will be ignored. If @pfo_service is true, dbType, ic,
7336 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7337 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7339 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7340 * possibly empty. Input NULL or valid old structure. The same memebers as
7341 * in described for @criteria argument will be NULL according to @pfo_service
7344 * IE_SUCCESS if search succeeded, @boxes contains useful data
7345 * IE_NOEXIST if no such box exists, @boxes will be NULL
7346 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7347 * contains still valid data
7348 * other code if something bad happens. @boxes will be NULL. */
7349 static isds_error
build_send_findbox_request_check_parse_drop_response(
7350 struct isds_ctx
*context
, const xmlChar
*service_name
,
7351 _Bool pfo_service
, const struct isds_DbOwnerInfo
*criteria
,
7352 struct isds_list
**boxes
) {
7353 isds_error err
= IE_SUCCESS
;
7355 char *service_name_locale
= NULL
;
7356 _Bool truncated
= 0;
7357 xmlNsPtr isds_ns
= NULL
;
7358 xmlNodePtr request
= NULL
;
7359 xmlDocPtr response
= NULL
;
7360 xmlChar
*code
= NULL
, *message
= NULL
;
7361 xmlNodePtr db_owner_info
;
7362 xmlXPathContextPtr xpath_ctx
= NULL
;
7363 xmlXPathObjectPtr result
= NULL
;
7364 xmlChar
*string
= NULL
;
7368 if (!context
) return IE_INVALID_CONTEXT
;
7369 zfree(context
->long_message
);
7370 if (!boxes
) return IE_INVAL
;
7371 isds_list_free(boxes
);
7378 /* Check if connection is established
7379 * TODO: This check should be done downstairs. */
7380 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7381 service_name_locale
= _isds_utf82locale((char *) service_name
);
7384 request
= xmlNewNode(NULL
, service_name
);
7386 isds_printf_message(context
, _("Could not build %s request"),
7387 service_name_locale
);
7388 free(service_name_locale
);
7391 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7393 isds_log_message(context
, _("Could not create ISDS name space"));
7394 free(service_name_locale
);
7395 xmlFreeNode(request
);
7398 xmlSetNs(request
, isds_ns
);
7399 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7400 if (!db_owner_info
) {
7401 isds_printf_message(context
,
7402 _("Could not add dbOwnerInfo child to %s element"),
7403 service_name_locale
);
7404 free(service_name_locale
);
7405 xmlFreeNode(request
);
7409 err
= insert_DbOwnerInfo(context
, criteria
, pfo_service
, db_owner_info
);
7410 if (err
) goto leave
;
7414 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
7415 service_name_locale
);
7416 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7418 /* Destroy request */
7419 xmlFreeNode(request
); request
= NULL
;
7422 isds_log(ILF_ISDS
, ILL_DEBUG
,
7423 _("Processing ISDS response on %s request failed\n"),
7424 service_name_locale
);
7428 /* Check for response status */
7429 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7430 &code
, &message
, NULL
);
7432 isds_log(ILF_ISDS
, ILL_DEBUG
,
7433 _("ISDS response on %s request is missing status\n"),
7434 service_name_locale
);
7438 /* Request processed, but nothing found */
7439 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7440 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7441 char *code_locale
= _isds_utf82locale((char*)code
);
7442 char *message_locale
= _isds_utf82locale((char*)message
);
7443 isds_log(ILF_ISDS
, ILL_DEBUG
,
7444 _("Server did not find any box on %s request "
7445 "(code=%s, message=%s)\n"), service_name_locale
,
7446 code_locale
, message_locale
);
7447 isds_log_message(context
, message_locale
);
7449 free(message_locale
);
7454 /* Warning, not an error */
7455 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7456 char *code_locale
= _isds_utf82locale((char*)code
);
7457 char *message_locale
= _isds_utf82locale((char*)message
);
7458 isds_log(ILF_ISDS
, ILL_DEBUG
,
7459 _("Server truncated response on %s request "
7460 "(code=%s, message=%s)\n"), service_name_locale
,
7461 code_locale
, message_locale
);
7462 isds_log_message(context
, message_locale
);
7464 free(message_locale
);
7469 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7470 char *code_locale
= _isds_utf82locale((char*)code
);
7471 char *message_locale
= _isds_utf82locale((char*)message
);
7472 isds_log(ILF_ISDS
, ILL_DEBUG
,
7473 _("Server refused %s request (code=%s, message=%s)\n"),
7474 service_name_locale
, code_locale
, message_locale
);
7475 isds_log_message(context
, message_locale
);
7477 free(message_locale
);
7482 xpath_ctx
= xmlXPathNewContext(response
);
7487 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7492 /* Extract boxes if they present */
7493 if (-1 == isds_asprintf((char **)&string
,
7494 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7499 result
= xmlXPathEvalExpression(string
, xpath_ctx
);
7505 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7506 struct isds_list
*item
, *prev_item
= NULL
;
7507 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7508 item
= calloc(1, sizeof(*item
));
7514 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7515 if (i
== 0) *boxes
= item
;
7516 else prev_item
->next
= item
;
7519 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7520 err
= extract_DbOwnerInfo(context
,
7521 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7522 if (err
) goto leave
;
7528 isds_list_free(boxes
);
7530 if (truncated
) err
= IE_2BIG
;
7534 xmlFreeNode(request
);
7535 xmlXPathFreeObject(result
);
7536 xmlXPathFreeContext(xpath_ctx
);
7540 xmlFreeDoc(response
);
7543 isds_log(ILF_ISDS
, ILL_DEBUG
,
7544 _("%s request processed by server successfully.\n"),
7545 service_name_locale
);
7546 free(service_name_locale
);
7547 #else /* not HAVE_LIBCURL */
7555 /* Find boxes suiting given criteria.
7556 * @criteria is filter. You should fill in at least some members. aifoIsds,
7557 * address->adCode, address->adDistrict members are ignored.
7558 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7559 * possibly empty. Input NULL or valid old structure.
7561 * IE_SUCCESS if search succeeded, @boxes contains useful data
7562 * IE_NOEXIST if no such box exists, @boxes will be NULL
7563 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7564 * contains still valid data
7565 * other code if something bad happens. @boxes will be NULL. */
7566 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7567 const struct isds_DbOwnerInfo
*criteria
,
7568 struct isds_list
**boxes
) {
7569 return build_send_findbox_request_check_parse_drop_response(context
,
7570 BAD_CAST
"FindDataBox", 0, criteria
, boxes
);
7574 /* Find accessible FO-type boxes suiting given criteria.
7575 * @criteria is filter. You should fill in at least some members. dbType, ic,
7576 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7577 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7578 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7579 * possibly empty. Input NULL or valid old structure.
7581 * IE_SUCCESS if search succeeded, @boxes contains useful data
7582 * IE_NOEXIST if no such box exists, @boxes will be NULL
7583 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7584 * contains still valid data
7585 * other code if something bad happens. @boxes will be NULL. */
7586 isds_error
isds_FindPersonalDataBox(struct isds_ctx
*context
,
7587 const struct isds_DbOwnerInfo
*criteria
,
7588 struct isds_list
**boxes
) {
7589 return build_send_findbox_request_check_parse_drop_response(context
,
7590 BAD_CAST
"FindPersonalDataBox", 1, criteria
, boxes
);
7595 /* Convert a string with match markers into a plain string with list of
7596 * pointers to the matches
7597 * @string is an UTF-8 encoded non-constant string with match markers
7598 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7599 * The markers will be removed from the string.
7600 * @starts is a reallocated list of static pointers into the @string pointing
7601 * to places where match start markers occured.
7602 * @ends is a reallocated list of static pointers into the @string pointing
7603 * to places where match end markers occured.
7604 * @return IE_SUCCESS in case of no failure. */
7605 static isds_error
interpret_matches(xmlChar
*string
,
7606 struct isds_list
**starts
, struct isds_list
**ends
) {
7607 isds_error err
= IE_SUCCESS
;
7608 xmlChar
*pointer
, *destination
, *source
;
7609 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7611 isds_list_free(starts
);
7612 isds_list_free(ends
);
7613 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7614 if (NULL
== string
) return IE_SUCCESS
;
7616 for (pointer
= string
; *pointer
!= '\0';) {
7617 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7618 /* Remove the start marker */
7619 for (source
= pointer
+ 14, destination
= pointer
;
7620 *source
!= '\0'; source
++, destination
++) {
7621 *destination
= *source
;
7623 *destination
= '\0';
7624 /* Append the pointer into the list */
7625 item
= calloc(1, sizeof(*item
));
7630 item
->destructor
= (void (*)(void **))NULL
;
7631 item
->data
= pointer
;
7632 if (NULL
== prev_start
) *starts
= item
;
7633 else prev_start
->next
= item
;
7635 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7636 /* Remove the end marker */
7637 for (source
= pointer
+ 12, destination
= pointer
;
7638 *source
!= '\0'; source
++, destination
++) {
7639 *destination
= *source
;
7641 *destination
= '\0';
7642 /* Append the pointer into the list */
7643 item
= calloc(1, sizeof(*item
));
7648 item
->destructor
= (void (*)(void **))NULL
;
7649 item
->data
= pointer
;
7650 if (NULL
== prev_end
) *ends
= item
;
7651 else prev_end
->next
= item
;
7660 isds_list_free(starts
);
7661 isds_list_free(ends
);
7667 /* Convert isds:dbResult XML tree into structure
7668 * @context is ISDS context.
7669 * @fulltext_result is automatically reallocated found box structure.
7670 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7671 * @collect_matches is true to interpret match markers.
7672 * In case of error @result will be freed. */
7673 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7674 struct isds_fulltext_result
**fulltext_result
,
7675 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7676 isds_error err
= IE_SUCCESS
;
7677 xmlXPathObjectPtr result
= NULL
;
7678 char *string
= NULL
;
7680 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7681 if (NULL
== fulltext_result
) return IE_INVAL
;
7682 isds_fulltext_result_free(fulltext_result
);
7683 if (!xpath_ctx
) return IE_INVAL
;
7686 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7687 if (NULL
== *fulltext_result
) {
7693 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7695 EXTRACT_STRING("isds:dbType", string
);
7696 if (NULL
== string
) {
7698 isds_log_message(context
, _("Empty isds:dbType element"));
7701 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7703 if (err
== IE_ENUM
) {
7705 char *string_locale
= _isds_utf82locale(string
);
7706 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7708 free(string_locale
);
7714 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7715 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7717 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7718 if (err
) goto leave
;
7720 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7721 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7722 (*fulltext_result
)->dbEffectiveOVM
);
7724 EXTRACT_STRING("isds:dbSendOptions", string
);
7725 if (NULL
== string
) {
7727 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7730 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7731 (*fulltext_result
)->active
= 1;
7732 (*fulltext_result
)->public_sending
= 1;
7733 (*fulltext_result
)->commercial_sending
= 0;
7734 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7735 (*fulltext_result
)->active
= 1;
7736 (*fulltext_result
)->public_sending
= 1;
7737 (*fulltext_result
)->commercial_sending
= 1;
7738 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7739 (*fulltext_result
)->active
= 1;
7740 (*fulltext_result
)->public_sending
= 0;
7741 (*fulltext_result
)->commercial_sending
= 1;
7742 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7743 (*fulltext_result
)->active
= 1;
7744 (*fulltext_result
)->public_sending
= 0;
7745 (*fulltext_result
)->commercial_sending
= 0;
7746 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7747 (*fulltext_result
)->active
= 0;
7748 (*fulltext_result
)->public_sending
= 0;
7749 (*fulltext_result
)->commercial_sending
= 0;
7752 char *string_locale
= _isds_utf82locale(string
);
7753 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7755 free(string_locale
);
7760 /* Interpret match marks */
7761 if (collect_matches
) {
7762 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7763 &((*fulltext_result
)->name_match_start
),
7764 &((*fulltext_result
)->name_match_end
));
7765 if (err
) goto leave
;
7766 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7767 &((*fulltext_result
)->address_match_start
),
7768 &((*fulltext_result
)->address_match_end
));
7769 if (err
) goto leave
;
7773 if (err
) isds_fulltext_result_free(fulltext_result
);
7775 xmlXPathFreeObject(result
);
7778 #endif /* HAVE_LIBCURL */
7781 /* Find boxes matching a given full-text criteria.
7782 * @context is a session context
7783 * @query is a non-empty string which consists of words to search
7784 * @target selects box attributes to search for @query words. Pass NULL if you
7786 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7787 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7788 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7789 * which is DBTYPE_SYSTEM.
7790 * @page_size defines count of boxes to constitute a response page. It counts
7791 * from zero. Pass NULL to let server to use a default value (50 now).
7792 * @page_number defines ordinar number of the response page to return. It
7793 * counts from zero. Pass NULL to let server to use a default value (0 now).
7794 * @track_matches points to true for marking @query words found in the box
7795 * attributes. It points to false for not marking. Pass NULL to let the server
7796 * to use default value (false now).
7797 * @total_matching_boxes outputs reallocated number of all boxes matching the
7798 * query. Will be pointer to NULL if server did not provide the value.
7799 * Pass NULL if you don't care.
7800 * @current_page_beginning outputs reallocated ordinar number of the first box
7801 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7802 * server did not provide the value. Pass NULL if you don't care.
7803 * @current_page_size outputs reallocated count of boxes in the this @boxes
7804 * page. It will be pointer to NULL if the server did not provide the value.
7805 * Pass NULL if you don't care.
7806 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7807 * is the last one, false if more boxes match, NULL if the server did not
7808 * provude the value. Pass NULL if you don't care.
7809 * @boxes outputs reallocated list of isds_fulltext_result structures,
7812 * IE_SUCCESS if search succeeded
7813 * IE_2BIG if @page_size is too large
7814 * other code if something bad happens; output arguments will be NULL. */
7815 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7817 const isds_fulltext_target
*target
,
7818 const isds_DbType
*box_type
,
7819 const unsigned long int *page_size
,
7820 const unsigned long int *page_number
,
7821 const _Bool
*track_matches
,
7822 unsigned long int **total_matching_boxes
,
7823 unsigned long int **current_page_beginning
,
7824 unsigned long int **current_page_size
,
7826 struct isds_list
**boxes
) {
7827 isds_error err
= IE_SUCCESS
;
7829 xmlNsPtr isds_ns
= NULL
;
7830 xmlNodePtr request
= NULL
;
7831 xmlDocPtr response
= NULL
;
7833 xmlXPathContextPtr xpath_ctx
= NULL
;
7834 xmlXPathObjectPtr result
= NULL
;
7835 const xmlChar
*static_string
= NULL
;
7836 xmlChar
*string
= NULL
;
7838 const xmlChar
*codes
[] = {
7848 const char *meanings
[] = {
7849 N_("You are not allowed to perform the search"),
7850 N_("The query string is empty"),
7851 N_("Searched box ID is malformed"),
7852 N_("Searched organization ID is malformed"),
7853 N_("Invalid input"),
7854 N_("Requested page size is too large"),
7855 N_("Search engine internal error")
7857 const isds_error errors
[] = {
7866 struct code_map_isds_error map
= {
7868 .meanings
= meanings
,
7874 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7875 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7876 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7877 if (NULL
!= last_page
) zfree(*last_page
);
7878 isds_list_free(boxes
);
7880 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7881 zfree(context
->long_message
);
7883 if (NULL
== boxes
) return IE_INVAL
;
7885 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7886 isds_log_message(context
, _("Query string must be non-empty"));
7891 /* Check if connection is established
7892 * TODO: This check should be done downstairs. */
7893 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7895 /* Build FindDataBox request */
7896 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7897 if (NULL
== request
) {
7898 isds_log_message(context
,
7899 _("Could not build ISDSSearch2 request"));
7902 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7903 if(NULL
== isds_ns
) {
7904 isds_log_message(context
, _("Could not create ISDS name space"));
7905 xmlFreeNode(request
);
7908 xmlSetNs(request
, isds_ns
);
7910 INSERT_STRING(request
, "searchText", query
);
7912 if (NULL
!= target
) {
7913 static_string
= isds_fulltext_target2string(*(target
));
7914 if (NULL
== static_string
) {
7915 isds_printf_message(context
, _("Invalid target value: %d"),
7921 INSERT_STRING(request
, "searchType", static_string
);
7922 static_string
= NULL
;
7924 if (NULL
!= box_type
) {
7925 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7926 if (DBTYPE_SYSTEM
== *box_type
) {
7927 static_string
= BAD_CAST
"ALL";
7928 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
7929 static_string
= BAD_CAST
"OVM_MAIN";
7931 static_string
= isds_DbType2string(*(box_type
));
7932 if (NULL
== static_string
) {
7933 isds_printf_message(context
, _("Invalid box type value: %d"),
7940 INSERT_STRING(request
, "searchScope", static_string
);
7941 static_string
= NULL
;
7943 INSERT_ULONGINT(request
, "page", page_number
, string
);
7944 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7945 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7947 /* Send request and check response */
7948 err
= send_destroy_request_check_response(context
,
7949 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7950 &request
, &response
, NULL
, &map
);
7951 if (err
) goto leave
;
7953 /* Parse response */
7954 xpath_ctx
= xmlXPathNewContext(response
);
7955 if (NULL
== xpath_ctx
) {
7959 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7963 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7969 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7970 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7974 if (result
->nodesetval
->nodeNr
> 1) {
7975 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7979 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7980 xmlXPathFreeObject(result
); result
= NULL
;
7983 /* Extract counters */
7984 if (NULL
!= total_matching_boxes
) {
7985 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7987 if (NULL
!= current_page_size
) {
7988 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7990 if (NULL
!= current_page_beginning
) {
7991 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7993 if (NULL
!= last_page
) {
7994 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7996 xmlXPathFreeObject(result
); result
= NULL
;
7998 /* Extract boxes if they present */
7999 result
= xmlXPathEvalExpression(BAD_CAST
8000 "isds:dbResults/isds:dbResult", xpath_ctx
);
8001 if (NULL
== result
) {
8005 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8006 struct isds_list
*item
, *prev_item
= NULL
;
8007 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8008 item
= calloc(1, sizeof(*item
));
8014 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
8015 if (i
== 0) *boxes
= item
;
8016 else prev_item
->next
= item
;
8019 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8020 err
= extract_dbResult(context
,
8021 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
8022 (NULL
== track_matches
) ? 0 : *track_matches
);
8023 if (err
) goto leave
;
8029 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
8030 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
8031 if (NULL
!= current_page_size
) zfree(*current_page_size
);
8032 if (NULL
!= last_page
) zfree(*last_page
);
8033 isds_list_free(boxes
);
8037 xmlFreeNode(request
);
8038 xmlXPathFreeObject(result
);
8039 xmlXPathFreeContext(xpath_ctx
);
8040 xmlFreeDoc(response
);
8043 isds_log(ILF_ISDS
, ILL_DEBUG
,
8044 _("ISDSSearch2 request processed by server successfully.\n"));
8045 #else /* not HAVE_LIBCURL */
8053 /* Get status of a box.
8054 * @context is ISDS session context.
8055 * @box_id is UTF-8 encoded box identifier as zero terminated string
8056 * @box_status is return value of box status.
8058 * IE_SUCCESS if box has been found and its status retrieved
8059 * IE_NOEXIST if box is not known to ISDS server
8060 * or other appropriate error.
8061 * You can use isds_DbState to enumerate box status. However out of enum
8062 * range value can be returned too. This is feature because ISDS
8063 * specification leaves the set of values open.
8064 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8065 * the box has been deleted, but ISDS still lists its former existence. */
8066 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
8067 long int *box_status
) {
8068 isds_error err
= IE_SUCCESS
;
8070 xmlNsPtr isds_ns
= NULL
;
8071 xmlNodePtr request
= NULL
, db_id
;
8072 xmlDocPtr response
= NULL
;
8073 xmlXPathContextPtr xpath_ctx
= NULL
;
8074 xmlXPathObjectPtr result
= NULL
;
8075 xmlChar
*string
= NULL
;
8077 const xmlChar
*codes
[] = {
8083 const char *meanings
[] = {
8084 "The box does not exist",
8085 "Box ID is malformed",
8088 const isds_error errors
[] = {
8093 struct code_map_isds_error map
= {
8095 .meanings
= meanings
,
8100 if (!context
) return IE_INVALID_CONTEXT
;
8101 zfree(context
->long_message
);
8102 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
8105 /* Check if connection is established
8106 * TODO: This check should be done downstairs. */
8107 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8110 /* Build CheckDataBox request */
8111 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
8113 isds_log_message(context
,
8114 _("Could build CheckDataBox request"));
8117 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8119 isds_log_message(context
, _("Could not create ISDS name space"));
8120 xmlFreeNode(request
);
8123 xmlSetNs(request
, isds_ns
);
8124 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
8126 isds_log_message(context
, _("Could not add dbID child to "
8127 "CheckDataBox element"));
8128 xmlFreeNode(request
);
8133 /* Send request and check response*/
8134 err
= send_destroy_request_check_response(context
,
8135 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
8136 &request
, &response
, NULL
, &map
);
8137 if (err
) goto leave
;
8141 xpath_ctx
= xmlXPathNewContext(response
);
8146 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8150 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
8156 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8157 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
8161 if (result
->nodesetval
->nodeNr
> 1) {
8162 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
8166 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8167 xmlXPathFreeObject(result
); result
= NULL
;
8169 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
8174 xmlXPathFreeObject(result
);
8175 xmlXPathFreeContext(xpath_ctx
);
8177 xmlFreeDoc(response
);
8180 isds_log(ILF_ISDS
, ILL_DEBUG
,
8181 _("CheckDataBox request processed by server successfully.\n"));
8182 #else /* not HAVE_LIBCURL */
8191 /* Convert XSD:tdbPeriod XML tree into structure
8192 * @context is ISDS context.
8193 * @period is automatically reallocated found box status period structure.
8194 * @xpath_ctx is XPath context with current node as element of
8195 * XSD:tDbPeriod type.
8196 * In case of error @period will be freed. */
8197 static isds_error
extract_Period(struct isds_ctx
*context
,
8198 struct isds_box_state_period
**period
, xmlXPathContextPtr xpath_ctx
) {
8199 isds_error err
= IE_SUCCESS
;
8200 xmlXPathObjectPtr result
= NULL
;
8201 char *string
= NULL
;
8202 long int *dbState_ptr
;
8204 if (NULL
== context
) return IE_INVALID_CONTEXT
;
8205 if (NULL
== period
) return IE_INVAL
;
8206 isds_box_state_period_free(period
);
8207 if (!xpath_ctx
) return IE_INVAL
;
8210 *period
= calloc(1, sizeof(**period
));
8211 if (NULL
== *period
) {
8217 EXTRACT_STRING("isds:PeriodFrom", string
);
8218 if (NULL
== string
) {
8220 isds_log_message(context
,
8221 _("Could not find PeriodFrom element value"));
8224 err
= timestring2static_timeval((xmlChar
*) string
,
8225 &((*period
)->from
));
8227 char *string_locale
= _isds_utf82locale(string
);
8228 if (err
== IE_DATE
) err
= IE_ISDS
;
8229 isds_printf_message(context
,
8230 _("Could not convert PeriodFrom as ISO time: %s"),
8232 free(string_locale
);
8237 EXTRACT_STRING("isds:PeriodTo", string
);
8238 if (NULL
== string
) {
8240 isds_log_message(context
,
8241 _("Could not find PeriodTo element value"));
8244 err
= timestring2static_timeval((xmlChar
*) string
,
8247 char *string_locale
= _isds_utf82locale(string
);
8248 if (err
== IE_DATE
) err
= IE_ISDS
;
8249 isds_printf_message(context
,
8250 _("Could not convert PeriodTo as ISO time: %s"),
8252 free(string_locale
);
8257 dbState_ptr
= &((*period
)->dbState
);
8258 EXTRACT_LONGINT("isds:DbState", dbState_ptr
, 1);
8261 if (err
) isds_box_state_period_free(period
);
8263 xmlXPathFreeObject(result
);
8266 #endif /* HAVE_LIBCURL */
8269 /* Get history of box state changes.
8270 * @context is ISDS session context.
8271 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8272 * @from_time is first second of history to return in @history. Server ignores
8273 * subseconds. NULL means time of creating the box.
8274 * @to_time is last second of history to return in @history. Server ignores
8275 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8276 * interval is closed from both ends. NULL means now.
8277 * @history outputs auto-reallocated list of pointers to struct
8278 * isds_box_state_period. Each item describes a continues time when the box
8279 * was in one state. The state is 1 for accessible box. Otherwise the box
8280 * is inaccessible (priviledged users will get exact box state as enumerated
8281 * in isds_DbState, other users 0).
8283 * IE_SUCCESS if the history has been obtained correctly,
8284 * or other appropriate error. Please note that server allows to retrieve
8285 * the history only to some users. */
8286 isds_error
isds_get_box_state_history(struct isds_ctx
*context
,
8288 const struct timeval
*from_time
, const struct timeval
*to_time
,
8289 struct isds_list
**history
) {
8290 isds_error err
= IE_SUCCESS
;
8292 char *box_id_locale
= NULL
;
8293 xmlNodePtr request
= NULL
, node
;
8294 xmlNsPtr isds_ns
= NULL
;
8295 xmlChar
*string
= NULL
;
8297 xmlDocPtr response
= NULL
;
8298 xmlXPathContextPtr xpath_ctx
= NULL
;
8299 xmlXPathObjectPtr result
= NULL
;
8302 if (!context
) return IE_INVALID_CONTEXT
;
8303 zfree(context
->long_message
);
8305 /* Free output argument */
8306 isds_list_free(history
);
8309 /* Check if connection is established */
8310 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8312 /* ??? XML schema allows empty box ID, textual documentation
8313 * requries the value. */
8314 /* Allow undefined box_id */
8315 if (NULL
!= box_id
) {
8316 box_id_locale
= _isds_utf82locale((char*)box_id
);
8317 if (NULL
== box_id_locale
) {
8324 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxActivityStatus");
8325 if (NULL
== request
) {
8326 isds_printf_message(context
,
8327 _("Could not build GetDataBoxActivityStatus request "
8333 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8335 isds_log_message(context
, _("Could not create ISDS name space"));
8339 xmlSetNs(request
, isds_ns
);
8341 /* Add mandatory XSD:tIdDbInput child */
8342 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8343 /* Add times elements only when defined */
8344 /* ???: XML schema requires the values, textual documentation does not. */
8346 err
= timeval2timestring(from_time
, &string
);
8348 isds_log_message(context
,
8349 _("Could not convert `from_time' argument to ISO time "
8353 INSERT_STRING(request
, "baFrom", string
);
8357 err
= timeval2timestring(to_time
, &string
);
8359 isds_log_message(context
,
8360 _("Could not convert `to_time' argument to ISO time "
8364 INSERT_STRING(request
, "baTo", string
);
8368 /* Send request and check response*/
8369 err
= send_destroy_request_check_response(context
,
8370 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxActivityStatus",
8371 &request
, &response
, NULL
, NULL
);
8372 if (err
) goto leave
;
8376 /* Set context to the root */
8377 xpath_ctx
= xmlXPathNewContext(response
);
8382 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8386 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:GetDataBoxActivityStatusResponse",
8392 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8393 isds_log_message(context
, _("Missing GetDataBoxActivityStatusResponse element"));
8397 if (result
->nodesetval
->nodeNr
> 1) {
8398 isds_log_message(context
, _("Multiple GetDataBoxActivityStatusResponse element"));
8402 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8403 xmlXPathFreeObject(result
); result
= NULL
;
8405 /* Ignore dbID, it's the same as the input argument. */
8407 /* Extract records */
8408 if (NULL
== history
) goto leave
;
8409 result
= xmlXPathEvalExpression(BAD_CAST
"isds:Periods/isds:Period",
8415 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8416 struct isds_list
*prev_item
= NULL
;
8418 /* Iterate over all records */
8419 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8420 struct isds_list
*item
;
8422 /* Prepare structure */
8423 item
= calloc(1, sizeof(*item
));
8428 item
->destructor
= (void(*)(void**))isds_box_state_period_free
;
8429 if (i
== 0) *history
= item
;
8430 else prev_item
->next
= item
;
8434 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8435 err
= extract_Period(context
,
8436 (struct isds_box_state_period
**) (&item
->data
),
8438 if (err
) goto leave
;
8444 isds_log(ILF_ISDS
, ILL_DEBUG
,
8445 _("GetDataBoxActivityStatus request for %s box "
8446 "processed by server successfully.\n"), box_id_locale
);
8449 isds_list_free(history
);
8452 free(box_id_locale
);
8453 xmlXPathFreeObject(result
);
8454 xmlXPathFreeContext(xpath_ctx
);
8455 xmlFreeDoc(response
);
8457 #else /* not HAVE_LIBCURL */
8465 /* Get list of permissions to send commercial messages.
8466 * @context is ISDS session context.
8467 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8468 * @permissions is a reallocated list of permissions (struct
8469 * isds_commercial_permission*) to send commercial messages from @box_id. The
8470 * order of permissions is significant as the server applies the permissions
8471 * and associated pre-paid credits in the order. Empty list means no
8474 * IE_SUCCESS if the list has been obtained correctly,
8475 * or other appropriate error. */
8476 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8477 const char *box_id
, struct isds_list
**permissions
) {
8478 isds_error err
= IE_SUCCESS
;
8480 xmlDocPtr response
= NULL
;
8481 xmlXPathContextPtr xpath_ctx
= NULL
;
8482 xmlXPathObjectPtr result
= NULL
;
8485 if (!context
) return IE_INVALID_CONTEXT
;
8486 zfree(context
->long_message
);
8487 if (NULL
== permissions
) return IE_INVAL
;
8488 isds_list_free(permissions
);
8489 if (NULL
== box_id
) return IE_INVAL
;
8492 /* Check if connection is established */
8493 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8495 /* Do request and check for success */
8496 err
= build_send_dbid_request_check_response(context
,
8497 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8498 BAD_CAST box_id
, NULL
, &response
, NULL
);
8500 isds_log(ILF_ISDS
, ILL_DEBUG
,
8501 _("PDZInfo request processed by server successfully.\n"));
8505 /* Prepare structure */
8506 xpath_ctx
= xmlXPathNewContext(response
);
8511 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8516 /* Set context node */
8517 result
= xmlXPathEvalExpression(BAD_CAST
8518 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8524 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8525 struct isds_list
*prev_item
= NULL
;
8527 /* Iterate over all permission records */
8528 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8529 struct isds_list
*item
;
8531 /* Prepare structure */
8532 item
= calloc(1, sizeof(*item
));
8537 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8538 if (i
== 0) *permissions
= item
;
8539 else prev_item
->next
= item
;
8543 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8544 err
= extract_DbPDZRecord(context
,
8545 (struct isds_commercial_permission
**) (&item
->data
),
8547 if (err
) goto leave
;
8553 isds_list_free(permissions
);
8556 xmlXPathFreeObject(result
);
8557 xmlXPathFreeContext(xpath_ctx
);
8558 xmlFreeDoc(response
);
8560 #else /* not HAVE_LIBCURL */
8568 /* Get details about credit for sending pre-paid commercial messages.
8569 * @context is ISDS session context.
8570 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8571 * @from_date is first day of credit history to return in @history. Only
8572 * tm_year, tm_mon and tm_mday carry sane value.
8573 * @to_date is last day of credit history to return in @history. Only
8574 * tm_year, tm_mon and tm_mday carry sane value.
8575 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8576 * if you don't care. This and all other credit values are integers in
8577 * hundredths of Czech Crowns.
8578 * @email outputs notification e-mail address where notifications about credit
8579 * are sent. This is automatically reallocated string. Pass NULL if you don't
8580 * care. It can return NULL if no address is defined.
8581 * @history outputs auto-reallocated list of pointers to struct
8582 * isds_credit_event. Events in closed interval @from_time to @to_time are
8583 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8584 * are sorted by time.
8586 * IE_SUCCESS if the credit details have been obtained correctly,
8587 * or other appropriate error. Please note that server allows to retrieve
8588 * only limited history of events. */
8589 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8591 const struct tm
*from_date
, const struct tm
*to_date
,
8592 long int *credit
, char **email
, struct isds_list
**history
) {
8593 isds_error err
= IE_SUCCESS
;
8595 char *box_id_locale
= NULL
;
8596 xmlNodePtr request
= NULL
, node
;
8597 xmlNsPtr isds_ns
= NULL
;
8598 xmlChar
*string
= NULL
;
8600 xmlDocPtr response
= NULL
;
8601 xmlXPathContextPtr xpath_ctx
= NULL
;
8602 xmlXPathObjectPtr result
= NULL
;
8604 const xmlChar
*codes
[] = {
8612 const char *meanings
[] = {
8613 "Insufficient priviledges for the box",
8614 "The box does not exist",
8615 "Date is too long (history is not available after 15 months)",
8616 "Interval is too long (limit is 3 months)",
8619 const isds_error errors
[] = {
8626 struct code_map_isds_error map
= {
8628 .meanings
= meanings
,
8633 if (!context
) return IE_INVALID_CONTEXT
;
8634 zfree(context
->long_message
);
8636 /* Free output argument */
8637 if (NULL
!= credit
) *credit
= 0;
8638 if (NULL
!= email
) zfree(*email
);
8639 isds_list_free(history
);
8641 if (NULL
== box_id
) return IE_INVAL
;
8644 /* Check if connection is established */
8645 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8647 box_id_locale
= _isds_utf82locale((char*)box_id
);
8648 if (NULL
== box_id_locale
) {
8654 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8655 if (NULL
== request
) {
8656 isds_printf_message(context
,
8657 _("Could not build DataBoxCreditInfo request for %s box"),
8662 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8664 isds_log_message(context
, _("Could not create ISDS name space"));
8668 xmlSetNs(request
, isds_ns
);
8670 /* Add mandatory XSD:tIdDbInput child */
8671 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8672 /* Add mandatory dates elements with optional values */
8674 err
= tm2datestring(from_date
, &string
);
8676 isds_log_message(context
,
8677 _("Could not convert `from_date' argument to ISO date "
8681 INSERT_STRING(request
, "ciFromDate", string
);
8684 INSERT_STRING(request
, "ciFromDate", NULL
);
8687 err
= tm2datestring(to_date
, &string
);
8689 isds_log_message(context
,
8690 _("Could not convert `to_date' argument to ISO date "
8694 INSERT_STRING(request
, "ciTodate", string
);
8697 INSERT_STRING(request
, "ciTodate", NULL
);
8700 /* Send request and check response*/
8701 err
= send_destroy_request_check_response(context
,
8702 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8703 &request
, &response
, NULL
, &map
);
8704 if (err
) goto leave
;
8708 /* Set context to the root */
8709 xpath_ctx
= xmlXPathNewContext(response
);
8714 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8718 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8724 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8725 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8729 if (result
->nodesetval
->nodeNr
> 1) {
8730 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8734 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8735 xmlXPathFreeObject(result
); result
= NULL
;
8737 /* Extract common data */
8738 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8739 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8741 /* Extract records */
8742 if (NULL
== history
) goto leave
;
8743 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8749 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8750 struct isds_list
*prev_item
= NULL
;
8752 /* Iterate over all records */
8753 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8754 struct isds_list
*item
;
8756 /* Prepare structure */
8757 item
= calloc(1, sizeof(*item
));
8762 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8763 if (i
== 0) *history
= item
;
8764 else prev_item
->next
= item
;
8768 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8769 err
= extract_CiRecord(context
,
8770 (struct isds_credit_event
**) (&item
->data
),
8772 if (err
) goto leave
;
8778 isds_log(ILF_ISDS
, ILL_DEBUG
,
8779 _("DataBoxCreditInfo request processed by server successfully.\n"));
8782 isds_list_free(history
);
8783 if (NULL
!= email
) zfree(*email
)
8786 free(box_id_locale
);
8787 xmlXPathFreeObject(result
);
8788 xmlXPathFreeContext(xpath_ctx
);
8789 xmlFreeDoc(response
);
8791 #else /* not HAVE_LIBCURL */
8799 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8800 * code, destroy response and log success.
8801 * @context is ISDS session context.
8802 * @service_name is name of SERVICE_DB_MANIPULATION service
8803 * @box_id is UTF-8 encoded box identifier as zero terminated string
8804 * @approval is optional external approval of box manipulation
8805 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8806 * NULL, if you don't care. */
8807 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8808 struct isds_ctx
*context
, const xmlChar
*service_name
,
8809 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8810 xmlChar
**refnumber
) {
8811 isds_error err
= IE_SUCCESS
;
8813 xmlDocPtr response
= NULL
;
8816 if (!context
) return IE_INVALID_CONTEXT
;
8817 zfree(context
->long_message
);
8818 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8821 /* Check if connection is established */
8822 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8824 /* Do request and check for success */
8825 err
= build_send_dbid_request_check_response(context
,
8826 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8827 &response
, refnumber
);
8828 xmlFreeDoc(response
);
8831 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8832 isds_log(ILF_ISDS
, ILL_DEBUG
,
8833 _("%s request processed by server successfully.\n"),
8834 service_name_locale
);
8835 free(service_name_locale
);
8837 #else /* not HAVE_LIBCURL */
8845 /* Switch box into state where box can receive commercial messages (off by
8847 * @context is ISDS session context.
8848 * @box_id is UTF-8 encoded box identifier as zero terminated string
8849 * @allow is true for enable, false for disable commercial messages income
8850 * @approval is optional external approval of box manipulation
8851 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8852 * NULL, if you don't care. */
8853 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8854 const char *box_id
, const _Bool allow
,
8855 const struct isds_approval
*approval
, char **refnumber
) {
8856 return build_send_manipulationdbid_request_check_drop_response(context
,
8857 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8858 BAD_CAST
"ClearOpenAddressing",
8859 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8863 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8864 * message acceptance). This is just a box permission. Sender must apply
8865 * such role by sending each message.
8866 * @context is ISDS session context.
8867 * @box_id is UTF-8 encoded box identifier as zero terminated string
8868 * @allow is true for enable, false for disable OVM role permission
8869 * @approval is optional external approval of box manipulation
8870 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8871 * NULL, if you don't care. */
8872 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8873 const char *box_id
, const _Bool allow
,
8874 const struct isds_approval
*approval
, char **refnumber
) {
8875 return build_send_manipulationdbid_request_check_drop_response(context
,
8876 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8877 BAD_CAST
"ClearEffectiveOVM",
8878 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8882 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8883 * code, destroy response and log success.
8884 * @context is ISDS session context.
8885 * @service_name is name of SERVICE_DB_MANIPULATION service
8886 * @owner is structure describing box. aifoIsds, address->adCode,
8887 * address->adDistrict members are ignored.
8888 * @approval is optional external approval of box manipulation
8889 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8890 * NULL, if you don't care. */
8891 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8892 struct isds_ctx
*context
, const xmlChar
*service_name
,
8893 const struct isds_DbOwnerInfo
*owner
,
8894 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8895 isds_error err
= IE_SUCCESS
;
8897 char *service_name_locale
= NULL
;
8898 xmlNodePtr request
= NULL
, db_owner_info
;
8899 xmlNsPtr isds_ns
= NULL
;
8903 if (!context
) return IE_INVALID_CONTEXT
;
8904 zfree(context
->long_message
);
8905 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8908 service_name_locale
= _isds_utf82locale((char*)service_name
);
8909 if (!service_name_locale
) {
8915 request
= xmlNewNode(NULL
, service_name
);
8917 isds_printf_message(context
,
8918 _("Could not build %s request"), service_name_locale
);
8922 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8924 isds_log_message(context
, _("Could not create ISDS name space"));
8928 xmlSetNs(request
, isds_ns
);
8931 /* Add XSD:tOwnerInfoInput child*/
8932 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8933 err
= insert_DbOwnerInfo(context
, owner
, 0, db_owner_info
);
8934 if (err
) goto leave
;
8936 /* Add XSD:gExtApproval*/
8937 err
= insert_GExtApproval(context
, approval
, request
);
8938 if (err
) goto leave
;
8940 /* Send it to server and process response */
8941 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8942 service_name
, &request
, refnumber
);
8945 xmlFreeNode(request
);
8946 free(service_name_locale
);
8947 #else /* not HAVE_LIBCURL */
8955 /* Switch box accessibility state on request of box owner.
8956 * Despite the name, owner must do the request off-line. This function is
8957 * designed for such off-line meeting points (e.g. Czech POINT).
8958 * @context is ISDS session context.
8959 * @box identifies box to switch accessibility state. aifoIsds,
8960 * address->adCode, address->adDistrict members are ignored.
8961 * @allow is true for making accessible, false to disallow access.
8962 * @approval is optional external approval of box manipulation
8963 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8964 * NULL, if you don't care. */
8965 isds_error
isds_switch_box_accessibility_on_owner_request(
8966 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8967 const _Bool allow
, const struct isds_approval
*approval
,
8969 return build_send_manipulationdbowner_request_check_drop_response(context
,
8970 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8971 BAD_CAST
"DisableOwnDataBox",
8972 box
, approval
, (xmlChar
**) refnumber
);
8976 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8978 * @context is ISDS session context.
8979 * @box identifies box to switch accessibility state. aifoIsds,
8980 * address->adCode, address->adDistrict members are ignored.
8981 * @since is date since accessibility has been denied. This can be past too.
8982 * Only tm_year, tm_mon and tm_mday carry sane value.
8983 * @approval is optional external approval of box manipulation
8984 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8985 * NULL, if you don't care. */
8986 isds_error
isds_disable_box_accessibility_externaly(
8987 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8988 const struct tm
*since
, const struct isds_approval
*approval
,
8990 isds_error err
= IE_SUCCESS
;
8992 char *service_name_locale
= NULL
;
8993 xmlNodePtr request
= NULL
, node
;
8994 xmlNsPtr isds_ns
= NULL
;
8995 xmlChar
*string
= NULL
;
8999 if (!context
) return IE_INVALID_CONTEXT
;
9000 zfree(context
->long_message
);
9001 if (!box
|| !since
) return IE_INVAL
;
9005 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
9007 isds_printf_message(context
,
9008 _("Could not build %s request"), "DisableDataBoxExternally");
9012 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9014 isds_log_message(context
, _("Could not create ISDS name space"));
9018 xmlSetNs(request
, isds_ns
);
9021 /* Add @box identification */
9022 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
9023 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
9024 if (err
) goto leave
;
9026 /* Add @since date */
9027 err
= tm2datestring(since
, &string
);
9029 isds_log_message(context
,
9030 _("Could not convert `since' argument to ISO date string"));
9033 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
9037 err
= insert_GExtApproval(context
, approval
, request
);
9038 if (err
) goto leave
;
9040 /* Send it to server and process response */
9041 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
9042 BAD_CAST
"DisableDataBoxExternally", &request
,
9043 (xmlChar
**) refnumber
);
9047 xmlFreeNode(request
);
9048 free(service_name_locale
);
9049 #else /* not HAVE_LIBCURL */
9058 /* Insert struct isds_message data (envelope (recipient data optional) and
9059 * documents into XML tree
9060 * @context is session context
9061 * @outgoing_message is libisds structure with message data
9062 * @create_message is XML CreateMessage or CreateMultipleMessage element
9063 * @process_recipient true for recipient data serialization, false for no
9065 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
9066 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
9067 const _Bool process_recipient
) {
9069 isds_error err
= IE_SUCCESS
;
9070 xmlNodePtr envelope
, dm_files
, node
;
9071 xmlChar
*string
= NULL
;
9073 if (!context
) return IE_INVALID_CONTEXT
;
9074 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
9077 /* Build envelope */
9078 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
9080 isds_printf_message(context
, _("Could not add dmEnvelope child to "
9081 "%s element"), create_message
->name
);
9085 if (!outgoing_message
->envelope
) {
9086 isds_log_message(context
, _("Outgoing message is missing envelope"));
9091 /* Insert optional message type */
9092 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
9094 if (err
) goto leave
;
9096 INSERT_STRING(envelope
, "dmSenderOrgUnit",
9097 outgoing_message
->envelope
->dmSenderOrgUnit
);
9098 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
9099 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
9101 if (process_recipient
) {
9102 if (!outgoing_message
->envelope
->dbIDRecipient
) {
9103 isds_log_message(context
,
9104 _("Outgoing message is missing recipient box identifier"));
9108 INSERT_STRING(envelope
, "dbIDRecipient",
9109 outgoing_message
->envelope
->dbIDRecipient
);
9111 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
9112 outgoing_message
->envelope
->dmRecipientOrgUnit
);
9113 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
9114 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
9115 INSERT_STRING(envelope
, "dmToHands",
9116 outgoing_message
->envelope
->dmToHands
);
9119 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
9121 INSERT_STRING(envelope
, "dmAnnotation",
9122 outgoing_message
->envelope
->dmAnnotation
);
9124 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
9125 0, 50, "dmRecipientRefNumber");
9126 INSERT_STRING(envelope
, "dmRecipientRefNumber",
9127 outgoing_message
->envelope
->dmRecipientRefNumber
);
9129 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
9130 0, 50, "dmSenderRefNumber");
9131 INSERT_STRING(envelope
, "dmSenderRefNumber",
9132 outgoing_message
->envelope
->dmSenderRefNumber
);
9134 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
9135 0, 50, "dmRecipientIdent");
9136 INSERT_STRING(envelope
, "dmRecipientIdent",
9137 outgoing_message
->envelope
->dmRecipientIdent
);
9139 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
9140 0, 50, "dmSenderIdent");
9141 INSERT_STRING(envelope
, "dmSenderIdent",
9142 outgoing_message
->envelope
->dmSenderIdent
);
9144 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
9145 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
9146 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
9147 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
9148 INSERT_STRING(envelope
, "dmLegalTitleSect",
9149 outgoing_message
->envelope
->dmLegalTitleSect
);
9150 INSERT_STRING(envelope
, "dmLegalTitlePar",
9151 outgoing_message
->envelope
->dmLegalTitlePar
);
9152 INSERT_STRING(envelope
, "dmLegalTitlePoint",
9153 outgoing_message
->envelope
->dmLegalTitlePoint
);
9155 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
9156 outgoing_message
->envelope
->dmPersonalDelivery
);
9157 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
9158 outgoing_message
->envelope
->dmAllowSubstDelivery
);
9160 /* ???: Should we require value for dbEffectiveOVM sender?
9161 * ISDS has default as true */
9162 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
9163 INSERT_BOOLEAN(envelope
, "dmPublishOwnID",
9164 outgoing_message
->envelope
->dmPublishOwnID
);
9167 /* Append dmFiles */
9168 if (!outgoing_message
->documents
) {
9169 isds_log_message(context
,
9170 _("Outgoing message is missing list of documents"));
9174 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
9176 isds_printf_message(context
, _("Could not add dmFiles child to "
9177 "%s element"), create_message
->name
);
9182 /* Check for document hierarchy */
9183 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
9184 if (err
) goto leave
;
9186 /* Process each document */
9187 for (struct isds_list
*item
=
9188 (struct isds_list
*) outgoing_message
->documents
;
9189 item
; item
= item
->next
) {
9191 isds_log_message(context
,
9192 _("List of documents contains empty item"));
9196 /* FIXME: Check for dmFileMetaType and for document references.
9197 * Only first document can be of MAIN type */
9198 err
= insert_document(context
, (struct isds_document
*) item
->data
,
9201 if (err
) goto leave
;
9208 #endif /* HAVE_LIBCURL */
9211 /* Send a message via ISDS to a recipient
9212 * @context is session context
9213 * @outgoing_message is message to send; Some members are mandatory (like
9214 * dbIDRecipient), some are optional and some are irrelevant (especially data
9215 * about sender). Included pointer to isds_list documents must contain at
9216 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9217 * members will be filled with valid data from ISDS. Exact list of write
9218 * members is subject to change. Currently dmID is changed.
9219 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9220 isds_error
isds_send_message(struct isds_ctx
*context
,
9221 struct isds_message
*outgoing_message
) {
9223 isds_error err
= IE_SUCCESS
;
9225 xmlNsPtr isds_ns
= NULL
;
9226 xmlNodePtr request
= NULL
;
9227 xmlDocPtr response
= NULL
;
9228 xmlChar
*code
= NULL
, *message
= NULL
;
9229 xmlXPathContextPtr xpath_ctx
= NULL
;
9230 xmlXPathObjectPtr result
= NULL
;
9231 /*_Bool message_is_complete = 0;*/
9234 if (!context
) return IE_INVALID_CONTEXT
;
9235 zfree(context
->long_message
);
9236 if (!outgoing_message
) return IE_INVAL
;
9239 /* Check if connection is established
9240 * TODO: This check should be done downstairs. */
9241 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9244 /* Build CreateMessage request */
9245 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
9247 isds_log_message(context
,
9248 _("Could not build CreateMessage request"));
9251 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9253 isds_log_message(context
, _("Could not create ISDS name space"));
9254 xmlFreeNode(request
);
9257 xmlSetNs(request
, isds_ns
);
9259 /* Append envelope and files */
9260 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
9261 if (err
) goto leave
;
9264 /* Signal we can serialize message since now */
9265 /*message_is_complete = 1;*/
9268 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
9271 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9273 /* Don't' destroy request, we want to provide it to application later */
9276 isds_log(ILF_ISDS
, ILL_DEBUG
,
9277 _("Processing ISDS response on CreateMessage "
9278 "request failed\n"));
9282 /* Check for response status */
9283 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9284 &code
, &message
, NULL
);
9286 isds_log(ILF_ISDS
, ILL_DEBUG
,
9287 _("ISDS response on CreateMessage request "
9288 "is missing status\n"));
9292 /* Request processed, but refused by server or server failed */
9293 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9294 char *box_id_locale
=
9295 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9296 char *code_locale
= _isds_utf82locale((char*)code
);
9297 char *message_locale
= _isds_utf82locale((char*)message
);
9298 isds_log(ILF_ISDS
, ILL_DEBUG
,
9299 _("Server did not accept message for %s on CreateMessage "
9300 "request (code=%s, message=%s)\n"),
9301 box_id_locale
, code_locale
, message_locale
);
9302 isds_log_message(context
, message_locale
);
9303 free(box_id_locale
);
9305 free(message_locale
);
9312 xpath_ctx
= xmlXPathNewContext(response
);
9317 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9321 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
9327 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9328 isds_log_message(context
, _("Missing CreateMessageResponse element"));
9332 if (result
->nodesetval
->nodeNr
> 1) {
9333 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
9337 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9338 xmlXPathFreeObject(result
); result
= NULL
;
9340 if (outgoing_message
->envelope
->dmID
) {
9341 free(outgoing_message
->envelope
->dmID
);
9342 outgoing_message
->envelope
->dmID
= NULL
;
9344 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
9345 if (!outgoing_message
->envelope
->dmID
) {
9346 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
9347 "but did not return assigned message ID\n"));
9351 /* TODO: Serialize message into structure member raw */
9352 /* XXX: Each web service transport message in different format.
9353 * Therefore it's not possible to save them directly.
9354 * To save them, one must figure out common format.
9355 * We can leave it on application, or we can implement the ESS format. */
9356 /*if (message_is_complete) {
9357 if (outgoing_message->envelope->dmID) {
9359 /* Add assigned message ID as first child*/
9360 /*xmlNodePtr dmid_text = xmlNewText(
9361 (xmlChar *) outgoing_message->envelope->dmID);
9362 if (!dmid_text) goto serialization_failed;
9364 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9366 if (!dmid_element) {
9367 xmlFreeNode(dmid_text);
9368 goto serialization_failed;
9371 xmlNodePtr dmid_element_with_text =
9372 xmlAddChild(dmid_element, dmid_text);
9373 if (!dmid_element_with_text) {
9374 xmlFreeNode(dmid_element);
9375 xmlFreeNode(dmid_text);
9376 goto serialization_failed;
9379 node = xmlAddPrevSibling(envelope->childern,
9380 dmid_element_with_text);
9382 xmlFreeNodeList(dmid_element_with_text);
9383 goto serialization_failed;
9387 /* Serialize message with ID into raw */
9388 /*buffer = serialize_element(envelope)*/
9391 serialization_failed:
9396 xmlXPathFreeObject(result
);
9397 xmlXPathFreeContext(xpath_ctx
);
9401 xmlFreeDoc(response
);
9402 xmlFreeNode(request
);
9405 isds_log(ILF_ISDS
, ILL_DEBUG
,
9406 _("CreateMessage request processed by server "
9407 "successfully.\n"));
9408 #else /* not HAVE_LIBCURL */
9416 /* Send a message via ISDS to a multiple recipients
9417 * @context is session context
9418 * @outgoing_message is message to send; Some members are mandatory,
9419 * some are optional and some are irrelevant (especially data
9420 * about sender). Data about recipient will be substituted by ISDS from
9421 * @copies. Included pointer to isds_list documents must
9422 * contain at least one document of FILEMETATYPE_MAIN.
9423 * @copies is list of isds_message_copy structures addressing all desired
9424 * recipients. This is read-write structure, some members will be filled with
9425 * valid data from ISDS (message IDs, error codes, error descriptions).
9427 * ISDS_SUCCESS if all messages have been sent
9428 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9429 * succeeded messages can be identified by copies->data->error),
9430 * or other error code if something other goes wrong. */
9431 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
9432 const struct isds_message
*outgoing_message
,
9433 struct isds_list
*copies
) {
9435 isds_error err
= IE_SUCCESS
;
9437 isds_error append_err
;
9438 xmlNsPtr isds_ns
= NULL
;
9439 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
9440 struct isds_list
*item
;
9441 struct isds_message_copy
*copy
;
9442 xmlDocPtr response
= NULL
;
9443 xmlChar
*code
= NULL
, *message
= NULL
;
9444 xmlXPathContextPtr xpath_ctx
= NULL
;
9445 xmlXPathObjectPtr result
= NULL
;
9446 xmlChar
*string
= NULL
;
9450 if (!context
) return IE_INVALID_CONTEXT
;
9451 zfree(context
->long_message
);
9452 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9455 /* Check if connection is established
9456 * TODO: This check should be done downstairs. */
9457 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9460 /* Build CreateMultipleMessage request */
9461 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9463 isds_log_message(context
,
9464 _("Could not build CreateMultipleMessage request"));
9467 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9469 isds_log_message(context
, _("Could not create ISDS name space"));
9470 xmlFreeNode(request
);
9473 xmlSetNs(request
, isds_ns
);
9476 /* Build recipients */
9477 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9479 isds_log_message(context
, _("Could not add dmRecipients child to "
9480 "CreateMultipleMessage element"));
9481 xmlFreeNode(request
);
9485 /* Insert each recipient */
9486 for (item
= copies
; item
; item
= item
->next
) {
9487 copy
= (struct isds_message_copy
*) item
->data
;
9489 isds_log_message(context
,
9490 _("`copies' list item contains empty data"));
9495 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9497 isds_log_message(context
, _("Could not add dmRecipient child to "
9498 "dmRecipients element"));
9503 if (!copy
->dbIDRecipient
) {
9504 isds_log_message(context
,
9505 _("Message copy is missing recipient box identifier"));
9509 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9510 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9511 copy
->dmRecipientOrgUnit
);
9512 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9513 copy
->dmRecipientOrgUnitNum
, string
);
9514 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9517 /* Append envelope and files */
9518 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9519 if (err
) goto leave
;
9522 isds_log(ILF_ISDS
, ILL_DEBUG
,
9523 _("Sending CreateMultipleMessage request to ISDS\n"));
9526 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9528 isds_log(ILF_ISDS
, ILL_DEBUG
,
9529 _("Processing ISDS response on CreateMultipleMessage "
9530 "request failed\n"));
9534 /* Check for response status */
9535 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9536 &code
, &message
, NULL
);
9538 isds_log(ILF_ISDS
, ILL_DEBUG
,
9539 _("ISDS response on CreateMultipleMessage request "
9540 "is missing status\n"));
9544 /* Request processed, but some copies failed */
9545 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9546 char *box_id_locale
=
9547 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9548 char *code_locale
= _isds_utf82locale((char*)code
);
9549 char *message_locale
= _isds_utf82locale((char*)message
);
9550 isds_log(ILF_ISDS
, ILL_DEBUG
,
9551 _("Server did accept message for multiple recipients "
9552 "on CreateMultipleMessage request but delivery to "
9553 "some of them failed (code=%s, message=%s)\n"),
9554 box_id_locale
, code_locale
, message_locale
);
9555 isds_log_message(context
, message_locale
);
9556 free(box_id_locale
);
9558 free(message_locale
);
9559 err
= IE_PARTIAL_SUCCESS
;
9562 /* Request refused by server as whole */
9563 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9564 char *box_id_locale
=
9565 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9566 char *code_locale
= _isds_utf82locale((char*)code
);
9567 char *message_locale
= _isds_utf82locale((char*)message
);
9568 isds_log(ILF_ISDS
, ILL_DEBUG
,
9569 _("Server did not accept message for multiple recipients "
9570 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9571 box_id_locale
, code_locale
, message_locale
);
9572 isds_log_message(context
, message_locale
);
9573 free(box_id_locale
);
9575 free(message_locale
);
9582 xpath_ctx
= xmlXPathNewContext(response
);
9587 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9591 result
= xmlXPathEvalExpression(
9592 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9593 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9599 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9600 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9605 /* Extract message ID and delivery status for each copy */
9606 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9607 item
= item
->next
, i
++) {
9608 copy
= (struct isds_message_copy
*) item
->data
;
9609 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9611 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9617 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9618 isds_printf_message(context
, _("ISDS returned unexpected number of "
9619 "message copy delivery states: %d"),
9620 result
->nodesetval
->nodeNr
);
9629 xmlXPathFreeObject(result
);
9630 xmlXPathFreeContext(xpath_ctx
);
9634 xmlFreeDoc(response
);
9635 xmlFreeNode(request
);
9638 isds_log(ILF_ISDS
, ILL_DEBUG
,
9639 _("CreateMultipleMessageResponse request processed by server "
9640 "successfully.\n"));
9641 #else /* not HAVE_LIBCURL */
9649 /* Get list of messages. This is common core for getting sent or received
9651 * Any criterion argument can be NULL, if you don't care about it.
9652 * @context is session context. Must not be NULL.
9653 * @outgoing_direction is true if you want list of outgoing messages,
9654 * it's false if you want incoming messages.
9655 * @from_time is minimal time and date of message sending inclusive.
9656 * @to_time is maximal time and date of message sending inclusive
9657 * @organization_unit_number is number of sender/recipient respectively.
9658 * @status_filter is bit field of isds_message_status values. Use special
9659 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9660 * all values, you can use bit-wise arithmetic if you want.)
9661 * @offset is index of first message we are interested in. First message is 1.
9662 * Set to 0 (or 1) if you don't care.
9663 * @number is maximal length of list you want to get as input value, outputs
9664 * number of messages matching these criteria. Can be NULL if you don't care
9665 * (applies to output value either).
9666 * @messages is automatically reallocated list of isds_message's. Be ware that
9667 * it returns only brief overview (envelope and some other fields) about each
9668 * message, not the complete message. FIXME: Specify exact fields.
9669 * The list is sorted by delivery time in ascending order.
9670 * Use NULL if you don't care about don't need the data (useful if you want to
9671 * know only the @number). If you provide &NULL, list will be allocated on
9672 * heap, if you provide pointer to non-NULL, list will be freed automatically
9673 * at first. Also in case of error the list will be NULLed.
9674 * @return IE_SUCCESS or appropriate error code. */
9675 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9676 _Bool outgoing_direction
,
9677 const struct timeval
*from_time
, const struct timeval
*to_time
,
9678 const long int *organization_unit_number
,
9679 const unsigned int status_filter
,
9680 const unsigned long int offset
, unsigned long int *number
,
9681 struct isds_list
**messages
) {
9683 isds_error err
= IE_SUCCESS
;
9685 xmlNsPtr isds_ns
= NULL
;
9686 xmlNodePtr request
= NULL
, node
;
9687 xmlDocPtr response
= NULL
;
9688 xmlChar
*code
= NULL
, *message
= NULL
;
9689 xmlXPathContextPtr xpath_ctx
= NULL
;
9690 xmlXPathObjectPtr result
= NULL
;
9691 xmlChar
*string
= NULL
;
9695 if (!context
) return IE_INVALID_CONTEXT
;
9696 zfree(context
->long_message
);
9698 /* Free former message list if any */
9699 if (messages
) isds_list_free(messages
);
9702 /* Check if connection is established
9703 * TODO: This check should be done downstairs. */
9704 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9706 /* Build GetListOf*Messages request */
9707 request
= xmlNewNode(NULL
,
9708 (outgoing_direction
) ?
9709 BAD_CAST
"GetListOfSentMessages" :
9710 BAD_CAST
"GetListOfReceivedMessages"
9713 isds_log_message(context
,
9714 (outgoing_direction
) ?
9715 _("Could not build GetListOfSentMessages request") :
9716 _("Could not build GetListOfReceivedMessages request")
9720 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9722 isds_log_message(context
, _("Could not create ISDS name space"));
9723 xmlFreeNode(request
);
9726 xmlSetNs(request
, isds_ns
);
9730 err
= timeval2timestring(from_time
, &string
);
9731 if (err
) goto leave
;
9733 INSERT_STRING(request
, "dmFromTime", string
);
9734 free(string
); string
= NULL
;
9737 err
= timeval2timestring(to_time
, &string
);
9738 if (err
) goto leave
;
9740 INSERT_STRING(request
, "dmToTime", string
);
9741 free(string
); string
= NULL
;
9743 if (outgoing_direction
) {
9744 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9745 organization_unit_number
, string
);
9747 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9748 organization_unit_number
, string
);
9751 if (status_filter
> MESSAGESTATE_ANY
) {
9752 isds_printf_message(context
,
9753 _("Invalid message state filter value: %ld"), status_filter
);
9757 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9760 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9762 INSERT_STRING(request
, "dmOffset", "1");
9765 /* number 0 means no limit */
9766 if (number
&& *number
== 0) {
9767 INSERT_STRING(request
, "dmLimit", NULL
);
9769 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9773 isds_log(ILF_ISDS
, ILL_DEBUG
,
9774 (outgoing_direction
) ?
9775 _("Sending GetListOfSentMessages request to ISDS\n") :
9776 _("Sending GetListOfReceivedMessages request to ISDS\n")
9780 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9781 xmlFreeNode(request
); request
= NULL
;
9784 isds_log(ILF_ISDS
, ILL_DEBUG
,
9785 (outgoing_direction
) ?
9786 _("Processing ISDS response on GetListOfSentMessages "
9787 "request failed\n") :
9788 _("Processing ISDS response on GetListOfReceivedMessages "
9794 /* Check for response status */
9795 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9796 &code
, &message
, NULL
);
9798 isds_log(ILF_ISDS
, ILL_DEBUG
,
9799 (outgoing_direction
) ?
9800 _("ISDS response on GetListOfSentMessages request "
9801 "is missing status\n") :
9802 _("ISDS response on GetListOfReceivedMessages request "
9803 "is missing status\n")
9808 /* Request processed, but nothing found */
9809 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9810 char *code_locale
= _isds_utf82locale((char*)code
);
9811 char *message_locale
= _isds_utf82locale((char*)message
);
9812 isds_log(ILF_ISDS
, ILL_DEBUG
,
9813 (outgoing_direction
) ?
9814 _("Server refused GetListOfSentMessages request "
9815 "(code=%s, message=%s)\n") :
9816 _("Server refused GetListOfReceivedMessages request "
9817 "(code=%s, message=%s)\n"),
9818 code_locale
, message_locale
);
9819 isds_log_message(context
, message_locale
);
9821 free(message_locale
);
9828 xpath_ctx
= xmlXPathNewContext(response
);
9833 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9837 result
= xmlXPathEvalExpression(
9838 (outgoing_direction
) ?
9839 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9840 "isds:dmRecords/isds:dmRecord" :
9841 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9842 "isds:dmRecords/isds:dmRecord",
9849 /* Fill output arguments in */
9850 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9851 struct isds_envelope
*envelope
;
9852 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9854 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9855 /* Create new message */
9856 item
= calloc(1, sizeof(*item
));
9861 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9862 item
->data
= calloc(1, sizeof(struct isds_message
));
9864 isds_list_free(&item
);
9869 /* Extract envelope data */
9870 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9872 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9874 isds_list_free(&item
);
9878 /* Attach extracted envelope */
9879 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9881 /* Append new message into the list */
9883 *messages
= last_item
= item
;
9885 last_item
->next
= item
;
9890 if (number
) *number
= count
;
9894 isds_list_free(messages
);
9898 xmlXPathFreeObject(result
);
9899 xmlXPathFreeContext(xpath_ctx
);
9903 xmlFreeDoc(response
);
9904 xmlFreeNode(request
);
9907 isds_log(ILF_ISDS
, ILL_DEBUG
,
9908 (outgoing_direction
) ?
9909 _("GetListOfSentMessages request processed by server "
9910 "successfully.\n") :
9911 _("GetListOfReceivedMessages request processed by server "
9914 #else /* not HAVE_LIBCURL */
9921 /* Get list of outgoing (already sent) messages.
9922 * Any criterion argument can be NULL, if you don't care about it.
9923 * @context is session context. Must not be NULL.
9924 * @from_time is minimal time and date of message sending inclusive.
9925 * @to_time is maximal time and date of message sending inclusive
9926 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9927 * @status_filter is bit field of isds_message_status values. Use special
9928 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9929 * all values, you can use bit-wise arithmetic if you want.)
9930 * @offset is index of first message we are interested in. First message is 1.
9931 * Set to 0 (or 1) if you don't care.
9932 * @number is maximal length of list you want to get as input value, outputs
9933 * number of messages matching these criteria. Can be NULL if you don't care
9934 * (applies to output value either).
9935 * @messages is automatically reallocated list of isds_message's. Be ware that
9936 * it returns only brief overview (envelope and some other fields) about each
9937 * message, not the complete message. FIXME: Specify exact fields.
9938 * The list is sorted by delivery time in ascending order.
9939 * Use NULL if you don't care about the meta data (useful if you want to know
9940 * only the @number). If you provide &NULL, list will be allocated on heap,
9941 * if you provide pointer to non-NULL, list will be freed automatically at
9942 * first. Also in case of error the list will be NULLed.
9943 * @return IE_SUCCESS or appropriate error code. */
9944 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9945 const struct timeval
*from_time
, const struct timeval
*to_time
,
9946 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9947 const unsigned long int offset
, unsigned long int *number
,
9948 struct isds_list
**messages
) {
9950 return isds_get_list_of_messages(
9952 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9958 /* Get list of incoming (addressed to you) messages.
9959 * Any criterion argument can be NULL, if you don't care about it.
9960 * @context is session context. Must not be NULL.
9961 * @from_time is minimal time and date of message sending inclusive.
9962 * @to_time is maximal time and date of message sending inclusive
9963 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9964 * @status_filter is bit field of isds_message_status values. Use special
9965 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9966 * all values, you can use bit-wise arithmetic if you want.)
9967 * @offset is index of first message we are interested in. First message is 1.
9968 * Set to 0 (or 1) if you don't care.
9969 * @number is maximal length of list you want to get as input value, outputs
9970 * number of messages matching these criteria. Can be NULL if you don't care
9971 * (applies to output value either).
9972 * @messages is automatically reallocated list of isds_message's. Be ware that
9973 * it returns only brief overview (envelope and some other fields) about each
9974 * message, not the complete message. FIXME: Specify exact fields.
9975 * Use NULL if you don't care about the meta data (useful if you want to know
9976 * only the @number). If you provide &NULL, list will be allocated on heap,
9977 * if you provide pointer to non-NULL, list will be freed automatically at
9978 * first. Also in case of error the list will be NULLed.
9979 * @return IE_SUCCESS or appropriate error code. */
9980 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9981 const struct timeval
*from_time
, const struct timeval
*to_time
,
9982 const long int *dmRecipientOrgUnitNum
,
9983 const unsigned int status_filter
,
9984 const unsigned long int offset
, unsigned long int *number
,
9985 struct isds_list
**messages
) {
9987 return isds_get_list_of_messages(
9989 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9995 /* Get list of sent message state changes.
9996 * Any criterion argument can be NULL, if you don't care about it.
9997 * @context is session context. Must not be NULL.
9998 * @from_time is minimal time and date of status changes inclusive
9999 * @to_time is maximal time and date of status changes inclusive
10000 * @changed_states is automatically reallocated list of
10001 * isds_message_status_change's. If you provide &NULL, list will be allocated
10002 * on heap, if you provide pointer to non-NULL, list will be freed
10003 * automatically at first. Also in case of error the list will be NULLed.
10004 * XXX: The list item ordering is not specified.
10005 * XXX: Server provides only `recent' changes.
10006 * @return IE_SUCCESS or appropriate error code. */
10007 isds_error
isds_get_list_of_sent_message_state_changes(
10008 struct isds_ctx
*context
,
10009 const struct timeval
*from_time
, const struct timeval
*to_time
,
10010 struct isds_list
**changed_states
) {
10012 isds_error err
= IE_SUCCESS
;
10014 xmlNsPtr isds_ns
= NULL
;
10015 xmlNodePtr request
= NULL
, node
;
10016 xmlDocPtr response
= NULL
;
10017 xmlXPathContextPtr xpath_ctx
= NULL
;
10018 xmlXPathObjectPtr result
= NULL
;
10019 xmlChar
*string
= NULL
;
10023 if (!context
) return IE_INVALID_CONTEXT
;
10024 zfree(context
->long_message
);
10026 /* Free former message list if any */
10027 isds_list_free(changed_states
);
10030 /* Check if connection is established
10031 * TODO: This check should be done downstairs. */
10032 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10034 /* Build GetMessageStateChanges request */
10035 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
10037 isds_log_message(context
,
10038 _("Could not build GetMessageStateChanges request"));
10041 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10043 isds_log_message(context
, _("Could not create ISDS name space"));
10044 xmlFreeNode(request
);
10047 xmlSetNs(request
, isds_ns
);
10051 err
= timeval2timestring(from_time
, &string
);
10052 if (err
) goto leave
;
10054 INSERT_STRING(request
, "dmFromTime", string
);
10058 err
= timeval2timestring(to_time
, &string
);
10059 if (err
) goto leave
;
10061 INSERT_STRING(request
, "dmToTime", string
);
10066 err
= send_destroy_request_check_response(context
,
10067 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
10068 &response
, NULL
, NULL
);
10069 if (err
) goto leave
;
10073 xpath_ctx
= xmlXPathNewContext(response
);
10078 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10082 result
= xmlXPathEvalExpression(
10083 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
10084 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
10090 /* Fill output arguments in */
10091 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10092 struct isds_list
*item
= NULL
, *last_item
= NULL
;
10094 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
10095 /* Create new status change */
10096 item
= calloc(1, sizeof(*item
));
10102 (void(*)(void**)) &isds_message_status_change_free
;
10104 /* Extract message status change */
10105 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
10106 err
= extract_StateChangesRecord(context
,
10107 (struct isds_message_status_change
**) &item
->data
,
10110 isds_list_free(&item
);
10114 /* Append new message status change into the list */
10115 if (!*changed_states
) {
10116 *changed_states
= last_item
= item
;
10118 last_item
->next
= item
;
10126 isds_list_free(changed_states
);
10130 xmlXPathFreeObject(result
);
10131 xmlXPathFreeContext(xpath_ctx
);
10132 xmlFreeDoc(response
);
10133 xmlFreeNode(request
);
10136 isds_log(ILF_ISDS
, ILL_DEBUG
,
10137 _("GetMessageStateChanges request processed by server "
10138 "successfully.\n"));
10139 #else /* not HAVE_LIBCURL */
10147 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10149 * @context is session context
10150 * @service is ISDS WS service handler
10151 * @service_name is name of SERVICE_DM_OPERATIONS
10152 * @message_id is message ID to send as service argument to ISDS
10153 * @response is reallocated server SOAP body response as XML document
10154 * @raw_response is reallocated bit stream with response body. Use
10155 * NULL if you don't care
10156 * @raw_response_length is size of @raw_response in bytes
10157 * @code is reallocated ISDS status code
10158 * @status_message is reallocated ISDS status message
10159 * @return error coded from lower layer, context message will be set up
10160 * appropriately. */
10161 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
10162 const isds_service service
, const xmlChar
*service_name
,
10163 const char *message_id
,
10164 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
10165 xmlChar
**code
, xmlChar
**status_message
) {
10167 isds_error err
= IE_SUCCESS
;
10168 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
10169 xmlNodePtr request
= NULL
, node
;
10170 xmlNsPtr isds_ns
= NULL
;
10172 if (!context
) return IE_INVALID_CONTEXT
;
10173 if (!service_name
|| !message_id
) return IE_INVAL
;
10174 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
10175 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
10177 /* Free output argument */
10178 xmlFreeDoc(*response
); *response
= NULL
;
10179 if (raw_response
) zfree(*raw_response
);
10181 zfree(*status_message
);
10184 /* Check if connection is established
10185 * TODO: This check should be done downstairs. */
10186 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10188 service_name_locale
= _isds_utf82locale((char*)service_name
);
10189 message_id_locale
= _isds_utf82locale(message_id
);
10190 if (!service_name_locale
|| !message_id_locale
) {
10195 /* Build request */
10196 request
= xmlNewNode(NULL
, service_name
);
10198 isds_printf_message(context
,
10199 _("Could not build %s request for %s message ID"),
10200 service_name_locale
, message_id_locale
);
10204 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10206 isds_log_message(context
, _("Could not create ISDS name space"));
10210 xmlSetNs(request
, isds_ns
);
10213 /* Add requested ID */
10214 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
10215 if (err
) goto leave
;
10216 INSERT_STRING(request
, "dmID", message_id
);
10219 isds_log(ILF_ISDS
, ILL_DEBUG
,
10220 _("Sending %s request for %s message ID to ISDS\n"),
10221 service_name_locale
, message_id_locale
);
10224 err
= _isds(context
, service
, request
, response
,
10225 raw_response
, raw_response_length
);
10226 xmlFreeNode(request
); request
= NULL
;
10229 isds_log(ILF_ISDS
, ILL_DEBUG
,
10230 _("Processing ISDS response on %s request failed\n"),
10231 service_name_locale
);
10235 /* Check for response status */
10236 err
= isds_response_status(context
, service
, *response
,
10237 code
, status_message
, NULL
);
10239 isds_log(ILF_ISDS
, ILL_DEBUG
,
10240 _("ISDS response on %s request is missing status\n"),
10241 service_name_locale
);
10245 /* Request processed, but nothing found */
10246 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
10247 char *code_locale
= _isds_utf82locale((char*) *code
);
10248 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
10249 isds_log(ILF_ISDS
, ILL_DEBUG
,
10250 _("Server refused %s request for %s message ID "
10251 "(code=%s, message=%s)\n"),
10252 service_name_locale
, message_id_locale
,
10253 code_locale
, status_message_locale
);
10254 isds_log_message(context
, status_message_locale
);
10256 free(status_message_locale
);
10262 free(message_id_locale
);
10263 free(service_name_locale
);
10264 xmlFreeNode(request
);
10269 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10270 * signed data and free ISDS response.
10271 * @context is session context
10272 * @message_id is UTF-8 encoded message ID for logging purpose
10273 * @response is parsed XML document. It will be freed and NULLed in the middle
10274 * of function run to save memory. This is not guaranteed in case of error.
10275 * @request_name is name of ISDS request used to construct response root
10276 * element name and for logging purpose.
10277 * @raw is reallocated output buffer with DER encoded CMS data
10278 * @raw_length is size of @raw buffer in bytes
10279 * @returns standard error codes, in case of error, @raw will be freed and
10280 * NULLed, @response sometimes. */
10281 static isds_error
find_extract_signed_data_free_response(
10282 struct isds_ctx
*context
, const xmlChar
*message_id
,
10283 xmlDocPtr
*response
, const xmlChar
*request_name
,
10284 void **raw
, size_t *raw_length
) {
10286 isds_error err
= IE_SUCCESS
;
10287 char *xpath_expression
= NULL
;
10288 xmlXPathContextPtr xpath_ctx
= NULL
;
10289 xmlXPathObjectPtr result
= NULL
;
10290 char *encoded_structure
= NULL
;
10292 if (!context
) return IE_INVALID_CONTEXT
;
10293 if (!raw
) return IE_INVAL
;
10295 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
10298 /* Build XPath expression */
10299 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
10300 "Response/isds:dmSignature");
10301 if (!xpath_expression
) return IE_NOMEM
;
10304 xpath_ctx
= xmlXPathNewContext(*response
);
10309 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10313 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
10318 /* Empty response */
10319 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10320 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10321 isds_printf_message(context
,
10322 _("Server did not return any signed data for message ID `%s' "
10324 message_id_locale
, request_name
);
10325 free(message_id_locale
);
10329 /* More responses */
10330 if (result
->nodesetval
->nodeNr
> 1) {
10331 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10332 isds_printf_message(context
,
10333 _("Server did return more signed data for message ID `%s' "
10335 message_id_locale
, request_name
);
10336 free(message_id_locale
);
10341 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10343 /* Extract PKCS#7 structure */
10344 EXTRACT_STRING(".", encoded_structure
);
10345 if (!encoded_structure
) {
10346 isds_log_message(context
, _("dmSignature element is empty"));
10349 /* Here we have delivery info as standalone CMS in encoded_structure.
10350 * We don't need any other data, free them: */
10351 xmlXPathFreeObject(result
); result
= NULL
;
10352 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
10353 xmlFreeDoc(*response
); *response
= NULL
;
10356 /* Decode PKCS#7 to DER format */
10357 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
10358 if (*raw_length
== (size_t) -1) {
10359 isds_log_message(context
,
10360 _("Error while Base64-decoding PKCS#7 structure"));
10371 free(encoded_structure
);
10372 xmlXPathFreeObject(result
);
10373 xmlXPathFreeContext(xpath_ctx
);
10374 free(xpath_expression
);
10378 #endif /* HAVE_LIBCURL */
10381 /* Download incoming message envelope identified by ID.
10382 * @context is session context
10383 * @message_id is message identifier (you can get them from
10384 * isds_get_list_of_received_messages())
10385 * @message is automatically reallocated message retrieved from ISDS.
10386 * It will miss documents per se. Use isds_get_received_message(), if you are
10387 * interested in documents (content) too.
10388 * Returned hash and timestamp require documents to be verifiable. */
10389 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
10390 const char *message_id
, struct isds_message
**message
) {
10392 isds_error err
= IE_SUCCESS
;
10394 xmlDocPtr response
= NULL
;
10395 xmlChar
*code
= NULL
, *status_message
= NULL
;
10396 xmlXPathContextPtr xpath_ctx
= NULL
;
10397 xmlXPathObjectPtr result
= NULL
;
10400 if (!context
) return IE_INVALID_CONTEXT
;
10401 zfree(context
->long_message
);
10403 /* Free former message if any */
10404 if (!message
) return IE_INVAL
;
10405 isds_message_free(message
);
10408 /* Do request and check for success */
10409 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10410 BAD_CAST
"MessageEnvelopeDownload", message_id
,
10411 &response
, NULL
, NULL
, &code
, &status_message
);
10412 if (err
) goto leave
;
10415 xpath_ctx
= xmlXPathNewContext(response
);
10420 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10424 result
= xmlXPathEvalExpression(
10425 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
10426 "isds:dmReturnedMessageEnvelope",
10432 /* Empty response */
10433 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10434 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10435 isds_printf_message(context
,
10436 _("Server did not return any envelope for ID `%s' "
10437 "on MessageEnvelopeDownload request"), message_id_locale
);
10438 free(message_id_locale
);
10442 /* More envelops */
10443 if (result
->nodesetval
->nodeNr
> 1) {
10444 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10445 isds_printf_message(context
,
10446 _("Server did return more envelopes for ID `%s' "
10447 "on MessageEnvelopeDownload request"), message_id_locale
);
10448 free(message_id_locale
);
10453 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10455 /* Extract the envelope (= message without documents, hence 0) */
10456 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10457 if (err
) goto leave
;
10459 /* Save XML blob */
10460 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10461 &(*message
)->raw_length
);
10465 isds_message_free(message
);
10468 xmlXPathFreeObject(result
);
10469 xmlXPathFreeContext(xpath_ctx
);
10472 free(status_message
);
10473 if (!*message
|| !(*message
)->xml
) {
10474 xmlFreeDoc(response
);
10478 isds_log(ILF_ISDS
, ILL_DEBUG
,
10479 _("MessageEnvelopeDownload request processed by server "
10482 #else /* not HAVE_LIBCURL */
10489 /* Load delivery info of any format from buffer.
10490 * @context is session context
10491 * @raw_type advertises format of @buffer content. Only delivery info types
10493 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10494 * retrieve such data from message->raw after calling
10495 * isds_get_signed_delivery_info().
10496 * @length is length of buffer in bytes.
10497 * @message is automatically reallocated message parsed from @buffer.
10498 * @strategy selects how buffer will be attached into raw isds_message member.
10500 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10501 const isds_raw_type raw_type
,
10502 const void *buffer
, const size_t length
,
10503 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10505 isds_error err
= IE_SUCCESS
;
10506 message_ns_type message_ns
;
10507 xmlDocPtr message_doc
= NULL
;
10508 xmlXPathContextPtr xpath_ctx
= NULL
;
10509 xmlXPathObjectPtr result
= NULL
;
10510 void *xml_stream
= NULL
;
10511 size_t xml_stream_length
= 0;
10513 if (!context
) return IE_INVALID_CONTEXT
;
10514 zfree(context
->long_message
);
10515 if (!message
) return IE_INVAL
;
10516 isds_message_free(message
);
10517 if (!buffer
) return IE_INVAL
;
10520 /* Select buffer format and extract XML from CMS*/
10521 switch (raw_type
) {
10522 case RAWTYPE_DELIVERYINFO
:
10523 message_ns
= MESSAGE_NS_UNSIGNED
;
10524 xml_stream
= (void *) buffer
;
10525 xml_stream_length
= length
;
10528 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10529 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10530 xml_stream
= (void *) buffer
;
10531 xml_stream_length
= length
;
10534 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10535 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10536 err
= _isds_extract_cms_data(context
, buffer
, length
,
10537 &xml_stream
, &xml_stream_length
);
10538 if (err
) goto leave
;
10542 isds_log_message(context
, _("Bad raw delivery representation type"));
10547 if (_isds_sizet2int(xml_stream_length
) >= 0) {
10548 isds_log(ILF_ISDS
, ILL_DEBUG
,
10549 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10550 _isds_sizet2int(xml_stream_length
), xml_stream
);
10553 /* Convert delivery info XML stream into XPath context */
10554 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10555 if (!message_doc
) {
10559 xpath_ctx
= xmlXPathNewContext(message_doc
);
10564 /* XXX: Name spaces mangled for signed delivery info:
10565 * http://isds.czechpoint.cz/v20/delivery:
10567 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10569 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10570 * <p:dmID>170272</p:dmID>
10573 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10575 * </q:dmEvents>...</q:dmEvents>
10577 * </q:GetDeliveryInfoResponse>
10579 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10583 result
= xmlXPathEvalExpression(
10584 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10590 /* Empty delivery info */
10591 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10592 isds_printf_message(context
,
10593 _("XML document is not sisds:dmDelivery document"));
10597 /* More delivery info's */
10598 if (result
->nodesetval
->nodeNr
> 1) {
10599 isds_printf_message(context
,
10600 _("XML document has more sisds:dmDelivery elements"));
10604 /* One delivery info */
10605 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10607 /* Extract the envelope (= message without documents, hence 0).
10608 * XXX: extract_TReturnedMessage() can obtain attachments size,
10609 * but delivery info carries none. It's coded as option elements,
10610 * so it should work. */
10611 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10612 if (err
) goto leave
;
10614 /* Extract events */
10615 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10616 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10617 if (err
) { err
= IE_ERROR
; goto leave
; }
10618 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10619 if (err
) goto leave
;
10621 /* Append raw CMS structure into message */
10622 (*message
)->raw_type
= raw_type
;
10623 switch (strategy
) {
10624 case BUFFER_DONT_STORE
:
10627 (*message
)->raw
= malloc(length
);
10628 if (!(*message
)->raw
) {
10632 memcpy((*message
)->raw
, buffer
, length
);
10633 (*message
)->raw_length
= length
;
10636 (*message
)->raw
= (void *) buffer
;
10637 (*message
)->raw_length
= length
;
10646 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10647 isds_message_free(message
);
10650 xmlXPathFreeObject(result
);
10651 xmlXPathFreeContext(xpath_ctx
);
10652 if (!*message
|| !(*message
)->xml
) {
10653 xmlFreeDoc(message_doc
);
10655 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10658 isds_log(ILF_ISDS
, ILL_DEBUG
,
10659 _("Delivery info loaded successfully.\n"));
10664 /* Download signed delivery info-sheet of given message identified by ID.
10665 * @context is session context
10666 * @message_id is message identifier (you can get them from
10667 * isds_get_list_of_{sent,received}_messages())
10668 * @message is automatically reallocated message retrieved from ISDS.
10669 * It will miss documents per se. Use isds_get_signed_received_message(),
10670 * if you are interested in documents (content). OTOH, only this function
10671 * can get list events message has gone through. */
10672 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10673 const char *message_id
, struct isds_message
**message
) {
10675 isds_error err
= IE_SUCCESS
;
10677 xmlDocPtr response
= NULL
;
10678 xmlChar
*code
= NULL
, *status_message
= NULL
;
10680 size_t raw_length
= 0;
10683 if (!context
) return IE_INVALID_CONTEXT
;
10684 zfree(context
->long_message
);
10686 /* Free former message if any */
10687 if (!message
) return IE_INVAL
;
10688 isds_message_free(message
);
10691 /* Do request and check for success */
10692 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10693 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10694 &response
, NULL
, NULL
, &code
, &status_message
);
10695 if (err
) goto leave
;
10697 /* Find signed delivery info, extract it into raw and maybe free
10699 err
= find_extract_signed_data_free_response(context
,
10700 (xmlChar
*)message_id
, &response
,
10701 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10702 if (err
) goto leave
;
10704 /* Parse delivery info */
10705 err
= isds_load_delivery_info(context
,
10706 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10707 message
, BUFFER_MOVE
);
10708 if (err
) goto leave
;
10714 isds_message_free(message
);
10719 free(status_message
);
10720 xmlFreeDoc(response
);
10723 isds_log(ILF_ISDS
, ILL_DEBUG
,
10724 _("GetSignedDeliveryInfo request processed by server "
10727 #else /* not HAVE_LIBCURL */
10734 /* Download delivery info-sheet of given message identified by ID.
10735 * @context is session context
10736 * @message_id is message identifier (you can get them from
10737 * isds_get_list_of_{sent,received}_messages())
10738 * @message is automatically reallocated message retrieved from ISDS.
10739 * It will miss documents per se. Use isds_get_received_message(), if you are
10740 * interested in documents (content). OTOH, only this function can get list
10741 * of events message has gone through. */
10742 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10743 const char *message_id
, struct isds_message
**message
) {
10745 isds_error err
= IE_SUCCESS
;
10747 xmlDocPtr response
= NULL
;
10748 xmlChar
*code
= NULL
, *status_message
= NULL
;
10749 xmlNodePtr delivery_node
= NULL
;
10751 size_t raw_length
= 0;
10754 if (!context
) return IE_INVALID_CONTEXT
;
10755 zfree(context
->long_message
);
10757 /* Free former message if any */
10758 if (!message
) return IE_INVAL
;
10759 isds_message_free(message
);
10762 /* Do request and check for success */
10763 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10764 BAD_CAST
"GetDeliveryInfo", message_id
,
10765 &response
, NULL
, NULL
, &code
, &status_message
);
10766 if (err
) goto leave
;
10769 /* Serialize delivery info */
10770 delivery_node
= xmlDocGetRootElement(response
);
10771 if (!delivery_node
) {
10772 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10773 isds_printf_message(context
,
10774 _("Server did not return any delivery info for ID `%s' "
10775 "on GetDeliveryInfo request"), message_id_locale
);
10776 free(message_id_locale
);
10780 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10781 if (err
) goto leave
;
10783 /* Parse delivery info */
10784 /* TODO: Here we parse the response second time. We could single delivery
10785 * parser from isds_load_delivery_info() to make things faster. */
10786 err
= isds_load_delivery_info(context
,
10787 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10788 message
, BUFFER_MOVE
);
10789 if (err
) goto leave
;
10796 isds_message_free(message
);
10801 free(status_message
);
10802 xmlFreeDoc(response
);
10805 isds_log(ILF_ISDS
, ILL_DEBUG
,
10806 _("GetDeliveryInfo request processed by server "
10809 #else /* not HAVE_LIBCURL */
10816 /* Download incoming message identified by ID.
10817 * @context is session context
10818 * @message_id is message identifier (you can get them from
10819 * isds_get_list_of_received_messages())
10820 * @message is automatically reallocated message retrieved from ISDS */
10821 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10822 const char *message_id
, struct isds_message
**message
) {
10824 isds_error err
= IE_SUCCESS
;
10826 xmlDocPtr response
= NULL
;
10827 void *xml_stream
= NULL
;
10828 size_t xml_stream_length
;
10829 xmlChar
*code
= NULL
, *status_message
= NULL
;
10830 xmlXPathContextPtr xpath_ctx
= NULL
;
10831 xmlXPathObjectPtr result
= NULL
;
10832 char *phys_path
= NULL
;
10833 size_t phys_start
, phys_end
;
10836 if (!context
) return IE_INVALID_CONTEXT
;
10837 zfree(context
->long_message
);
10839 /* Free former message if any */
10840 if (NULL
== message
) return IE_INVAL
;
10841 if (message
) isds_message_free(message
);
10844 /* Do request and check for success */
10845 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10846 BAD_CAST
"MessageDownload", message_id
,
10847 &response
, &xml_stream
, &xml_stream_length
,
10848 &code
, &status_message
);
10849 if (err
) goto leave
;
10852 xpath_ctx
= xmlXPathNewContext(response
);
10857 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10861 result
= xmlXPathEvalExpression(
10862 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10868 /* Empty response */
10869 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10870 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10871 isds_printf_message(context
,
10872 _("Server did not return any message for ID `%s' "
10873 "on MessageDownload request"), message_id_locale
);
10874 free(message_id_locale
);
10878 /* More messages */
10879 if (result
->nodesetval
->nodeNr
> 1) {
10880 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10881 isds_printf_message(context
,
10882 _("Server did return more messages for ID `%s' "
10883 "on MessageDownload request"), message_id_locale
);
10884 free(message_id_locale
);
10889 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10891 /* Extract the message */
10892 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10893 if (err
) goto leave
;
10895 /* Locate raw XML blob */
10896 phys_path
= strdup(
10897 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10898 PHYSXML_ELEMENT_SEPARATOR
10899 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10900 PHYSXML_ELEMENT_SEPARATOR
10901 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10907 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10908 phys_path
, &phys_start
, &phys_end
);
10911 isds_log_message(context
,
10912 _("Substring with isds:MessageDownloadResponse element "
10913 "could not be located in raw SOAP message"));
10916 /* Save XML blob */
10917 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10918 &(*message)->raw_length);*/
10919 /* TODO: Store name space declarations from ancestors */
10920 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10921 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10922 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10923 (*message
)->raw
= malloc((*message
)->raw_length
);
10924 if (!(*message
)->raw
) {
10928 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10933 isds_message_free(message
);
10938 xmlXPathFreeObject(result
);
10939 xmlXPathFreeContext(xpath_ctx
);
10942 free(status_message
);
10944 if (!*message
|| !(*message
)->xml
) {
10945 xmlFreeDoc(response
);
10949 isds_log(ILF_ISDS
, ILL_DEBUG
,
10950 _("MessageDownload request processed by server "
10953 #else /* not HAVE_LIBCURL */
10960 /* Load message of any type from buffer.
10961 * @context is session context
10962 * @raw_type defines content type of @buffer. Only message types are allowed.
10963 * @buffer is message raw representation. Format (CMS, plain signed,
10964 * message direction) is defined in @raw_type. You can retrieve such data
10965 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10966 * @length is length of buffer in bytes.
10967 * @message is automatically reallocated message parsed from @buffer.
10968 * @strategy selects how buffer will be attached into raw isds_message member.
10970 isds_error
isds_load_message(struct isds_ctx
*context
,
10971 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10972 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10974 isds_error err
= IE_SUCCESS
;
10975 void *xml_stream
= NULL
;
10976 size_t xml_stream_length
= 0;
10977 message_ns_type message_ns
;
10978 xmlDocPtr message_doc
= NULL
;
10979 xmlXPathContextPtr xpath_ctx
= NULL
;
10980 xmlXPathObjectPtr result
= NULL
;
10982 if (!context
) return IE_INVALID_CONTEXT
;
10983 zfree(context
->long_message
);
10984 if (!message
) return IE_INVAL
;
10985 isds_message_free(message
);
10986 if (!buffer
) return IE_INVAL
;
10989 /* Select buffer format and extract XML from CMS*/
10990 switch (raw_type
) {
10991 case RAWTYPE_INCOMING_MESSAGE
:
10992 message_ns
= MESSAGE_NS_UNSIGNED
;
10993 xml_stream
= (void *) buffer
;
10994 xml_stream_length
= length
;
10997 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10998 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10999 xml_stream
= (void *) buffer
;
11000 xml_stream_length
= length
;
11003 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11004 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
11005 err
= _isds_extract_cms_data(context
, buffer
, length
,
11006 &xml_stream
, &xml_stream_length
);
11007 if (err
) goto leave
;
11010 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11011 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
11012 xml_stream
= (void *) buffer
;
11013 xml_stream_length
= length
;
11016 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11017 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
11018 err
= _isds_extract_cms_data(context
, buffer
, length
,
11019 &xml_stream
, &xml_stream_length
);
11020 if (err
) goto leave
;
11024 isds_log_message(context
, _("Bad raw message representation type"));
11029 if (_isds_sizet2int(xml_stream_length
) >= 0) {
11030 isds_log(ILF_ISDS
, ILL_DEBUG
,
11031 _("Loading message:\n%.*s\nEnd of message\n"),
11032 _isds_sizet2int(xml_stream_length
), xml_stream
);
11035 /* Convert messages XML stream into XPath context */
11036 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
11037 if (!message_doc
) {
11041 xpath_ctx
= xmlXPathNewContext(message_doc
);
11046 /* XXX: Standard name space for unsigned incoming direction:
11047 * http://isds.czechpoint.cz/v20/
11049 * XXX: Name spaces mangled for signed outgoing direction:
11050 * http://isds.czechpoint.cz/v20/SentMessage:
11052 * <q:MessageDownloadResponse
11053 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11054 * <q:dmReturnedMessage>
11055 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11056 * <p:dmID>151916</p:dmID>
11059 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11061 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11062 * </q:dmReturnedMessage>
11063 * </q:MessageDownloadResponse>
11065 * XXX: Name spaces mangled for signed incoming direction:
11066 * http://isds.czechpoint.cz/v20/message:
11068 * <q:MessageDownloadResponse
11069 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11070 * <q:dmReturnedMessage>
11071 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11072 * <p:dmID>151916</p:dmID>
11075 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11077 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11078 * </q:dmReturnedMessage>
11079 * </q:MessageDownloadResponse>
11081 * Stupidity of ISDS developers is unlimited */
11082 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
11086 result
= xmlXPathEvalExpression(
11087 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11093 /* Empty message */
11094 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11095 isds_printf_message(context
,
11096 _("XML document does not contain "
11097 "sisds:dmReturnedMessage element"));
11101 /* More messages */
11102 if (result
->nodesetval
->nodeNr
> 1) {
11103 isds_printf_message(context
,
11104 _("XML document has more sisds:dmReturnedMessage elements"));
11109 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11111 /* Extract the message */
11112 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
11113 if (err
) goto leave
;
11115 /* Append raw buffer into message */
11116 (*message
)->raw_type
= raw_type
;
11117 switch (strategy
) {
11118 case BUFFER_DONT_STORE
:
11121 (*message
)->raw
= malloc(length
);
11122 if (!(*message
)->raw
) {
11126 memcpy((*message
)->raw
, buffer
, length
);
11127 (*message
)->raw_length
= length
;
11130 (*message
)->raw
= (void *) buffer
;
11131 (*message
)->raw_length
= length
;
11141 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
11142 isds_message_free(message
);
11145 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11146 xmlXPathFreeObject(result
);
11147 xmlXPathFreeContext(xpath_ctx
);
11148 if (!*message
|| !(*message
)->xml
) {
11149 xmlFreeDoc(message_doc
);
11153 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
11158 /* Determine type of raw message or delivery info according some heuristics.
11159 * It does not validate the raw blob.
11160 * @context is session context
11161 * @raw_type returns content type of @buffer. Valid only if exit code of this
11162 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11163 * reallocated memory.
11164 * @buffer is message raw representation.
11165 * @length is length of buffer in bytes. */
11166 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
11167 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
11169 void *xml_stream
= NULL
;
11170 size_t xml_stream_length
= 0;
11171 xmlDocPtr document
= NULL
;
11172 xmlNodePtr root
= NULL
;
11174 if (!context
) return IE_INVALID_CONTEXT
;
11175 zfree(context
->long_message
);
11176 if (length
== 0 || !buffer
) return IE_INVAL
;
11177 if (!raw_type
) return IE_INVAL
;
11180 err
= _isds_extract_cms_data(context
, buffer
, length
,
11181 &xml_stream
, &xml_stream_length
);
11183 xml_stream
= (void *) buffer
;
11184 xml_stream_length
= (size_t) length
;
11189 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
11191 isds_printf_message(context
,
11192 _("Could not parse data as XML document"));
11197 /* Get root element */
11198 root
= xmlDocGetRootElement(document
);
11200 isds_printf_message(context
,
11201 _("XML document is missing root element"));
11206 if (!root
->ns
|| !root
->ns
->href
) {
11207 isds_printf_message(context
,
11208 _("Root element does not belong to any name space"));
11213 /* Test name space */
11214 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
11215 if (xml_stream
== buffer
)
11216 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
11218 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
11219 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
11220 if (xml_stream
== buffer
)
11221 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
11223 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
11224 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
11225 if (xml_stream
== buffer
)
11226 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
11228 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
11229 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
11230 if (xml_stream
!= buffer
) {
11231 isds_printf_message(context
,
11232 _("Document in ISDS name space is encapsulated into CMS" ));
11234 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
11235 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
11236 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
11237 *raw_type
= RAWTYPE_DELIVERYINFO
;
11239 isds_printf_message(context
,
11240 _("Unknown root element in ISDS name space"));
11244 isds_printf_message(context
,
11245 _("Unknown name space"));
11250 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11251 xmlFreeDoc(document
);
11256 /* Download signed incoming/outgoing message identified by ID.
11257 * @context is session context
11258 * @output is true for outgoing message, false for incoming message
11259 * @message_id is message identifier (you can get them from
11260 * isds_get_list_of_{sent,received}_messages())
11261 * @message is automatically reallocated message retrieved from ISDS. The raw
11262 * member will be filled with PKCS#7 structure in DER format. */
11263 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
11264 const _Bool outgoing
, const char *message_id
,
11265 struct isds_message
**message
) {
11267 isds_error err
= IE_SUCCESS
;
11269 xmlDocPtr response
= NULL
;
11270 xmlChar
*code
= NULL
, *status_message
= NULL
;
11271 xmlXPathContextPtr xpath_ctx
= NULL
;
11272 xmlXPathObjectPtr result
= NULL
;
11273 char *encoded_structure
= NULL
;
11275 size_t raw_length
= 0;
11278 if (!context
) return IE_INVALID_CONTEXT
;
11279 zfree(context
->long_message
);
11280 if (!message
) return IE_INVAL
;
11281 isds_message_free(message
);
11284 /* Do request and check for success */
11285 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
11286 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11287 BAD_CAST
"SignedMessageDownload",
11288 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11289 if (err
) goto leave
;
11291 /* Find signed message, extract it into raw and maybe free
11293 err
= find_extract_signed_data_free_response(context
,
11294 (xmlChar
*)message_id
, &response
,
11295 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11296 BAD_CAST
"SignedMessageDownload",
11297 &raw
, &raw_length
);
11298 if (err
) goto leave
;
11300 /* Parse message */
11301 err
= isds_load_message(context
,
11302 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11303 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
11304 raw
, raw_length
, message
, BUFFER_MOVE
);
11305 if (err
) goto leave
;
11311 isds_message_free(message
);
11314 free(encoded_structure
);
11315 xmlXPathFreeObject(result
);
11316 xmlXPathFreeContext(xpath_ctx
);
11320 free(status_message
);
11321 xmlFreeDoc(response
);
11324 isds_log(ILF_ISDS
, ILL_DEBUG
,
11326 _("SignedSentMessageDownload request processed by server "
11327 "successfully.\n") :
11328 _("SignedMessageDownload request processed by server "
11331 #else /* not HAVE_LIBCURL */
11338 /* Download signed incoming message identified by ID.
11339 * @context is session context
11340 * @message_id is message identifier (you can get them from
11341 * isds_get_list_of_received_messages())
11342 * @message is automatically reallocated message retrieved from ISDS. The raw
11343 * member will be filled with PKCS#7 structure in DER format. */
11344 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
11345 const char *message_id
, struct isds_message
**message
) {
11346 return isds_get_signed_message(context
, 0, message_id
, message
);
11350 /* Download signed outgoing message identified by ID.
11351 * @context is session context
11352 * @message_id is message identifier (you can get them from
11353 * isds_get_list_of_sent_messages())
11354 * @message is automatically reallocated message retrieved from ISDS. The raw
11355 * member will be filled with PKCS#7 structure in DER format. */
11356 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
11357 const char *message_id
, struct isds_message
**message
) {
11358 return isds_get_signed_message(context
, 1, message_id
, message
);
11362 /* Get type and name of user who sent a message identified by ID.
11363 * @context is session context
11364 * @message_id is message identifier
11365 * @sender_type is pointer to automatically allocated type of sender detected
11366 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11367 * library or to the server, NULL will be returned. Pass NULL if you don't
11369 * @raw_sender_type is automatically reallocated UTF-8 string describing
11370 * sender type or NULL if not known to server. Pass NULL if you don't care.
11371 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11372 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11373 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
11374 const char *message_id
, isds_sender_type
**sender_type
,
11375 char **raw_sender_type
, char **sender_name
) {
11376 isds_error err
= IE_SUCCESS
;
11378 xmlDocPtr response
= NULL
;
11379 xmlChar
*code
= NULL
, *status_message
= NULL
;
11380 xmlXPathContextPtr xpath_ctx
= NULL
;
11381 xmlXPathObjectPtr result
= NULL
;
11382 char *type_string
= NULL
;
11385 if (!context
) return IE_INVALID_CONTEXT
;
11386 zfree(context
->long_message
);
11387 if (sender_type
) zfree(*sender_type
);
11388 if (raw_sender_type
) zfree(*raw_sender_type
);
11389 if (sender_name
) zfree(*sender_name
);
11390 if (!message_id
) return IE_INVAL
;
11393 /* Do request and check for success */
11394 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11395 BAD_CAST
"GetMessageAuthor",
11396 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11397 if (err
) goto leave
;
11400 xpath_ctx
= xmlXPathNewContext(response
);
11405 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11409 result
= xmlXPathEvalExpression(
11410 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
11415 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11416 isds_log_message(context
,
11417 _("Missing GetMessageAuthorResponse element"));
11421 if (result
->nodesetval
->nodeNr
> 1) {
11422 isds_log_message(context
,
11423 _("Multiple GetMessageAuthorResponse element"));
11427 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11428 xmlXPathFreeObject(result
); result
= NULL
;
11430 /* Fill output arguments in */
11431 EXTRACT_STRING("isds:userType", type_string
);
11432 if (NULL
!= type_string
) {
11433 if (NULL
!= sender_type
) {
11434 *sender_type
= calloc(1, sizeof(**sender_type
));
11435 if (NULL
== *sender_type
) {
11440 err
= string2isds_sender_type((xmlChar
*)type_string
,
11443 zfree(*sender_type
);
11444 if (err
== IE_ENUM
) {
11446 char *type_string_locale
= _isds_utf82locale(type_string
);
11447 isds_log(ILF_ISDS
, ILL_WARNING
,
11448 _("Unknown isds:userType value: %s"),
11449 type_string_locale
);
11450 free(type_string_locale
);
11455 if (NULL
!= sender_name
)
11456 EXTRACT_STRING("isds:authorName", *sender_name
);
11460 if (NULL
!= sender_type
) zfree(*sender_type
);
11461 zfree(type_string
);
11462 if (NULL
!= sender_name
) zfree(*sender_name
);
11464 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11466 xmlXPathFreeObject(result
);
11467 xmlXPathFreeContext(xpath_ctx
);
11470 free(status_message
);
11471 xmlFreeDoc(response
);
11474 isds_log(ILF_ISDS
, ILL_DEBUG
,
11475 _("GetMessageAuthor request processed by server "
11476 "successfully.\n"));
11477 #else /* not HAVE_LIBCURL */
11484 /* Retrieve hash of message identified by ID stored in ISDS.
11485 * @context is session context
11486 * @message_id is message identifier
11487 * @hash is automatically reallocated message hash downloaded from ISDS.
11488 * Message must exist in system and must not be deleted. */
11489 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11490 const char *message_id
, struct isds_hash
**hash
) {
11492 isds_error err
= IE_SUCCESS
;
11494 xmlDocPtr response
= NULL
;
11495 xmlChar
*code
= NULL
, *status_message
= NULL
;
11496 xmlXPathContextPtr xpath_ctx
= NULL
;
11497 xmlXPathObjectPtr result
= NULL
;
11500 if (!context
) return IE_INVALID_CONTEXT
;
11501 zfree(context
->long_message
);
11503 isds_hash_free(hash
);
11506 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11507 BAD_CAST
"VerifyMessage", message_id
,
11508 &response
, NULL
, NULL
, &code
, &status_message
);
11509 if (err
) goto leave
;
11513 xpath_ctx
= xmlXPathNewContext(response
);
11518 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11522 result
= xmlXPathEvalExpression(
11523 BAD_CAST
"/isds:VerifyMessageResponse",
11529 /* Empty response */
11530 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11531 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11532 isds_printf_message(context
,
11533 _("Server did not return any response for ID `%s' "
11534 "on VerifyMessage request"), message_id_locale
);
11535 free(message_id_locale
);
11539 /* More responses */
11540 if (result
->nodesetval
->nodeNr
> 1) {
11541 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11542 isds_printf_message(context
,
11543 _("Server did return more responses for ID `%s' "
11544 "on VerifyMessage request"), message_id_locale
);
11545 free(message_id_locale
);
11550 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11552 /* Extract the hash */
11553 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11557 isds_hash_free(hash
);
11560 xmlXPathFreeObject(result
);
11561 xmlXPathFreeContext(xpath_ctx
);
11564 free(status_message
);
11565 xmlFreeDoc(response
);
11568 isds_log(ILF_ISDS
, ILL_DEBUG
,
11569 _("VerifyMessage request processed by server "
11572 #else /* not HAVE_LIBCURL */
11579 /* Erase message specified by @message_id from long term storage. Other
11580 * message cannot be erased on user request.
11581 * @context is session context
11582 * @message_id is message identifier.
11583 * @incoming is true for incoming message, false for outgoing message.
11585 * IE_SUCCESS if message has ben removed
11586 * IE_INVAL if message does not exist in long term storage or message
11587 * belongs to different box
11588 * TODO: IE_NOEPRM if user has no permission to erase a message */
11589 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11590 const char *message_id
, _Bool incoming
) {
11591 isds_error err
= IE_SUCCESS
;
11593 xmlNodePtr request
= NULL
, node
;
11594 xmlNsPtr isds_ns
= NULL
;
11595 xmlDocPtr response
= NULL
;
11596 xmlChar
*code
= NULL
, *status_message
= NULL
;
11599 if (!context
) return IE_INVALID_CONTEXT
;
11600 zfree(context
->long_message
);
11601 if (NULL
== message_id
) return IE_INVAL
;
11604 /* Check if connection is established
11605 * TODO: This check should be done downstairs. */
11606 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11608 /* Build request */
11609 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11611 isds_log_message(context
,
11612 _("Could build EraseMessage request"));
11615 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11617 isds_log_message(context
, _("Could not create ISDS name space"));
11618 xmlFreeNode(request
);
11621 xmlSetNs(request
, isds_ns
);
11623 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11624 if (err
) goto leave
;
11625 INSERT_STRING(request
, "dmID", message_id
);
11627 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11631 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11632 "message ID %s to ISDS\n"), message_id
);
11633 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11634 xmlFreeNode(request
); request
= NULL
;
11637 isds_log(ILF_ISDS
, ILL_DEBUG
,
11638 _("Processing ISDS response on EraseMessage request "
11643 /* Check for response status */
11644 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11645 &code
, &status_message
, NULL
);
11647 isds_log(ILF_ISDS
, ILL_DEBUG
,
11648 _("ISDS response on EraseMessage request is missing "
11653 /* Check server status code */
11654 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11655 isds_log_message(context
, _("Message to erase belongs to other box"));
11657 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11658 isds_log_message(context
, _("Message to erase is not saved in "
11659 "long term storage or the direction does not match"));
11661 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11662 char *code_locale
= _isds_utf82locale((char*) code
);
11663 char *message_locale
= _isds_utf82locale((char*) status_message
);
11664 isds_log(ILF_ISDS
, ILL_DEBUG
,
11665 _("Server refused EraseMessage request "
11666 "(code=%s, message=%s)\n"),
11667 code_locale
, message_locale
);
11668 isds_log_message(context
, message_locale
);
11670 free(message_locale
);
11677 free(status_message
);
11678 xmlFreeDoc(response
);
11679 xmlFreeNode(request
);
11682 isds_log(ILF_ISDS
, ILL_DEBUG
,
11683 _("EraseMessage request processed by server "
11686 #else /* not HAVE_LIBCURL */
11693 /* Mark message as read. This is a transactional commit function to acknowledge
11694 * to ISDS the message has been downloaded and processed by client properly.
11695 * @context is session context
11696 * @message_id is message identifier. */
11697 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11698 const char *message_id
) {
11700 isds_error err
= IE_SUCCESS
;
11702 xmlDocPtr response
= NULL
;
11703 xmlChar
*code
= NULL
, *status_message
= NULL
;
11706 if (!context
) return IE_INVALID_CONTEXT
;
11707 zfree(context
->long_message
);
11710 /* Do request and check for success */
11711 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11712 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11713 &response
, NULL
, NULL
, &code
, &status_message
);
11716 free(status_message
);
11717 xmlFreeDoc(response
);
11720 isds_log(ILF_ISDS
, ILL_DEBUG
,
11721 _("MarkMessageAsDownloaded request processed by server "
11724 #else /* not HAVE_LIBCURL */
11731 /* Mark message as received by recipient. This is applicable only to
11732 * commercial message. Use envelope->dmType message member to distinguish
11733 * commercial message from government message. Government message is
11734 * received automatically (by law), commercial message on recipient request.
11735 * @context is session context
11736 * @message_id is message identifier. */
11737 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11738 const char *message_id
) {
11740 isds_error err
= IE_SUCCESS
;
11742 xmlDocPtr response
= NULL
;
11743 xmlChar
*code
= NULL
, *status_message
= NULL
;
11746 if (!context
) return IE_INVALID_CONTEXT
;
11747 zfree(context
->long_message
);
11750 /* Do request and check for success */
11751 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11752 BAD_CAST
"ConfirmDelivery", message_id
,
11753 &response
, NULL
, NULL
, &code
, &status_message
);
11756 free(status_message
);
11757 xmlFreeDoc(response
);
11760 isds_log(ILF_ISDS
, ILL_DEBUG
,
11761 _("ConfirmDelivery request processed by server "
11764 #else /* not HAVE_LIBCURL */
11771 /* Send document for authorized conversion into Czech POINT system.
11772 * This is public anonymous service, no log-in necessary. Special context is
11773 * used to reuse keep-a-live HTTPS connection.
11774 * @context is Czech POINT session context. DO NOT use context connected to
11775 * ISDS server. Use new context or context used by this function previously.
11776 * @document is document to convert. Only data, data_length, dmFileDescr and
11777 * is_xml members are significant. Be ware that not all document formats can be
11778 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11779 * @id is reallocated identifier assigned by Czech POINT system to
11780 * your document on submit. Use is to tell it to Czech POINT officer.
11781 * @date is reallocated document submit date (submitted documents
11782 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11784 isds_error
czp_convert_document(struct isds_ctx
*context
,
11785 const struct isds_document
*document
,
11786 char **id
, struct tm
**date
) {
11787 isds_error err
= IE_SUCCESS
;
11789 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11790 xmlNodePtr request
= NULL
, node
;
11791 xmlDocPtr response
= NULL
;
11793 xmlXPathContextPtr xpath_ctx
= NULL
;
11794 xmlXPathObjectPtr result
= NULL
;
11795 long int status
= -1;
11796 long int *status_ptr
= &status
;
11797 char *string
= NULL
;
11801 if (!context
) return IE_INVALID_CONTEXT
;
11802 zfree(context
->long_message
);
11803 if (!document
|| !id
|| !date
) return IE_INVAL
;
11805 if (document
->is_xml
) {
11806 isds_log_message(context
,
11807 _("XML documents cannot be submitted to conversion"));
11811 /* Free output arguments */
11816 /* Store configuration */
11817 context
->type
= CTX_TYPE_CZP
;
11818 free(context
->url
);
11819 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11820 if (!(context
->url
))
11823 /* Prepare CURL handle if not yet connected */
11824 if (!context
->curl
) {
11825 context
->curl
= curl_easy_init();
11826 if (!(context
->curl
))
11830 /* Build conversion request */
11831 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11833 isds_log_message(context
,
11834 _("Could not build Czech POINT conversion request"));
11837 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11839 isds_log_message(context
,
11840 _("Could not create Czech POINT deposit name space"));
11841 xmlFreeNode(request
);
11844 xmlSetNs(request
, deposit_ns
);
11846 /* Insert children. They are in empty namespace! */
11847 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11849 isds_log_message(context
, _("Could not create empty name space"));
11853 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11854 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11855 document
->dmFileDescr
);
11857 /* Document encoded in Base64 */
11858 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11859 document
->data
, document
->data_length
);
11860 if (err
) goto leave
;
11862 isds_log(ILF_ISDS
, ILL_DEBUG
,
11863 _("Submitting document for conversion into Czech POINT deposit"));
11865 /* Send conversion request */
11866 err
= _czp_czpdeposit(context
, request
, &response
);
11867 xmlFreeNode(request
); request
= NULL
;
11870 czp_do_close_connection(context
);
11875 /* Extract response */
11876 xpath_ctx
= xmlXPathNewContext(response
);
11881 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11885 result
= xmlXPathEvalExpression(
11886 BAD_CAST
"/deposit:saveDocumentResponse/return",
11892 /* Empty response */
11893 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11894 isds_printf_message(context
,
11895 _("Missing `return' element in Czech POINT deposit response"));
11899 /* More responses */
11900 if (result
->nodesetval
->nodeNr
> 1) {
11901 isds_printf_message(context
,
11902 _("Multiple `return' element in Czech POINT deposit response"));
11907 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11910 EXTRACT_LONGINT("status", status_ptr
, 1);
11912 EXTRACT_STRING("statusMsg", string
);
11913 char *string_locale
= _isds_utf82locale(string
);
11914 isds_printf_message(context
,
11915 _("Czech POINT deposit refused document for conversion "
11916 "(code=%ld, message=%s)"),
11917 status
, string_locale
);
11918 free(string_locale
);
11923 /* Get document ID */
11924 EXTRACT_STRING("documentID", *id
);
11926 /* Get submit date */
11927 EXTRACT_STRING("dateInserted", string
);
11929 *date
= calloc(1, sizeof(**date
));
11934 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11936 if (err
== IE_NOTSUP
) {
11938 char *string_locale
= _isds_utf82locale(string
);
11939 isds_printf_message(context
,
11940 _("Invalid dateInserted value: %s"), string_locale
);
11941 free(string_locale
);
11949 xmlXPathFreeObject(result
);
11950 xmlXPathFreeContext(xpath_ctx
);
11952 xmlFreeDoc(response
);
11953 xmlFreeNode(request
);
11956 char *id_locale
= _isds_utf82locale((char *) *id
);
11957 isds_log(ILF_ISDS
, ILL_DEBUG
,
11958 _("Document %s has been submitted for conversion "
11959 "to server successfully\n"), id_locale
);
11962 #else /* not HAVE_LIBCURL */
11969 /* Close possibly opened connection to Czech POINT document deposit.
11970 * @context is Czech POINT session context. */
11971 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11972 if (!context
) return IE_INVALID_CONTEXT
;
11973 zfree(context
->long_message
);
11975 return czp_do_close_connection(context
);
11982 /* Send request for new box creation in testing ISDS instance.
11983 * It's not possible to request for a production box currently, as it
11984 * communicates via e-mail.
11985 * XXX: This function does not work either. Server complains about invalid
11987 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11989 * @context is special session context for box creation request. DO NOT use
11990 * standard context as it could reveal your password. Use fresh new context or
11991 * context previously used by this function.
11992 * @box is box description to create including single primary user (in case of
11993 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11994 * ignored. It outputs box ID assigned by ISDS in dbID element.
11995 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11996 * box, or contact address of PFO box owner). The email member is mandatory as
11997 * it will be used to deliver credentials.
11998 * @former_names is former name of box owner. Pass NULL if you don't care.
11999 * @approval is optional external approval of box manipulation
12000 * @refnumber is reallocated serial number of request assigned by ISDS. Use
12001 * NULL, if you don't care.*/
12002 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
12003 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
12004 const char *former_names
, const struct isds_approval
*approval
,
12005 char **refnumber
) {
12006 isds_error err
= IE_SUCCESS
;
12008 xmlNodePtr request
= NULL
;
12009 xmlDocPtr response
= NULL
;
12010 xmlXPathContextPtr xpath_ctx
= NULL
;
12011 xmlXPathObjectPtr result
= NULL
;
12015 if (!context
) return IE_INVALID_CONTEXT
;
12016 zfree(context
->long_message
);
12017 if (!box
) return IE_INVAL
;
12020 if (!box
->email
|| box
->email
[0] == '\0') {
12021 isds_log_message(context
, _("E-mail field is mandatory"));
12025 /* Scratch box ID */
12028 /* Store configuration */
12029 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
12030 free(context
->url
);
12031 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
12032 if (!(context
->url
))
12035 /* Prepare CURL handle if not yet connected */
12036 if (!context
->curl
) {
12037 context
->curl
= curl_easy_init();
12038 if (!(context
->curl
))
12042 /* Build CreateDataBox request */
12043 err
= build_CreateDBInput_request(context
,
12044 &request
, BAD_CAST
"CreateDataBox",
12045 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
12046 if (err
) goto leave
;
12048 /* Send it to server and process response */
12049 err
= send_destroy_request_check_response(context
,
12050 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
12051 &response
, (xmlChar
**) refnumber
, NULL
);
12052 if (err
) goto leave
;
12054 /* Extract box ID */
12055 xpath_ctx
= xmlXPathNewContext(response
);
12060 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12064 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
12067 xmlXPathFreeObject(result
);
12068 xmlXPathFreeContext(xpath_ctx
);
12069 xmlFreeDoc(response
);
12070 xmlFreeNode(request
);
12073 isds_log(ILF_ISDS
, ILL_DEBUG
,
12074 _("CreateDataBox request processed by server successfully.\n"));
12076 #else /* not HAVE_LIBCURL */
12084 /* Submit CMS signed message to ISDS to verify its originality. This is
12085 * stronger form of isds_verify_message_hash() because ISDS does more checks
12086 * than simple one (potentialy old weak) hash comparison.
12087 * @context is session context
12088 * @message is memory with raw CMS signed message bit stream
12089 * @length is @message size in bytes
12091 * IE_SUCCESS if message originates in ISDS
12092 * IE_NOTEQUAL if message is unknown to ISDS
12093 * other code for other errors */
12094 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
12095 const void *message
, size_t length
) {
12096 isds_error err
= IE_SUCCESS
;
12098 xmlNsPtr isds_ns
= NULL
;
12099 xmlNodePtr request
= NULL
;
12100 xmlDocPtr response
= NULL
;
12101 xmlXPathContextPtr xpath_ctx
= NULL
;
12102 xmlXPathObjectPtr result
= NULL
;
12103 _Bool
*authentic
= NULL
;
12106 if (!context
) return IE_INVALID_CONTEXT
;
12107 zfree(context
->long_message
);
12108 if (!message
|| length
== 0) return IE_INVAL
;
12111 /* Check if connection is established
12112 * TODO: This check should be done downstairs. */
12113 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12116 /* Build AuthenticateMessage request */
12117 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
12119 isds_log_message(context
,
12120 _("Could not build AuthenticateMessage request"));
12123 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12125 isds_log_message(context
, _("Could not create ISDS name space"));
12126 xmlFreeNode(request
);
12129 xmlSetNs(request
, isds_ns
);
12131 /* Insert Base64 encoded message */
12132 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
12134 if (err
) goto leave
;
12136 /* Send request to server and process response */
12137 err
= send_destroy_request_check_response(context
,
12138 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
12139 &response
, NULL
, NULL
);
12140 if (err
) goto leave
;
12143 /* ISDS has decided */
12144 xpath_ctx
= xmlXPathNewContext(response
);
12149 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12154 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
12157 isds_log_message(context
,
12158 _("Server did not return any response on "
12159 "AuthenticateMessage request"));
12164 isds_log(ILF_ISDS
, ILL_DEBUG
,
12165 _("ISDS authenticated the message successfully\n"));
12167 isds_log_message(context
, _("ISDS does not know the message"));
12174 xmlXPathFreeObject(result
);
12175 xmlXPathFreeContext(xpath_ctx
);
12177 xmlFreeDoc(response
);
12178 xmlFreeNode(request
);
12179 #else /* not HAVE_LIBCURL */
12187 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12188 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12190 * @context is session context
12191 * @input_data is memory with raw CMS signed message or delivery info bit
12192 * stream to re-sign
12193 * @input_length is @input_data size in bytes
12194 * @output_data is pointer to auto-allocated memory where to store re-signed
12195 * input data blob. Caller must free it.
12196 * @output_data is pointer where to store @output_data size in bytes
12197 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12198 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12200 * IE_SUCCESS if CMS blob has been re-signed successfully
12201 * other code for other errors */
12202 isds_error
isds_resign_message(struct isds_ctx
*context
,
12203 const void *input_data
, size_t input_length
,
12204 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
12205 isds_error err
= IE_SUCCESS
;
12207 xmlNsPtr isds_ns
= NULL
;
12208 xmlNodePtr request
= NULL
;
12209 xmlDocPtr response
= NULL
;
12210 xmlXPathContextPtr xpath_ctx
= NULL
;
12211 xmlXPathObjectPtr result
= NULL
;
12212 char *string
= NULL
;
12213 const xmlChar
*codes
[] = {
12220 const char *meanings
[] = {
12222 "Message is not original",
12223 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12224 "Time stamp could not been generated in time"
12226 const isds_error errors
[] = {
12232 struct code_map_isds_error map
= {
12234 .meanings
= meanings
,
12239 if (NULL
!= output_data
) *output_data
= NULL
;
12240 if (NULL
!= output_length
) *output_length
= 0;
12241 if (NULL
!= valid_to
) *valid_to
= NULL
;
12243 if (NULL
== context
) return IE_INVALID_CONTEXT
;
12244 zfree(context
->long_message
);
12245 if (NULL
== input_data
|| 0 == input_length
) {
12246 isds_log_message(context
, _("Empty CMS blob on input"));
12249 if (NULL
== output_data
|| NULL
== output_length
) {
12250 isds_log_message(context
,
12251 _("NULL pointer provided for output CMS blob"));
12256 /* Check if connection is established
12257 * TODO: This check should be done downstairs. */
12258 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12261 /* Build Re-signISDSDocument request */
12262 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
12264 isds_log_message(context
,
12265 _("Could not build Re-signISDSDocument request"));
12268 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12270 isds_log_message(context
, _("Could not create ISDS name space"));
12271 xmlFreeNode(request
);
12274 xmlSetNs(request
, isds_ns
);
12276 /* Insert Base64 encoded CMS blob */
12277 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
12278 input_data
, input_length
);
12279 if (err
) goto leave
;
12281 /* Send request to server and process response */
12282 err
= send_destroy_request_check_response(context
,
12283 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
12284 &response
, NULL
, &map
);
12285 if (err
) goto leave
;
12288 /* Extract re-signed data */
12289 xpath_ctx
= xmlXPathNewContext(response
);
12294 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12298 result
= xmlXPathEvalExpression(
12299 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
12304 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
12305 isds_log_message(context
,
12306 _("Missing Re-signISDSDocumentResponse element"));
12310 if (result
->nodesetval
->nodeNr
> 1) {
12311 isds_log_message(context
,
12312 _("Multiple Re-signISDSDocumentResponse element"));
12316 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
12317 xmlXPathFreeObject(result
); result
= NULL
;
12319 EXTRACT_STRING("isds:dmResultDoc", string
);
12320 /* Decode non-empty data */
12321 if (NULL
!= string
&& string
[0] != '\0') {
12322 *output_length
= _isds_b64decode(string
, output_data
);
12323 if (*output_length
== (size_t) -1) {
12324 isds_log_message(context
,
12325 _("Error while Base64-decoding re-signed data"));
12330 isds_log_message(context
, _("Server did not send re-signed data"));
12336 if (NULL
!= valid_to
) {
12337 /* Get time stamp expiration date */
12338 EXTRACT_STRING("isds:dmValidTo", string
);
12339 if (NULL
!= string
) {
12340 *valid_to
= calloc(1, sizeof(**valid_to
));
12345 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
12347 if (err
== IE_NOTSUP
) {
12349 char *string_locale
= _isds_utf82locale(string
);
12350 isds_printf_message(context
,
12351 _("Invalid dmValidTo value: %s"), string_locale
);
12352 free(string_locale
);
12362 xmlXPathFreeObject(result
);
12363 xmlXPathFreeContext(xpath_ctx
);
12365 xmlFreeDoc(response
);
12366 xmlFreeNode(request
);
12367 #else /* not HAVE_LIBCURL */
12374 #undef INSERT_ELEMENT
12375 #undef CHECK_FOR_STRING_LENGTH
12376 #undef INSERT_STRING_ATTRIBUTE
12377 #undef INSERT_ULONGINTNOPTR
12378 #undef INSERT_ULONGINT
12379 #undef INSERT_LONGINT
12380 #undef INSERT_BOOLEAN
12381 #undef INSERT_SCALAR_BOOLEAN
12382 #undef INSERT_STRING
12383 #undef INSERT_STRING_WITH_NS
12384 #undef EXTRACT_STRING_ATTRIBUTE
12385 #undef EXTRACT_ULONGINT
12386 #undef EXTRACT_LONGINT
12387 #undef EXTRACT_BOOLEAN
12388 #undef EXTRACT_STRING
12391 /* Compute hash of message from raw representation and store it into envelope.
12392 * Original hash structure will be destroyed in envelope.
12393 * @context is session context
12394 * @message is message carrying raw XML message blob
12395 * @algorithm is desired hash algorithm to use */
12396 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
12397 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
12398 isds_error err
= IE_SUCCESS
;
12400 void *xml_stream
= NULL
;
12401 size_t xml_stream_length
;
12402 size_t phys_start
, phys_end
;
12403 char *phys_path
= NULL
;
12404 struct isds_hash
*new_hash
= NULL
;
12407 if (!context
) return IE_INVALID_CONTEXT
;
12408 zfree(context
->long_message
);
12409 if (!message
) return IE_INVAL
;
12411 if (!message
->raw
) {
12412 isds_log_message(context
,
12413 _("Message does not carry raw representation"));
12417 switch (message
->raw_type
) {
12418 case RAWTYPE_INCOMING_MESSAGE
:
12420 xml_stream
= message
->raw
;
12421 xml_stream_length
= message
->raw_length
;
12424 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
12425 nsuri
= SISDS_INCOMING_NS
;
12426 xml_stream
= message
->raw
;
12427 xml_stream_length
= message
->raw_length
;
12430 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
12431 nsuri
= SISDS_INCOMING_NS
;
12432 err
= _isds_extract_cms_data(context
,
12433 message
->raw
, message
->raw_length
,
12434 &xml_stream
, &xml_stream_length
);
12435 if (err
) goto leave
;
12438 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
12439 nsuri
= SISDS_OUTGOING_NS
;
12440 xml_stream
= message
->raw
;
12441 xml_stream_length
= message
->raw_length
;
12444 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
12445 nsuri
= SISDS_OUTGOING_NS
;
12446 err
= _isds_extract_cms_data(context
,
12447 message
->raw
, message
->raw_length
,
12448 &xml_stream
, &xml_stream_length
);
12449 if (err
) goto leave
;
12453 isds_log_message(context
, _("Bad raw representation type"));
12459 /* XXX: Hash is computed from original string representing isds:dmDm
12460 * subtree. That means no encoding, white space, xmlns attributes changes.
12461 * In other words, input for hash can be invalid XML stream. */
12462 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12463 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12464 PHYSXML_ELEMENT_SEPARATOR
,
12465 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12466 PHYSXML_ELEMENT_SEPARATOR
12467 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12471 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12472 phys_path
, &phys_start
, &phys_end
);
12475 isds_log_message(context
,
12476 _("Substring with isds:dmDM element could not be located "
12477 "in raw message"));
12483 new_hash
= calloc(1, sizeof(*new_hash
));
12488 new_hash
->algorithm
= algorithm
;
12489 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12492 isds_log_message(context
, _("Could not compute message hash"));
12496 /* Save computed hash */
12497 if (!message
->envelope
) {
12498 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12499 if (!message
->envelope
) {
12504 isds_hash_free(&message
->envelope
->hash
);
12505 message
->envelope
->hash
= new_hash
;
12509 isds_hash_free(&new_hash
);
12513 if (xml_stream
!= message
->raw
) free(xml_stream
);
12518 /* Compare two hashes.
12519 * @h1 is first hash
12520 * @h2 is another hash
12522 * IE_SUCCESS if hashes equal
12523 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12524 * IE_ENUM if not comparable, but both structures defined
12525 * IE_INVAL if some of the structures are undefined (NULL)
12526 * IE_ERROR if internal error occurs */
12527 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12528 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12529 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12530 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12531 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12532 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12534 for (size_t i
= 0; i
< h1
->length
; i
++) {
12535 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12536 return IE_NOTEQUAL
;
12542 /* Check message has gone through ISDS by comparing message hash stored in
12543 * ISDS and locally computed hash. You must provide message with valid raw
12544 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12545 * This is convenient wrapper for isds_download_message_hash(),
12546 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12547 * @context is session context
12548 * @message is message with valid raw and envelope member; envelope->hash
12549 * member will be changed during function run. Use envelope on heap only.
12551 * IE_SUCCESS if message originates in ISDS
12552 * IE_NOTEQUAL if message is unknown to ISDS
12553 * other code for other errors */
12554 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12555 struct isds_message
*message
) {
12556 isds_error err
= IE_SUCCESS
;
12557 struct isds_hash
*downloaded_hash
= NULL
;
12559 if (!context
) return IE_INVALID_CONTEXT
;
12560 zfree(context
->long_message
);
12561 if (!message
) return IE_INVAL
;
12563 if (!message
->envelope
) {
12564 isds_log_message(context
,
12565 _("Given message structure is missing envelope"));
12568 if (!message
->raw
) {
12569 isds_log_message(context
,
12570 _("Given message structure is missing raw representation"));
12574 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12576 if (err
) goto leave
;
12578 err
= isds_compute_message_hash(context
, message
,
12579 downloaded_hash
->algorithm
);
12580 if (err
) goto leave
;
12582 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12585 isds_hash_free(&downloaded_hash
);
12590 /* Search for document by document ID in list of documents. IDs are compared
12592 * @documents is list of isds_documents
12593 * @id is document identifier
12594 * @return first matching document or NULL. */
12595 const struct isds_document
*isds_find_document_by_id(
12596 const struct isds_list
*documents
, const char *id
) {
12597 const struct isds_list
*item
;
12598 const struct isds_document
*document
;
12600 for (item
= documents
; item
; item
= item
->next
) {
12601 document
= (struct isds_document
*) item
->data
;
12602 if (!document
) continue;
12604 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12612 /* Normalize @mime_type to be proper MIME type.
12613 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12614 * guess regular MIME type (e.g. "application/pdf").
12615 * @mime_type is UTF-8 encoded MIME type to fix
12616 * @return original @mime_type if no better interpretation exists, or
12617 * constant static UTF-8 encoded string with proper MIME type. */
12618 const char *isds_normalize_mime_type(const char *mime_type
) {
12619 if (!mime_type
) return NULL
;
12621 for (size_t offset
= 0;
12622 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12624 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12625 extension_map_mime
[offset
]))
12626 return (const char *) extension_map_mime
[offset
+ 1];
12633 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12634 struct isds_message **message);
12635 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12636 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12637 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12638 struct isds_address **address);
12640 int isds_message_free(struct isds_message **message);
12641 int isds_address_free(struct isds_address **address);
12645 /* Makes known all relevant namespaces to given XPath context
12646 * @xpath_ctx is XPath context
12647 * @message_ns selects proper message name space. Unsigned and signed
12648 * messages and delivery info's differ in prefix and URI. */
12649 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12650 const message_ns_type message_ns
) {
12651 const xmlChar
*message_namespace
= NULL
;
12653 if (!xpath_ctx
) return IE_ERROR
;
12655 switch(message_ns
) {
12657 message_namespace
= BAD_CAST ISDS1_NS
; break;
12658 case MESSAGE_NS_UNSIGNED
:
12659 message_namespace
= BAD_CAST ISDS_NS
; break;
12660 case MESSAGE_NS_SIGNED_INCOMING
:
12661 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12662 case MESSAGE_NS_SIGNED_OUTGOING
:
12663 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12664 case MESSAGE_NS_SIGNED_DELIVERY
:
12665 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12670 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12672 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12674 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12676 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12678 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12680 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))