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/";
35 const char isds_mep_locator
[] = "https://www.mojedatovaschranka.cz/";
37 /* Base URL of production ISDS instance */
38 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
39 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
40 const char isds_otp_testing_locator
[] = "https://www.czebox.cz/";
41 const char isds_mep_testing_locator
[] = "https://www.czebox.cz/";
43 /* Extension to MIME type map */
44 static const xmlChar
*extension_map_mime
[] = {
45 BAD_CAST
"cer", BAD_CAST
"application/x-x509-ca-cert",
46 BAD_CAST
"crt", BAD_CAST
"application/x-x509-ca-cert",
47 BAD_CAST
"der", BAD_CAST
"application/x-x509-ca-cert",
48 BAD_CAST
"doc", BAD_CAST
"application/msword",
49 BAD_CAST
"docx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
50 "wordprocessingml.document",
51 BAD_CAST
"dbf", BAD_CAST
"application/octet-stream",
52 BAD_CAST
"prj", BAD_CAST
"application/octet-stream",
53 BAD_CAST
"qix", BAD_CAST
"application/octet-stream",
54 BAD_CAST
"sbn", BAD_CAST
"application/octet-stream",
55 BAD_CAST
"sbx", BAD_CAST
"application/octet-stream",
56 BAD_CAST
"shp", BAD_CAST
"application/octet-stream",
57 BAD_CAST
"shx", BAD_CAST
"application/octet-stream",
58 BAD_CAST
"dgn", BAD_CAST
"application/octet-stream",
59 BAD_CAST
"dwg", BAD_CAST
"image/vnd.dwg",
60 BAD_CAST
"edi", BAD_CAST
"application/edifact",
61 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.form+xml",
62 BAD_CAST
"gfs", BAD_CAST
"application/xml",
63 BAD_CAST
"gml", BAD_CAST
"application/xml",
64 BAD_CAST
"gif", BAD_CAST
"image/gif",
65 BAD_CAST
"htm", BAD_CAST
"text/html",
66 BAD_CAST
"html", BAD_CAST
"text/html",
67 BAD_CAST
"isdoc", BAD_CAST
"text/isdoc",
68 BAD_CAST
"isdocx", BAD_CAST
"text/isdocx",
69 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
70 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
71 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
72 BAD_CAST
"mpeg", BAD_CAST
"video/mpeg",
73 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
74 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg",
75 BAD_CAST
"mpg", BAD_CAST
"video/mpeg",
76 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
77 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
78 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
79 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
80 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
81 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
82 BAD_CAST
"p7b", BAD_CAST
"application/pkcs7-certificates",
83 BAD_CAST
"p7c", BAD_CAST
"application/pkcs7-mime",
84 BAD_CAST
"p7m", BAD_CAST
"application/pkcs7-mime",
85 BAD_CAST
"p7f", BAD_CAST
"application/pkcs7-signature",
86 BAD_CAST
"p7s", BAD_CAST
"application/pkcs7-signature",
87 BAD_CAST
"pk7", BAD_CAST
"application/pkcs7-mime",
88 BAD_CAST
"png", BAD_CAST
"image/png",
89 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
90 BAD_CAST
"pptx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
91 "presentationml.presentation",
92 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
93 BAD_CAST
"tif", BAD_CAST
"image/tiff",
94 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
95 BAD_CAST
"tsr", BAD_CAST
"application/timestamp-reply",
96 BAD_CAST
"tst", BAD_CAST
"application/timestamp-reply",
97 BAD_CAST
"txt", BAD_CAST
"text/plain",
98 BAD_CAST
"wav", BAD_CAST
"audio/wav",
99 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
100 BAD_CAST
"xlsx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
101 "spreadsheetml.sheet",
102 BAD_CAST
"xml", BAD_CAST
"application/xml",
103 BAD_CAST
"xsd", BAD_CAST
"application/xml",
104 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.form-xml-zip"
107 /* Structure type to hold conversion table from status code to isds_error and
109 struct code_map_isds_error
{
110 const xmlChar
**codes
; /* NULL terminated array of status codes */
111 const char **meanings
; /* Mapping to non-localized long messages */
112 const isds_error
*errors
; /* Mapping to isds_error code */
115 /* Deallocate structure isds_pki_credentials and NULL it.
116 * Pass-phrase is discarded.
117 * @pki credentials to to free */
118 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
119 if(!pki
|| !*pki
) return;
121 free((*pki
)->engine
);
122 free((*pki
)->certificate
);
125 if ((*pki
)->passphrase
) {
126 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
127 free((*pki
)->passphrase
);
134 /* Free isds_list with all member data.
135 * @list list to free, on return will be NULL */
136 void isds_list_free(struct isds_list
**list
) {
137 struct isds_list
*item
, *next_item
;
139 if (!list
|| !*list
) return;
141 for(item
= *list
; item
; item
= next_item
) {
142 if (item
->destructor
) (item
->destructor
)(&(item
->data
));
143 next_item
= item
->next
;
151 /* Deallocate structure isds_hash and NULL it.
152 * @hash hash to to free */
153 void isds_hash_free(struct isds_hash
**hash
) {
154 if(!hash
|| !*hash
) return;
155 free((*hash
)->value
);
160 /* Deallocate structure isds_PersonName recursively and NULL it */
161 void isds_PersonName_free(struct isds_PersonName
**person_name
) {
162 if (!person_name
|| !*person_name
) return;
164 free((*person_name
)->pnFirstName
);
165 free((*person_name
)->pnMiddleName
);
166 free((*person_name
)->pnLastName
);
167 free((*person_name
)->pnLastNameAtBirth
);
174 /* Deallocate structure isds_BirthInfo recursively and NULL it */
175 void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
176 if (!birth_info
|| !*birth_info
) return;
178 free((*birth_info
)->biDate
);
179 free((*birth_info
)->biCity
);
180 free((*birth_info
)->biCounty
);
181 free((*birth_info
)->biState
);
188 /* Deallocate structure isds_Address recursively and NULL it */
189 void isds_Address_free(struct isds_Address
**address
) {
190 if (!address
|| !*address
) return;
192 free((*address
)->adCode
);
193 free((*address
)->adCity
);
194 free((*address
)->adDistrict
);
195 free((*address
)->adStreet
);
196 free((*address
)->adNumberInStreet
);
197 free((*address
)->adNumberInMunicipality
);
198 free((*address
)->adZipCode
);
199 free((*address
)->adState
);
206 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
207 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
208 if (!db_owner_info
|| !*db_owner_info
) return;
210 free((*db_owner_info
)->dbID
);
211 free((*db_owner_info
)->dbType
);
212 free((*db_owner_info
)->ic
);
213 isds_PersonName_free(&((*db_owner_info
)->personName
));
214 free((*db_owner_info
)->firmName
);
215 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
216 isds_Address_free(&((*db_owner_info
)->address
));
217 free((*db_owner_info
)->nationality
);
218 free((*db_owner_info
)->email
);
219 free((*db_owner_info
)->telNumber
);
220 free((*db_owner_info
)->identifier
);
221 free((*db_owner_info
)->aifoIsds
);
222 free((*db_owner_info
)->registryCode
);
223 free((*db_owner_info
)->dbState
);
224 free((*db_owner_info
)->dbEffectiveOVM
);
225 free((*db_owner_info
)->dbOpenAddressing
);
227 free(*db_owner_info
);
228 *db_owner_info
= NULL
;
231 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
232 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
233 if (!db_user_info
|| !*db_user_info
) return;
235 free((*db_user_info
)->userID
);
236 free((*db_user_info
)->userType
);
237 free((*db_user_info
)->userPrivils
);
238 isds_PersonName_free(&((*db_user_info
)->personName
));
239 isds_Address_free(&((*db_user_info
)->address
));
240 free((*db_user_info
)->biDate
);
241 free((*db_user_info
)->ic
);
242 free((*db_user_info
)->firmName
);
243 free((*db_user_info
)->caStreet
);
244 free((*db_user_info
)->caCity
);
245 free((*db_user_info
)->caZipCode
);
246 free((*db_user_info
)->caState
);
247 free((*db_user_info
)->aifo_ticket
);
249 zfree(*db_user_info
);
253 /* Deallocate struct isds_event recursively and NULL it */
254 void isds_event_free(struct isds_event
**event
) {
255 if (!event
|| !*event
) return;
257 free((*event
)->time
);
258 free((*event
)->type
);
259 free((*event
)->description
);
264 /* Deallocate struct isds_envelope recursively and NULL it */
265 void isds_envelope_free(struct isds_envelope
**envelope
) {
266 if (!envelope
|| !*envelope
) return;
268 free((*envelope
)->dmID
);
269 free((*envelope
)->dbIDSender
);
270 free((*envelope
)->dmSender
);
271 free((*envelope
)->dmSenderAddress
);
272 free((*envelope
)->dmSenderType
);
273 free((*envelope
)->dmRecipient
);
274 free((*envelope
)->dmRecipientAddress
);
275 free((*envelope
)->dmAmbiguousRecipient
);
277 free((*envelope
)->dmOrdinal
);
278 free((*envelope
)->dmMessageStatus
);
279 free((*envelope
)->dmAttachmentSize
);
280 free((*envelope
)->dmDeliveryTime
);
281 free((*envelope
)->dmAcceptanceTime
);
282 isds_hash_free(&(*envelope
)->hash
);
283 free((*envelope
)->timestamp
);
284 isds_list_free(&(*envelope
)->events
);
286 free((*envelope
)->dmSenderOrgUnit
);
287 free((*envelope
)->dmSenderOrgUnitNum
);
288 free((*envelope
)->dbIDRecipient
);
289 free((*envelope
)->dmRecipientOrgUnit
);
290 free((*envelope
)->dmRecipientOrgUnitNum
);
291 free((*envelope
)->dmToHands
);
292 free((*envelope
)->dmAnnotation
);
293 free((*envelope
)->dmRecipientRefNumber
);
294 free((*envelope
)->dmSenderRefNumber
);
295 free((*envelope
)->dmRecipientIdent
);
296 free((*envelope
)->dmSenderIdent
);
298 free((*envelope
)->dmLegalTitleLaw
);
299 free((*envelope
)->dmLegalTitleYear
);
300 free((*envelope
)->dmLegalTitleSect
);
301 free((*envelope
)->dmLegalTitlePar
);
302 free((*envelope
)->dmLegalTitlePoint
);
304 free((*envelope
)->dmPersonalDelivery
);
305 free((*envelope
)->dmAllowSubstDelivery
);
306 free((*envelope
)->dmType
);
308 free((*envelope
)->dmOVM
);
309 free((*envelope
)->dmPublishOwnID
);
316 /* Deallocate struct isds_message recursively and NULL it */
317 void isds_message_free(struct isds_message
**message
) {
318 if (!message
|| !*message
) return;
320 free((*message
)->raw
);
321 isds_envelope_free(&((*message
)->envelope
));
322 isds_list_free(&((*message
)->documents
));
323 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
330 /* Deallocate struct isds_document recursively and NULL it */
331 void isds_document_free(struct isds_document
**document
) {
332 if (!document
|| !*document
) return;
334 if (!(*document
)->is_xml
) {
335 free((*document
)->data
);
337 free((*document
)->dmMimeType
);
338 free((*document
)->dmFileGuid
);
339 free((*document
)->dmUpFileGuid
);
340 free((*document
)->dmFileDescr
);
341 free((*document
)->dmFormat
);
348 /* Deallocate struct isds_message_copy recursively and NULL it */
349 void isds_message_copy_free(struct isds_message_copy
**copy
) {
350 if (!copy
|| !*copy
) return;
352 free((*copy
)->dbIDRecipient
);
353 free((*copy
)->dmRecipientOrgUnit
);
354 free((*copy
)->dmRecipientOrgUnitNum
);
355 free((*copy
)->dmToHands
);
357 free((*copy
)->dmStatus
);
364 /* Deallocate struct isds_message_status_change recursively and NULL it */
365 void isds_message_status_change_free(
366 struct isds_message_status_change
**message_status_change
) {
367 if (!message_status_change
|| !*message_status_change
) return;
369 free((*message_status_change
)->dmID
);
370 free((*message_status_change
)->time
);
371 free((*message_status_change
)->dmMessageStatus
);
373 zfree(*message_status_change
);
377 /* Deallocate struct isds_approval recursively and NULL it */
378 void isds_approval_free(struct isds_approval
**approval
) {
379 if (!approval
|| !*approval
) return;
381 free((*approval
)->refference
);
387 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
388 * The email string is deallocated too. */
389 void isds_credentials_delivery_free(
390 struct isds_credentials_delivery
**credentials_delivery
) {
391 if (!credentials_delivery
|| !*credentials_delivery
) return;
393 free((*credentials_delivery
)->email
);
394 free((*credentials_delivery
)->token
);
395 free((*credentials_delivery
)->new_user_name
);
397 zfree(*credentials_delivery
);
401 /* Deallocate struct isds_commercial_permission recursively and NULL it */
402 void isds_commercial_permission_free(
403 struct isds_commercial_permission
**permission
) {
404 if (NULL
== permission
|| NULL
== *permission
) return;
406 free((*permission
)->recipient
);
407 free((*permission
)->payer
);
408 free((*permission
)->expiration
);
409 free((*permission
)->count
);
410 free((*permission
)->reply_identifier
);
416 /* Deallocate struct isds_credit_event recursively and NULL it */
417 void isds_credit_event_free(struct isds_credit_event
**event
) {
418 if (NULL
== event
|| NULL
== *event
) return;
420 free((*event
)->time
);
421 switch ((*event
)->type
) {
422 case ISDS_CREDIT_CHARGED
:
423 free((*event
)->details
.charged
.transaction
);
425 case ISDS_CREDIT_DISCHARGED
:
426 free((*event
)->details
.discharged
.transaction
);
428 case ISDS_CREDIT_MESSAGE_SENT
:
429 free((*event
)->details
.message_sent
.recipient
);
430 free((*event
)->details
.message_sent
.message_id
);
432 case ISDS_CREDIT_STORAGE_SET
:
433 free((*event
)->details
.storage_set
.new_valid_from
);
434 free((*event
)->details
.storage_set
.new_valid_to
);
435 free((*event
)->details
.storage_set
.old_capacity
);
436 free((*event
)->details
.storage_set
.old_valid_from
);
437 free((*event
)->details
.storage_set
.old_valid_to
);
438 free((*event
)->details
.storage_set
.initiator
);
440 case ISDS_CREDIT_EXPIRED
:
448 /* Deallocate struct isds_fulltext_result recursively and NULL it */
449 void isds_fulltext_result_free(
450 struct isds_fulltext_result
**result
) {
451 if (NULL
== result
|| NULL
== *result
) return;
453 free((*result
)->dbID
);
454 free((*result
)->name
);
455 isds_list_free(&((*result
)->name_match_start
));
456 isds_list_free(&((*result
)->name_match_end
));
457 free((*result
)->address
);
458 isds_list_free(&((*result
)->address_match_start
));
459 isds_list_free(&((*result
)->address_match_end
));
461 free((*result
)->biDate
);
467 /* Deallocate struct isds_box_state_period recursively and NULL it */
468 void isds_box_state_period_free(struct isds_box_state_period
**period
) {
469 if (NULL
== period
|| NULL
== *period
) return;
474 /* *DUP_OR_ERROR macros needs error label */
475 #define STRDUP_OR_ERROR(new, template) { \
479 (new) = strdup(template); \
480 if (!new) goto error; \
484 #define FLATDUP_OR_ERROR(new, template) { \
488 (new) = malloc(sizeof(*(new))); \
489 if (!new) goto error; \
490 memcpy((new), (template), sizeof(*(template))); \
494 /* Copy structure isds_pki_credentials recursively. */
495 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
496 const struct isds_pki_credentials
*template) {
497 struct isds_pki_credentials
*new = NULL
;
499 if(!template) return NULL
;
501 new = calloc(1, sizeof(*new));
502 if (!new) return NULL
;
504 STRDUP_OR_ERROR(new->engine
, template->engine
);
505 new->certificate_format
= template->certificate_format
;
506 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
507 new->key_format
= template->key_format
;
508 STRDUP_OR_ERROR(new->key
, template->key
);
509 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
514 isds_pki_credentials_free(&new);
519 /* Copy structure isds_PersonName recursively */
520 struct isds_PersonName
*isds_PersonName_duplicate(
521 const struct isds_PersonName
*src
) {
522 struct isds_PersonName
*new = NULL
;
524 if (!src
) return NULL
;
526 new = calloc(1, sizeof(*new));
527 if (!new) return NULL
;
529 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
530 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
531 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
532 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
537 isds_PersonName_free(&new);
542 /* Copy structure isds_BirthInfo recursively */
543 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
544 const struct isds_BirthInfo
*template) {
545 struct isds_BirthInfo
*new = NULL
;
547 if (!template) return NULL
;
549 new = calloc(1, sizeof(*new));
550 if (!new) return NULL
;
552 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
553 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
554 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
555 STRDUP_OR_ERROR(new->biState
, template->biState
);
560 isds_BirthInfo_free(&new);
565 /* Copy structure isds_Address recursively */
566 struct isds_Address
*isds_Address_duplicate(
567 const struct isds_Address
*src
) {
568 struct isds_Address
*new = NULL
;
570 if (!src
) return NULL
;
572 new = calloc(1, sizeof(*new));
573 if (!new) return NULL
;
575 FLATDUP_OR_ERROR(new->adCode
, src
->adCode
);
576 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
577 STRDUP_OR_ERROR(new->adDistrict
, src
->adDistrict
);
578 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
579 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
580 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
581 src
->adNumberInMunicipality
);
582 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
583 STRDUP_OR_ERROR(new->adState
, src
->adState
);
588 isds_Address_free(&new);
593 /* Copy structure isds_DbOwnerInfo recursively */
594 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
595 const struct isds_DbOwnerInfo
*src
) {
596 struct isds_DbOwnerInfo
*new = NULL
;
597 if (!src
) return NULL
;
599 new = calloc(1, sizeof(*new));
600 if (!new) return NULL
;
602 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
603 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
604 STRDUP_OR_ERROR(new->ic
, src
->ic
);
606 if (src
->personName
) {
607 if (!(new->personName
=
608 isds_PersonName_duplicate(src
->personName
)))
612 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
614 if (src
->birthInfo
) {
615 if (!(new->birthInfo
=
616 isds_BirthInfo_duplicate(src
->birthInfo
)))
621 if (!(new->address
= isds_Address_duplicate(src
->address
)))
625 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
626 STRDUP_OR_ERROR(new->email
, src
->email
);
627 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
628 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
629 FLATDUP_OR_ERROR(new->aifoIsds
, src
->aifoIsds
);
630 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
631 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
632 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
633 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
638 isds_DbOwnerInfo_free(&new);
643 /* Copy structure isds_DbUserInfo recursively */
644 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
645 const struct isds_DbUserInfo
*src
) {
646 struct isds_DbUserInfo
*new = NULL
;
647 if (!src
) return NULL
;
649 new = calloc(1, sizeof(*new));
650 if (!new) return NULL
;
652 STRDUP_OR_ERROR(new->userID
, src
->userID
);
653 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
654 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
656 if (src
->personName
) {
657 if (!(new->personName
=
658 isds_PersonName_duplicate(src
->personName
)))
663 if (!(new->address
= isds_Address_duplicate(src
->address
)))
667 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
668 STRDUP_OR_ERROR(new->ic
, src
->ic
);
669 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
670 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
671 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
672 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
673 STRDUP_OR_ERROR(new->caState
, src
->caState
);
674 STRDUP_OR_ERROR(new->aifo_ticket
, src
->aifo_ticket
);
679 isds_DbUserInfo_free(&new);
684 /* Copy structure isds_box_state_period recursively */
685 struct isds_box_state_period
*isds_box_state_period_duplicate(
686 const struct isds_box_state_period
*src
) {
687 struct isds_box_state_period
*new = NULL
;
688 if (!src
) return NULL
;
690 new = calloc(1, sizeof(*new));
691 if (!new) return NULL
;
693 memcpy(&new->from
, &src
->from
, sizeof(src
->from
));
694 memcpy(&new->to
, &src
->to
, sizeof(src
->to
));
695 new->dbState
= src
->dbState
;
700 #undef FLATDUP_OR_ERROR
701 #undef STRDUP_OR_ERROR
704 /* Logs libxml2 errors. Should be registered to libxml2 library.
705 * @ctx is unused currently
706 * @msg is printf-like formated message from libxml2 (UTF-8?)
707 * @... are variadic arguments for @msg */
708 static void log_xml(void *ctx
, const char *msg
, ...) {
712 /* Silent warning for unused function argument.
713 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
719 isds_vasprintf(&text
, msg
, ap
);
723 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
728 /* Initialize ISDS library.
729 * Global function, must be called before other functions.
730 * If it fails you can not use ISDS library and must call isds_cleanup() to
731 * free partially initialized global variables. */
732 isds_error
isds_init(void) {
733 /* NULL global variables */
734 log_facilities
= ILF_ALL
;
735 log_level
= ILL_WARNING
;
737 log_callback_data
= NULL
;
740 /* Initialize gettext */
741 bindtextdomain(PACKAGE
, LOCALEDIR
);
745 /* Initialize CURL */
746 if (curl_global_init(CURL_GLOBAL_ALL
)) {
747 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
750 #endif /* HAVE_LIBCURL */
752 /* Initialise cryptographic back-ends. */
753 if (IE_SUCCESS
!= _isds_init_crypto()) {
754 isds_log(ILF_ISDS
, ILL_CRIT
,
755 _("Initialization of cryptographic back-end failed\n"));
759 /* This can _exit() current program. Find not so assertive check. */
761 xmlSetGenericErrorFunc(NULL
, log_xml
);
764 if (_isds_init_expat(&version_expat
)) {
765 isds_log(ILF_ISDS
, ILL_CRIT
,
766 _("expat library initialization failed\n"));
770 /* Allocate global variables */
777 /* Deinitialize ISDS library.
778 * Global function, must be called as last library function. */
779 isds_error
isds_cleanup(void) {
785 curl_global_cleanup();
792 /* Return version string of this library. Version of dependencies can be
793 * embedded. Do no try to parse it. You must free it. */
794 char *isds_version(void) {
797 isds_asprintf(&buffer
,
799 # ifndef USE_OPENSSL_BACKEND
800 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
802 _("%s (%s, %s, %s, libxml2 %s)"),
805 # ifndef USE_OPENSSL_BACKEND
806 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
808 _("%s (%s, %s, libxml2 %s)"),
815 #ifndef USE_OPENSSL_BACKEND
816 version_gpgme
, version_gcrypt
,
820 version_expat
, xmlParserVersion
);
825 /* Return text description of ISDS error */
826 const char *isds_strerror(const isds_error error
) {
829 return(_("Success")); break;
831 return(_("Unspecified error")); break;
833 return(_("Not supported")); break;
835 return(_("Invalid value")); break;
836 case IE_INVALID_CONTEXT
:
837 return(_("Invalid context")); break;
838 case IE_NOT_LOGGED_IN
:
839 return(_("Not logged in")); break;
840 case IE_CONNECTION_CLOSED
:
841 return(_("Connection closed")); break;
843 return(_("Timed out")); break;
845 return(_("Not exist")); break;
847 return(_("Out of memory")); break;
849 return(_("Network problem")); break;
851 return(_("HTTP problem")); break;
853 return(_("SOAP problem")); break;
855 return(_("XML problem")); break;
857 return(_("ISDS server problem")); break;
859 return(_("Invalid enum value")); break;
861 return(_("Invalid date value")); break;
863 return(_("Too big")); break;
865 return(_("Too small")); break;
867 return(_("Value not unique")); break;
869 return(_("Values not equal")); break;
870 case IE_PARTIAL_SUCCESS
:
871 return(_("Some suboperations failed")); break;
873 return(_("Operation aborted")); break;
875 return(_("Security problem")); break;
877 return(_("Unknown error"));
882 /* Create ISDS context.
883 * Each context can be used for different sessions to (possibly) different
884 * ISDS server with different credentials. */
885 struct isds_ctx
*isds_ctx_create(void) {
886 struct isds_ctx
*context
;
887 context
= malloc(sizeof(*context
));
888 if (context
) memset(context
, 0, sizeof(*context
));
893 /* Close possibly opened connection to Czech POINT document deposit without
894 * resetting long_message buffer.
895 * XXX: Do not use czp_close_connection() if you do not want to destroy log
897 * @context is Czech POINT session context. */
898 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
899 if (!context
) return IE_INVALID_CONTEXT
;
900 _isds_close_connection(context
);
905 /* Discard credentials.
906 * @context is ISDS context
907 * @discard_saved_username is true for removing saved username, false for
909 * Only that. It does not cause log out, connection close or similar. */
910 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
911 _Bool discard_saved_username
) {
912 if(!context
) return IE_INVALID_CONTEXT
;
914 if (context
->username
) {
915 memset(context
->username
, 0, strlen(context
->username
));
916 zfree(context
->username
);
918 if (context
->password
) {
919 memset(context
->password
, 0, strlen(context
->password
));
920 zfree(context
->password
);
922 isds_pki_credentials_free(&context
->pki_credentials
);
923 if (discard_saved_username
&& context
->saved_username
) {
924 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
925 zfree(context
->saved_username
);
930 #endif /* HAVE_LIBCURL */
933 /* Destroy ISDS context and free memory.
934 * @context will be NULLed on success. */
935 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
936 if (!context
|| !*context
) {
937 return IE_INVALID_CONTEXT
;
941 /* Discard credentials and close connection */
942 switch ((*context
)->type
) {
943 case CTX_TYPE_NONE
: break;
944 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
946 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
947 czp_do_close_connection(*context
); break;
951 _isds_discard_credentials(*context
, 1);
953 /* Free other structures */
954 free((*context
)->url
);
955 free((*context
)->tls_verify_server
);
956 free((*context
)->tls_ca_file
);
957 free((*context
)->tls_ca_dir
);
958 free((*context
)->tls_crl_file
);
959 #endif /* HAVE_LIBCURL */
960 free((*context
)->long_message
);
968 /* Return long message text produced by library function, e.g. detailed error
969 * message. Returned pointer is only valid until new library function is
970 * called for the same context. Could be NULL, especially if NULL context is
971 * supplied. Return string is locale encoded. */
972 char *isds_long_message(const struct isds_ctx
*context
) {
973 if (!context
) return NULL
;
974 return context
->long_message
;
978 /* Stores message into context' long_message buffer.
979 * Application can pick the message up using isds_long_message().
980 * NULL @message truncates the buffer but does not deallocate it.
981 * @message is coded in locale encoding */
982 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
983 const char *message
) {
987 if (!context
) return IE_INVALID_CONTEXT
;
989 /* FIXME: Check for integer overflow */
990 length
= 1 + ((message
) ? strlen(message
) : 0);
991 buffer
= realloc(context
->long_message
, length
);
992 if (!buffer
) return IE_NOMEM
;
995 strcpy(buffer
, message
);
999 context
->long_message
= buffer
;
1004 /* Appends message into context' long_message buffer.
1005 * Application can pick the message up using isds_long_message().
1006 * NULL message has void effect. */
1007 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
1008 const char *message
) {
1010 size_t old_length
, length
;
1012 if (!context
) return IE_INVALID_CONTEXT
;
1013 if (!message
) return IE_SUCCESS
;
1014 if (!context
->long_message
)
1015 return isds_log_message(context
, message
);
1017 old_length
= strlen(context
->long_message
);
1018 /* FIXME: Check for integer overflow */
1019 length
= 1 + old_length
+ strlen(message
);
1020 buffer
= realloc(context
->long_message
, length
);
1021 if (!buffer
) return IE_NOMEM
;
1023 strcpy(buffer
+ old_length
, message
);
1025 context
->long_message
= buffer
;
1030 /* Stores formatted message into context' long_message buffer.
1031 * Application can pick the message up using isds_long_message(). */
1032 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
1033 const char *format
, ...) {
1037 if (!context
) return IE_INVALID_CONTEXT
;
1038 va_start(ap
, format
);
1039 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
1042 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
1047 * @facilities is bit mask of isds_log_facility values,
1048 * @level is verbosity level. */
1049 void isds_set_logging(const unsigned int facilities
,
1050 const isds_log_level level
) {
1051 log_facilities
= facilities
;
1056 /* Register callback function libisds calls when new global log message is
1057 * produced by library. Library logs to stderr by default.
1058 * @callback is function provided by application libisds will call. See type
1059 * definition for @callback argument explanation. Pass NULL to revert logging to
1060 * default behaviour.
1061 * @data is application specific data @callback gets as last argument */
1062 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1063 log_callback
= callback
;
1064 log_callback_data
= data
;
1068 /* Log @message in class @facility with log @level into global log. @message
1069 * is printf(3) formatting string, variadic arguments may be necessary.
1070 * For debugging purposes. */
1071 _hidden isds_error
isds_log(const isds_log_facility facility
,
1072 const isds_log_level level
, const char *message
, ...) {
1074 char *buffer
= NULL
;
1077 if (level
> log_level
) return IE_SUCCESS
;
1078 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1079 if (!message
) return IE_INVAL
;
1082 /* Pass message to application supplied callback function */
1083 va_start(ap
, message
);
1084 length
= isds_vasprintf(&buffer
, message
, ap
);
1091 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1095 /* Default: Log it to stderr */
1096 va_start(ap
, message
);
1097 vfprintf(stderr
, message
, ap
);
1099 /* Line buffered printf is default.
1107 /* Set timeout in milliseconds for each network job like connecting to server
1108 * or sending message. Use 0 to disable timeout limits. */
1109 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1110 const unsigned int timeout
) {
1111 if (!context
) return IE_INVALID_CONTEXT
;
1112 zfree(context
->long_message
);
1115 context
->timeout
= timeout
;
1117 if (context
->curl
) {
1120 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1122 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1123 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1126 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1127 context
->timeout
/ 1000);
1128 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1129 if (curl_err
) return IE_ERROR
;
1133 #else /* not HAVE_LIBCURL */
1139 /* Register callback function libisds calls periodically during HTTP data
1141 * @context is session context
1142 * @callback is function provided by application libisds will call. See type
1143 * definition for @callback argument explanation.
1144 * @data is application specific data @callback gets as last argument */
1145 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1146 isds_progress_callback callback
, void *data
) {
1147 if (!context
) return IE_INVALID_CONTEXT
;
1148 zfree(context
->long_message
);
1151 context
->progress_callback
= callback
;
1152 context
->progress_callback_data
= data
;
1155 #else /* not HAVE_LIBCURL */
1161 /* Change context settings.
1162 * @context is context which setting will be applied to
1163 * @option is name of option. It determines the type of last argument. See
1164 * isds_option definition for more info.
1165 * @... is value of new setting. Type is determined by @option
1167 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1169 isds_error err
= IE_SUCCESS
;
1172 char *pointer
, *string
;
1175 if (!context
) return IE_INVALID_CONTEXT
;
1176 zfree(context
->long_message
);
1178 va_start(ap
, option
);
1180 #define REPLACE_VA_BOOLEAN(destination) { \
1181 if (!(destination)) { \
1182 (destination) = malloc(sizeof(*(destination))); \
1183 if (!(destination)) { \
1184 err = IE_NOMEM; goto leave; \
1187 *(destination) = (_Bool) !!va_arg(ap, int); \
1190 #define REPLACE_VA_STRING(destination) { \
1191 string = va_arg(ap, char *); \
1193 pointer = realloc((destination), 1 + strlen(string)); \
1194 if (!pointer) { err = IE_NOMEM; goto leave; } \
1195 strcpy(pointer, string); \
1196 (destination) = pointer; \
1198 free(destination); \
1199 (destination) = NULL; \
1204 case IOPT_TLS_VERIFY_SERVER
:
1206 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1208 err
= IE_NOTSUP
; goto leave
;
1211 case IOPT_TLS_CA_FILE
:
1213 REPLACE_VA_STRING(context
->tls_ca_file
);
1215 err
= IE_NOTSUP
; goto leave
;
1218 case IOPT_TLS_CA_DIRECTORY
:
1220 REPLACE_VA_STRING(context
->tls_ca_dir
);
1222 err
= IE_NOTSUP
; goto leave
;
1225 case IOPT_TLS_CRL_FILE
:
1227 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1228 REPLACE_VA_STRING(context
->tls_crl_file
);
1230 isds_log_message(context
,
1231 _("Curl library does not support CRL definition"));
1233 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1235 err
= IE_NOTSUP
; goto leave
;
1236 #endif /* not HAVE_LIBCURL */
1238 case IOPT_NORMALIZE_MIME_TYPE
:
1239 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1243 err
= IE_ENUM
; goto leave
;
1246 #undef REPLACE_VA_STRING
1247 #undef REPLACE_VA_BOOLEAN
1256 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1257 * Destination for NULL argument will not be touched.
1258 * Destination pointers must be freed before calling this function.
1259 * If @username is @context->saved_username, the saved_username will not be
1260 * replaced. The saved_username is clobbered only if context has set otp
1262 * Return IE_SUCCESS on success. */
1263 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1264 const char *username
, const char *password
,
1265 const struct isds_pki_credentials
*pki_credentials
) {
1266 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1268 /* FIXME: mlock password
1269 * (I have a library) */
1272 context
->username
= strdup(username
);
1273 if (context
->otp
&& context
->saved_username
!= username
)
1274 context
->saved_username
= strdup(username
);
1277 if (NULL
== context
->otp_credentials
)
1278 context
->password
= strdup(password
);
1280 context
->password
= _isds_astrcat(password
,
1281 context
->otp_credentials
->otp_code
);
1283 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1285 if ((NULL
!= username
&& NULL
== context
->username
) ||
1286 (NULL
!= password
&& NULL
== context
->password
) ||
1287 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1288 (context
->otp
&& NULL
!= context
->username
&&
1289 NULL
== context
->saved_username
)) {
1298 /* Connect and log into ISDS server.
1299 * All required arguments will be copied, you do not have to keep them after
1301 * ISDS supports six different authentication methods. Exact method is
1302 * selected on @username, @password, @pki_credentials, and @otp arguments:
1303 * - If @pki_credentials == NULL, @username and @password must be supplied
1305 * - If @otp == NULL, simple authentication by username and password will
1307 * - If @otp != NULL, authentication by username and password and OTP
1309 * - If @pki_credentials != NULL, then
1310 * - If @username == NULL, only certificate will be used
1311 * - If @username != NULL, then
1312 * - If @password == NULL, then certificate will be used and
1313 * @username shifts meaning to box ID. This is used for hosted
1315 * - Otherwise all three arguments will be used.
1316 * Please note, that different cases require different certificate type
1317 * (system qualified one or commercial non qualified one). This library
1318 * does not check such political issues. Please see ISDS Specification
1320 * @url is base address of ISDS web service. Pass extern isds_locator
1321 * variable to use production ISDS instance without client certificate
1322 * authentication (or extern isds_cert_locator with client certificate
1323 * authentication or extern isds_otp_locators with OTP authentication).
1324 * Passing NULL has the same effect, autoselection between isds_locator,
1325 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1326 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1327 * isds_otp_testing_locator) variable to select testing instance.
1328 * @username is user name of ISDS user or box ID
1329 * @password is user's secret password
1330 * @pki_credentials defines public key cryptographic material to use in client
1332 * @otp selects one-time password authentication method to use, defines OTP
1333 * code (if known) and returns fine grade resolution of OTP procedure.
1335 * IE_SUCCESS if authentication succeeds
1336 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1337 * requested, fine grade reason will be set into @otp->resolution. Error
1338 * message from server can be obtained by isds_long_message() call.
1339 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1340 * server has sent OTP code through side channel. Application is expected to
1341 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1342 * this call to complete second phase of TOTP authentication;
1343 * or other appropriate error. */
1344 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1345 const char *username
, const char *password
,
1346 const struct isds_pki_credentials
*pki_credentials
,
1347 struct isds_otp
*otp
) {
1349 isds_error err
= IE_NOT_LOGGED_IN
;
1350 isds_error soap_err
;
1351 xmlNsPtr isds_ns
= NULL
;
1352 xmlNodePtr request
= NULL
;
1353 #endif /* HAVE_LIBCURL */
1355 if (!context
) return IE_INVALID_CONTEXT
;
1356 zfree(context
->long_message
);
1359 /* Close connection if already logged in */
1360 if (context
->curl
) {
1361 _isds_close_connection(context
);
1364 /* Store configuration */
1365 context
->type
= CTX_TYPE_ISDS
;
1366 zfree(context
->url
);
1368 /* Mangle base URI according to requested authentication method */
1369 if (NULL
== pki_credentials
) {
1370 isds_log(ILF_SEC
, ILL_INFO
,
1371 _("Selected authentication method: no certificate, "
1372 "username and password\n"));
1373 if (!username
|| !password
) {
1374 isds_log_message(context
,
1375 _("Both username and password must be supplied"));
1378 context
->otp_credentials
= otp
;
1379 context
->otp
= (NULL
!= context
->otp_credentials
);
1381 if (!context
->otp
) {
1382 /* Default locator is official system (without certificate or
1384 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1386 const char *authenticator_uri
= NULL
;
1387 if (!url
) url
= isds_otp_locator
;
1388 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1389 switch (context
->otp_credentials
->method
) {
1391 isds_log(ILF_SEC
, ILL_INFO
,
1392 _("Selected authentication method: "
1393 "HMAC-based one-time password\n"));
1395 "%sas/processLogin?type=hotp&uri=%sapps/";
1398 isds_log(ILF_SEC
, ILL_INFO
,
1399 _("Selected authentication method: "
1400 "Time-based one-time password\n"));
1401 if (context
->otp_credentials
->otp_code
== NULL
) {
1402 isds_log(ILF_SEC
, ILL_INFO
,
1403 _("OTP code has not been provided by "
1404 "application, requesting server for "
1407 "%sas/processLogin?type=totp&sendSms=true&"
1410 isds_log(ILF_SEC
, ILL_INFO
,
1411 _("OTP code has been provided by "
1412 "application, not requesting server "
1415 "%sas/processLogin?type=totp&"
1420 isds_log_message(context
,
1421 _("Unknown one-time password authentication "
1422 "method requested by application"));
1425 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
, url
))
1429 /* Default locator is official system (with client certificate) */
1431 context
->otp_credentials
= NULL
;
1432 if (!url
) url
= isds_cert_locator
;
1435 isds_log(ILF_SEC
, ILL_INFO
,
1436 _("Selected authentication method: system certificate, "
1437 "no username and no password\n"));
1439 context
->url
= _isds_astrcat(url
, "cert/");
1442 isds_log(ILF_SEC
, ILL_INFO
,
1443 _("Selected authentication method: system certificate, "
1444 "box ID and no password\n"));
1445 context
->url
= _isds_astrcat(url
, "hspis/");
1447 isds_log(ILF_SEC
, ILL_INFO
,
1448 _("Selected authentication method: commercial "
1449 "certificate, username and password\n"));
1450 context
->url
= _isds_astrcat(url
, "certds/");
1454 if (!(context
->url
))
1457 /* Prepare CURL handle */
1458 context
->curl
= curl_easy_init();
1459 if (!(context
->curl
))
1462 /* Build log-in request */
1463 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1465 isds_log_message(context
, _("Could not build ISDS log-in request"));
1468 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1470 isds_log_message(context
, _("Could not create ISDS name space"));
1471 xmlFreeNode(request
);
1474 xmlSetNs(request
, isds_ns
);
1476 /* Store credentials */
1477 _isds_discard_credentials(context
, 1);
1478 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1479 _isds_discard_credentials(context
, 1);
1480 xmlFreeNode(request
);
1484 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1487 /* XXX: ISDS documentation does not specify response body for
1488 * DummyOperation request. However real server sends back
1489 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1490 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1491 * SOAP body content, e.g. the dmStatus element. */
1493 /* Send log-in request */
1494 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1497 /* Revert context URL from OTP authentication service URL to OTP web
1498 * service base URL for subsequent calls. Potential isds_login() retry
1499 * will re-set context URL again. */
1500 zfree(context
->url
);
1501 context
->url
= _isds_astrcat(url
, "apps/");
1502 if (context
->url
== NULL
) {
1503 soap_err
= IE_NOMEM
;
1505 /* Detach pointer to OTP credentials from context */
1506 context
->otp_credentials
= NULL
;
1509 /* Remove credentials */
1510 _isds_discard_credentials(context
, 0);
1512 /* Destroy log-in request */
1513 xmlFreeNode(request
);
1516 _isds_close_connection(context
);
1520 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1521 * authentication succeeded if soap_err == IE_SUCCESS */
1525 isds_log(ILF_ISDS
, ILL_DEBUG
,
1526 _("User %s has been logged into server %s successfully\n"),
1529 #else /* not HAVE_LIBCURL */
1535 /* Connect and log into ISDS server using the MEP login method.
1536 * All arguments are copied, you don't have to keep them after successful
1538 * @url is base address of ISDS web service. Pass extern isds_mep_locator to use
1539 * the production ISDS environment (pass extern isds_mep_testing_locator to
1540 * access the testing environment). Passing null causes the production
1541 * environment locator to be used.
1542 * @username is the username of ISDS user or box ID
1543 * @code is the communication code. The code is generated when enabling
1544 * the mobile key authentication and can be found in the web-based portal
1545 * of the data-box service.
1547 * IE_SUCCESS if authentication succeeds
1548 * IE_NOT_LOGGED_IN if authentication fails
1549 * IE_PARTIAL_SUCCESS if MEP authentication has been requested, fine-grade
1550 * resolution is returned via @mep->resolution, keep arguments unchanged and
1551 * repeat the function call as long as IE_PARTIAL_SUCCESS is being returned;
1552 * or other appropriate error. */
1553 isds_error
isds_login_mep(struct isds_ctx
*context
, const char *url
,
1554 const char *username
, const char *code
, struct isds_mep
*mep
) {
1556 isds_error err
= IE_NOT_LOGGED_IN
;
1557 isds_error soap_err
;
1558 xmlNsPtr isds_ns
= NULL
;
1559 xmlNodePtr request
= NULL
;
1560 #endif /* HAVE_LIBCURL */
1562 if (NULL
== context
) {
1563 return IE_INVALID_CONTEXT
;
1565 zfree(context
->long_message
);
1568 context
->type
= CTX_TYPE_ISDS
;
1570 if ((NULL
!= username
) && (NULL
!= code
) && (NULL
!= mep
)) {
1571 isds_log(ILF_SEC
, ILL_INFO
,
1572 _("Selected authentication method: username and mobile key\n"));
1574 isds_log_message(context
,
1575 "Username, communication code and mep context must be supplied.\n");
1578 /* Close connection if already logged in, but don't close the connection
1579 * if continuing to negotiate MEP authentication.*/
1580 if ((NULL
!= context
->curl
) && (NULL
== mep
->intermediate_uri
)) {
1581 _isds_close_connection(context
);
1584 context
->mep_credentials
= mep
;
1585 context
->mep
= (NULL
!= context
->mep_credentials
);
1589 url
= isds_mep_locator
;
1591 mep
->resolution
= MEP_RESOLUTION_UNKNOWN
;
1592 const char *authenticator_uri
=
1593 "%sas/processLogin?type=mep-ws&applicationName=%s&"
1595 const char *app_name
= context
->mep_credentials
->app_name
;
1596 if (NULL
== app_name
) {
1599 char *escaped_app_name
= curl_easy_escape(context
->curl
, app_name
, 0);
1600 if (NULL
== escaped_app_name
) {
1603 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
,
1604 escaped_app_name
, url
)) {
1605 curl_free(escaped_app_name
);
1608 curl_free(escaped_app_name
);
1610 if (NULL
== context
->url
) {
1614 /* Prepare CURL handle */
1615 if (NULL
== context
->curl
) {
1616 context
->curl
= curl_easy_init();
1618 if (NULL
== context
->curl
) {
1622 /* Build log-in request */
1623 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1624 if (NULL
== request
) {
1625 isds_log_message(context
, _("Could not build ISDS log-in request"));
1628 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1629 if(NULL
== isds_ns
) {
1630 isds_log_message(context
, _("Could not create ISDS name space"));
1631 xmlFreeNode(request
);
1634 xmlSetNs(request
, isds_ns
);
1636 /* Store credentials, use mobile key code for password. */
1637 _isds_discard_credentials(context
, 1);
1638 if (IE_SUCCESS
!= _isds_store_credentials(context
, username
, code
, NULL
)) {
1639 _isds_discard_credentials(context
, 1);
1640 xmlFreeNode(request
);
1644 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1647 /* XXX: ISDS documentation does not specify response body for
1648 * DummyOperation request. However real server sends back
1649 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1650 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1651 * SOAP body content, e.g. the dmStatus element. */
1653 /* Send log-in request */
1654 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1657 /* Revert context URL from mobile key authentication service to web
1658 * service base URL for subsequent calls. */
1659 zfree(context
->url
);
1660 context
->url
= _isds_astrcat(url
, "apps/");
1661 if (context
->url
== NULL
) {
1662 soap_err
= IE_NOMEM
;
1664 /* Detach credentials pointer from context. */
1665 context
->mep_credentials
= NULL
;
1668 /* Remove credentials */
1669 _isds_discard_credentials(context
, 0);
1671 /* Destroy log-in request */
1672 xmlFreeNode(request
);
1674 if ((IE_SUCCESS
!= soap_err
) && (context
->mep
&& (IE_PARTIAL_SUCCESS
!= soap_err
))) {
1675 /* Don't close connection when using MEP authentication. */
1676 _isds_close_connection(context
);
1680 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1681 * authentication succeeded if soap_err == IE_SUCCESS */
1684 if (IE_SUCCESS
== err
) {
1685 isds_log(ILF_ISDS
, ILL_DEBUG
,
1686 _("User %s has been logged into server %s successfully\n"),
1690 #else /* not HAVE_LIBCURL */
1696 /* Log out from ISDS server discards credentials and connection configuration. */
1697 isds_error
isds_logout(struct isds_ctx
*context
) {
1698 if (!context
) return IE_INVALID_CONTEXT
;
1699 zfree(context
->long_message
);
1702 if (context
->curl
) {
1703 if (context
->otp
|| context
->mep
) {
1704 isds_error err
= _isds_invalidate_otp_cookie(context
);
1705 if (err
) return err
;
1708 /* Close connection */
1709 _isds_close_connection(context
);
1711 /* Discard credentials for sure. They should not survive isds_login(),
1712 * even successful .*/
1713 _isds_discard_credentials(context
, 1);
1715 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1717 _isds_discard_credentials(context
, 1);
1719 zfree(context
->url
);
1721 #else /* not HAVE_LIBCURL */
1727 /* Verify connection to ISDS is alive and server is responding.
1728 * Send dummy request to ISDS and expect dummy response. */
1729 isds_error
isds_ping(struct isds_ctx
*context
) {
1731 isds_error soap_err
;
1732 xmlNsPtr isds_ns
= NULL
;
1733 xmlNodePtr request
= NULL
;
1734 #endif /* HAVE_LIBCURL */
1736 if (!context
) return IE_INVALID_CONTEXT
;
1737 zfree(context
->long_message
);
1740 /* Check if connection is established */
1741 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1744 /* Build dummy request */
1745 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1747 isds_log_message(context
, _("Could build ISDS dummy request"));
1750 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1752 isds_log_message(context
, _("Could not create ISDS name space"));
1753 xmlFreeNode(request
);
1756 xmlSetNs(request
, isds_ns
);
1758 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1760 /* XXX: ISDS documentation does not specify response body for
1761 * DummyOperation request. However real server sends back
1762 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1763 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1764 * SOAP body content, e.g. the dmStatus element. */
1766 /* Send dummy request */
1767 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1769 /* Destroy log-in request */
1770 xmlFreeNode(request
);
1773 isds_log(ILF_ISDS
, ILL_DEBUG
,
1774 _("ISDS server could not be contacted\n"));
1778 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1779 * authentication succeeded if soap_err == IE_SUCCESS */
1782 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1785 #else /* not HAVE_LIBCURL */
1791 /* Send bogus request to ISDS.
1792 * Just for test purposes */
1793 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1796 xmlNsPtr isds_ns
= NULL
;
1797 xmlNodePtr request
= NULL
;
1798 xmlDocPtr response
= NULL
;
1799 xmlChar
*code
= NULL
, *message
= NULL
;
1802 if (!context
) return IE_INVALID_CONTEXT
;
1803 zfree(context
->long_message
);
1806 /* Check if connection is established */
1807 if (!context
->curl
) {
1808 /* Testing printf message */
1809 isds_printf_message(context
, "%s", _("I said connection closed"));
1810 return IE_CONNECTION_CLOSED
;
1814 /* Build dummy request */
1815 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1817 isds_log_message(context
, _("Could build ISDS bogus request"));
1820 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1822 isds_log_message(context
, _("Could not create ISDS name space"));
1823 xmlFreeNode(request
);
1826 xmlSetNs(request
, isds_ns
);
1828 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1830 /* Sent bogus request */
1831 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1833 /* Destroy request */
1834 xmlFreeNode(request
);
1837 isds_log(ILF_ISDS
, ILL_DEBUG
,
1838 _("Processing ISDS response on bogus request failed\n"));
1839 xmlFreeDoc(response
);
1843 /* Check for response status */
1844 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1845 &code
, &message
, NULL
);
1847 isds_log(ILF_ISDS
, ILL_DEBUG
,
1848 _("ISDS response on bogus request is missing status\n"));
1851 xmlFreeDoc(response
);
1854 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1855 char *code_locale
= _isds_utf82locale((char*)code
);
1856 char *message_locale
= _isds_utf82locale((char*)message
);
1857 isds_log(ILF_ISDS
, ILL_DEBUG
,
1858 _("Server refused bogus request (code=%s, message=%s)\n"),
1859 code_locale
, message_locale
);
1860 /* XXX: Literal error messages from ISDS are Czech messages
1861 * (English sometimes) in UTF-8. It's hard to catch them for
1862 * translation. Successfully gettextized would return in locale
1863 * encoding, unsuccessfully translated would pass in UTF-8. */
1864 isds_log_message(context
, message_locale
);
1866 free(message_locale
);
1869 xmlFreeDoc(response
);
1876 xmlFreeDoc(response
);
1878 isds_log(ILF_ISDS
, ILL_DEBUG
,
1879 _("Bogus message accepted by server. This should not happen.\n"));
1882 #else /* not HAVE_LIBCURL */
1889 /* Serialize XML subtree to buffer preserving XML indentation.
1890 * @context is session context
1891 * @subtree is XML element to be serialized (with children)
1892 * @buffer is automatically reallocated buffer where serialize to
1893 * @length is size of serialized stream in bytes
1894 * @return standard error code, free @buffer in case of error */
1895 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1896 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1897 isds_error err
= IE_SUCCESS
;
1898 xmlBufferPtr xml_buffer
= NULL
;
1899 xmlSaveCtxtPtr save_ctx
= NULL
;
1900 xmlDocPtr subtree_doc
= NULL
;
1901 xmlNodePtr subtree_copy
;
1905 if (!context
) return IE_INVALID_CONTEXT
;
1906 if (!buffer
) return IE_INVAL
;
1908 if (!subtree
|| !length
) return IE_INVAL
;
1910 /* Make temporary XML document with @subtree root element */
1911 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1912 * It can result in not well-formed on invalid XML tree (e.g. name space
1913 * prefix definition can miss. */
1916 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1918 isds_log_message(context
, _("Could not build temporary document"));
1923 /* XXX: Copy subtree and attach the copy to document.
1924 * One node can not bee attached into more document at the same time.
1925 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1927 * XXX: Check xmlSaveTree() too. */
1928 subtree_copy
= xmlCopyNodeList(subtree
);
1929 if (!subtree_copy
) {
1930 isds_log_message(context
, _("Could not copy subtree"));
1934 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1936 /* Only this way we get namespace definition as @xmlns:isds,
1937 * otherwise we get namespace prefix without definition */
1938 /* FIXME: Don't overwrite original default namespace */
1939 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1941 isds_log_message(context
, _("Could not create ISDS name space"));
1945 xmlSetNs(subtree_copy
, isds_ns
);
1948 /* Serialize the document into buffer */
1949 xml_buffer
= xmlBufferCreate();
1951 isds_log_message(context
, _("Could not create xmlBuffer"));
1955 /* Last argument 0 means to not format the XML tree */
1956 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1958 isds_log_message(context
, _("Could not create XML serializer"));
1962 /* XXX: According LibXML documentation, this function does not return
1963 * meaningful value yet */
1964 xmlSaveDoc(save_ctx
, subtree_doc
);
1965 if (-1 == xmlSaveFlush(save_ctx
)) {
1966 isds_log_message(context
,
1967 _("Could not serialize XML subtree"));
1971 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1972 * even after xmlSaveFlush(). Thus close it here */
1973 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1976 /* Store and detach buffer from xml_buffer */
1977 *buffer
= xml_buffer
->content
;
1978 *length
= xml_buffer
->use
;
1979 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1982 new_buffer
= realloc(*buffer
, *length
);
1983 if (new_buffer
) *buffer
= new_buffer
;
1991 xmlSaveClose(save_ctx
);
1992 xmlBufferFree(xml_buffer
);
1993 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1996 #endif /* HAVE_LIBCURL */
2000 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
2001 * @context is session context
2002 * @document is original document where @nodeset points to
2003 * @nodeset is XPath node set to dump (recursively)
2004 * @buffer is automatically reallocated buffer where serialize to
2005 * @length is size of serialized stream in bytes
2006 * @return standard error code, free @buffer in case of error */
2007 static isds_error
dump_nodeset(struct isds_ctx
*context
,
2008 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
2009 void **buffer
, size_t *length
) {
2010 isds_error err
= IE_SUCCESS
;
2011 xmlBufferPtr xml_buffer
= NULL
;
2014 if (!context
) return IE_INVALID_CONTEXT
;
2015 if (!buffer
) return IE_INVAL
;
2017 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
2020 /* Empty node set results into NULL buffer */
2021 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
2025 /* Resulting the document into buffer */
2026 xml_buffer
= xmlBufferCreate();
2028 isds_log_message(context
, _("Could not create xmlBuffer"));
2033 /* Iterate over all nodes */
2034 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
2036 * XXX: xmlNodeDump() appends to xml_buffer. */
2038 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
2039 isds_log_message(context
, _("Could not dump XML node"));
2045 /* Store and detach buffer from xml_buffer */
2046 *buffer
= xml_buffer
->content
;
2047 *length
= xml_buffer
->use
;
2048 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
2051 new_buffer
= realloc(*buffer
, *length
);
2052 if (new_buffer
) *buffer
= new_buffer
;
2061 xmlBufferFree(xml_buffer
);
2067 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
2068 * @context is session context
2069 * @document is original document where @nodeset points to
2070 * @nodeset is XPath node set to dump (recursively)
2071 * @buffer is automatically reallocated buffer where serialize to
2072 * @length is size of serialized stream in bytes
2073 * @return standard error code, free @buffer in case of error */
2074 static isds_error
dump_nodeset(struct isds_ctx
*context
,
2075 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
2076 void **buffer
, size_t *length
) {
2077 isds_error err
= IE_SUCCESS
;
2078 xmlBufferPtr xml_buffer
= NULL
;
2079 xmlSaveCtxtPtr save_ctx
= NULL
;
2082 if (!context
) return IE_INVALID_CONTEXT
;
2083 if (!buffer
) return IE_INVAL
;
2085 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
2088 /* Empty node set results into NULL buffer */
2089 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
2093 /* Resulting the document into buffer */
2094 xml_buffer
= xmlBufferCreate();
2096 isds_log_message(context
, _("Could not create xmlBuffer"));
2100 if (xmlSubstituteEntitiesDefault(1)) {
2101 isds_log_message(context
, _("Could not disable attribute escaping"));
2105 /* Last argument means:
2106 * 0 to not format the XML tree
2107 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
2108 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
2109 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
2111 isds_log_message(context
, _("Could not create XML serializer"));
2115 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
2116 isds_log_message(context, _("Could not disable attribute escaping"));
2122 /* Iterate over all nodes */
2123 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
2125 * XXX: xmlNodeDump() appends to xml_buffer. */
2127 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
2129 /* XXX: According LibXML documentation, this function does not return
2130 * meaningful value yet */
2131 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
2132 if (-1 == xmlSaveFlush(save_ctx
)) {
2133 isds_log_message(context
,
2134 _("Could not serialize XML subtree"));
2140 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
2141 * even after xmlSaveFlush(). Thus close it here */
2142 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
2144 /* Store and detach buffer from xml_buffer */
2145 *buffer
= xml_buffer
->content
;
2146 *length
= xml_buffer
->use
;
2147 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
2150 new_buffer
= realloc(*buffer
, *length
);
2151 if (new_buffer
) *buffer
= new_buffer
;
2159 xmlSaveClose(save_ctx
);
2160 xmlBufferFree(xml_buffer
);
2167 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
2168 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
2169 if (!string
|| !type
) return IE_INVAL
;
2171 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
2173 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
2175 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
2176 *type
= DBTYPE_PFO_ADVOK
;
2177 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
2178 *type
= DBTYPE_PFO_DANPOR
;
2179 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
2180 *type
= DBTYPE_PFO_INSSPR
;
2181 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_AUDITOR"))
2182 *type
= DBTYPE_PFO_AUDITOR
;
2183 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
2185 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
2186 *type
= DBTYPE_PO_ZAK
;
2187 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
2188 *type
= DBTYPE_PO_REQ
;
2189 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
2191 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
2192 *type
= DBTYPE_OVM_NOTAR
;
2193 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
2194 *type
= DBTYPE_OVM_EXEKUT
;
2195 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
2196 *type
= DBTYPE_OVM_REQ
;
2197 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_FO"))
2198 *type
= DBTYPE_OVM_FO
;
2199 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_PFO"))
2200 *type
= DBTYPE_OVM_PFO
;
2201 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_PO"))
2202 *type
= DBTYPE_OVM_PO
;
2209 /* Convert ISDS dbType enum @type to UTF-8 string.
2210 * @Return pointer to static string, or NULL if unknown enum value */
2211 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2213 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2214 * of view of generic public SOAP interface. */
2215 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2216 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2217 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2218 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2219 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2220 case DBTYPE_PFO_AUDITOR
: return(BAD_CAST
"PFO_AUDITOR"); break;
2221 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2222 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2223 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2224 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2225 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2226 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2227 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2228 case DBTYPE_OVM_FO
: return(BAD_CAST
"OVM_FO"); break;
2229 case DBTYPE_OVM_PFO
: return(BAD_CAST
"OVM_PFO"); break;
2230 case DBTYPE_OVM_PO
: return(BAD_CAST
"OVM_PO"); break;
2231 default: return NULL
; break;
2236 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2237 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2238 if (!string
|| !type
) return IE_INVAL
;
2240 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2241 *type
= USERTYPE_PRIMARY
;
2242 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2243 *type
= USERTYPE_ENTRUSTED
;
2244 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2245 *type
= USERTYPE_ADMINISTRATOR
;
2246 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2247 *type
= USERTYPE_OFFICIAL
;
2248 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2249 *type
= USERTYPE_OFFICIAL_CERT
;
2250 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2251 *type
= USERTYPE_LIQUIDATOR
;
2252 else if (!xmlStrcmp(string
, BAD_CAST
"RECEIVER"))
2253 *type
= USERTYPE_RECEIVER
;
2254 else if (!xmlStrcmp(string
, BAD_CAST
"GUARDIAN"))
2255 *type
= USERTYPE_GUARDIAN
;
2262 /* Convert ISDS userType enum @type to UTF-8 string.
2263 * @Return pointer to static string, or NULL if unknown enum value */
2264 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2266 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2267 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2268 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2269 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2270 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2271 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2272 case USERTYPE_RECEIVER
: return(BAD_CAST
"RECEIVER"); break;
2273 case USERTYPE_GUARDIAN
: return(BAD_CAST
"GUARDIAN"); break;
2274 default: return NULL
; break;
2279 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2280 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2281 isds_sender_type
*type
) {
2282 if (!string
|| !type
) return IE_INVAL
;
2284 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2285 *type
= SENDERTYPE_PRIMARY
;
2286 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2287 *type
= SENDERTYPE_ENTRUSTED
;
2288 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2289 *type
= SENDERTYPE_ADMINISTRATOR
;
2290 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2291 *type
= SENDERTYPE_OFFICIAL
;
2292 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2293 *type
= SENDERTYPE_VIRTUAL
;
2294 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2295 *type
= SENDERTYPE_OFFICIAL_CERT
;
2296 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2297 *type
= SENDERTYPE_LIQUIDATOR
;
2298 else if (!xmlStrcmp(string
, BAD_CAST
"RECEIVER"))
2299 *type
= SENDERTYPE_RECEIVER
;
2300 else if (!xmlStrcmp(string
, BAD_CAST
"GUARDIAN"))
2301 *type
= SENDERTYPE_GUARDIAN
;
2308 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2309 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2310 isds_payment_type
*type
) {
2311 if (!string
|| !type
) return IE_INVAL
;
2313 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2314 *type
= PAYMENT_SENDER
;
2315 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2316 *type
= PAYMENT_RESPONSE
;
2317 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2318 *type
= PAYMENT_SPONSOR
;
2319 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2320 *type
= PAYMENT_SPONSOR_LIMITED
;
2321 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2322 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2323 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2324 *type
= PAYMENT_STAMP
;
2331 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2332 * ciEventType is integer but we convert it from string representation
2334 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2335 isds_credit_event_type
*type
) {
2336 if (!string
|| !type
) return IE_INVAL
;
2338 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2339 *type
= ISDS_CREDIT_CHARGED
;
2340 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2341 *type
= ISDS_CREDIT_DISCHARGED
;
2342 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2343 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2344 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2345 *type
= ISDS_CREDIT_STORAGE_SET
;
2346 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2347 *type
= ISDS_CREDIT_EXPIRED
;
2354 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2355 * @Return pointer to static string, or NULL if unknown enum value */
2356 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2358 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2359 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2360 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2361 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2362 default: return NULL
; break;
2367 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2368 * ISDSSearch2/searchType value.
2369 * @Return pointer to static string, or NULL if unknown enum value */
2370 static const xmlChar
*isds_fulltext_target2string(
2371 const isds_fulltext_target type
) {
2373 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2374 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2375 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2376 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2377 default: return NULL
; break;
2380 #endif /* HAVE_LIBCURL */
2383 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2384 * @Return IE_ENUM if @string is not valid enum member */
2385 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2386 isds_FileMetaType
*type
) {
2387 if (!string
|| !type
) return IE_INVAL
;
2389 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2390 *type
= FILEMETATYPE_MAIN
;
2391 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2392 *type
= FILEMETATYPE_ENCLOSURE
;
2393 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2394 *type
= FILEMETATYPE_SIGNATURE
;
2395 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2396 *type
= FILEMETATYPE_META
;
2403 /* Convert UTF-8 @string to ISDS hash @algorithm.
2404 * @Return IE_ENUM if @string is not valid enum member */
2405 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2406 isds_hash_algorithm
*algorithm
) {
2407 if (!string
|| !algorithm
) return IE_INVAL
;
2409 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2410 *algorithm
= HASH_ALGORITHM_MD5
;
2411 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2412 *algorithm
= HASH_ALGORITHM_SHA_1
;
2413 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2414 *algorithm
= HASH_ALGORITHM_SHA_224
;
2415 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2416 *algorithm
= HASH_ALGORITHM_SHA_256
;
2417 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2418 *algorithm
= HASH_ALGORITHM_SHA_384
;
2419 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2420 *algorithm
= HASH_ALGORITHM_SHA_512
;
2428 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2429 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2430 if (!time
|| !string
) return IE_INVAL
;
2432 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2433 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2440 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2441 * respects the @time microseconds too. */
2442 static isds_error
timeval2timestring(const struct timeval
*time
,
2445 time_t seconds_as_time_t
;
2447 if (!time
|| !string
) return IE_INVAL
;
2449 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2450 * 32-bit long in Microsoft API. Convert value to the type expected by
2452 seconds_as_time_t
= time
->tv_sec
;
2453 if (!gmtime_r(&seconds_as_time_t
, &broken
)) return IE_DATE
;
2454 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2456 /* TODO: small negative year should be formatted as "-0012". This is not
2457 * true for glibc "%04d". We should implement it.
2458 * time->tv_usec type is su_seconds_t which is required to be signed
2459 * integer to accomodate values from range [-1, 1000000].
2460 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2461 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2462 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2463 * of the range check above. */
2464 if (-1 == isds_asprintf((char **) string
,
2465 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32
,
2466 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2467 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2468 (int32_t)time
->tv_usec
))
2473 #endif /* HAVE_LIBCURL */
2476 /* Convert UTF-8 ISO 8601 date-time @string to static struct timeval.
2477 * It respects microseconds too. Microseconds are rounded half up.
2478 * In case of error, @time will be undefined. */
2479 static isds_error
timestring2static_timeval(const xmlChar
*string
,
2480 struct timeval
*time
) {
2482 char *offset
, *delim
, *endptr
;
2483 const int subsecond_resolution
= 6;
2484 char subseconds
[subsecond_resolution
+ 1];
2486 int offset_hours
, offset_minutes
;
2488 long int long_number
;
2493 if (!time
) return IE_INVAL
;
2498 memset(&broken
, 0, sizeof(broken
));
2499 memset(time
, 0, sizeof(*time
));
2502 /* xsd:date is ISO 8601 string, thus ASCII */
2503 /*TODO: negative year */
2507 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2508 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2509 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2514 broken
.tm_year
-= 1900;
2516 broken
.tm_isdst
= -1;
2517 offset
= (char*)string
+ i
;
2519 /* Parse date and time without subseconds and offset */
2520 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2526 /* Get subseconds */
2527 if (*offset
== '.' ) {
2530 /* Copy first 6 digits, pad it with zeros.
2531 * Current server implementation uses only millisecond resolution. */
2532 /* TODO: isdigit() is locale sensitive */
2534 i
< subsecond_resolution
&& isdigit(*offset
);
2536 subseconds
[i
] = *offset
;
2538 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2539 /* Check 7th digit for rounding */
2540 if (*offset
>= '5') round_up
= 1;
2543 for (; i
< subsecond_resolution
; i
++) {
2544 subseconds
[i
] = '0';
2546 subseconds
[subsecond_resolution
] = '\0';
2548 /* Convert it into integer */
2549 long_number
= strtol(subseconds
, &endptr
, 10);
2550 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2551 long_number
== LONG_MAX
) {
2554 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2555 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2556 * microseconds" and "the type shall be a signed integer capable of
2557 * storing values at least in the range [-1, 1000000]. */
2558 if (long_number
< -1 || long_number
>= 1000000) {
2561 time
->tv_usec
= long_number
;
2563 /* Round the subseconds */
2565 if (999999 == time
->tv_usec
) {
2573 /* move to the zone offset delimiter or signal NULL*/
2574 delim
= strchr(offset
, '-');
2576 delim
= strchr(offset
, '+');
2578 delim
= strchr(offset
, 'Z');
2582 /* Get zone offset */
2583 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2584 * "" equals to "Z" and it means UTC zone. */
2585 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2586 * colon separator */
2587 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2588 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2591 if (*offset
== '+') {
2592 broken
.tm_hour
-= offset_hours
;
2593 broken
.tm_min
-= offset_minutes
;
2595 broken
.tm_hour
+= offset_hours
;
2596 broken
.tm_min
+= offset_minutes
;
2600 /* Convert to time_t */
2601 time
->tv_sec
= _isds_timegm(&broken
);
2602 if (time
->tv_sec
== (time_t) -1) {
2610 /* Convert UTF-8 ISO 8601 date-time @string to reallocated struct timeval.
2611 * It respects microseconds too. Microseconds are rounded half up.
2612 * In case of error, @time will be freed. */
2613 static isds_error
timestring2timeval(const xmlChar
*string
,
2614 struct timeval
**time
) {
2617 if (!time
) return IE_INVAL
;
2624 *time
= calloc(1, sizeof(**time
));
2625 if (!*time
) return IE_NOMEM
;
2627 memset(*time
, 0, sizeof(**time
));
2630 error
= timestring2static_timeval(string
, *time
);
2639 /* Convert unsigned int into isds_message_status.
2640 * @context is session context
2641 * @number is pointer to number value. NULL will be treated as invalid value.
2642 * @status is automatically reallocated status
2643 * @return IE_SUCCESS, or error code and free status */
2644 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2645 const unsigned long int *number
, isds_message_status
**status
) {
2646 if (!context
) return IE_INVALID_CONTEXT
;
2647 if (!status
) return IE_INVAL
;
2649 free(*status
); *status
= NULL
;
2650 if (!number
) return IE_INVAL
;
2652 if (*number
< 1 || *number
> 10) {
2653 isds_printf_message(context
, _("Invalid message status value: %lu"),
2658 *status
= malloc(sizeof(**status
));
2659 if (!*status
) return IE_NOMEM
;
2661 **status
= 1 << *number
;
2666 /* Convert event description string into isds_event members type and
2668 * @string is raw event description starting with event prefix
2669 * @event is structure where to store type and stripped description to
2670 * @return standard error code, unknown prefix is not classified as an error.
2672 static isds_error
eventstring2event(const xmlChar
*string
,
2673 struct isds_event
* event
) {
2674 const xmlChar
*known_prefixes
[] = {
2686 const isds_event_type types
[] = {
2687 EVENT_ENTERED_SYSTEM
,
2688 EVENT_ACCEPTED_BY_RECIPIENT
,
2689 EVENT_ACCEPTED_BY_FICTION
,
2690 EVENT_UNDELIVERABLE
,
2691 EVENT_COMMERCIAL_ACCEPTED
,
2693 EVENT_UNDELIVERED_AV_CHECK
,
2694 EVENT_PRIMARY_LOGIN
,
2695 EVENT_ENTRUSTED_LOGIN
,
2701 if (!string
|| !event
) return IE_INVAL
;
2704 event
->type
= malloc(sizeof(*event
->type
));
2705 if (!(event
->type
)) return IE_NOMEM
;
2707 zfree(event
->description
);
2709 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2711 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2713 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2714 /* Prefix is known */
2715 *event
->type
= types
[index
];
2717 /* Strip prefix from description and spaces */
2718 /* TODO: Recognize all white spaces from UCS blank class and
2719 * operate on UTF-8 chars. */
2720 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2721 event
->description
= strdup((char *) (string
+ length
));
2722 if (!(event
->description
)) return IE_NOMEM
;
2728 /* Unknown event prefix.
2729 * XSD allows any string */
2730 char *string_locale
= _isds_utf82locale((char *) string
);
2731 isds_log(ILF_ISDS
, ILL_WARNING
,
2732 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2733 free(string_locale
);
2735 *event
->type
= EVENT_UKNOWN
;
2736 event
->description
= strdup((char *) string
);
2737 if (!(event
->description
)) return IE_NOMEM
;
2743 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2744 * and leave label */
2745 #define EXTRACT_STRING(element, string) { \
2746 xmlXPathFreeObject(result); \
2747 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2748 if (NULL == (result)) { \
2752 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2753 if (result->nodesetval->nodeNr > 1) { \
2754 isds_printf_message(context, _("Multiple %s element"), element); \
2758 (string) = (char *) \
2759 xmlXPathCastNodeSetToString(result->nodesetval); \
2760 if (NULL == (string)) { \
2767 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2769 char *string = NULL; \
2770 EXTRACT_STRING(element, string); \
2773 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2774 if (!(booleanPtr)) { \
2780 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2781 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2782 *(booleanPtr) = 1; \
2783 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2784 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2785 *(booleanPtr) = 0; \
2787 char *string_locale = _isds_utf82locale((char*)string); \
2788 isds_printf_message(context, \
2789 _("%s value is not valid boolean: %s"), \
2790 element, string_locale); \
2791 free(string_locale); \
2801 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2803 char *string = NULL; \
2804 EXTRACT_STRING(element, string); \
2806 if (NULL == string) { \
2807 isds_printf_message(context, _("%s element is empty"), element); \
2811 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2812 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2814 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2815 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2818 char *string_locale = _isds_utf82locale((char*)string); \
2819 isds_printf_message(context, \
2820 _("%s value is not valid boolean: %s"), \
2821 element, string_locale); \
2822 free(string_locale); \
2831 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2833 char *string = NULL; \
2834 EXTRACT_STRING(element, string); \
2839 number = strtol((char*)string, &endptr, 10); \
2841 if (*endptr != '\0') { \
2842 char *string_locale = _isds_utf82locale((char *)string); \
2843 isds_printf_message(context, \
2844 _("%s is not valid integer: %s"), \
2845 element, string_locale); \
2846 free(string_locale); \
2852 if (number == LONG_MIN || number == LONG_MAX) { \
2853 char *string_locale = _isds_utf82locale((char *)string); \
2854 isds_printf_message(context, \
2855 _("%s value out of range of long int: %s"), \
2856 element, string_locale); \
2857 free(string_locale); \
2863 free(string); string = NULL; \
2865 if (!(preallocated)) { \
2866 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2867 if (!(longintPtr)) { \
2872 *(longintPtr) = number; \
2876 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2878 char *string = NULL; \
2879 EXTRACT_STRING(element, string); \
2884 number = strtol((char*)string, &endptr, 10); \
2886 if (*endptr != '\0') { \
2887 char *string_locale = _isds_utf82locale((char *)string); \
2888 isds_printf_message(context, \
2889 _("%s is not valid integer: %s"), \
2890 element, string_locale); \
2891 free(string_locale); \
2897 if (number == LONG_MIN || number == LONG_MAX) { \
2898 char *string_locale = _isds_utf82locale((char *)string); \
2899 isds_printf_message(context, \
2900 _("%s value out of range of long int: %s"), \
2901 element, string_locale); \
2902 free(string_locale); \
2908 free(string); string = NULL; \
2910 isds_printf_message(context, \
2911 _("%s value is negative: %ld"), element, number); \
2916 if (!(preallocated)) { \
2917 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2918 if (!(ulongintPtr)) { \
2923 *(ulongintPtr) = number; \
2927 #define EXTRACT_DATE(element, tmPtr) { \
2928 char *string = NULL; \
2929 EXTRACT_STRING(element, string); \
2930 if (NULL != string) { \
2931 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2932 if (NULL == (tmPtr)) { \
2937 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2939 if (err == IE_NOTSUP) { \
2941 char *string_locale = _isds_utf82locale(string); \
2942 char *element_locale = _isds_utf82locale(element); \
2943 isds_printf_message(context, _("Invalid %s value: %s"), \
2944 element_locale, string_locale); \
2945 free(string_locale); \
2946 free(element_locale); \
2955 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2956 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2958 if ((required) && (!string)) { \
2959 char *attribute_locale = _isds_utf82locale(attribute); \
2960 char *element_locale = \
2961 _isds_utf82locale((char *)xpath_ctx->node->name); \
2962 isds_printf_message(context, \
2963 _("Could not extract required %s attribute value from " \
2964 "%s element"), attribute_locale, element_locale); \
2965 free(element_locale); \
2966 free(attribute_locale); \
2973 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2975 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2976 (xmlChar *) (string)); \
2978 isds_printf_message(context, \
2979 _("Could not add %s child to %s element"), \
2980 element, (parent)->name); \
2986 #define INSERT_STRING(parent, element, string) \
2987 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2989 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2991 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2992 else { INSERT_STRING(parent, element, "false"); } \
2995 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2998 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
3000 INSERT_STRING(parent, element, NULL); \
3004 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
3005 if ((longintPtr)) { \
3006 /* FIXME: locale sensitive */ \
3007 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
3011 INSERT_STRING(parent, element, buffer) \
3012 free(buffer); (buffer) = NULL; \
3013 } else { INSERT_STRING(parent, element, NULL) } \
3016 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
3017 if ((ulongintPtr)) { \
3018 /* FIXME: locale sensitive */ \
3019 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
3023 INSERT_STRING(parent, element, buffer) \
3024 free(buffer); (buffer) = NULL; \
3025 } else { INSERT_STRING(parent, element, NULL) } \
3028 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
3030 /* FIXME: locale sensitive */ \
3031 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
3035 INSERT_STRING(parent, element, buffer) \
3036 free(buffer); (buffer) = NULL; \
3039 /* Requires attribute_node variable, do not free it. Can be used to reffer to
3041 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
3043 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
3044 (xmlChar *) (string)); \
3045 if (!attribute_node) { \
3046 isds_printf_message(context, _("Could not add %s " \
3047 "attribute to %s element"), \
3048 (attribute), (parent)->name); \
3054 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
3056 int length = xmlUTF8Strlen((xmlChar *) (string)); \
3057 if (length > (maximum)) { \
3058 isds_printf_message(context, \
3059 ngettext("%s has more than %d characters", \
3060 "%s has more than %d characters", (maximum)), \
3061 (name), (maximum)); \
3065 if (length < (minimum)) { \
3066 isds_printf_message(context, \
3067 ngettext("%s has less than %d characters", \
3068 "%s has less than %d characters", (minimum)), \
3069 (name), (minimum)); \
3076 #define INSERT_ELEMENT(child, parent, element) \
3078 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
3080 isds_printf_message(context, \
3081 _("Could not add %s child to %s element"), \
3082 (element), (parent)->name); \
3089 /* Find child element by name in given XPath context and switch context onto
3090 * it. The child must be uniq and must exist. Otherwise fails.
3091 * @context is ISDS context
3092 * @child is child element name
3093 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
3094 * into it child. In error case, the @xpath_ctx keeps original value. */
3095 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
3096 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
3097 isds_error err
= IE_SUCCESS
;
3098 xmlXPathObjectPtr result
= NULL
;
3100 if (!context
) return IE_INVALID_CONTEXT
;
3101 if (!child
|| !xpath_ctx
) return IE_INVAL
;
3104 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
3111 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3112 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
3113 char *child_locale
= _isds_utf82locale((char*) child
);
3114 isds_printf_message(context
,
3115 _("%s element does not contain %s child"),
3116 parent_locale
, child_locale
);
3118 free(parent_locale
);
3124 if (result
->nodesetval
->nodeNr
> 1) {
3125 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
3126 char *child_locale
= _isds_utf82locale((char*) child
);
3127 isds_printf_message(context
,
3128 _("%s element contains multiple %s children"),
3129 parent_locale
, child_locale
);
3131 free(parent_locale
);
3136 /* Switch context */
3137 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
3140 xmlXPathFreeObject(result
);
3147 /* Find and convert XSD:gPersonName group in current node into structure
3148 * @context is ISDS context
3149 * @personName is automatically reallocated person name structure. If no member
3150 * value is found, will be freed.
3151 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
3153 * In case of error @personName will be freed. */
3154 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
3155 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
3156 isds_error err
= IE_SUCCESS
;
3157 xmlXPathObjectPtr result
= NULL
;
3159 if (!context
) return IE_INVALID_CONTEXT
;
3160 if (!personName
) return IE_INVAL
;
3161 isds_PersonName_free(personName
);
3162 if (!xpath_ctx
) return IE_INVAL
;
3165 *personName
= calloc(1, sizeof(**personName
));
3171 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
3172 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
3173 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
3174 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
3176 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
3177 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
3178 isds_PersonName_free(personName
);
3181 if (err
) isds_PersonName_free(personName
);
3182 xmlXPathFreeObject(result
);
3187 /* Find and convert XSD:gAddress group extended with relevant
3188 * tdbPersonalOwnerinfo members in current node into structure
3189 * @context is ISDS context
3190 * @address is automatically reallocated address structure. If no member
3191 * value is found, will be freed.
3192 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
3194 * In case of error @address will be freed. */
3195 static isds_error
extract_gAddress(struct isds_ctx
*context
,
3196 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
3197 isds_error err
= IE_SUCCESS
;
3198 xmlXPathObjectPtr result
= NULL
;
3200 if (!context
) return IE_INVALID_CONTEXT
;
3201 if (!address
) return IE_INVAL
;
3202 isds_Address_free(address
);
3203 if (!xpath_ctx
) return IE_INVAL
;
3206 *address
= calloc(1, sizeof(**address
));
3212 EXTRACT_LONGINT("isds:adCode", (*address
)->adCode
, 0);
3213 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
3214 EXTRACT_STRING("isds:adDistrict", (*address
)->adDistrict
);
3215 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
3216 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
3217 EXTRACT_STRING("isds:adNumberInMunicipality",
3218 (*address
)->adNumberInMunicipality
);
3219 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
3220 EXTRACT_STRING("isds:adState", (*address
)->adState
);
3222 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
3223 !(*address
)->adNumberInStreet
&&
3224 !(*address
)->adNumberInMunicipality
&&
3225 !(*address
)->adZipCode
&& !(*address
)->adState
)
3226 isds_Address_free(address
);
3229 if (err
) isds_Address_free(address
);
3230 xmlXPathFreeObject(result
);
3235 /* Find and convert isds:biDate element in current node into structure
3236 * @context is ISDS context
3237 * @biDate is automatically reallocated birth date structure. If no member
3238 * value is found, will be freed.
3239 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3241 * In case of error @biDate will be freed. */
3242 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3243 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3244 isds_error err
= IE_SUCCESS
;
3245 xmlXPathObjectPtr result
= NULL
;
3246 char *string
= NULL
;
3248 if (!context
) return IE_INVALID_CONTEXT
;
3249 if (!biDate
) return IE_INVAL
;
3251 if (!xpath_ctx
) return IE_INVAL
;
3253 EXTRACT_STRING("isds:biDate", string
);
3255 *biDate
= calloc(1, sizeof(**biDate
));
3260 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3262 if (err
== IE_NOTSUP
) {
3264 char *string_locale
= _isds_utf82locale(string
);
3265 isds_printf_message(context
,
3266 _("Invalid isds:biDate value: %s"), string_locale
);
3267 free(string_locale
);
3274 if (err
) zfree(*biDate
);
3276 xmlXPathFreeObject(result
);
3281 /* Convert XSD:tDbOwnerInfo or XSD:tdbPersonalOwenerInfo XML tree into structure
3282 * @context is ISDS context
3283 * @db_owner_info is automatically reallocated box owner info structure
3284 * @xpath_ctx is XPath context with current node as XSD:tDbOwnerInfo or
3285 * XSD:tdbPersonalOwenerInfo element
3286 * In case of error @db_owner_info will be freed. */
3287 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3288 struct isds_DbOwnerInfo
**db_owner_info
,
3289 xmlXPathContextPtr xpath_ctx
) {
3290 isds_error err
= IE_SUCCESS
;
3291 xmlXPathObjectPtr result
= NULL
;
3292 char *string
= NULL
;
3294 if (!context
) return IE_INVALID_CONTEXT
;
3295 if (!db_owner_info
) return IE_INVAL
;
3296 isds_DbOwnerInfo_free(db_owner_info
);
3297 if (!xpath_ctx
) return IE_INVAL
;
3300 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3301 if (!*db_owner_info
) {
3306 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3308 EXTRACT_BOOLEAN("isds:aifoIsds", (*db_owner_info
)->aifoIsds
);
3310 EXTRACT_STRING("isds:dbType", string
);
3312 (*db_owner_info
)->dbType
=
3313 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3314 if (!(*db_owner_info
)->dbType
) {
3318 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3320 zfree((*db_owner_info
)->dbType
);
3321 if (err
== IE_ENUM
) {
3323 char *string_locale
= _isds_utf82locale(string
);
3324 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3326 free(string_locale
);
3333 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3335 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3337 if (err
) goto leave
;
3339 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3341 (*db_owner_info
)->birthInfo
=
3342 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3343 if (!(*db_owner_info
)->birthInfo
) {
3347 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3349 if (err
) goto leave
;
3350 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3351 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3352 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3353 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3354 !(*db_owner_info
)->birthInfo
->biCity
&&
3355 !(*db_owner_info
)->birthInfo
->biCounty
&&
3356 !(*db_owner_info
)->birthInfo
->biState
)
3357 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3359 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3360 if (err
) goto leave
;
3362 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3363 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3364 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3365 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3366 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3368 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3370 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3371 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3372 (*db_owner_info
)->dbOpenAddressing
);
3375 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3377 xmlXPathFreeObject(result
);
3382 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3383 * @context is session context
3384 * @owner is libisds structure with box description.
3385 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3386 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3387 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3388 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3390 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3391 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3392 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3393 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3395 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3396 const struct isds_DbOwnerInfo
*owner
, _Bool pfo_subtype
,
3397 xmlNodePtr db_owner_info
) {
3399 isds_error err
= IE_SUCCESS
;
3401 xmlChar
*string
= NULL
;
3402 const xmlChar
*type_string
= NULL
;
3404 if (!context
) return IE_INVALID_CONTEXT
;
3405 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3408 /* XXX: All the elements except email and telNumber are mandatory. */
3409 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3410 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3413 INSERT_BOOLEAN(db_owner_info
, "aifoIsds", owner
->aifoIsds
);
3418 if (owner
->dbType
) {
3419 type_string
= isds_DbType2string(*(owner
->dbType
));
3421 isds_printf_message(context
, _("Invalid dbType value: %d"),
3427 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3429 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3432 INSERT_STRING(db_owner_info
, "pnFirstName",
3433 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnFirstName
);
3434 INSERT_STRING(db_owner_info
, "pnMiddleName",
3435 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnMiddleName
);
3436 INSERT_STRING(db_owner_info
, "pnLastName",
3437 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnLastName
);
3439 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3440 (NULL
== owner
->personName
) ? NULL
:
3441 owner
->personName
->pnLastNameAtBirth
);
3443 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3446 if (NULL
!= owner
->birthInfo
&& NULL
!= owner
->birthInfo
->biDate
) {
3447 err
= tm2datestring(owner
->birthInfo
->biDate
, &string
);
3448 if (err
) goto leave
;
3450 INSERT_STRING(db_owner_info
, "biDate", string
);
3453 INSERT_STRING(db_owner_info
, "biCity",
3454 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCity
);
3455 INSERT_STRING(db_owner_info
, "biCounty",
3456 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCounty
);
3457 INSERT_STRING(db_owner_info
, "biState",
3458 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biState
);
3461 INSERT_LONGINT(db_owner_info
, "adCode",
3462 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCode
,
3465 INSERT_STRING(db_owner_info
, "adCity",
3466 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCity
);
3468 INSERT_STRING(db_owner_info
, "adDistrict",
3469 (NULL
== owner
->address
) ? NULL
: owner
->address
->adDistrict
);
3471 INSERT_STRING(db_owner_info
, "adStreet",
3472 (NULL
== owner
->address
) ? NULL
: owner
->address
->adStreet
);
3473 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3474 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInStreet
);
3475 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3476 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInMunicipality
);
3477 INSERT_STRING(db_owner_info
, "adZipCode",
3478 (NULL
== owner
->address
) ? NULL
: owner
->address
->adZipCode
);
3479 INSERT_STRING(db_owner_info
, "adState",
3480 (NULL
== owner
->address
) ? NULL
: owner
->address
->adState
);
3482 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3485 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3486 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3488 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3489 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3491 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3492 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3494 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3496 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3497 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3498 owner
->dbOpenAddressing
);
3507 /* Convert XSD:tDbUserInfo XML tree into structure
3508 * @context is ISDS context
3509 * @db_user_info is automatically reallocated user info structure
3510 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3511 * In case of error @db_user_info will be freed. */
3512 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3513 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3514 isds_error err
= IE_SUCCESS
;
3515 xmlXPathObjectPtr result
= NULL
;
3516 char *string
= NULL
;
3518 if (!context
) return IE_INVALID_CONTEXT
;
3519 if (!db_user_info
) return IE_INVAL
;
3520 isds_DbUserInfo_free(db_user_info
);
3521 if (!xpath_ctx
) return IE_INVAL
;
3524 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3525 if (!*db_user_info
) {
3530 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info
)->aifo_ticket
, 0);
3532 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3534 EXTRACT_STRING("isds:userType", string
);
3536 (*db_user_info
)->userType
=
3537 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3538 if (!(*db_user_info
)->userType
) {
3542 err
= string2isds_UserType((xmlChar
*)string
,
3543 (*db_user_info
)->userType
);
3545 zfree((*db_user_info
)->userType
);
3546 if (err
== IE_ENUM
) {
3548 char *string_locale
= _isds_utf82locale(string
);
3549 isds_printf_message(context
,
3550 _("Unknown isds:userType value: %s"), string_locale
);
3551 free(string_locale
);
3558 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3560 (*db_user_info
)->personName
=
3561 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3562 if (!(*db_user_info
)->personName
) {
3567 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3569 if (err
) goto leave
;
3571 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3572 if (err
) goto leave
;
3574 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3575 if (err
) goto leave
;
3577 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3578 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3580 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3581 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3582 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3584 /* ???: Default value is "CZ" according specification. Should we provide
3586 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3589 if (err
) isds_DbUserInfo_free(db_user_info
);
3591 xmlXPathFreeObject(result
);
3596 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3597 * @context is session context
3598 * @user is libisds structure with user description
3599 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3600 * @db_user_info is XML element of XSD:tDbUserInfo */
3601 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3602 const struct isds_DbUserInfo
*user
, _Bool honor_aifo_ticket
,
3603 xmlNodePtr db_user_info
) {
3605 isds_error err
= IE_SUCCESS
;
3607 xmlAttrPtr attribute_node
;
3608 xmlChar
*string
= NULL
;
3610 if (!context
) return IE_INVALID_CONTEXT
;
3611 if (!user
|| !db_user_info
) return IE_INVAL
;
3613 /* Build XSD:tDbUserInfo */
3615 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3616 * allow it everywhere. */
3617 if (honor_aifo_ticket
&& user
->aifo_ticket
) {
3618 INSERT_STRING_ATTRIBUTE(db_user_info
, "AIFOTicket", user
->aifo_ticket
);
3621 if (user
->personName
) {
3622 INSERT_STRING(db_user_info
, "pnFirstName",
3623 user
->personName
->pnFirstName
);
3624 INSERT_STRING(db_user_info
, "pnMiddleName",
3625 user
->personName
->pnMiddleName
);
3626 INSERT_STRING(db_user_info
, "pnLastName",
3627 user
->personName
->pnLastName
);
3628 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3629 user
->personName
->pnLastNameAtBirth
);
3631 if (user
->address
) {
3632 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3633 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3634 INSERT_STRING(db_user_info
, "adNumberInStreet",
3635 user
->address
->adNumberInStreet
);
3636 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3637 user
->address
->adNumberInMunicipality
);
3638 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3639 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3642 if (!tm2datestring(user
->biDate
, &string
))
3643 INSERT_STRING(db_user_info
, "biDate", string
);
3646 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3647 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3650 if (user
->userType
) {
3651 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3653 isds_printf_message(context
, _("Invalid userType value: %d"),
3658 INSERT_STRING(db_user_info
, "userType", type_string
);
3661 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3662 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3663 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3664 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3665 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3666 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3667 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3668 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3669 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3677 /* Convert XSD:tPDZRec XML tree into structure
3678 * @context is ISDS context
3679 * @permission is automatically reallocated commercial permission structure
3680 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3681 * In case of error @permission will be freed. */
3682 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3683 struct isds_commercial_permission
**permission
,
3684 xmlXPathContextPtr xpath_ctx
) {
3685 isds_error err
= IE_SUCCESS
;
3686 xmlXPathObjectPtr result
= NULL
;
3687 char *string
= NULL
;
3689 if (!context
) return IE_INVALID_CONTEXT
;
3690 if (!permission
) return IE_INVAL
;
3691 isds_commercial_permission_free(permission
);
3692 if (!xpath_ctx
) return IE_INVAL
;
3695 *permission
= calloc(1, sizeof(**permission
));
3701 EXTRACT_STRING("isds:PDZType", string
);
3703 err
= string2isds_payment_type((xmlChar
*)string
,
3704 &(*permission
)->type
);
3706 if (err
== IE_ENUM
) {
3708 char *string_locale
= _isds_utf82locale(string
);
3709 isds_printf_message(context
,
3710 _("Unknown isds:PDZType value: %s"), string_locale
);
3711 free(string_locale
);
3718 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3719 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3721 EXTRACT_STRING("isds:PDZExpire", string
);
3723 err
= timestring2timeval((xmlChar
*) string
,
3724 &((*permission
)->expiration
));
3726 char *string_locale
= _isds_utf82locale(string
);
3727 if (err
== IE_DATE
) err
= IE_ISDS
;
3728 isds_printf_message(context
,
3729 _("Could not convert PDZExpire as ISO time: %s"),
3731 free(string_locale
);
3737 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3738 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3741 if (err
) isds_commercial_permission_free(permission
);
3743 xmlXPathFreeObject(result
);
3748 /* Convert XSD:tCiRecord XML tree into structure
3749 * @context is ISDS context
3750 * @event is automatically reallocated commercial credit event structure
3751 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3752 * In case of error @event will be freed. */
3753 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3754 struct isds_credit_event
**event
,
3755 xmlXPathContextPtr xpath_ctx
) {
3756 isds_error err
= IE_SUCCESS
;
3757 xmlXPathObjectPtr result
= NULL
;
3758 char *string
= NULL
;
3759 long int *number_ptr
;
3761 if (!context
) return IE_INVALID_CONTEXT
;
3762 if (!event
) return IE_INVAL
;
3763 isds_credit_event_free(event
);
3764 if (!xpath_ctx
) return IE_INVAL
;
3767 *event
= calloc(1, sizeof(**event
));
3773 EXTRACT_STRING("isds:ciEventTime", string
);
3775 err
= timestring2timeval((xmlChar
*) string
,
3778 char *string_locale
= _isds_utf82locale(string
);
3779 if (err
== IE_DATE
) err
= IE_ISDS
;
3780 isds_printf_message(context
,
3781 _("Could not convert ciEventTime as ISO time: %s"),
3783 free(string_locale
);
3789 EXTRACT_STRING("isds:ciEventType", string
);
3791 err
= string2isds_credit_event_type((xmlChar
*)string
,
3794 if (err
== IE_ENUM
) {
3796 char *string_locale
= _isds_utf82locale(string
);
3797 isds_printf_message(context
,
3798 _("Unknown isds:ciEventType value: %s"), string_locale
);
3799 free(string_locale
);
3806 number_ptr
= &((*event
)->credit_change
);
3807 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3808 number_ptr
= &(*event
)->new_credit
;
3809 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3811 switch((*event
)->type
) {
3812 case ISDS_CREDIT_CHARGED
:
3813 EXTRACT_STRING("isds:ciTransID",
3814 (*event
)->details
.charged
.transaction
);
3816 case ISDS_CREDIT_DISCHARGED
:
3817 EXTRACT_STRING("isds:ciTransID",
3818 (*event
)->details
.discharged
.transaction
);
3820 case ISDS_CREDIT_MESSAGE_SENT
:
3821 EXTRACT_STRING("isds:ciRecipientID",
3822 (*event
)->details
.message_sent
.recipient
);
3823 EXTRACT_STRING("isds:ciPDZID",
3824 (*event
)->details
.message_sent
.message_id
);
3826 case ISDS_CREDIT_STORAGE_SET
:
3827 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3828 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3829 EXTRACT_DATE("isds:ciNewFrom",
3830 (*event
)->details
.storage_set
.new_valid_from
);
3831 EXTRACT_DATE("isds:ciNewTo",
3832 (*event
)->details
.storage_set
.new_valid_to
);
3833 EXTRACT_LONGINT("isds:ciOldCapacity",
3834 (*event
)->details
.storage_set
.old_capacity
, 0);
3835 EXTRACT_DATE("isds:ciOldFrom",
3836 (*event
)->details
.storage_set
.old_valid_from
);
3837 EXTRACT_DATE("isds:ciOldTo",
3838 (*event
)->details
.storage_set
.old_valid_to
);
3839 EXTRACT_STRING("isds:ciDoneBy",
3840 (*event
)->details
.storage_set
.initiator
);
3842 case ISDS_CREDIT_EXPIRED
:
3847 if (err
) isds_credit_event_free(event
);
3849 xmlXPathFreeObject(result
);
3854 #endif /* HAVE_LIBCURL */
3857 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3858 * isds_envelope structure. The envelope is automatically allocated but not
3859 * reallocated. The date are just appended into envelope structure.
3860 * @context is ISDS context
3861 * @envelope is automatically allocated message envelope structure
3862 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3863 * In case of error @envelope will be freed. */
3864 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3865 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3866 isds_error err
= IE_SUCCESS
;
3867 xmlXPathObjectPtr result
= NULL
;
3869 if (!context
) return IE_INVALID_CONTEXT
;
3870 if (!envelope
) return IE_INVAL
;
3871 if (!xpath_ctx
) return IE_INVAL
;
3875 /* Allocate envelope */
3876 *envelope
= calloc(1, sizeof(**envelope
));
3882 /* Else free former data */
3883 zfree((*envelope
)->dmSenderOrgUnit
);
3884 zfree((*envelope
)->dmSenderOrgUnitNum
);
3885 zfree((*envelope
)->dbIDRecipient
);
3886 zfree((*envelope
)->dmRecipientOrgUnit
);
3887 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3888 zfree((*envelope
)->dmToHands
);
3889 zfree((*envelope
)->dmAnnotation
);
3890 zfree((*envelope
)->dmRecipientRefNumber
);
3891 zfree((*envelope
)->dmSenderRefNumber
);
3892 zfree((*envelope
)->dmRecipientIdent
);
3893 zfree((*envelope
)->dmSenderIdent
);
3894 zfree((*envelope
)->dmLegalTitleLaw
);
3895 zfree((*envelope
)->dmLegalTitleYear
);
3896 zfree((*envelope
)->dmLegalTitleSect
);
3897 zfree((*envelope
)->dmLegalTitlePar
);
3898 zfree((*envelope
)->dmLegalTitlePoint
);
3899 zfree((*envelope
)->dmPersonalDelivery
);
3900 zfree((*envelope
)->dmAllowSubstDelivery
);
3903 /* Extract envelope elements added by sender or ISDS
3904 * (XSD: gMessageEnvelopeSub type) */
3905 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3906 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3907 (*envelope
)->dmSenderOrgUnitNum
, 0);
3908 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3909 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3910 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3911 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3912 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3913 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3914 EXTRACT_STRING("isds:dmRecipientRefNumber",
3915 (*envelope
)->dmRecipientRefNumber
);
3916 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3917 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3918 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3920 /* Extract envelope elements regarding law reference */
3921 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3922 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3923 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3924 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3925 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3927 /* Extract envelope other elements */
3928 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3929 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3930 (*envelope
)->dmAllowSubstDelivery
);
3933 if (err
) isds_envelope_free(envelope
);
3934 xmlXPathFreeObject(result
);
3940 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3941 * isds_envelope structure. The envelope is automatically allocated but not
3942 * reallocated. The date are just appended into envelope structure.
3943 * @context is ISDS context
3944 * @envelope is automatically allocated message envelope structure
3945 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3946 * In case of error @envelope will be freed. */
3947 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3948 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3949 isds_error err
= IE_SUCCESS
;
3950 xmlXPathObjectPtr result
= NULL
;
3952 if (!context
) return IE_INVALID_CONTEXT
;
3953 if (!envelope
) return IE_INVAL
;
3954 if (!xpath_ctx
) return IE_INVAL
;
3958 /* Allocate envelope */
3959 *envelope
= calloc(1, sizeof(**envelope
));
3965 /* Else free former data */
3966 zfree((*envelope
)->dmID
);
3967 zfree((*envelope
)->dbIDSender
);
3968 zfree((*envelope
)->dmSender
);
3969 zfree((*envelope
)->dmSenderAddress
);
3970 zfree((*envelope
)->dmSenderType
);
3971 zfree((*envelope
)->dmRecipient
);
3972 zfree((*envelope
)->dmRecipientAddress
);
3973 zfree((*envelope
)->dmAmbiguousRecipient
);
3976 /* Extract envelope elements added by ISDS
3977 * (XSD: gMessageEnvelope type) */
3978 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3979 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3980 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3981 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3982 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3983 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3984 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3985 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3986 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3987 (*envelope
)->dmAmbiguousRecipient
);
3989 /* Extract envelope elements added by sender and ISDS
3990 * (XSD: gMessageEnvelope type) */
3991 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3992 if (err
) goto leave
;
3995 if (err
) isds_envelope_free(envelope
);
3996 xmlXPathFreeObject(result
);
4001 /* Convert other envelope elements from XML tree into isds_envelope structure:
4002 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
4003 * The envelope is automatically allocated but not reallocated.
4004 * The data are just appended into envelope structure.
4005 * @context is ISDS context
4006 * @envelope is automatically allocated message envelope structure
4007 * @xpath_ctx is XPath context with current node as parent desired elements
4008 * In case of error @envelope will be freed. */
4009 static isds_error
append_status_size_times(struct isds_ctx
*context
,
4010 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4011 isds_error err
= IE_SUCCESS
;
4012 xmlXPathObjectPtr result
= NULL
;
4013 char *string
= NULL
;
4014 unsigned long int *unumber
= NULL
;
4016 if (!context
) return IE_INVALID_CONTEXT
;
4017 if (!envelope
) return IE_INVAL
;
4018 if (!xpath_ctx
) return IE_INVAL
;
4023 *envelope
= calloc(1, sizeof(**envelope
));
4030 zfree((*envelope
)->dmMessageStatus
);
4031 zfree((*envelope
)->dmAttachmentSize
);
4032 zfree((*envelope
)->dmDeliveryTime
);
4033 zfree((*envelope
)->dmAcceptanceTime
);
4037 /* dmMessageStatus element is mandatory */
4038 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
4040 isds_log_message(context
,
4041 _("Missing mandatory sisds:dmMessageStatus integer"));
4045 err
= uint2isds_message_status(context
, unumber
,
4046 &((*envelope
)->dmMessageStatus
));
4048 if (err
== IE_ENUM
) err
= IE_ISDS
;
4051 free(unumber
); unumber
= NULL
;
4053 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
4056 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
4058 err
= timestring2timeval((xmlChar
*) string
,
4059 &((*envelope
)->dmDeliveryTime
));
4061 char *string_locale
= _isds_utf82locale(string
);
4062 if (err
== IE_DATE
) err
= IE_ISDS
;
4063 isds_printf_message(context
,
4064 _("Could not convert dmDeliveryTime as ISO time: %s"),
4066 free(string_locale
);
4072 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
4074 err
= timestring2timeval((xmlChar
*) string
,
4075 &((*envelope
)->dmAcceptanceTime
));
4077 char *string_locale
= _isds_utf82locale(string
);
4078 if (err
== IE_DATE
) err
= IE_ISDS
;
4079 isds_printf_message(context
,
4080 _("Could not convert dmAcceptanceTime as ISO time: %s"),
4082 free(string_locale
);
4089 if (err
) isds_envelope_free(envelope
);
4092 xmlXPathFreeObject(result
);
4097 /* Convert message type attribute of current element into isds_envelope
4099 * TODO: This function can be incorporated into append_status_size_times() as
4100 * they are called always together.
4101 * The envelope is automatically allocated but not reallocated.
4102 * The data are just appended into envelope structure.
4103 * @context is ISDS context
4104 * @envelope is automatically allocated message envelope structure
4105 * @xpath_ctx is XPath context with current node as parent of attribute
4106 * carrying message type
4107 * In case of error @envelope will be freed. */
4108 static isds_error
append_message_type(struct isds_ctx
*context
,
4109 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4110 isds_error err
= IE_SUCCESS
;
4112 if (!context
) return IE_INVALID_CONTEXT
;
4113 if (!envelope
) return IE_INVAL
;
4114 if (!xpath_ctx
) return IE_INVAL
;
4119 *envelope
= calloc(1, sizeof(**envelope
));
4126 zfree((*envelope
)->dmType
);
4130 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
4132 if (!(*envelope
)->dmType
) {
4133 /* Use default value */
4134 (*envelope
)->dmType
= strdup("V");
4135 if (!(*envelope
)->dmType
) {
4139 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
4140 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
4141 isds_printf_message(context
,
4142 _("Message type in dmType attribute is not 1 character long: "
4151 if (err
) isds_envelope_free(envelope
);
4157 /* Convert dmType isds_envelope member into XML attribute and append it to
4159 * @context is ISDS context
4160 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
4161 * @dm_envelope is XML element the resulting attribute will be appended to.
4162 * @return error code, in case of error context' message is filled. */
4163 static isds_error
insert_message_type(struct isds_ctx
*context
,
4164 const char *type
, xmlNodePtr dm_envelope
) {
4165 isds_error err
= IE_SUCCESS
;
4166 xmlAttrPtr attribute_node
;
4168 if (!context
) return IE_INVALID_CONTEXT
;
4169 if (!dm_envelope
) return IE_INVAL
;
4171 /* Insert optional message type */
4173 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
4174 char *type_locale
= _isds_utf82locale(type
);
4175 isds_printf_message(context
,
4176 _("Message type in envelope is not 1 character long: %s"),
4182 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
4188 #endif /* HAVE_LIBCURL */
4191 /* Extract message document into reallocated document structure
4192 * @context is ISDS context
4193 * @document is automatically reallocated message documents structure
4194 * @xpath_ctx is XPath context with current node as isds:dmFile
4195 * In case of error @document will be freed. */
4196 static isds_error
extract_document(struct isds_ctx
*context
,
4197 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
4198 isds_error err
= IE_SUCCESS
;
4199 xmlXPathObjectPtr result
= NULL
;
4200 xmlNodePtr file_node
;
4201 char *string
= NULL
;
4203 if (!context
) return IE_INVALID_CONTEXT
;
4204 if (!document
) return IE_INVAL
;
4205 isds_document_free(document
);
4206 if (!xpath_ctx
) return IE_INVAL
;
4207 file_node
= xpath_ctx
->node
;
4209 *document
= calloc(1, sizeof(**document
));
4215 /* Extract document meta data */
4216 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
4217 if (context
->normalize_mime_type
) {
4218 const char *normalized_type
=
4219 isds_normalize_mime_type((*document
)->dmMimeType
);
4220 if (NULL
!= normalized_type
&&
4221 normalized_type
!= (*document
)->dmMimeType
) {
4222 char *new_type
= strdup(normalized_type
);
4223 if (NULL
== new_type
) {
4224 isds_printf_message(context
,
4225 _("Not enough memory to normalize document MIME type"));
4229 free((*document
)->dmMimeType
);
4230 (*document
)->dmMimeType
= new_type
;
4234 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
4235 err
= string2isds_FileMetaType((xmlChar
*)string
,
4236 &((*document
)->dmFileMetaType
));
4238 char *meta_type_locale
= _isds_utf82locale(string
);
4239 isds_printf_message(context
,
4240 _("Document has invalid dmFileMetaType attribute value: %s"),
4242 free(meta_type_locale
);
4248 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
4249 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
4250 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
4251 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
4254 /* Extract document data.
4255 * Base64 encoded blob or XML subtree must be presented. */
4257 /* Check for dmEncodedContent */
4258 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
4265 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4266 /* Here we have Base64 blob */
4267 (*document
)->is_xml
= 0;
4269 if (result
->nodesetval
->nodeNr
> 1) {
4270 isds_printf_message(context
,
4271 _("Document has more dmEncodedContent elements"));
4276 xmlXPathFreeObject(result
); result
= NULL
;
4277 EXTRACT_STRING("isds:dmEncodedContent", string
);
4279 /* Decode non-empty document */
4280 if (string
&& string
[0] != '\0') {
4281 (*document
)->data_length
=
4282 _isds_b64decode(string
, &((*document
)->data
));
4283 if ((*document
)->data_length
== (size_t) -1) {
4284 isds_printf_message(context
,
4285 _("Error while Base64-decoding document content"));
4291 /* No Base64 blob, try XML document */
4292 xmlXPathFreeObject(result
); result
= NULL
;
4293 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4300 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4301 /* Here we have XML document */
4302 (*document
)->is_xml
= 1;
4304 if (result
->nodesetval
->nodeNr
> 1) {
4305 isds_printf_message(context
,
4306 _("Document has more dmXMLContent elements"));
4311 /* XXX: We cannot serialize the content simply because:
4312 * - XML document may point out of its scope (e.g. to message
4314 * - isds:dmXMLContent can contain more elements, no element,
4316 * - it's not the XML way
4317 * Thus we provide the only right solution: XML DOM. Let's
4318 * application to cope with this hot potato :) */
4319 (*document
)->xml_node_list
=
4320 result
->nodesetval
->nodeTab
[0]->children
;
4322 /* No base64 blob, nor XML document */
4323 isds_printf_message(context
,
4324 _("Document has no dmEncodedContent, nor dmXMLContent "
4333 if (err
) isds_document_free(document
);
4335 xmlXPathFreeObject(result
);
4336 xpath_ctx
->node
= file_node
;
4342 /* Extract message documents into reallocated list of documents
4343 * @context is ISDS context
4344 * @documents is automatically reallocated message documents list structure
4345 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4346 * In case of error @documents will be freed. */
4347 static isds_error
extract_documents(struct isds_ctx
*context
,
4348 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4349 isds_error err
= IE_SUCCESS
;
4350 xmlXPathObjectPtr result
= NULL
;
4351 xmlNodePtr files_node
;
4352 struct isds_list
*document
, *prev_document
= NULL
;
4354 if (!context
) return IE_INVALID_CONTEXT
;
4355 if (!documents
) return IE_INVAL
;
4356 isds_list_free(documents
);
4357 if (!xpath_ctx
) return IE_INVAL
;
4358 files_node
= xpath_ctx
->node
;
4360 /* Find documents */
4361 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4368 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4369 isds_printf_message(context
,
4370 _("Message does not contain any document"));
4376 /* Iterate over documents */
4377 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4379 /* Allocate and append list item */
4380 document
= calloc(1, sizeof(*document
));
4385 document
->destructor
= (void (*)(void **))isds_document_free
;
4386 if (i
== 0) *documents
= document
;
4387 else prev_document
->next
= document
;
4388 prev_document
= document
;
4390 /* Extract document */
4391 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4392 err
= extract_document(context
,
4393 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4394 if (err
) goto leave
;
4399 if (err
) isds_list_free(documents
);
4400 xmlXPathFreeObject(result
);
4401 xpath_ctx
->node
= files_node
;
4407 /* Convert isds:dmRecord XML tree into structure
4408 * @context is ISDS context
4409 * @envelope is automatically reallocated message envelope structure
4410 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4411 * In case of error @envelope will be freed. */
4412 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4413 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4414 isds_error err
= IE_SUCCESS
;
4415 xmlXPathObjectPtr result
= NULL
;
4417 if (!context
) return IE_INVALID_CONTEXT
;
4418 if (!envelope
) return IE_INVAL
;
4419 isds_envelope_free(envelope
);
4420 if (!xpath_ctx
) return IE_INVAL
;
4423 *envelope
= calloc(1, sizeof(**envelope
));
4430 /* Extract tRecord data */
4431 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4433 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4434 * dmAcceptanceTime. */
4435 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4436 if (err
) goto leave
;
4438 /* Extract envelope elements added by sender and ISDS
4439 * (XSD: gMessageEnvelope type) */
4440 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4441 if (err
) goto leave
;
4443 /* Get message type */
4444 err
= append_message_type(context
, envelope
, xpath_ctx
);
4445 if (err
) goto leave
;
4449 if (err
) isds_envelope_free(envelope
);
4450 xmlXPathFreeObject(result
);
4455 /* Convert XSD:tStateChangesRecord type XML tree into structure
4456 * @context is ISDS context
4457 * @changed_status is automatically reallocated message state change structure
4458 * @xpath_ctx is XPath context with current node as element of
4459 * XSD:tStateChangesRecord type
4460 * In case of error @changed_status will be freed. */
4461 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4462 struct isds_message_status_change
**changed_status
,
4463 xmlXPathContextPtr xpath_ctx
) {
4464 isds_error err
= IE_SUCCESS
;
4465 xmlXPathObjectPtr result
= NULL
;
4466 unsigned long int *unumber
= NULL
;
4467 char *string
= NULL
;
4469 if (!context
) return IE_INVALID_CONTEXT
;
4470 if (!changed_status
) return IE_INVAL
;
4471 isds_message_status_change_free(changed_status
);
4472 if (!xpath_ctx
) return IE_INVAL
;
4475 *changed_status
= calloc(1, sizeof(**changed_status
));
4476 if (!*changed_status
) {
4482 /* Extract tGetStateChangesInput data */
4483 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4485 /* dmEventTime is mandatory */
4486 EXTRACT_STRING("isds:dmEventTime", string
);
4488 err
= timestring2timeval((xmlChar
*) string
,
4489 &((*changed_status
)->time
));
4491 char *string_locale
= _isds_utf82locale(string
);
4492 if (err
== IE_DATE
) err
= IE_ISDS
;
4493 isds_printf_message(context
,
4494 _("Could not convert dmEventTime as ISO time: %s"),
4496 free(string_locale
);
4502 /* dmMessageStatus element is mandatory */
4503 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4505 isds_log_message(context
,
4506 _("Missing mandatory isds:dmMessageStatus integer"));
4510 err
= uint2isds_message_status(context
, unumber
,
4511 &((*changed_status
)->dmMessageStatus
));
4513 if (err
== IE_ENUM
) err
= IE_ISDS
;
4522 if (err
) isds_message_status_change_free(changed_status
);
4523 xmlXPathFreeObject(result
);
4526 #endif /* HAVE_LIBCURL */
4529 /* Find and convert isds:dmHash XML tree into structure
4530 * @context is ISDS context
4531 * @envelope is automatically reallocated message hash structure
4532 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4533 * In case of error @hash will be freed. */
4534 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4535 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4536 isds_error err
= IE_SUCCESS
;
4537 xmlNodePtr old_ctx_node
;
4538 xmlXPathObjectPtr result
= NULL
;
4539 char *string
= NULL
;
4541 if (!context
) return IE_INVALID_CONTEXT
;
4542 if (!hash
) return IE_INVAL
;
4543 isds_hash_free(hash
);
4544 if (!xpath_ctx
) return IE_INVAL
;
4546 old_ctx_node
= xpath_ctx
->node
;
4548 *hash
= calloc(1, sizeof(**hash
));
4555 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4556 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4565 /* Get hash algorithm */
4566 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4567 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4569 if (err
== IE_ENUM
) {
4570 char *string_locale
= _isds_utf82locale(string
);
4571 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4573 free(string_locale
);
4579 /* Get hash value */
4580 EXTRACT_STRING(".", string
);
4582 isds_printf_message(context
,
4583 _("sisds:dmHash element is missing hash value"));
4587 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4588 if ((*hash
)->length
== (size_t) -1) {
4589 isds_printf_message(context
,
4590 _("Error while Base64-decoding hash value"));
4596 if (err
) isds_hash_free(hash
);
4598 xmlXPathFreeObject(result
);
4599 xpath_ctx
->node
= old_ctx_node
;
4604 /* Find and append isds:dmQTimestamp XML tree into envelope.
4605 * Because one service is allowed to miss time-stamp content, and we think
4606 * other could too (flaw in specification), this function is deliberated and
4607 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4608 * @context is ISDS context
4609 * @envelope is automatically allocated envelope structure
4610 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4612 * In case of error @envelope will be freed. */
4613 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4614 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4615 isds_error err
= IE_SUCCESS
;
4616 xmlXPathObjectPtr result
= NULL
;
4617 char *string
= NULL
;
4619 if (!context
) return IE_INVALID_CONTEXT
;
4620 if (!envelope
) return IE_INVAL
;
4622 isds_envelope_free(envelope
);
4627 *envelope
= calloc(1, sizeof(**envelope
));
4633 zfree((*envelope
)->timestamp
);
4634 (*envelope
)->timestamp_length
= 0;
4637 /* Get dmQTimestamp */
4638 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4640 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4643 (*envelope
)->timestamp_length
=
4644 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4645 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4646 isds_printf_message(context
,
4647 _("Error while Base64-decoding time stamp value"));
4653 if (err
) isds_envelope_free(envelope
);
4655 xmlXPathFreeObject(result
);
4660 /* Convert XSD tReturnedMessage XML tree into message structure.
4661 * It does not store serialized XML tree into message->raw.
4662 * It does store (pointer to) parsed XML tree into message->xml if needed.
4663 * @context is ISDS context
4664 * @include_documents Use true if documents must be extracted
4665 * (tReturnedMessage XSD type), use false if documents shall be omitted
4666 * (tReturnedMessageEnvelope).
4667 * @message is automatically reallocated message structure
4668 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4670 * In case of error @message will be freed. */
4671 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4672 const _Bool include_documents
, struct isds_message
**message
,
4673 xmlXPathContextPtr xpath_ctx
) {
4674 isds_error err
= IE_SUCCESS
;
4675 xmlNodePtr message_node
;
4677 if (!context
) return IE_INVALID_CONTEXT
;
4678 if (!message
) return IE_INVAL
;
4679 isds_message_free(message
);
4680 if (!xpath_ctx
) return IE_INVAL
;
4683 *message
= calloc(1, sizeof(**message
));
4689 /* Save message XPATH context node */
4690 message_node
= xpath_ctx
->node
;
4694 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4695 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4696 if (err
) { err
= IE_ERROR
; goto leave
; }
4697 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4698 if (err
) goto leave
;
4700 if (include_documents
) {
4701 struct isds_list
*item
;
4703 /* Extract dmFiles */
4704 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4706 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4707 err
= IE_ISDS
; goto leave
;
4709 if (err
) { err
= IE_ERROR
; goto leave
; }
4710 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4711 if (err
) goto leave
;
4713 /* Store xmlDoc of this message if needed */
4714 /* Only if we got a XML document in all the documents. */
4715 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4716 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4717 (*message
)->xml
= xpath_ctx
->doc
;
4724 /* Restore context to message */
4725 xpath_ctx
->node
= message_node
;
4727 /* Extract dmHash */
4728 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4730 if (err
) goto leave
;
4732 /* Extract dmQTimestamp, */
4733 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4735 if (err
) goto leave
;
4737 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4738 * dmAcceptanceTime. */
4739 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4740 if (err
) goto leave
;
4742 /* Get message type */
4743 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4744 if (err
) goto leave
;
4747 if (err
) isds_message_free(message
);
4752 /* Extract message event into reallocated isds_event structure
4753 * @context is ISDS context
4754 * @event is automatically reallocated message event structure
4755 * @xpath_ctx is XPath context with current node as isds:dmEvent
4756 * In case of error @event will be freed. */
4757 static isds_error
extract_event(struct isds_ctx
*context
,
4758 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4759 isds_error err
= IE_SUCCESS
;
4760 xmlXPathObjectPtr result
= NULL
;
4761 xmlNodePtr event_node
;
4762 char *string
= NULL
;
4764 if (!context
) return IE_INVALID_CONTEXT
;
4765 if (!event
) return IE_INVAL
;
4766 isds_event_free(event
);
4767 if (!xpath_ctx
) return IE_INVAL
;
4768 event_node
= xpath_ctx
->node
;
4770 *event
= calloc(1, sizeof(**event
));
4776 /* Extract event data.
4777 * All elements are optional according XSD. That's funny. */
4778 EXTRACT_STRING("sisds:dmEventTime", string
);
4780 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4782 char *string_locale
= _isds_utf82locale(string
);
4783 if (err
== IE_DATE
) err
= IE_ISDS
;
4784 isds_printf_message(context
,
4785 _("Could not convert dmEventTime as ISO time: %s"),
4787 free(string_locale
);
4793 /* dmEventDescr element has prefix and the rest */
4794 EXTRACT_STRING("sisds:dmEventDescr", string
);
4796 err
= eventstring2event((xmlChar
*) string
, *event
);
4797 if (err
) goto leave
;
4802 if (err
) isds_event_free(event
);
4804 xmlXPathFreeObject(result
);
4805 xpath_ctx
->node
= event_node
;
4810 /* Convert element of XSD tEventsArray type from XML tree into
4811 * isds_list of isds_event's structure. The list is automatically reallocated.
4812 * @context is ISDS context
4813 * @events is automatically reallocated list of event structures
4814 * @xpath_ctx is XPath context with current node as tEventsArray
4815 * In case of error @events will be freed. */
4816 static isds_error
extract_events(struct isds_ctx
*context
,
4817 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4818 isds_error err
= IE_SUCCESS
;
4819 xmlXPathObjectPtr result
= NULL
;
4820 xmlNodePtr events_node
;
4821 struct isds_list
*event
, *prev_event
= NULL
;
4823 if (!context
) return IE_INVALID_CONTEXT
;
4824 if (!events
) return IE_INVAL
;
4825 if (!xpath_ctx
) return IE_INVAL
;
4826 events_node
= xpath_ctx
->node
;
4829 isds_list_free(events
);
4832 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4839 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4840 isds_printf_message(context
,
4841 _("Delivery info does not contain any event"));
4847 /* Iterate over events */
4848 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4850 /* Allocate and append list item */
4851 event
= calloc(1, sizeof(*event
));
4856 event
->destructor
= (void (*)(void **))isds_event_free
;
4857 if (i
== 0) *events
= event
;
4858 else prev_event
->next
= event
;
4862 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4863 err
= extract_event(context
,
4864 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4865 if (err
) goto leave
;
4870 if (err
) isds_list_free(events
);
4871 xmlXPathFreeObject(result
);
4872 xpath_ctx
->node
= events_node
;
4878 /* Insert Base64 encoded data as element with text child.
4879 * @context is session context
4880 * @parent is XML node to append @element with @data as child
4881 * @ns is XML namespace of @element, use NULL to inherit from @parent
4882 * @element is UTF-8 encoded name of new element
4883 * @data is bit stream to encode into @element
4884 * @length is size of @data in bytes
4885 * @return standard error code and fill long error message if needed */
4886 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4887 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4888 const void *data
, size_t length
) {
4889 isds_error err
= IE_SUCCESS
;
4892 if (!context
) return IE_INVALID_CONTEXT
;
4893 if (!data
&& length
> 0) return IE_INVAL
;
4894 if (!parent
|| !element
) return IE_INVAL
;
4896 xmlChar
*base64data
= NULL
;
4897 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4899 isds_printf_message(context
,
4900 ngettext("Not enough memory to encode %zd byte into Base64",
4901 "Not enough memory to encode %zd bytes into Base64",
4907 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4915 /* Convert isds_document structure into XML tree and append to dmFiles node.
4916 * @context is session context
4917 * @document is ISDS document
4918 * @dm_files is XML element the resulting tree will be appended to as a child.
4919 * @return error code, in case of error context' message is filled. */
4920 static isds_error
insert_document(struct isds_ctx
*context
,
4921 struct isds_document
*document
, xmlNodePtr dm_files
) {
4922 isds_error err
= IE_SUCCESS
;
4923 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4924 xmlAttrPtr attribute_node
;
4926 if (!context
) return IE_INVALID_CONTEXT
;
4927 if (!document
|| !dm_files
) return IE_INVAL
;
4929 /* Allocate new dmFile */
4930 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4932 isds_printf_message(context
, _("Could not allocate main dmFile"));
4936 /* Append the new dmFile.
4937 * XXX: Main document must go first */
4938 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4939 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4941 file
= xmlAddChild(dm_files
, new_file
);
4944 xmlFreeNode(new_file
); new_file
= NULL
;
4945 isds_printf_message(context
, _("Could not add dmFile child to "
4946 "%s element"), dm_files
->name
);
4951 /* @dmMimeType is required */
4952 if (!document
->dmMimeType
) {
4953 isds_log_message(context
,
4954 _("Document is missing mandatory MIME type definition"));
4958 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4960 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4962 isds_printf_message(context
,
4963 _("Document has unknown dmFileMetaType: %ld"),
4964 document
->dmFileMetaType
);
4968 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4970 if (document
->dmFileGuid
) {
4971 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4973 if (document
->dmUpFileGuid
) {
4974 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4977 /* @dmFileDescr is required */
4978 if (!document
->dmFileDescr
) {
4979 isds_log_message(context
,
4980 _("Document is missing mandatory description (title)"));
4984 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4986 if (document
->dmFormat
) {
4987 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4991 /* Insert content (body) of the document. */
4992 if (document
->is_xml
) {
4993 /* XML document requested */
4995 /* Allocate new dmXMLContent */
4996 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4998 isds_printf_message(context
,
4999 _("Could not allocate dmXMLContent element"));
5004 node
= xmlAddChild(file
, xmlcontent
);
5006 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
5007 isds_printf_message(context
,
5008 _("Could not add dmXMLContent child to %s element"),
5014 /* Copy non-empty node list */
5015 if (document
->xml_node_list
) {
5016 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
5017 document
->xml_node_list
);
5019 isds_printf_message(context
,
5020 _("Not enough memory to copy XML document"));
5025 if (!xmlAddChildList(node
, content
)) {
5026 xmlFreeNodeList(content
);
5027 isds_printf_message(context
,
5028 _("Error while adding XML document into dmXMLContent"));
5032 /* XXX: We cannot free the content here because it's part of node's
5033 * document since now. It will be freed with it automatically. */
5036 /* Binary document requested */
5037 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
5038 document
->data
, document
->data_length
);
5039 if (err
) goto leave
;
5047 /* Append XSD tMStatus XML tree into isds_message_copy structure.
5048 * The copy must be preallocated, the date are just appended into structure.
5049 * @context is ISDS context
5050 * @copy is message copy structure
5051 * @xpath_ctx is XPath context with current node as tMStatus */
5052 static isds_error
append_TMStatus(struct isds_ctx
*context
,
5053 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
5054 isds_error err
= IE_SUCCESS
;
5055 xmlXPathObjectPtr result
= NULL
;
5056 char *code
= NULL
, *message
= NULL
;
5058 if (!context
) return IE_INVALID_CONTEXT
;
5059 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
5061 /* Free old values */
5062 zfree(copy
->dmStatus
);
5065 /* Get error specific to this copy */
5066 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
5068 isds_log_message(context
,
5069 _("Missing isds:dmStatusCode under "
5070 "XSD:tMStatus type element"));
5075 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
5076 /* This copy failed */
5077 copy
->error
= IE_ISDS
;
5078 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
5080 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
5081 if (!copy
->dmStatus
) {
5082 copy
->dmStatus
= code
;
5086 copy
->dmStatus
= code
;
5090 /* This copy succeeded. In this case only, message ID is valid */
5091 copy
->error
= IE_SUCCESS
;
5093 EXTRACT_STRING("isds:dmID", copy
->dmID
);
5095 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
5096 "but did not returned assigned message ID\n"));
5104 xmlXPathFreeObject(result
);
5109 /* Insert struct isds_approval data (box approval) into XML tree
5110 * @context is session context
5111 * @approval is libisds structure with approval description. NULL is
5113 * @parent is XML element to append @approval to */
5114 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
5115 const struct isds_approval
*approval
, xmlNodePtr parent
) {
5117 isds_error err
= IE_SUCCESS
;
5120 if (!context
) return IE_INVALID_CONTEXT
;
5121 if (!parent
) return IE_INVAL
;
5123 if (!approval
) return IE_SUCCESS
;
5125 /* Build XSD:gExtApproval */
5126 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
5127 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
5134 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
5136 * @context is session context
5137 * @service_name is name of SERVICE_DB_ACCESS
5138 * @response is reallocated server SOAP body response as XML document
5139 * @raw_response is reallocated bit stream with response body. Use
5140 * NULL if you don't care
5141 * @raw_response_length is size of @raw_response in bytes
5142 * @code is reallocated ISDS status code
5143 * @status_message is reallocated ISDS status message
5144 * @return error coded from lower layer, context message will be set up
5146 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
5147 const xmlChar
*service_name
,
5148 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
5149 xmlChar
**code
, xmlChar
**status_message
) {
5151 isds_error err
= IE_SUCCESS
;
5152 char *service_name_locale
= NULL
;
5153 xmlNodePtr request
= NULL
, node
;
5154 xmlNsPtr isds_ns
= NULL
;
5156 if (!context
) return IE_INVALID_CONTEXT
;
5157 if (!service_name
) return IE_INVAL
;
5158 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
5159 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
5161 /* Free output argument */
5162 xmlFreeDoc(*response
); *response
= NULL
;
5163 if (raw_response
) zfree(*raw_response
);
5165 zfree(*status_message
);
5168 /* Check if connection is established
5169 * TODO: This check should be done downstairs. */
5170 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5172 service_name_locale
= _isds_utf82locale((char*)service_name
);
5173 if (!service_name_locale
)
5177 request
= xmlNewNode(NULL
, service_name
);
5179 isds_printf_message(context
,
5180 _("Could not build %s request"), service_name_locale
);
5184 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5186 isds_log_message(context
, _("Could not create ISDS name space"));
5190 xmlSetNs(request
, isds_ns
);
5193 /* Add XSD:tDummyInput child */
5194 INSERT_STRING(request
, "dbDummy", NULL
);
5197 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5198 service_name_locale
);
5201 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
5202 raw_response
, raw_response_length
);
5203 xmlFreeNode(request
); request
= NULL
;
5206 isds_log(ILF_ISDS
, ILL_DEBUG
,
5207 _("Processing ISDS response on %s request failed\n"),
5208 service_name_locale
);
5212 /* Check for response status */
5213 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
5214 code
, status_message
, NULL
);
5216 isds_log(ILF_ISDS
, ILL_DEBUG
,
5217 _("ISDS response on %s request is missing status\n"),
5218 service_name_locale
);
5222 /* Request processed, but nothing found */
5223 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
5224 char *code_locale
= _isds_utf82locale((char*) *code
);
5225 char *status_message_locale
=
5226 _isds_utf82locale((char*) *status_message
);
5227 isds_log(ILF_ISDS
, ILL_DEBUG
,
5228 _("Server refused %s request (code=%s, message=%s)\n"),
5229 service_name_locale
, code_locale
, status_message_locale
);
5230 isds_log_message(context
, status_message_locale
);
5232 free(status_message_locale
);
5238 free(service_name_locale
);
5239 xmlFreeNode(request
);
5245 /* Get data about logged in user and his box.
5246 * @context is session context
5247 * @db_owner_info is reallocated box owner description. It will be freed on
5249 * @return error code from lower layer, context message will be set up
5251 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
5252 struct isds_DbOwnerInfo
**db_owner_info
) {
5253 isds_error err
= IE_SUCCESS
;
5255 xmlDocPtr response
= NULL
;
5256 xmlChar
*code
= NULL
, *message
= NULL
;
5257 xmlXPathContextPtr xpath_ctx
= NULL
;
5258 xmlXPathObjectPtr result
= NULL
;
5259 char *string
= NULL
;
5262 if (!context
) return IE_INVALID_CONTEXT
;
5263 zfree(context
->long_message
);
5264 if (!db_owner_info
) return IE_INVAL
;
5265 isds_DbOwnerInfo_free(db_owner_info
);
5268 /* Check if connection is established */
5269 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5272 /* Do request and check for success */
5273 err
= build_send_check_dbdummy_request(context
,
5274 BAD_CAST
"GetOwnerInfoFromLogin",
5275 &response
, NULL
, NULL
, &code
, &message
);
5276 if (err
) goto leave
;
5280 /* Prepare structure */
5281 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5282 if (!*db_owner_info
) {
5286 xpath_ctx
= xmlXPathNewContext(response
);
5291 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5296 /* Set context node */
5297 result
= xmlXPathEvalExpression(BAD_CAST
5298 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5303 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5304 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5308 if (result
->nodesetval
->nodeNr
> 1) {
5309 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5313 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5314 xmlXPathFreeObject(result
); result
= NULL
;
5317 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5322 isds_DbOwnerInfo_free(db_owner_info
);
5326 xmlXPathFreeObject(result
);
5327 xmlXPathFreeContext(xpath_ctx
);
5331 xmlFreeDoc(response
);
5334 isds_log(ILF_ISDS
, ILL_DEBUG
,
5335 _("GetOwnerInfoFromLogin request processed by server "
5336 "successfully.\n"));
5337 #else /* not HAVE_LIBCURL */
5345 /* Get data about logged in user. */
5346 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5347 struct isds_DbUserInfo
**db_user_info
) {
5348 isds_error err
= IE_SUCCESS
;
5350 xmlDocPtr response
= NULL
;
5351 xmlChar
*code
= NULL
, *message
= NULL
;
5352 xmlXPathContextPtr xpath_ctx
= NULL
;
5353 xmlXPathObjectPtr result
= NULL
;
5356 if (!context
) return IE_INVALID_CONTEXT
;
5357 zfree(context
->long_message
);
5358 if (!db_user_info
) return IE_INVAL
;
5359 isds_DbUserInfo_free(db_user_info
);
5362 /* Check if connection is established */
5363 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5366 /* Do request and check for success */
5367 err
= build_send_check_dbdummy_request(context
,
5368 BAD_CAST
"GetUserInfoFromLogin",
5369 &response
, NULL
, NULL
, &code
, &message
);
5370 if (err
) goto leave
;
5374 /* Prepare structure */
5375 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5376 if (!*db_user_info
) {
5380 xpath_ctx
= xmlXPathNewContext(response
);
5385 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5390 /* Set context node */
5391 result
= xmlXPathEvalExpression(BAD_CAST
5392 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5397 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5398 isds_log_message(context
, _("Missing dbUserInfo element"));
5402 if (result
->nodesetval
->nodeNr
> 1) {
5403 isds_log_message(context
, _("Multiple dbUserInfo element"));
5407 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5408 xmlXPathFreeObject(result
); result
= NULL
;
5411 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5415 isds_DbUserInfo_free(db_user_info
);
5418 xmlXPathFreeObject(result
);
5419 xmlXPathFreeContext(xpath_ctx
);
5423 xmlFreeDoc(response
);
5426 isds_log(ILF_ISDS
, ILL_DEBUG
,
5427 _("GetUserInfoFromLogin request processed by server "
5428 "successfully.\n"));
5429 #else /* not HAVE_LIBCURL */
5437 /* Get expiration time of current password
5438 * @context is session context
5439 * @expiration is automatically reallocated time when password expires. If
5440 * password expiration is disabled, NULL will be returned. In case of error
5441 * it will be nulled too. */
5442 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5443 struct timeval
**expiration
) {
5444 isds_error err
= IE_SUCCESS
;
5446 xmlDocPtr response
= NULL
;
5447 xmlChar
*code
= NULL
, *message
= NULL
;
5448 xmlXPathContextPtr xpath_ctx
= NULL
;
5449 xmlXPathObjectPtr result
= NULL
;
5450 char *string
= NULL
;
5453 if (!context
) return IE_INVALID_CONTEXT
;
5454 zfree(context
->long_message
);
5455 if (!expiration
) return IE_INVAL
;
5459 /* Check if connection is established */
5460 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5463 /* Do request and check for success */
5464 err
= build_send_check_dbdummy_request(context
,
5465 BAD_CAST
"GetPasswordInfo",
5466 &response
, NULL
, NULL
, &code
, &message
);
5467 if (err
) goto leave
;
5471 xpath_ctx
= xmlXPathNewContext(response
);
5476 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5481 /* Set context node */
5482 result
= xmlXPathEvalExpression(BAD_CAST
5483 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5488 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5489 isds_log_message(context
,
5490 _("Missing GetPasswordInfoResponse element"));
5494 if (result
->nodesetval
->nodeNr
> 1) {
5495 isds_log_message(context
,
5496 _("Multiple GetPasswordInfoResponse element"));
5500 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5501 xmlXPathFreeObject(result
); result
= NULL
;
5503 /* Extract expiration date */
5504 EXTRACT_STRING("isds:pswExpDate", string
);
5506 /* And convert it if any returned. Otherwise expiration is disabled. */
5507 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5509 char *string_locale
= _isds_utf82locale(string
);
5510 if (err
== IE_DATE
) err
= IE_ISDS
;
5511 isds_printf_message(context
,
5512 _("Could not convert pswExpDate as ISO time: %s"),
5514 free(string_locale
);
5527 xmlXPathFreeObject(result
);
5528 xmlXPathFreeContext(xpath_ctx
);
5532 xmlFreeDoc(response
);
5535 isds_log(ILF_ISDS
, ILL_DEBUG
,
5536 _("GetPasswordInfo request processed by server "
5537 "successfully.\n"));
5538 #else /* not HAVE_LIBCURL */
5547 /* Request delivering new TOTP code from ISDS through side channel before
5548 * changing password.
5549 * @context is session context
5550 * @password is current password.
5551 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5552 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5553 * function for more details.
5554 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5555 * NULL, if you don't care.
5556 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5558 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5559 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5560 isds_error err
= IE_SUCCESS
;
5561 char *saved_url
= NULL
; /* No copy */
5562 #if HAVE_CURL_REAUTHORIZATION_BUG
5563 CURL
*saved_curl
= NULL
; /* No copy */
5565 xmlNsPtr isds_ns
= NULL
;
5566 xmlNodePtr request
= NULL
;
5567 xmlDocPtr response
= NULL
;
5568 xmlChar
*code
= NULL
, *message
= NULL
;
5569 const xmlChar
*codes
[] = {
5574 const char *meanings
[] = {
5575 N_("Unexpected error"),
5576 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5577 N_("One-time code could not been sent. Try later again.")
5579 const isds_otp_resolution resolutions
[] = {
5580 OTP_RESOLUTION_UNKNOWN
,
5581 OTP_RESOLUTION_TO_FAST
,
5582 OTP_RESOLUTION_TOTP_NOT_SENT
5585 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5586 zfree(context
->long_message
);
5587 if (NULL
== password
) {
5588 isds_log_message(context
,
5589 _("Second argument (password) of isds_change_password() "
5594 /* Check if connection is established
5595 * TODO: This check should be done downstairs. */
5596 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5598 if (!context
->otp
) {
5599 isds_log_message(context
, _("This function requires OTP-authenticated "
5601 return IE_INVALID_CONTEXT
;
5604 isds_log_message(context
, _("If one-time password authentication "
5605 "method is in use, requesting new OTP code requires "
5606 "one-time credentials argument either"));
5609 if (otp
->method
!= OTP_TIME
) {
5610 isds_log_message(context
, _("Requesting new time-based OTP code from "
5611 "server requires one-time password authentication "
5615 if (otp
->otp_code
!= NULL
) {
5616 isds_log_message(context
, _("Requesting new time-based OTP code from "
5617 "server requires undefined OTP code member in "
5618 "one-time credentials argument"));
5624 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5626 isds_log_message(context
, _("Could not build SendSMSCode request"));
5629 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5631 isds_log_message(context
, _("Could not create ISDS name space"));
5632 xmlFreeNode(request
);
5635 xmlSetNs(request
, isds_ns
);
5637 /* Change URL temporarily for sending this request only */
5639 char *new_url
= NULL
;
5640 if ((err
= _isds_build_url_from_context(context
,
5641 "%.*sasws/changePassword", &new_url
))) {
5644 saved_url
= context
->url
;
5645 context
->url
= new_url
;
5648 /* Store credentials for sending this request only */
5649 context
->otp_credentials
= otp
;
5650 _isds_discard_credentials(context
, 0);
5651 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5653 _isds_discard_credentials(context
, 0);
5656 #if HAVE_CURL_REAUTHORIZATION_BUG
5657 saved_curl
= context
->curl
;
5658 context
->curl
= curl_easy_init();
5659 if (NULL
== context
->curl
) {
5663 if (context
->timeout
) {
5664 err
= isds_set_timeout(context
, context
->timeout
);
5665 if (err
) goto leave
;
5669 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5672 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5674 /* Remove temporal credentials */
5675 _isds_discard_credentials(context
, 0);
5676 /* Detach pointer to OTP credentials from context */
5677 context
->otp_credentials
= NULL
;
5678 /* Keep context->otp true to keep signaling this is OTP session */
5680 /* Destroy request */
5681 xmlFreeNode(request
); request
= NULL
;
5684 isds_log(ILF_ISDS
, ILL_DEBUG
,
5685 _("Processing ISDS response on SendSMSCode request failed\n"));
5689 /* Check for response status */
5690 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5691 &code
, &message
, (xmlChar
**)refnumber
);
5693 isds_log(ILF_ISDS
, ILL_DEBUG
,
5694 _("ISDS response on SendSMSCode request is missing "
5699 /* Check for error */
5700 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5701 char *code_locale
= _isds_utf82locale((char*)code
);
5702 char *message_locale
= _isds_utf82locale((char*)message
);
5704 isds_log(ILF_ISDS
, ILL_DEBUG
,
5705 _("Server refused to send new code on SendSMSCode "
5706 "request (code=%s, message=%s)\n"),
5707 code_locale
, message_locale
);
5709 /* Check for known error codes */
5710 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5711 if (!xmlStrcmp(code
, codes
[i
])) break;
5713 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5714 isds_log_message(context
, _(meanings
[i
]));
5715 /* Mimic otp->resolution according to the code, specification does
5716 * prescribe OTP header to be available. */
5717 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5718 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5719 otp
->resolution
= resolutions
[i
];
5721 isds_log_message(context
, message_locale
);
5724 free(message_locale
);
5730 /* Otherwise new code sent successfully */
5731 /* Mimic otp->resolution according to the code, specification does
5732 * prescribe OTP header to be available. */
5733 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5734 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5737 if (NULL
!= saved_url
) {
5738 /* Revert URL to original one */
5739 zfree(context
->url
);
5740 context
->url
= saved_url
;
5742 #if HAVE_CURL_REAUTHORIZATION_BUG
5743 if (NULL
!= saved_curl
) {
5744 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5745 context
->curl
= saved_curl
;
5751 xmlFreeDoc(response
);
5752 xmlFreeNode(request
);
5755 isds_log(ILF_ISDS
, ILL_DEBUG
,
5756 _("New OTP code has been sent successfully on SendSMSCode "
5762 /* Convert response status code to isds_error code and set long message
5763 * @context is context to save long message to
5764 * @map is mapping from codes to errors and messages. Pass NULL for generic
5766 * @code is status code to translate
5767 * @message is non-localized status message to put into long message in case
5768 * of uknown error. It can be NULL if server did not provide any.
5769 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5770 * invalid invocation. */
5771 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5772 const struct code_map_isds_error
*map
,
5773 const xmlChar
*code
, const xmlChar
*message
) {
5775 isds_log_message(context
,
5776 _("NULL status code passed to statuscode2isds_error()"));
5781 /* Check for known error codes */
5782 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5783 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5784 isds_log_message(context
, _(map
->meanings
[i
]));
5785 return map
->errors
[i
];
5791 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5792 char *message_locale
= _isds_utf82locale((char*)message
);
5793 if (NULL
== message_locale
)
5794 isds_log_message(context
, _("ISDS server returned unknown error"));
5796 isds_log_message(context
, message_locale
);
5797 free(message_locale
);
5806 /* Change user password in ISDS.
5807 * User must supply old password, new password will takes effect after some
5808 * time, current session can continue. Password must fulfill some constraints.
5809 * @context is session context
5810 * @old_password is current password.
5811 * @new_password is requested new password
5812 * @otp auxiliary data required if one-time password authentication is in use,
5813 * defines OTP code (if known) and returns fine grade resolution of OTP
5814 * procedure. Pass NULL, if one-time password authentication is not needed.
5815 * Please note the @otp argument must match OTP method used at log-in time. See
5816 * isds_login() function for more details.
5817 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5818 * NULL, if you don't care.
5819 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5820 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5821 * awaiting OTP code that has been delivered by side channel to the user. */
5822 isds_error
isds_change_password(struct isds_ctx
*context
,
5823 const char *old_password
, const char *new_password
,
5824 struct isds_otp
*otp
, char **refnumber
) {
5825 isds_error err
= IE_SUCCESS
;
5827 char *saved_url
= NULL
; /* No copy */
5828 #if HAVE_CURL_REAUTHORIZATION_BUG
5829 CURL
*saved_curl
= NULL
; /* No copy */
5831 xmlNsPtr isds_ns
= NULL
;
5832 xmlNodePtr request
= NULL
, node
;
5833 xmlDocPtr response
= NULL
;
5834 xmlChar
*code
= NULL
, *message
= NULL
;
5835 const xmlChar
*codes
[] = {
5848 const char *meanings
[] = {
5849 N_("Password length must be between 8 and 32 characters"),
5850 N_("Password cannot be reused"), /* Server does not distinguish 1067
5851 and 1091 on ChangePasswordOTP */
5852 N_("Password contains forbidden character"),
5853 N_("Password must contain at least one upper-case letter, "
5854 "one lower-case, and one digit"),
5855 N_("Password cannot contain sequence of three identical characters"),
5856 N_("Password cannot contain user identifier"),
5857 N_("Password is too simmple"),
5858 N_("Old password is not valid"),
5859 N_("Password cannot be reused"),
5860 N_("Unexpected error"),
5861 N_("LDAP update error")
5865 if (!context
) return IE_INVALID_CONTEXT
;
5866 zfree(context
->long_message
);
5867 if (NULL
!= refnumber
)
5869 if (NULL
== old_password
) {
5870 isds_log_message(context
,
5871 _("Second argument (old password) of isds_change_password() "
5875 if (NULL
== otp
&& NULL
== new_password
) {
5876 isds_log_message(context
,
5877 _("Third argument (new password) of isds_change_password() "
5883 /* Check if connection is established
5884 * TODO: This check should be done downstairs. */
5885 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5887 if (context
->otp
&& NULL
== otp
) {
5888 isds_log_message(context
, _("If one-time password authentication "
5889 "method is in use, changing password requires one-time "
5890 "credentials either"));
5894 /* Build ChangeISDSPassword request */
5895 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5896 BAD_CAST
"ChangePasswordOTP");
5898 isds_log_message(context
, (NULL
== otp
) ?
5899 _("Could not build ChangeISDSPassword request") :
5900 _("Could not build ChangePasswordOTP request"));
5903 isds_ns
= xmlNewNs(request
,
5904 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5907 isds_log_message(context
, _("Could not create ISDS name space"));
5908 xmlFreeNode(request
);
5911 xmlSetNs(request
, isds_ns
);
5913 INSERT_STRING(request
, "dbOldPassword", old_password
);
5914 INSERT_STRING(request
, "dbNewPassword", new_password
);
5917 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5918 switch (otp
->method
) {
5920 isds_log(ILF_SEC
, ILL_INFO
,
5921 _("Selected authentication method: "
5922 "HMAC-based one-time password\n"));
5923 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5926 isds_log(ILF_SEC
, ILL_INFO
,
5927 _("Selected authentication method: "
5928 "Time-based one-time password\n"));
5929 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5930 if (otp
->otp_code
== NULL
) {
5931 isds_log(ILF_SEC
, ILL_INFO
,
5932 _("OTP code has not been provided by "
5933 "application, requesting server for "
5935 err
= _isds_request_totp_code(context
, old_password
, otp
,
5937 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5941 isds_log(ILF_SEC
, ILL_INFO
,
5942 _("OTP code has been provided by "
5943 "application, not requesting server "
5948 isds_log_message(context
,
5949 _("Unknown one-time password authentication "
5950 "method requested by application"));
5955 /* Change URL temporarily for sending this request only */
5957 char *new_url
= NULL
;
5958 if ((err
= _isds_build_url_from_context(context
,
5959 "%.*sasws/changePassword", &new_url
))) {
5962 saved_url
= context
->url
;
5963 context
->url
= new_url
;
5966 /* Store credentials for sending this request only */
5967 context
->otp_credentials
= otp
;
5968 _isds_discard_credentials(context
, 0);
5969 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5970 old_password
, NULL
))) {
5971 _isds_discard_credentials(context
, 0);
5974 #if HAVE_CURL_REAUTHORIZATION_BUG
5975 saved_curl
= context
->curl
;
5976 context
->curl
= curl_easy_init();
5977 if (NULL
== context
->curl
) {
5981 if (context
->timeout
) {
5982 err
= isds_set_timeout(context
, context
->timeout
);
5983 if (err
) goto leave
;
5988 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5989 _("Sending ChangeISDSPassword request to ISDS\n") :
5990 _("Sending ChangePasswordOTP request to ISDS\n"));
5993 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5994 request
, &response
, NULL
, NULL
);
5997 /* Remove temporal credentials */
5998 _isds_discard_credentials(context
, 0);
5999 /* Detach pointer to OTP credentials from context */
6000 context
->otp_credentials
= NULL
;
6001 /* Keep context->otp true to keep signaling this is OTP session */
6004 /* Destroy request */
6005 xmlFreeNode(request
); request
= NULL
;
6008 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6009 _("Processing ISDS response on ChangeISDSPassword "
6010 "request failed\n") :
6011 _("Processing ISDS response on ChangePasswordOTP "
6012 "request failed\n"));
6016 /* Check for response status */
6017 err
= isds_response_status(context
,
6018 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
6019 &code
, &message
, (xmlChar
**)refnumber
);
6021 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6022 _("ISDS response on ChangeISDSPassword request is missing "
6024 _("ISDS response on ChangePasswordOTP request is missing "
6029 /* Check for known error codes */
6030 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
6031 if (!xmlStrcmp(code
, codes
[i
])) {
6032 char *code_locale
= _isds_utf82locale((char*)code
);
6033 char *message_locale
= _isds_utf82locale((char*)message
);
6034 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6035 _("Server refused to change password on ChangeISDSPassword "
6036 "request (code=%s, message=%s)\n") :
6037 _("Server refused to change password on ChangePasswordOTP "
6038 "request (code=%s, message=%s)\n"),
6039 code_locale
, message_locale
);
6041 free(message_locale
);
6042 isds_log_message(context
, _(meanings
[i
]));
6049 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6050 char *code_locale
= _isds_utf82locale((char*)code
);
6051 char *message_locale
= _isds_utf82locale((char*)message
);
6052 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6053 _("Server refused to change password on ChangeISDSPassword "
6054 "request (code=%s, message=%s)\n") :
6055 _("Server refused to change password on ChangePasswordOTP "
6056 "request (code=%s, message=%s)\n"),
6057 code_locale
, message_locale
);
6058 isds_log_message(context
, message_locale
);
6060 free(message_locale
);
6065 /* Otherwise password changed successfully */
6068 if (NULL
!= saved_url
) {
6069 /* Revert URL to original one */
6070 zfree(context
->url
);
6071 context
->url
= saved_url
;
6073 #if HAVE_CURL_REAUTHORIZATION_BUG
6074 if (NULL
!= saved_curl
) {
6075 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
6076 context
->curl
= saved_curl
;
6082 xmlFreeDoc(response
);
6083 xmlFreeNode(request
);
6086 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6087 _("Password changed successfully on ChangeISDSPassword "
6089 _("Password changed successfully on ChangePasswordOTP "
6091 #else /* not HAVE_LIBCURL */
6100 /* Generic middle part with request sending and response check.
6101 * It sends prepared request and checks for error code.
6102 * @context is ISDS session context.
6103 * @service is ISDS service handler
6104 * @service_name is name in scope of given @service
6105 * @request is XML tree with request. Will be freed to save memory.
6106 * @response is XML document outputting ISDS response.
6107 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6108 * @map is mapping from status code to library error. Pass NULL if no special
6109 * handling is requested.
6110 * NULL, if you don't care. */
6111 static isds_error
send_destroy_request_check_response(
6112 struct isds_ctx
*context
,
6113 const isds_service service
, const xmlChar
*service_name
,
6114 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
6115 const struct code_map_isds_error
*map
) {
6116 isds_error err
= IE_SUCCESS
;
6117 char *service_name_locale
= NULL
;
6118 xmlChar
*code
= NULL
, *message
= NULL
;
6121 if (!context
) return IE_INVALID_CONTEXT
;
6122 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
6126 /* Check if connection is established
6127 * TODO: This check should be done downstairs. */
6128 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6130 service_name_locale
= _isds_utf82locale((char*) service_name
);
6131 if (!service_name_locale
) {
6136 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
6137 service_name_locale
);
6140 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
6141 xmlFreeNode(*request
); *request
= NULL
;
6144 isds_log(ILF_ISDS
, ILL_DEBUG
,
6145 _("Processing ISDS response on %s request failed\n"),
6146 service_name_locale
);
6150 /* Check for response status */
6151 err
= isds_response_status(context
, service
, *response
,
6152 &code
, &message
, refnumber
);
6154 isds_log(ILF_ISDS
, ILL_DEBUG
,
6155 _("ISDS response on %s request is missing status\n"),
6156 service_name_locale
);
6160 err
= statuscode2isds_error(context
, map
, code
, message
);
6162 /* Request processed, but server failed */
6163 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6164 char *code_locale
= _isds_utf82locale((char*) code
);
6165 char *message_locale
= _isds_utf82locale((char*) message
);
6166 isds_log(ILF_ISDS
, ILL_DEBUG
,
6167 _("Server refused %s request (code=%s, message=%s)\n"),
6168 service_name_locale
, code_locale
, message_locale
);
6170 free(message_locale
);
6178 if (err
&& *response
) {
6179 xmlFreeDoc(*response
);
6183 xmlFreeNode(*request
);
6186 free(service_name_locale
);
6192 /* Generic bottom half with request sending.
6193 * It sends prepared request, checks for error code, destroys response and
6194 * request and log success or failure.
6195 * @context is ISDS session context.
6196 * @service is ISDS service handler
6197 * @service_name is name in scope of given @service
6198 * @request is XML tree with request. Will be freed to save memory.
6199 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6200 * NULL, if you don't care. */
6201 static isds_error
send_request_check_drop_response(
6202 struct isds_ctx
*context
,
6203 const isds_service service
, const xmlChar
*service_name
,
6204 xmlNodePtr
*request
, xmlChar
**refnumber
) {
6205 isds_error err
= IE_SUCCESS
;
6206 xmlDocPtr response
= NULL
;
6209 if (!context
) return IE_INVALID_CONTEXT
;
6210 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
6213 /* Send request and check response*/
6214 err
= send_destroy_request_check_response(context
,
6215 service
, service_name
, request
, &response
, refnumber
, NULL
);
6217 xmlFreeDoc(response
);
6220 xmlFreeNode(*request
);
6225 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6226 isds_log(ILF_ISDS
, ILL_DEBUG
,
6227 _("%s request processed by server successfully.\n"),
6228 service_name_locale
);
6229 free(service_name_locale
);
6236 /* Insert isds_credentials_delivery structure into XML request if not NULL
6237 * @context is session context
6238 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6239 * credentials delivery. The email field is passed.
6240 * @parent is XML element where to insert */
6241 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
6242 const struct isds_credentials_delivery
*credentials_delivery
,
6243 xmlNodePtr parent
) {
6244 isds_error err
= IE_SUCCESS
;
6247 if (!context
) return IE_INVALID_CONTEXT
;
6248 if (!parent
) return IE_INVAL
;
6250 if (credentials_delivery
) {
6251 /* Following elements are valid only for services:
6252 * NewAccessData, AddDataBoxUser, CreateDataBox */
6253 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
6254 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
6262 /* Extract credentials delivery from ISDS response.
6263 * @context is session context
6264 * @credentials_delivery is pointer to valid structure to fill in returned
6265 * user's password (and new log-in name). If NULL, do not extract the data.
6266 * @response is pointer to XML document with ISDS response
6267 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6268 * @return IE_SUCCESS even if new user name has not been found because it's not
6269 * clear whether it's returned always. */
6270 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
6271 struct isds_credentials_delivery
*credentials_delivery
,
6272 xmlDocPtr response
, const char *request_name
) {
6273 isds_error err
= IE_SUCCESS
;
6274 xmlXPathContextPtr xpath_ctx
= NULL
;
6275 xmlXPathObjectPtr result
= NULL
;
6276 char *xpath_query
= NULL
;
6278 if (!context
) return IE_INVALID_CONTEXT
;
6279 if (credentials_delivery
) {
6280 zfree(credentials_delivery
->token
);
6281 zfree(credentials_delivery
->new_user_name
);
6283 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6286 /* Extract optional token */
6287 if (credentials_delivery
) {
6288 xpath_ctx
= xmlXPathNewContext(response
);
6293 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6298 /* Verify root element */
6299 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6304 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6309 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6310 char *request_name_locale
= _isds_utf82locale(request_name
);
6311 isds_log(ILF_ISDS
, ILL_WARNING
,
6312 _("Wrong element in ISDS response for %s request "
6313 "while extracting credentials delivery details\n"),
6314 request_name_locale
);
6315 free(request_name_locale
);
6319 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6322 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6324 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6326 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6327 if (!credentials_delivery
->token
) {
6328 char *request_name_locale
= _isds_utf82locale(request_name
);
6329 isds_log(ILF_ISDS
, ILL_ERR
,
6330 _("ISDS did not return token on %s request "
6331 "even if requested\n"), request_name_locale
);
6332 free(request_name_locale
);
6339 xmlXPathFreeObject(result
);
6340 xmlXPathFreeContext(xpath_ctx
);
6346 /* Build XSD:tCreateDBInput request type for box creating.
6347 * @context is session context
6348 * @request outputs built XML tree
6349 * @service_name is request name of SERVICE_DB_MANIPULATION service
6350 * @box is box description to create including single primary user (in case of
6351 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6353 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6354 * box, or contact address of PFO box owner)
6355 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6356 * @upper_box_id is optional ID of supper box if currently created box is
6358 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6360 * @credentials_delivery is valid pointer if ISDS should return token that box
6361 * owner can use to obtain his new credentials in on-line way. Then valid email
6362 * member value should be supplied.
6363 * @approval is optional external approval of box manipulation */
6364 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6365 xmlNodePtr
*request
, const xmlChar
*service_name
,
6366 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6367 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6368 const xmlChar
*ceo_label
,
6369 const struct isds_credentials_delivery
*credentials_delivery
,
6370 const struct isds_approval
*approval
) {
6371 isds_error err
= IE_SUCCESS
;
6372 xmlNsPtr isds_ns
= NULL
;
6373 xmlNodePtr node
, dbPrimaryUsers
;
6374 xmlChar
*string
= NULL
;
6375 const struct isds_list
*item
;
6378 if (!context
) return IE_INVALID_CONTEXT
;
6379 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6383 /* Build CreateDataBox-similar request */
6384 *request
= xmlNewNode(NULL
, service_name
);
6386 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6387 isds_printf_message(context
, _("Could build %s request"),
6388 service_name_locale
);
6389 free(service_name_locale
);
6392 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6393 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6395 isds_log_message(context
, _("Could not create ISDS1 name space"));
6396 xmlFreeNode(*request
);
6400 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6402 isds_log_message(context
, _("Could not create ISDS name space"));
6403 xmlFreeNode(*request
);
6407 xmlSetNs(*request
, isds_ns
);
6409 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6410 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6411 if (err
) goto leave
;
6414 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6415 * verbose documentation allows none dbUserInfo */
6416 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6417 for (item
= users
; item
; item
= item
->next
) {
6419 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6420 err
= insert_DbUserInfo(context
,
6421 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6422 if (err
) goto leave
;
6426 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6427 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6428 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6430 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6431 if (err
) goto leave
;
6433 err
= insert_GExtApproval(context
, approval
, *request
);
6434 if (err
) goto leave
;
6438 xmlFreeNode(*request
);
6444 #endif /* HAVE_LIBCURL */
6448 * @context is session context
6449 * @box is box description to create including single primary user (in case of
6450 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6451 * ignored. It outputs box ID assigned by ISDS in dbID element.
6452 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6453 * box, or contact address of PFO box owner)
6454 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6455 * @upper_box_id is optional ID of supper box if currently created box is
6457 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6458 * @credentials_delivery is NULL if new password should be delivered off-line
6459 * to box owner. It is valid pointer if owner should obtain new password on-line
6460 * on dedicated web server. Then input @credentials_delivery.email value is
6461 * his e-mail address he must provide to dedicated web server together
6462 * with output reallocated @credentials_delivery.token member. Output
6463 * member @credentials_delivery.new_user_name is unused up on this call.
6464 * @approval is optional external approval of box manipulation
6465 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6466 * NULL, if you don't care.*/
6467 isds_error
isds_add_box(struct isds_ctx
*context
,
6468 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6469 const char *former_names
, const char *upper_box_id
,
6470 const char *ceo_label
,
6471 struct isds_credentials_delivery
*credentials_delivery
,
6472 const struct isds_approval
*approval
, char **refnumber
) {
6473 isds_error err
= IE_SUCCESS
;
6475 xmlNodePtr request
= NULL
;
6476 xmlDocPtr response
= NULL
;
6477 xmlXPathContextPtr xpath_ctx
= NULL
;
6478 xmlXPathObjectPtr result
= NULL
;
6482 if (!context
) return IE_INVALID_CONTEXT
;
6483 zfree(context
->long_message
);
6484 if (credentials_delivery
) {
6485 zfree(credentials_delivery
->token
);
6486 zfree(credentials_delivery
->new_user_name
);
6488 if (!box
) return IE_INVAL
;
6491 /* Scratch box ID */
6494 /* Build CreateDataBox request */
6495 err
= build_CreateDBInput_request(context
,
6496 &request
, BAD_CAST
"CreateDataBox",
6497 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6498 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6499 if (err
) goto leave
;
6501 /* Send it to server and process response */
6502 err
= send_destroy_request_check_response(context
,
6503 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6504 &response
, (xmlChar
**) refnumber
, NULL
);
6506 /* Extract box ID */
6507 xpath_ctx
= xmlXPathNewContext(response
);
6512 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6516 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6518 /* Extract optional token */
6519 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6523 xmlXPathFreeObject(result
);
6524 xmlXPathFreeContext(xpath_ctx
);
6525 xmlFreeDoc(response
);
6526 xmlFreeNode(request
);
6529 isds_log(ILF_ISDS
, ILL_DEBUG
,
6530 _("CreateDataBox request processed by server successfully.\n"));
6532 #else /* not HAVE_LIBCURL */
6540 /* Notify ISDS about new PFO entity.
6541 * This function has no real effect.
6542 * @context is session context
6543 * @box is PFO description including single primary user. aifoIsds,
6544 * address->adCode, address->adDistrict members are ignored.
6545 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6546 * @former_names is optional undocumented string. Pass NULL if you don't care.
6547 * @upper_box_id is optional ID of supper box if currently created box is
6549 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6550 * @approval is optional external approval of box manipulation
6551 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6552 * NULL, if you don't care.*/
6553 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6554 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6555 const char *former_names
, const char *upper_box_id
,
6556 const char *ceo_label
, const struct isds_approval
*approval
,
6558 isds_error err
= IE_SUCCESS
;
6560 xmlNodePtr request
= NULL
;
6563 if (!context
) return IE_INVALID_CONTEXT
;
6564 zfree(context
->long_message
);
6565 if (!box
) return IE_INVAL
;
6568 /* Build CreateDataBoxPFOInfo request */
6569 err
= build_CreateDBInput_request(context
,
6570 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6571 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6572 (xmlChar
*) ceo_label
, NULL
, approval
);
6573 if (err
) goto leave
;
6575 /* Send it to server and process response */
6576 err
= send_request_check_drop_response(context
,
6577 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6578 (xmlChar
**) refnumber
);
6579 /* XXX: XML Schema names output dbID element but textual documentation
6580 * states no box identifier is returned. */
6582 xmlFreeNode(request
);
6583 #else /* not HAVE_LIBCURL */
6590 /* Common implementation for removing given box.
6591 * @context is session context
6592 * @service_name is UTF-8 encoded name fo ISDS service
6593 * @box is box description to delete. aifoIsds, address->adCode,
6594 * address->adDistrict members are ignored.
6595 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6596 * carry sane value. If NULL, do not inject this information into request.
6597 * @approval is optional external approval of box manipulation
6598 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6599 * NULL, if you don't care.*/
6600 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6601 const xmlChar
*service_name
,
6602 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6603 const struct isds_approval
*approval
, char **refnumber
) {
6604 isds_error err
= IE_SUCCESS
;
6606 xmlNsPtr isds_ns
= NULL
;
6607 xmlNodePtr request
= NULL
;
6609 xmlChar
*string
= NULL
;
6613 if (!context
) return IE_INVALID_CONTEXT
;
6614 zfree(context
->long_message
);
6615 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6619 /* Build DeleteDataBox(Promptly) request */
6620 request
= xmlNewNode(NULL
, service_name
);
6622 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6623 isds_printf_message(context
,
6624 _("Could build %s request"), service_name_locale
);
6625 free(service_name_locale
);
6628 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6630 isds_log_message(context
, _("Could not create ISDS name space"));
6631 xmlFreeNode(request
);
6634 xmlSetNs(request
, isds_ns
);
6636 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6637 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6638 if (err
) goto leave
;
6641 err
= tm2datestring(since
, &string
);
6643 isds_log_message(context
,
6644 _("Could not convert `since' argument to ISO date string"));
6647 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6651 err
= insert_GExtApproval(context
, approval
, request
);
6652 if (err
) goto leave
;
6655 /* Send it to server and process response */
6656 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6657 service_name
, &request
, (xmlChar
**) refnumber
);
6660 xmlFreeNode(request
);
6662 #else /* not HAVE_LIBCURL */
6669 /* Remove given box permanently.
6670 * @context is session context
6671 * @box is box description to delete. aifoIsds, address->adCode,
6672 * address->adDistrict members are ignored.
6673 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6675 * @approval is optional external approval of box manipulation
6676 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6677 * NULL, if you don't care.*/
6678 isds_error
isds_delete_box(struct isds_ctx
*context
,
6679 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6680 const struct isds_approval
*approval
, char **refnumber
) {
6681 if (!context
) return IE_INVALID_CONTEXT
;
6682 zfree(context
->long_message
);
6683 if (!box
|| !since
) return IE_INVAL
;
6685 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6686 box
, since
, approval
, refnumber
);
6690 /* Undocumented function.
6691 * @context is session context
6692 * @box is box description to delete. aifoIsds, address->adCode,
6693 * address->adDistrict members are ignored.
6694 * @approval is optional external approval of box manipulation
6695 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6696 * NULL, if you don't care.*/
6697 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6698 const struct isds_DbOwnerInfo
*box
,
6699 const struct isds_approval
*approval
, char **refnumber
) {
6700 if (!context
) return IE_INVALID_CONTEXT
;
6701 zfree(context
->long_message
);
6702 if (!box
) return IE_INVAL
;
6704 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6705 box
, NULL
, approval
, refnumber
);
6709 /* Update data about given box.
6710 * @context is session context
6711 * @old_box current box description. aifoIsds, address->adCode,
6712 * address->adDistrict members are ignored.
6713 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6714 * address->adDistrict members are ignored.
6715 * @approval is optional external approval of box manipulation
6716 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6717 * NULL, if you don't care.*/
6718 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6719 const struct isds_DbOwnerInfo
*old_box
,
6720 const struct isds_DbOwnerInfo
*new_box
,
6721 const struct isds_approval
*approval
, char **refnumber
) {
6722 isds_error err
= IE_SUCCESS
;
6724 xmlNsPtr isds_ns
= NULL
;
6725 xmlNodePtr request
= NULL
;
6730 if (!context
) return IE_INVALID_CONTEXT
;
6731 zfree(context
->long_message
);
6732 if (!old_box
|| !new_box
) return IE_INVAL
;
6736 /* Build UpdateDataBoxDescr request */
6737 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6739 isds_log_message(context
,
6740 _("Could build UpdateDataBoxDescr request"));
6743 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6745 isds_log_message(context
, _("Could not create ISDS name space"));
6746 xmlFreeNode(request
);
6749 xmlSetNs(request
, isds_ns
);
6751 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6752 err
= insert_DbOwnerInfo(context
, old_box
, 0, node
);
6753 if (err
) goto leave
;
6755 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6756 err
= insert_DbOwnerInfo(context
, new_box
, 0, node
);
6757 if (err
) goto leave
;
6759 err
= insert_GExtApproval(context
, approval
, request
);
6760 if (err
) goto leave
;
6763 /* Send it to server and process response */
6764 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6765 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6768 xmlFreeNode(request
);
6769 #else /* not HAVE_LIBCURL */
6778 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6780 * @context is session context
6781 * @service is SOAP service
6782 * @service_name is name of request in @service
6783 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6784 * @box_id is box ID of interest
6785 * @approval is optional external approval of box manipulation
6786 * @response is server SOAP body response as XML document
6787 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6788 * NULL, if you don't care.
6789 * @return error coded from lower layer, context message will be set up
6791 static isds_error
build_send_dbid_request_check_response(
6792 struct isds_ctx
*context
, const isds_service service
,
6793 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6794 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6795 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6797 isds_error err
= IE_SUCCESS
;
6798 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6799 xmlNodePtr request
= NULL
, node
;
6800 xmlNsPtr isds_ns
= NULL
;
6802 if (!context
) return IE_INVALID_CONTEXT
;
6803 if (!service_name
|| !box_id
) return IE_INVAL
;
6804 if (!response
) return IE_INVAL
;
6806 /* Free output argument */
6807 xmlFreeDoc(*response
); *response
= NULL
;
6809 /* Prepare strings */
6810 service_name_locale
= _isds_utf82locale((char*)service_name
);
6811 if (!service_name_locale
) {
6815 box_id_locale
= _isds_utf82locale((char*)box_id
);
6816 if (!box_id_locale
) {
6822 request
= xmlNewNode(NULL
, service_name
);
6824 isds_printf_message(context
,
6825 _("Could not build %s request for %s box"), service_name_locale
,
6830 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6832 isds_log_message(context
, _("Could not create ISDS name space"));
6836 xmlSetNs(request
, isds_ns
);
6838 /* Add XSD:tIdDbInput children */
6839 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6840 INSERT_STRING(request
, box_id_element
, box_id
);
6841 err
= insert_GExtApproval(context
, approval
, request
);
6842 if (err
) goto leave
;
6844 /* Send request and check response*/
6845 err
= send_destroy_request_check_response(context
,
6846 service
, service_name
, &request
, response
, refnumber
, NULL
);
6849 free(service_name_locale
);
6850 free(box_id_locale
);
6851 xmlFreeNode(request
);
6854 #endif /* HAVE_LIBCURL */
6857 /* Get data about all users assigned to given box.
6858 * @context is session context
6860 * @users is automatically reallocated list of struct isds_DbUserInfo */
6861 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6862 struct isds_list
**users
) {
6863 isds_error err
= IE_SUCCESS
;
6865 xmlDocPtr response
= NULL
;
6866 xmlXPathContextPtr xpath_ctx
= NULL
;
6867 xmlXPathObjectPtr result
= NULL
;
6869 struct isds_list
*item
, *prev_item
= NULL
;
6872 if (!context
) return IE_INVALID_CONTEXT
;
6873 zfree(context
->long_message
);
6874 if (!users
|| !box_id
) return IE_INVAL
;
6875 isds_list_free(users
);
6879 /* Do request and check for success */
6880 err
= build_send_dbid_request_check_response(context
,
6881 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6882 BAD_CAST box_id
, NULL
, &response
, NULL
);
6883 if (err
) goto leave
;
6887 /* Prepare structure */
6888 xpath_ctx
= xmlXPathNewContext(response
);
6893 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6898 /* Set context node */
6899 result
= xmlXPathEvalExpression(BAD_CAST
6900 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6906 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6907 /* Iterate over all users */
6908 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6910 /* Prepare structure */
6911 item
= calloc(1, sizeof(*item
));
6916 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6917 if (i
== 0) *users
= item
;
6918 else prev_item
->next
= item
;
6922 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6923 err
= extract_DbUserInfo(context
,
6924 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6925 if (err
) goto leave
;
6931 isds_list_free(users
);
6934 xmlXPathFreeObject(result
);
6935 xmlXPathFreeContext(xpath_ctx
);
6936 xmlFreeDoc(response
);
6939 isds_log(ILF_ISDS
, ILL_DEBUG
,
6940 _("GetDataBoxUsers request processed by server "
6941 "successfully.\n"));
6942 #else /* not HAVE_LIBCURL */
6950 /* Update data about user assigned to given box.
6951 * @context is session context
6952 * @box is box identification. aifoIsds, address->adCode,
6953 * address->adDistrict members are ignored.
6954 * @old_user identifies user to update, aifo_ticket member is ignored
6955 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6956 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6957 * NULL, if you don't care.*/
6958 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6959 const struct isds_DbOwnerInfo
*box
,
6960 const struct isds_DbUserInfo
*old_user
,
6961 const struct isds_DbUserInfo
*new_user
,
6963 isds_error err
= IE_SUCCESS
;
6965 xmlNsPtr isds_ns
= NULL
;
6966 xmlNodePtr request
= NULL
;
6971 if (!context
) return IE_INVALID_CONTEXT
;
6972 zfree(context
->long_message
);
6973 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6977 /* Build UpdateDataBoxUser request */
6978 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6980 isds_log_message(context
,
6981 _("Could build UpdateDataBoxUser request"));
6984 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6986 isds_log_message(context
, _("Could not create ISDS name space"));
6987 xmlFreeNode(request
);
6990 xmlSetNs(request
, isds_ns
);
6992 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6993 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6994 if (err
) goto leave
;
6996 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6997 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
6998 if (err
) goto leave
;
7000 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
7001 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
7002 if (err
) goto leave
;
7004 /* Send it to server and process response */
7005 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7006 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
7009 xmlFreeNode(request
);
7010 #else /* not HAVE_LIBCURL */
7018 /* Undocumented function.
7019 * @context is session context
7020 * @box_id is UTF-8 encoded box identifier
7021 * @token is UTF-8 encoded temporary password
7022 * @user_id outputs UTF-8 encoded reallocated user identifier
7023 * @password outpus UTF-8 encoded reallocated user password
7024 * Output arguments will be nulled in case of error */
7025 isds_error
isds_activate(struct isds_ctx
*context
,
7026 const char *box_id
, const char *token
,
7027 char **user_id
, char **password
) {
7028 isds_error err
= IE_SUCCESS
;
7030 xmlNsPtr isds_ns
= NULL
;
7031 xmlNodePtr request
= NULL
, node
;
7032 xmlDocPtr response
= NULL
;
7033 xmlXPathContextPtr xpath_ctx
= NULL
;
7034 xmlXPathObjectPtr result
= NULL
;
7038 if (!context
) return IE_INVALID_CONTEXT
;
7039 zfree(context
->long_message
);
7041 if (user_id
) zfree(*user_id
);
7042 if (password
) zfree(*password
);
7044 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
7048 /* Build Activate request */
7049 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
7051 isds_log_message(context
, _("Could build Activate request"));
7054 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7056 isds_log_message(context
, _("Could not create ISDS name space"));
7057 xmlFreeNode(request
);
7060 xmlSetNs(request
, isds_ns
);
7062 INSERT_STRING(request
, "dbAccessDataId", token
);
7063 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
7064 INSERT_STRING(request
, "dbID", box_id
);
7067 /* Send request and check response*/
7068 err
= send_destroy_request_check_response(context
,
7069 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
7070 &response
, NULL
, NULL
);
7071 if (err
) goto leave
;
7075 xpath_ctx
= xmlXPathNewContext(response
);
7080 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7084 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
7090 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7091 isds_log_message(context
, _("Missing ActivateResponse element"));
7095 if (result
->nodesetval
->nodeNr
> 1) {
7096 isds_log_message(context
, _("Multiple ActivateResponse element"));
7100 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7101 xmlXPathFreeObject(result
); result
= NULL
;
7103 EXTRACT_STRING("isds:userId", *user_id
);
7105 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
7106 "but did not return `userId' element.\n"));
7108 EXTRACT_STRING("isds:password", *password
);
7110 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
7111 "but did not return `password' element.\n"));
7114 xmlXPathFreeObject(result
);
7115 xmlXPathFreeContext(xpath_ctx
);
7116 xmlFreeDoc(response
);
7117 xmlFreeNode(request
);
7120 isds_log(ILF_ISDS
, ILL_DEBUG
,
7121 _("Activate request processed by server successfully.\n"));
7122 #else /* not HAVE_LIBCURL */
7130 /* Reset credentials of user assigned to given box.
7131 * @context is session context
7132 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7133 * members are ignored.
7134 * @user identifies user to reset password, aifo_ticket member is ignored
7135 * @fee_paid is true if fee has been paid, false otherwise
7136 * @approval is optional external approval of box manipulation
7137 * @credentials_delivery is NULL if new password should be delivered off-line
7138 * to the user. It is valid pointer if user should obtain new password on-line
7139 * on dedicated web server. Then input @credentials_delivery.email value is
7140 * user's e-mail address user must provide to dedicated web server together
7141 * with @credentials_delivery.token. The output reallocated token user needs
7142 * to use to authorize on the web server to view his new password. Output
7143 * reallocated @credentials_delivery.new_user_name is user's log-in name that
7144 * ISDS changed up on this call. (No reason why server could change the name
7146 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7147 * NULL, if you don't care.*/
7148 isds_error
isds_reset_password(struct isds_ctx
*context
,
7149 const struct isds_DbOwnerInfo
*box
,
7150 const struct isds_DbUserInfo
*user
,
7151 const _Bool fee_paid
, const struct isds_approval
*approval
,
7152 struct isds_credentials_delivery
*credentials_delivery
,
7154 isds_error err
= IE_SUCCESS
;
7156 xmlNsPtr isds_ns
= NULL
;
7157 xmlNodePtr request
= NULL
, node
;
7158 xmlDocPtr response
= NULL
;
7162 if (!context
) return IE_INVALID_CONTEXT
;
7163 zfree(context
->long_message
);
7165 if (credentials_delivery
) {
7166 zfree(credentials_delivery
->token
);
7167 zfree(credentials_delivery
->new_user_name
);
7169 if (!box
|| !user
) return IE_INVAL
;
7173 /* Build NewAccessData request */
7174 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
7176 isds_log_message(context
,
7177 _("Could build NewAccessData request"));
7180 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7182 isds_log_message(context
, _("Could not create ISDS name space"));
7183 xmlFreeNode(request
);
7186 xmlSetNs(request
, isds_ns
);
7188 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7189 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7190 if (err
) goto leave
;
7192 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7193 err
= insert_DbUserInfo(context
, user
, 0, node
);
7194 if (err
) goto leave
;
7196 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
7198 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7199 if (err
) goto leave
;
7201 err
= insert_GExtApproval(context
, approval
, request
);
7202 if (err
) goto leave
;
7204 /* Send request and check response*/
7205 err
= send_destroy_request_check_response(context
,
7206 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
7207 &response
, (xmlChar
**) refnumber
, NULL
);
7208 if (err
) goto leave
;
7211 /* Extract optional token */
7212 err
= extract_credentials_delivery(context
, credentials_delivery
,
7213 response
, "NewAccessData");
7216 xmlFreeDoc(response
);
7217 xmlFreeNode(request
);
7220 isds_log(ILF_ISDS
, ILL_DEBUG
,
7221 _("NewAccessData request processed by server "
7222 "successfully.\n"));
7223 #else /* not HAVE_LIBCURL */
7231 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7232 * code, destroy response and log success.
7233 * @context is ISDS session context.
7234 * @service_name is name of SERVICE_DB_MANIPULATION service
7235 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7236 * members are ignored.
7237 * @user identifies user to remove
7238 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7239 * @credentials_delivery is NULL if new user's password should be delivered
7240 * off-line to the user. It is valid pointer if user should obtain new
7241 * password on-line on dedicated web server. Then input
7242 * @credentials_delivery.email value is user's e-mail address user must
7243 * provide to dedicated web server together with @credentials_delivery.token.
7244 * The output reallocated token user needs to use to authorize on the web
7245 * server to view his new password. Output reallocated
7246 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7247 * assingned or changed up on this call.
7248 * @approval is optional external approval of box manipulation
7249 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7250 * NULL, if you don't care. */
7251 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
7252 struct isds_ctx
*context
, const xmlChar
*service_name
,
7253 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7254 _Bool honor_aifo_ticket
,
7255 struct isds_credentials_delivery
*credentials_delivery
,
7256 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7257 isds_error err
= IE_SUCCESS
;
7259 xmlNsPtr isds_ns
= NULL
;
7260 xmlNodePtr request
= NULL
, node
;
7261 xmlDocPtr response
= NULL
;
7265 if (!context
) return IE_INVALID_CONTEXT
;
7266 zfree(context
->long_message
);
7267 if (credentials_delivery
) {
7268 zfree(credentials_delivery
->token
);
7269 zfree(credentials_delivery
->new_user_name
);
7271 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7276 /* Build NewAccessData or similar request */
7277 request
= xmlNewNode(NULL
, service_name
);
7279 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7280 isds_printf_message(context
, _("Could not build %s request"),
7281 service_name_locale
);
7282 free(service_name_locale
);
7285 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7287 isds_log_message(context
, _("Could not create ISDS name space"));
7288 xmlFreeNode(request
);
7291 xmlSetNs(request
, isds_ns
);
7293 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7294 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7295 if (err
) goto leave
;
7297 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7298 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7299 if (err
) goto leave
;
7301 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7302 if (err
) goto leave
;
7304 err
= insert_GExtApproval(context
, approval
, request
);
7305 if (err
) goto leave
;
7308 /* Send request and check response*/
7309 err
= send_destroy_request_check_response(context
,
7310 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7313 xmlFreeNode(request
);
7316 /* Pick up credentials_delivery if requested */
7317 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7318 (char *)service_name
);
7321 xmlFreeDoc(response
);
7322 if (request
) xmlFreeNode(request
);
7325 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7326 isds_log(ILF_ISDS
, ILL_DEBUG
,
7327 _("%s request processed by server successfully.\n"),
7328 service_name_locale
);
7329 free(service_name_locale
);
7331 #else /* not HAVE_LIBCURL */
7339 /* Assign new user to given box.
7340 * @context is session context
7341 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7342 * members are ignored.
7343 * @user defines new user to add
7344 * @credentials_delivery is NULL if new user's password should be delivered
7345 * off-line to the user. It is valid pointer if user should obtain new
7346 * password on-line on dedicated web server. Then input
7347 * @credentials_delivery.email value is user's e-mail address user must
7348 * provide to dedicated web server together with @credentials_delivery.token.
7349 * The output reallocated token user needs to use to authorize on the web
7350 * server to view his new password. Output reallocated
7351 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7352 * assingned up on this call.
7353 * @approval is optional external approval of box manipulation
7354 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7355 * NULL, if you don't care.*/
7356 isds_error
isds_add_user(struct isds_ctx
*context
,
7357 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7358 struct isds_credentials_delivery
*credentials_delivery
,
7359 const struct isds_approval
*approval
, char **refnumber
) {
7360 return build_send_manipulationboxuser_request_check_drop_response(context
,
7361 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7362 approval
, (xmlChar
**) refnumber
);
7366 /* Remove user assigned to given box.
7367 * @context is session context
7368 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7369 * members are ignored.
7370 * @user identifies user to remove, aifo_ticket member is ignored
7371 * @approval is optional external approval of box manipulation
7372 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7373 * NULL, if you don't care.*/
7374 isds_error
isds_delete_user(struct isds_ctx
*context
,
7375 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7376 const struct isds_approval
*approval
, char **refnumber
) {
7377 return build_send_manipulationboxuser_request_check_drop_response(context
,
7378 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7379 (xmlChar
**) refnumber
);
7383 /* Get list of boxes in ZIP archive.
7384 * @context is session context
7385 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7386 * System recognizes following values currently: ALL (all boxes), UPG
7387 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7388 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7389 * commercial messages). This argument is a string because specification
7390 * states new values can appear in the future. Not all list types are
7391 * available to all users.
7392 * @buffer is automatically reallocated memory to store the list of boxes. The
7393 * list is zipped CSV file.
7394 * @buffer_length is size of @buffer data in bytes.
7395 * In case of error @buffer will be freed and @buffer_length will be
7397 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7398 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7399 isds_error err
= IE_SUCCESS
;
7401 xmlNsPtr isds_ns
= NULL
;
7402 xmlNodePtr request
= NULL
, node
;
7403 xmlDocPtr response
= NULL
;
7404 xmlXPathContextPtr xpath_ctx
= NULL
;
7405 xmlXPathObjectPtr result
= NULL
;
7406 char *string
= NULL
;
7410 if (!context
) return IE_INVALID_CONTEXT
;
7411 zfree(context
->long_message
);
7412 if (buffer
) zfree(*buffer
);
7413 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7417 /* Check if connection is established
7418 * TODO: This check should be done downstairs. */
7419 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7422 /* Build AuthenticateMessage request */
7423 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7425 isds_log_message(context
,
7426 _("Could not build GetDataBoxList request"));
7429 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7431 isds_log_message(context
, _("Could not create ISDS name space"));
7432 xmlFreeNode(request
);
7435 xmlSetNs(request
, isds_ns
);
7436 INSERT_STRING(request
, "dblType", list_identifier
);
7438 /* Send request to server and process response */
7439 err
= send_destroy_request_check_response(context
,
7440 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7441 &response
, NULL
, NULL
);
7442 if (err
) goto leave
;
7445 /* Extract Base-64 encoded ZIP file */
7446 xpath_ctx
= xmlXPathNewContext(response
);
7451 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7455 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7457 /* Decode non-empty archive */
7458 if (string
&& string
[0] != '\0') {
7459 *buffer_length
= _isds_b64decode(string
, buffer
);
7460 if (*buffer_length
== (size_t) -1) {
7461 isds_printf_message(context
,
7462 _("Error while Base64-decoding box list archive"));
7471 xmlXPathFreeObject(result
);
7472 xmlXPathFreeContext(xpath_ctx
);
7473 xmlFreeDoc(response
);
7474 xmlFreeNode(request
);
7477 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7478 "processed by server successfully.\n"));
7480 #else /* not HAVE_LIBCURL */
7488 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7489 * send it, check for error code, extract list of results, destroy response
7491 * @context is ISDS session context.
7492 * @service_name is name of SERVICE_DB_SEARCH service
7493 * @pfo_service is false if tDbOwnerInfo request should be built from
7494 * @criteria and corresponding result extracted. It is true if
7495 * tDbPersonalOwnerInfoRequest request should be built. The request and
7496 * response differ subset of significant isds_DbOwnerInfo structure members.
7497 * @criteria is filter. You should fill in at least some members.
7498 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7499 * members will be ignored. If @pfo_service is true, dbType, ic,
7500 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7501 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7503 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7504 * possibly empty. Input NULL or valid old structure. The same memebers as
7505 * in described for @criteria argument will be NULL according to @pfo_service
7508 * IE_SUCCESS if search succeeded, @boxes contains useful data
7509 * IE_NOEXIST if no such box exists, @boxes will be NULL
7510 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7511 * contains still valid data
7512 * other code if something bad happens. @boxes will be NULL. */
7513 static isds_error
build_send_findbox_request_check_parse_drop_response(
7514 struct isds_ctx
*context
, const xmlChar
*service_name
,
7515 _Bool pfo_service
, const struct isds_DbOwnerInfo
*criteria
,
7516 struct isds_list
**boxes
) {
7517 isds_error err
= IE_SUCCESS
;
7519 char *service_name_locale
= NULL
;
7520 _Bool truncated
= 0;
7521 xmlNsPtr isds_ns
= NULL
;
7522 xmlNodePtr request
= NULL
;
7523 xmlDocPtr response
= NULL
;
7524 xmlChar
*code
= NULL
, *message
= NULL
;
7525 xmlNodePtr db_owner_info
;
7526 xmlXPathContextPtr xpath_ctx
= NULL
;
7527 xmlXPathObjectPtr result
= NULL
;
7528 xmlChar
*string
= NULL
;
7532 if (!context
) return IE_INVALID_CONTEXT
;
7533 zfree(context
->long_message
);
7534 if (!boxes
) return IE_INVAL
;
7535 isds_list_free(boxes
);
7542 /* Check if connection is established
7543 * TODO: This check should be done downstairs. */
7544 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7545 service_name_locale
= _isds_utf82locale((char *) service_name
);
7548 request
= xmlNewNode(NULL
, service_name
);
7550 isds_printf_message(context
, _("Could not build %s request"),
7551 service_name_locale
);
7552 free(service_name_locale
);
7555 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7557 isds_log_message(context
, _("Could not create ISDS name space"));
7558 free(service_name_locale
);
7559 xmlFreeNode(request
);
7562 xmlSetNs(request
, isds_ns
);
7563 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7564 if (!db_owner_info
) {
7565 isds_printf_message(context
,
7566 _("Could not add dbOwnerInfo child to %s element"),
7567 service_name_locale
);
7568 free(service_name_locale
);
7569 xmlFreeNode(request
);
7573 err
= insert_DbOwnerInfo(context
, criteria
, pfo_service
, db_owner_info
);
7574 if (err
) goto leave
;
7578 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
7579 service_name_locale
);
7580 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7582 /* Destroy request */
7583 xmlFreeNode(request
); request
= NULL
;
7586 isds_log(ILF_ISDS
, ILL_DEBUG
,
7587 _("Processing ISDS response on %s request failed\n"),
7588 service_name_locale
);
7592 /* Check for response status */
7593 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7594 &code
, &message
, NULL
);
7596 isds_log(ILF_ISDS
, ILL_DEBUG
,
7597 _("ISDS response on %s request is missing status\n"),
7598 service_name_locale
);
7602 /* Request processed, but nothing found */
7603 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7604 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7605 char *code_locale
= _isds_utf82locale((char*)code
);
7606 char *message_locale
= _isds_utf82locale((char*)message
);
7607 isds_log(ILF_ISDS
, ILL_DEBUG
,
7608 _("Server did not find any box on %s request "
7609 "(code=%s, message=%s)\n"), service_name_locale
,
7610 code_locale
, message_locale
);
7611 isds_log_message(context
, message_locale
);
7613 free(message_locale
);
7618 /* Warning, not an error */
7619 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7620 char *code_locale
= _isds_utf82locale((char*)code
);
7621 char *message_locale
= _isds_utf82locale((char*)message
);
7622 isds_log(ILF_ISDS
, ILL_DEBUG
,
7623 _("Server truncated response on %s request "
7624 "(code=%s, message=%s)\n"), service_name_locale
,
7625 code_locale
, message_locale
);
7626 isds_log_message(context
, message_locale
);
7628 free(message_locale
);
7633 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7634 char *code_locale
= _isds_utf82locale((char*)code
);
7635 char *message_locale
= _isds_utf82locale((char*)message
);
7636 isds_log(ILF_ISDS
, ILL_DEBUG
,
7637 _("Server refused %s request (code=%s, message=%s)\n"),
7638 service_name_locale
, code_locale
, message_locale
);
7639 isds_log_message(context
, message_locale
);
7641 free(message_locale
);
7646 xpath_ctx
= xmlXPathNewContext(response
);
7651 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7656 /* Extract boxes if they present */
7657 if (-1 == isds_asprintf((char **)&string
,
7658 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7663 result
= xmlXPathEvalExpression(string
, xpath_ctx
);
7669 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7670 struct isds_list
*item
, *prev_item
= NULL
;
7671 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7672 item
= calloc(1, sizeof(*item
));
7678 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7679 if (i
== 0) *boxes
= item
;
7680 else prev_item
->next
= item
;
7683 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7684 err
= extract_DbOwnerInfo(context
,
7685 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7686 if (err
) goto leave
;
7692 isds_list_free(boxes
);
7694 if (truncated
) err
= IE_2BIG
;
7698 xmlFreeNode(request
);
7699 xmlXPathFreeObject(result
);
7700 xmlXPathFreeContext(xpath_ctx
);
7704 xmlFreeDoc(response
);
7707 isds_log(ILF_ISDS
, ILL_DEBUG
,
7708 _("%s request processed by server successfully.\n"),
7709 service_name_locale
);
7710 free(service_name_locale
);
7711 #else /* not HAVE_LIBCURL */
7719 /* Find boxes suiting given criteria.
7720 * @criteria is filter. You should fill in at least some members. aifoIsds,
7721 * address->adCode, address->adDistrict members are ignored.
7722 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7723 * possibly empty. Input NULL or valid old structure.
7725 * IE_SUCCESS if search succeeded, @boxes contains useful data
7726 * IE_NOEXIST if no such box exists, @boxes will be NULL
7727 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7728 * contains still valid data
7729 * other code if something bad happens. @boxes will be NULL. */
7730 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7731 const struct isds_DbOwnerInfo
*criteria
,
7732 struct isds_list
**boxes
) {
7733 return build_send_findbox_request_check_parse_drop_response(context
,
7734 BAD_CAST
"FindDataBox", 0, criteria
, boxes
);
7738 /* Find accessible FO-type boxes suiting given criteria.
7739 * @criteria is filter. You should fill in at least some members. dbType, ic,
7740 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7741 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7742 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7743 * possibly empty. Input NULL or valid old structure.
7745 * IE_SUCCESS if search succeeded, @boxes contains useful data
7746 * IE_NOEXIST if no such box exists, @boxes will be NULL
7747 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7748 * contains still valid data
7749 * other code if something bad happens. @boxes will be NULL. */
7750 isds_error
isds_FindPersonalDataBox(struct isds_ctx
*context
,
7751 const struct isds_DbOwnerInfo
*criteria
,
7752 struct isds_list
**boxes
) {
7753 return build_send_findbox_request_check_parse_drop_response(context
,
7754 BAD_CAST
"FindPersonalDataBox", 1, criteria
, boxes
);
7759 /* Convert a string with match markers into a plain string with list of
7760 * pointers to the matches
7761 * @string is an UTF-8 encoded non-constant string with match markers
7762 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7763 * The markers will be removed from the string.
7764 * @starts is a reallocated list of static pointers into the @string pointing
7765 * to places where match start markers occured.
7766 * @ends is a reallocated list of static pointers into the @string pointing
7767 * to places where match end markers occured.
7768 * @return IE_SUCCESS in case of no failure. */
7769 static isds_error
interpret_matches(xmlChar
*string
,
7770 struct isds_list
**starts
, struct isds_list
**ends
) {
7771 isds_error err
= IE_SUCCESS
;
7772 xmlChar
*pointer
, *destination
, *source
;
7773 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7775 isds_list_free(starts
);
7776 isds_list_free(ends
);
7777 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7778 if (NULL
== string
) return IE_SUCCESS
;
7780 for (pointer
= string
; *pointer
!= '\0';) {
7781 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7782 /* Remove the start marker */
7783 for (source
= pointer
+ 14, destination
= pointer
;
7784 *source
!= '\0'; source
++, destination
++) {
7785 *destination
= *source
;
7787 *destination
= '\0';
7788 /* Append the pointer into the list */
7789 item
= calloc(1, sizeof(*item
));
7794 item
->destructor
= (void (*)(void **))NULL
;
7795 item
->data
= pointer
;
7796 if (NULL
== prev_start
) *starts
= item
;
7797 else prev_start
->next
= item
;
7799 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7800 /* Remove the end marker */
7801 for (source
= pointer
+ 12, destination
= pointer
;
7802 *source
!= '\0'; source
++, destination
++) {
7803 *destination
= *source
;
7805 *destination
= '\0';
7806 /* Append the pointer into the list */
7807 item
= calloc(1, sizeof(*item
));
7812 item
->destructor
= (void (*)(void **))NULL
;
7813 item
->data
= pointer
;
7814 if (NULL
== prev_end
) *ends
= item
;
7815 else prev_end
->next
= item
;
7824 isds_list_free(starts
);
7825 isds_list_free(ends
);
7831 /* Convert isds:dbResult XML tree into structure
7832 * @context is ISDS context.
7833 * @fulltext_result is automatically reallocated found box structure.
7834 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7835 * @collect_matches is true to interpret match markers.
7836 * In case of error @result will be freed. */
7837 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7838 struct isds_fulltext_result
**fulltext_result
,
7839 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7840 isds_error err
= IE_SUCCESS
;
7841 xmlXPathObjectPtr result
= NULL
;
7842 char *string
= NULL
;
7844 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7845 if (NULL
== fulltext_result
) return IE_INVAL
;
7846 isds_fulltext_result_free(fulltext_result
);
7847 if (!xpath_ctx
) return IE_INVAL
;
7850 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7851 if (NULL
== *fulltext_result
) {
7857 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7859 EXTRACT_STRING("isds:dbType", string
);
7860 if (NULL
== string
) {
7862 isds_log_message(context
, _("Empty isds:dbType element"));
7865 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7867 if (err
== IE_ENUM
) {
7869 char *string_locale
= _isds_utf82locale(string
);
7870 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7872 free(string_locale
);
7878 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7879 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7881 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7882 if (err
) goto leave
;
7884 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7885 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7886 (*fulltext_result
)->dbEffectiveOVM
);
7888 EXTRACT_STRING("isds:dbSendOptions", string
);
7889 if (NULL
== string
) {
7891 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7894 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7895 (*fulltext_result
)->active
= 1;
7896 (*fulltext_result
)->public_sending
= 1;
7897 (*fulltext_result
)->commercial_sending
= 0;
7898 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7899 (*fulltext_result
)->active
= 1;
7900 (*fulltext_result
)->public_sending
= 1;
7901 (*fulltext_result
)->commercial_sending
= 1;
7902 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7903 (*fulltext_result
)->active
= 1;
7904 (*fulltext_result
)->public_sending
= 0;
7905 (*fulltext_result
)->commercial_sending
= 1;
7906 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7907 (*fulltext_result
)->active
= 1;
7908 (*fulltext_result
)->public_sending
= 0;
7909 (*fulltext_result
)->commercial_sending
= 0;
7910 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7911 (*fulltext_result
)->active
= 0;
7912 (*fulltext_result
)->public_sending
= 0;
7913 (*fulltext_result
)->commercial_sending
= 0;
7916 char *string_locale
= _isds_utf82locale(string
);
7917 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7919 free(string_locale
);
7924 /* Interpret match marks */
7925 if (collect_matches
) {
7926 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7927 &((*fulltext_result
)->name_match_start
),
7928 &((*fulltext_result
)->name_match_end
));
7929 if (err
) goto leave
;
7930 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7931 &((*fulltext_result
)->address_match_start
),
7932 &((*fulltext_result
)->address_match_end
));
7933 if (err
) goto leave
;
7937 if (err
) isds_fulltext_result_free(fulltext_result
);
7939 xmlXPathFreeObject(result
);
7942 #endif /* HAVE_LIBCURL */
7945 /* Find boxes matching a given full-text criteria.
7946 * @context is a session context
7947 * @query is a non-empty string which consists of words to search
7948 * @target selects box attributes to search for @query words. Pass NULL if you
7950 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7951 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7952 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7953 * which is DBTYPE_SYSTEM.
7954 * @page_size defines count of boxes to constitute a response page. It counts
7955 * from zero. Pass NULL to let server to use a default value (50 now).
7956 * @page_number defines ordinar number of the response page to return. It
7957 * counts from zero. Pass NULL to let server to use a default value (0 now).
7958 * @track_matches points to true for marking @query words found in the box
7959 * attributes. It points to false for not marking. Pass NULL to let the server
7960 * to use default value (false now).
7961 * @total_matching_boxes outputs reallocated number of all boxes matching the
7962 * query. Will be pointer to NULL if server did not provide the value.
7963 * Pass NULL if you don't care.
7964 * @current_page_beginning outputs reallocated ordinar number of the first box
7965 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7966 * server did not provide the value. Pass NULL if you don't care.
7967 * @current_page_size outputs reallocated count of boxes in the this @boxes
7968 * page. It will be pointer to NULL if the server did not provide the value.
7969 * Pass NULL if you don't care.
7970 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7971 * is the last one, false if more boxes match, NULL if the server did not
7972 * provude the value. Pass NULL if you don't care.
7973 * @boxes outputs reallocated list of isds_fulltext_result structures,
7976 * IE_SUCCESS if search succeeded
7977 * IE_2BIG if @page_size is too large
7978 * other code if something bad happens; output arguments will be NULL. */
7979 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7981 const isds_fulltext_target
*target
,
7982 const isds_DbType
*box_type
,
7983 const unsigned long int *page_size
,
7984 const unsigned long int *page_number
,
7985 const _Bool
*track_matches
,
7986 unsigned long int **total_matching_boxes
,
7987 unsigned long int **current_page_beginning
,
7988 unsigned long int **current_page_size
,
7990 struct isds_list
**boxes
) {
7991 isds_error err
= IE_SUCCESS
;
7993 xmlNsPtr isds_ns
= NULL
;
7994 xmlNodePtr request
= NULL
;
7995 xmlDocPtr response
= NULL
;
7997 xmlXPathContextPtr xpath_ctx
= NULL
;
7998 xmlXPathObjectPtr result
= NULL
;
7999 const xmlChar
*static_string
= NULL
;
8000 xmlChar
*string
= NULL
;
8002 const xmlChar
*codes
[] = {
8012 const char *meanings
[] = {
8013 N_("You are not allowed to perform the search"),
8014 N_("The query string is empty"),
8015 N_("Searched box ID is malformed"),
8016 N_("Searched organization ID is malformed"),
8017 N_("Invalid input"),
8018 N_("Requested page size is too large"),
8019 N_("Search engine internal error")
8021 const isds_error errors
[] = {
8030 struct code_map_isds_error map
= {
8032 .meanings
= meanings
,
8038 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
8039 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
8040 if (NULL
!= current_page_size
) zfree(*current_page_size
);
8041 if (NULL
!= last_page
) zfree(*last_page
);
8042 isds_list_free(boxes
);
8044 if (NULL
== context
) return IE_INVALID_CONTEXT
;
8045 zfree(context
->long_message
);
8047 if (NULL
== boxes
) return IE_INVAL
;
8049 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
8050 isds_log_message(context
, _("Query string must be non-empty"));
8055 /* Check if connection is established
8056 * TODO: This check should be done downstairs. */
8057 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8059 /* Build FindDataBox request */
8060 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
8061 if (NULL
== request
) {
8062 isds_log_message(context
,
8063 _("Could not build ISDSSearch2 request"));
8066 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8067 if(NULL
== isds_ns
) {
8068 isds_log_message(context
, _("Could not create ISDS name space"));
8069 xmlFreeNode(request
);
8072 xmlSetNs(request
, isds_ns
);
8074 INSERT_STRING(request
, "searchText", query
);
8076 if (NULL
!= target
) {
8077 static_string
= isds_fulltext_target2string(*(target
));
8078 if (NULL
== static_string
) {
8079 isds_printf_message(context
, _("Invalid target value: %d"),
8085 INSERT_STRING(request
, "searchType", static_string
);
8086 static_string
= NULL
;
8088 if (NULL
!= box_type
) {
8089 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
8090 if (DBTYPE_SYSTEM
== *box_type
) {
8091 static_string
= BAD_CAST
"ALL";
8092 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
8093 static_string
= BAD_CAST
"OVM_MAIN";
8095 static_string
= isds_DbType2string(*(box_type
));
8096 if (NULL
== static_string
) {
8097 isds_printf_message(context
, _("Invalid box type value: %d"),
8104 INSERT_STRING(request
, "searchScope", static_string
);
8105 static_string
= NULL
;
8107 INSERT_ULONGINT(request
, "page", page_number
, string
);
8108 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
8109 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
8111 /* Send request and check response */
8112 err
= send_destroy_request_check_response(context
,
8113 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
8114 &request
, &response
, NULL
, &map
);
8115 if (err
) goto leave
;
8117 /* Parse response */
8118 xpath_ctx
= xmlXPathNewContext(response
);
8119 if (NULL
== xpath_ctx
) {
8123 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8127 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
8133 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8134 isds_log_message(context
, _("Missing ISDSSearch2 element"));
8138 if (result
->nodesetval
->nodeNr
> 1) {
8139 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
8143 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8144 xmlXPathFreeObject(result
); result
= NULL
;
8147 /* Extract counters */
8148 if (NULL
!= total_matching_boxes
) {
8149 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
8151 if (NULL
!= current_page_size
) {
8152 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
8154 if (NULL
!= current_page_beginning
) {
8155 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
8157 if (NULL
!= last_page
) {
8158 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
8160 xmlXPathFreeObject(result
); result
= NULL
;
8162 /* Extract boxes if they present */
8163 result
= xmlXPathEvalExpression(BAD_CAST
8164 "isds:dbResults/isds:dbResult", xpath_ctx
);
8165 if (NULL
== result
) {
8169 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8170 struct isds_list
*item
, *prev_item
= NULL
;
8171 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8172 item
= calloc(1, sizeof(*item
));
8178 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
8179 if (i
== 0) *boxes
= item
;
8180 else prev_item
->next
= item
;
8183 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8184 err
= extract_dbResult(context
,
8185 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
8186 (NULL
== track_matches
) ? 0 : *track_matches
);
8187 if (err
) goto leave
;
8193 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
8194 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
8195 if (NULL
!= current_page_size
) zfree(*current_page_size
);
8196 if (NULL
!= last_page
) zfree(*last_page
);
8197 isds_list_free(boxes
);
8201 xmlFreeNode(request
);
8202 xmlXPathFreeObject(result
);
8203 xmlXPathFreeContext(xpath_ctx
);
8204 xmlFreeDoc(response
);
8207 isds_log(ILF_ISDS
, ILL_DEBUG
,
8208 _("ISDSSearch2 request processed by server successfully.\n"));
8209 #else /* not HAVE_LIBCURL */
8217 /* Get status of a box.
8218 * @context is ISDS session context.
8219 * @box_id is UTF-8 encoded box identifier as zero terminated string
8220 * @box_status is return value of box status.
8222 * IE_SUCCESS if box has been found and its status retrieved
8223 * IE_NOEXIST if box is not known to ISDS server
8224 * or other appropriate error.
8225 * You can use isds_DbState to enumerate box status. However out of enum
8226 * range value can be returned too. This is feature because ISDS
8227 * specification leaves the set of values open.
8228 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8229 * the box has been deleted, but ISDS still lists its former existence. */
8230 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
8231 long int *box_status
) {
8232 isds_error err
= IE_SUCCESS
;
8234 xmlNsPtr isds_ns
= NULL
;
8235 xmlNodePtr request
= NULL
, db_id
;
8236 xmlDocPtr response
= NULL
;
8237 xmlXPathContextPtr xpath_ctx
= NULL
;
8238 xmlXPathObjectPtr result
= NULL
;
8239 xmlChar
*string
= NULL
;
8241 const xmlChar
*codes
[] = {
8247 const char *meanings
[] = {
8248 "The box does not exist",
8249 "Box ID is malformed",
8252 const isds_error errors
[] = {
8257 struct code_map_isds_error map
= {
8259 .meanings
= meanings
,
8264 if (!context
) return IE_INVALID_CONTEXT
;
8265 zfree(context
->long_message
);
8266 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
8269 /* Check if connection is established
8270 * TODO: This check should be done downstairs. */
8271 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8274 /* Build CheckDataBox request */
8275 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
8277 isds_log_message(context
,
8278 _("Could build CheckDataBox request"));
8281 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8283 isds_log_message(context
, _("Could not create ISDS name space"));
8284 xmlFreeNode(request
);
8287 xmlSetNs(request
, isds_ns
);
8288 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
8290 isds_log_message(context
, _("Could not add dbID child to "
8291 "CheckDataBox element"));
8292 xmlFreeNode(request
);
8297 /* Send request and check response*/
8298 err
= send_destroy_request_check_response(context
,
8299 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
8300 &request
, &response
, NULL
, &map
);
8301 if (err
) goto leave
;
8305 xpath_ctx
= xmlXPathNewContext(response
);
8310 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8314 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
8320 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8321 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
8325 if (result
->nodesetval
->nodeNr
> 1) {
8326 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
8330 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8331 xmlXPathFreeObject(result
); result
= NULL
;
8333 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
8338 xmlXPathFreeObject(result
);
8339 xmlXPathFreeContext(xpath_ctx
);
8341 xmlFreeDoc(response
);
8344 isds_log(ILF_ISDS
, ILL_DEBUG
,
8345 _("CheckDataBox request processed by server successfully.\n"));
8346 #else /* not HAVE_LIBCURL */
8355 /* Convert XSD:tdbPeriod XML tree into structure
8356 * @context is ISDS context.
8357 * @period is automatically reallocated found box status period structure.
8358 * @xpath_ctx is XPath context with current node as element of
8359 * XSD:tDbPeriod type.
8360 * In case of error @period will be freed. */
8361 static isds_error
extract_Period(struct isds_ctx
*context
,
8362 struct isds_box_state_period
**period
, xmlXPathContextPtr xpath_ctx
) {
8363 isds_error err
= IE_SUCCESS
;
8364 xmlXPathObjectPtr result
= NULL
;
8365 char *string
= NULL
;
8366 long int *dbState_ptr
;
8368 if (NULL
== context
) return IE_INVALID_CONTEXT
;
8369 if (NULL
== period
) return IE_INVAL
;
8370 isds_box_state_period_free(period
);
8371 if (!xpath_ctx
) return IE_INVAL
;
8374 *period
= calloc(1, sizeof(**period
));
8375 if (NULL
== *period
) {
8381 EXTRACT_STRING("isds:PeriodFrom", string
);
8382 if (NULL
== string
) {
8384 isds_log_message(context
,
8385 _("Could not find PeriodFrom element value"));
8388 err
= timestring2static_timeval((xmlChar
*) string
,
8389 &((*period
)->from
));
8391 char *string_locale
= _isds_utf82locale(string
);
8392 if (err
== IE_DATE
) err
= IE_ISDS
;
8393 isds_printf_message(context
,
8394 _("Could not convert PeriodFrom as ISO time: %s"),
8396 free(string_locale
);
8401 EXTRACT_STRING("isds:PeriodTo", string
);
8402 if (NULL
== string
) {
8404 isds_log_message(context
,
8405 _("Could not find PeriodTo element value"));
8408 err
= timestring2static_timeval((xmlChar
*) string
,
8411 char *string_locale
= _isds_utf82locale(string
);
8412 if (err
== IE_DATE
) err
= IE_ISDS
;
8413 isds_printf_message(context
,
8414 _("Could not convert PeriodTo as ISO time: %s"),
8416 free(string_locale
);
8421 dbState_ptr
= &((*period
)->dbState
);
8422 EXTRACT_LONGINT("isds:DbState", dbState_ptr
, 1);
8425 if (err
) isds_box_state_period_free(period
);
8427 xmlXPathFreeObject(result
);
8430 #endif /* HAVE_LIBCURL */
8433 /* Get history of box state changes.
8434 * @context is ISDS session context.
8435 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8436 * @from_time is first second of history to return in @history. Server ignores
8437 * subseconds. NULL means time of creating the box.
8438 * @to_time is last second of history to return in @history. Server ignores
8439 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8440 * interval is closed from both ends. NULL means now.
8441 * @history outputs auto-reallocated list of pointers to struct
8442 * isds_box_state_period. Each item describes a continues time when the box
8443 * was in one state. The state is 1 for accessible box. Otherwise the box
8444 * is inaccessible (priviledged users will get exact box state as enumerated
8445 * in isds_DbState, other users 0).
8447 * IE_SUCCESS if the history has been obtained correctly,
8448 * or other appropriate error. Please note that server allows to retrieve
8449 * the history only to some users. */
8450 isds_error
isds_get_box_state_history(struct isds_ctx
*context
,
8452 const struct timeval
*from_time
, const struct timeval
*to_time
,
8453 struct isds_list
**history
) {
8454 isds_error err
= IE_SUCCESS
;
8456 char *box_id_locale
= NULL
;
8457 xmlNodePtr request
= NULL
, node
;
8458 xmlNsPtr isds_ns
= NULL
;
8459 xmlChar
*string
= NULL
;
8461 xmlDocPtr response
= NULL
;
8462 xmlXPathContextPtr xpath_ctx
= NULL
;
8463 xmlXPathObjectPtr result
= NULL
;
8466 if (!context
) return IE_INVALID_CONTEXT
;
8467 zfree(context
->long_message
);
8469 /* Free output argument */
8470 isds_list_free(history
);
8473 /* Check if connection is established */
8474 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8476 /* ??? XML schema allows empty box ID, textual documentation
8477 * requries the value. */
8478 /* Allow undefined box_id */
8479 if (NULL
!= box_id
) {
8480 box_id_locale
= _isds_utf82locale((char*)box_id
);
8481 if (NULL
== box_id_locale
) {
8488 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxActivityStatus");
8489 if (NULL
== request
) {
8490 isds_printf_message(context
,
8491 _("Could not build GetDataBoxActivityStatus request "
8497 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8499 isds_log_message(context
, _("Could not create ISDS name space"));
8503 xmlSetNs(request
, isds_ns
);
8505 /* Add mandatory XSD:tIdDbInput child */
8506 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8507 /* Add times elements only when defined */
8508 /* ???: XML schema requires the values, textual documentation does not. */
8510 err
= timeval2timestring(from_time
, &string
);
8512 isds_log_message(context
,
8513 _("Could not convert `from_time' argument to ISO time "
8517 INSERT_STRING(request
, "baFrom", string
);
8521 err
= timeval2timestring(to_time
, &string
);
8523 isds_log_message(context
,
8524 _("Could not convert `to_time' argument to ISO time "
8528 INSERT_STRING(request
, "baTo", string
);
8532 /* Send request and check response*/
8533 err
= send_destroy_request_check_response(context
,
8534 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxActivityStatus",
8535 &request
, &response
, NULL
, NULL
);
8536 if (err
) goto leave
;
8540 /* Set context to the root */
8541 xpath_ctx
= xmlXPathNewContext(response
);
8546 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8550 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:GetDataBoxActivityStatusResponse",
8556 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8557 isds_log_message(context
, _("Missing GetDataBoxActivityStatusResponse element"));
8561 if (result
->nodesetval
->nodeNr
> 1) {
8562 isds_log_message(context
, _("Multiple GetDataBoxActivityStatusResponse element"));
8566 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8567 xmlXPathFreeObject(result
); result
= NULL
;
8569 /* Ignore dbID, it's the same as the input argument. */
8571 /* Extract records */
8572 if (NULL
== history
) goto leave
;
8573 result
= xmlXPathEvalExpression(BAD_CAST
"isds:Periods/isds:Period",
8579 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8580 struct isds_list
*prev_item
= NULL
;
8582 /* Iterate over all records */
8583 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8584 struct isds_list
*item
;
8586 /* Prepare structure */
8587 item
= calloc(1, sizeof(*item
));
8592 item
->destructor
= (void(*)(void**))isds_box_state_period_free
;
8593 if (i
== 0) *history
= item
;
8594 else prev_item
->next
= item
;
8598 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8599 err
= extract_Period(context
,
8600 (struct isds_box_state_period
**) (&item
->data
),
8602 if (err
) goto leave
;
8608 isds_log(ILF_ISDS
, ILL_DEBUG
,
8609 _("GetDataBoxActivityStatus request for %s box "
8610 "processed by server successfully.\n"), box_id_locale
);
8613 isds_list_free(history
);
8616 free(box_id_locale
);
8617 xmlXPathFreeObject(result
);
8618 xmlXPathFreeContext(xpath_ctx
);
8619 xmlFreeDoc(response
);
8621 #else /* not HAVE_LIBCURL */
8629 /* Get list of permissions to send commercial messages.
8630 * @context is ISDS session context.
8631 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8632 * @permissions is a reallocated list of permissions (struct
8633 * isds_commercial_permission*) to send commercial messages from @box_id. The
8634 * order of permissions is significant as the server applies the permissions
8635 * and associated pre-paid credits in the order. Empty list means no
8638 * IE_SUCCESS if the list has been obtained correctly,
8639 * or other appropriate error. */
8640 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8641 const char *box_id
, struct isds_list
**permissions
) {
8642 isds_error err
= IE_SUCCESS
;
8644 xmlDocPtr response
= NULL
;
8645 xmlXPathContextPtr xpath_ctx
= NULL
;
8646 xmlXPathObjectPtr result
= NULL
;
8649 if (!context
) return IE_INVALID_CONTEXT
;
8650 zfree(context
->long_message
);
8651 if (NULL
== permissions
) return IE_INVAL
;
8652 isds_list_free(permissions
);
8653 if (NULL
== box_id
) return IE_INVAL
;
8656 /* Check if connection is established */
8657 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8659 /* Do request and check for success */
8660 err
= build_send_dbid_request_check_response(context
,
8661 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8662 BAD_CAST box_id
, NULL
, &response
, NULL
);
8664 isds_log(ILF_ISDS
, ILL_DEBUG
,
8665 _("PDZInfo request processed by server successfully.\n"));
8669 /* Prepare structure */
8670 xpath_ctx
= xmlXPathNewContext(response
);
8675 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8680 /* Set context node */
8681 result
= xmlXPathEvalExpression(BAD_CAST
8682 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8688 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8689 struct isds_list
*prev_item
= NULL
;
8691 /* Iterate over all permission records */
8692 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8693 struct isds_list
*item
;
8695 /* Prepare structure */
8696 item
= calloc(1, sizeof(*item
));
8701 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8702 if (i
== 0) *permissions
= item
;
8703 else prev_item
->next
= item
;
8707 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8708 err
= extract_DbPDZRecord(context
,
8709 (struct isds_commercial_permission
**) (&item
->data
),
8711 if (err
) goto leave
;
8717 isds_list_free(permissions
);
8720 xmlXPathFreeObject(result
);
8721 xmlXPathFreeContext(xpath_ctx
);
8722 xmlFreeDoc(response
);
8724 #else /* not HAVE_LIBCURL */
8732 /* Get details about credit for sending pre-paid commercial messages.
8733 * @context is ISDS session context.
8734 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8735 * @from_date is first day of credit history to return in @history. Only
8736 * tm_year, tm_mon and tm_mday carry sane value.
8737 * @to_date is last day of credit history to return in @history. Only
8738 * tm_year, tm_mon and tm_mday carry sane value.
8739 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8740 * if you don't care. This and all other credit values are integers in
8741 * hundredths of Czech Crowns.
8742 * @email outputs notification e-mail address where notifications about credit
8743 * are sent. This is automatically reallocated string. Pass NULL if you don't
8744 * care. It can return NULL if no address is defined.
8745 * @history outputs auto-reallocated list of pointers to struct
8746 * isds_credit_event. Events in closed interval @from_time to @to_time are
8747 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8748 * are sorted by time.
8750 * IE_SUCCESS if the credit details have been obtained correctly,
8751 * or other appropriate error. Please note that server allows to retrieve
8752 * only limited history of events. */
8753 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8755 const struct tm
*from_date
, const struct tm
*to_date
,
8756 long int *credit
, char **email
, struct isds_list
**history
) {
8757 isds_error err
= IE_SUCCESS
;
8759 char *box_id_locale
= NULL
;
8760 xmlNodePtr request
= NULL
, node
;
8761 xmlNsPtr isds_ns
= NULL
;
8762 xmlChar
*string
= NULL
;
8764 xmlDocPtr response
= NULL
;
8765 xmlXPathContextPtr xpath_ctx
= NULL
;
8766 xmlXPathObjectPtr result
= NULL
;
8768 const xmlChar
*codes
[] = {
8776 const char *meanings
[] = {
8777 "Insufficient priviledges for the box",
8778 "The box does not exist",
8779 "Date is too long (history is not available after 15 months)",
8780 "Interval is too long (limit is 3 months)",
8783 const isds_error errors
[] = {
8790 struct code_map_isds_error map
= {
8792 .meanings
= meanings
,
8797 if (!context
) return IE_INVALID_CONTEXT
;
8798 zfree(context
->long_message
);
8800 /* Free output argument */
8801 if (NULL
!= credit
) *credit
= 0;
8802 if (NULL
!= email
) zfree(*email
);
8803 isds_list_free(history
);
8805 if (NULL
== box_id
) return IE_INVAL
;
8808 /* Check if connection is established */
8809 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8811 box_id_locale
= _isds_utf82locale((char*)box_id
);
8812 if (NULL
== box_id_locale
) {
8818 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8819 if (NULL
== request
) {
8820 isds_printf_message(context
,
8821 _("Could not build DataBoxCreditInfo request for %s box"),
8826 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8828 isds_log_message(context
, _("Could not create ISDS name space"));
8832 xmlSetNs(request
, isds_ns
);
8834 /* Add mandatory XSD:tIdDbInput child */
8835 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8836 /* Add mandatory dates elements with optional values */
8838 err
= tm2datestring(from_date
, &string
);
8840 isds_log_message(context
,
8841 _("Could not convert `from_date' argument to ISO date "
8845 INSERT_STRING(request
, "ciFromDate", string
);
8848 INSERT_STRING(request
, "ciFromDate", NULL
);
8851 err
= tm2datestring(to_date
, &string
);
8853 isds_log_message(context
,
8854 _("Could not convert `to_date' argument to ISO date "
8858 INSERT_STRING(request
, "ciTodate", string
);
8861 INSERT_STRING(request
, "ciTodate", NULL
);
8864 /* Send request and check response*/
8865 err
= send_destroy_request_check_response(context
,
8866 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8867 &request
, &response
, NULL
, &map
);
8868 if (err
) goto leave
;
8872 /* Set context to the root */
8873 xpath_ctx
= xmlXPathNewContext(response
);
8878 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8882 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8888 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8889 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8893 if (result
->nodesetval
->nodeNr
> 1) {
8894 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8898 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8899 xmlXPathFreeObject(result
); result
= NULL
;
8901 /* Extract common data */
8902 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8903 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8905 /* result gets overwritten in next step */
8906 xmlXPathFreeObject(result
); result
= NULL
;
8908 /* Extract records */
8909 if (NULL
== history
) goto leave
;
8910 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8916 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8917 struct isds_list
*prev_item
= NULL
;
8919 /* Iterate over all records */
8920 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8921 struct isds_list
*item
;
8923 /* Prepare structure */
8924 item
= calloc(1, sizeof(*item
));
8929 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8930 if (i
== 0) *history
= item
;
8931 else prev_item
->next
= item
;
8935 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8936 err
= extract_CiRecord(context
,
8937 (struct isds_credit_event
**) (&item
->data
),
8939 if (err
) goto leave
;
8945 isds_log(ILF_ISDS
, ILL_DEBUG
,
8946 _("DataBoxCreditInfo request processed by server successfully.\n"));
8949 isds_list_free(history
);
8950 if (NULL
!= email
) zfree(*email
)
8953 free(box_id_locale
);
8954 xmlXPathFreeObject(result
);
8955 xmlXPathFreeContext(xpath_ctx
);
8956 xmlFreeDoc(response
);
8958 #else /* not HAVE_LIBCURL */
8966 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8967 * code, destroy response and log success.
8968 * @context is ISDS session context.
8969 * @service_name is name of SERVICE_DB_MANIPULATION service
8970 * @box_id is UTF-8 encoded box identifier as zero terminated string
8971 * @approval is optional external approval of box manipulation
8972 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8973 * NULL, if you don't care. */
8974 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8975 struct isds_ctx
*context
, const xmlChar
*service_name
,
8976 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8977 xmlChar
**refnumber
) {
8978 isds_error err
= IE_SUCCESS
;
8980 xmlDocPtr response
= NULL
;
8983 if (!context
) return IE_INVALID_CONTEXT
;
8984 zfree(context
->long_message
);
8985 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8988 /* Check if connection is established */
8989 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8991 /* Do request and check for success */
8992 err
= build_send_dbid_request_check_response(context
,
8993 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8994 &response
, refnumber
);
8995 xmlFreeDoc(response
);
8998 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8999 isds_log(ILF_ISDS
, ILL_DEBUG
,
9000 _("%s request processed by server successfully.\n"),
9001 service_name_locale
);
9002 free(service_name_locale
);
9004 #else /* not HAVE_LIBCURL */
9012 /* Switch box into state where box can receive commercial messages (off by
9014 * @context is ISDS session context.
9015 * @box_id is UTF-8 encoded box identifier as zero terminated string
9016 * @allow is true for enable, false for disable commercial messages income
9017 * @approval is optional external approval of box manipulation
9018 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9019 * NULL, if you don't care. */
9020 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
9021 const char *box_id
, const _Bool allow
,
9022 const struct isds_approval
*approval
, char **refnumber
) {
9023 return build_send_manipulationdbid_request_check_drop_response(context
,
9024 (allow
) ? BAD_CAST
"SetOpenAddressing" :
9025 BAD_CAST
"ClearOpenAddressing",
9026 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
9030 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
9031 * message acceptance). This is just a box permission. Sender must apply
9032 * such role by sending each message.
9033 * @context is ISDS session context.
9034 * @box_id is UTF-8 encoded box identifier as zero terminated string
9035 * @allow is true for enable, false for disable OVM role permission
9036 * @approval is optional external approval of box manipulation
9037 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9038 * NULL, if you don't care. */
9039 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
9040 const char *box_id
, const _Bool allow
,
9041 const struct isds_approval
*approval
, char **refnumber
) {
9042 return build_send_manipulationdbid_request_check_drop_response(context
,
9043 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
9044 BAD_CAST
"ClearEffectiveOVM",
9045 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
9049 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
9050 * code, destroy response and log success.
9051 * @context is ISDS session context.
9052 * @service_name is name of SERVICE_DB_MANIPULATION service
9053 * @owner is structure describing box. aifoIsds, address->adCode,
9054 * address->adDistrict members are ignored.
9055 * @approval is optional external approval of box manipulation
9056 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9057 * NULL, if you don't care. */
9058 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
9059 struct isds_ctx
*context
, const xmlChar
*service_name
,
9060 const struct isds_DbOwnerInfo
*owner
,
9061 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
9062 isds_error err
= IE_SUCCESS
;
9064 char *service_name_locale
= NULL
;
9065 xmlNodePtr request
= NULL
, db_owner_info
;
9066 xmlNsPtr isds_ns
= NULL
;
9070 if (!context
) return IE_INVALID_CONTEXT
;
9071 zfree(context
->long_message
);
9072 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
9075 service_name_locale
= _isds_utf82locale((char*)service_name
);
9076 if (!service_name_locale
) {
9082 request
= xmlNewNode(NULL
, service_name
);
9084 isds_printf_message(context
,
9085 _("Could not build %s request"), service_name_locale
);
9089 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9091 isds_log_message(context
, _("Could not create ISDS name space"));
9095 xmlSetNs(request
, isds_ns
);
9098 /* Add XSD:tOwnerInfoInput child*/
9099 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
9100 err
= insert_DbOwnerInfo(context
, owner
, 0, db_owner_info
);
9101 if (err
) goto leave
;
9103 /* Add XSD:gExtApproval*/
9104 err
= insert_GExtApproval(context
, approval
, request
);
9105 if (err
) goto leave
;
9107 /* Send it to server and process response */
9108 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
9109 service_name
, &request
, refnumber
);
9112 xmlFreeNode(request
);
9113 free(service_name_locale
);
9114 #else /* not HAVE_LIBCURL */
9122 /* Switch box accessibility state on request of box owner.
9123 * Despite the name, owner must do the request off-line. This function is
9124 * designed for such off-line meeting points (e.g. Czech POINT).
9125 * @context is ISDS session context.
9126 * @box identifies box to switch accessibility state. aifoIsds,
9127 * address->adCode, address->adDistrict members are ignored.
9128 * @allow is true for making accessible, false to disallow access.
9129 * @approval is optional external approval of box manipulation
9130 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9131 * NULL, if you don't care. */
9132 isds_error
isds_switch_box_accessibility_on_owner_request(
9133 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
9134 const _Bool allow
, const struct isds_approval
*approval
,
9136 return build_send_manipulationdbowner_request_check_drop_response(context
,
9137 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
9138 BAD_CAST
"DisableOwnDataBox",
9139 box
, approval
, (xmlChar
**) refnumber
);
9143 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
9145 * @context is ISDS session context.
9146 * @box identifies box to switch accessibility state. aifoIsds,
9147 * address->adCode, address->adDistrict members are ignored.
9148 * @since is date since accessibility has been denied. This can be past too.
9149 * Only tm_year, tm_mon and tm_mday carry sane value.
9150 * @approval is optional external approval of box manipulation
9151 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9152 * NULL, if you don't care. */
9153 isds_error
isds_disable_box_accessibility_externaly(
9154 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
9155 const struct tm
*since
, const struct isds_approval
*approval
,
9157 isds_error err
= IE_SUCCESS
;
9159 char *service_name_locale
= NULL
;
9160 xmlNodePtr request
= NULL
, node
;
9161 xmlNsPtr isds_ns
= NULL
;
9162 xmlChar
*string
= NULL
;
9166 if (!context
) return IE_INVALID_CONTEXT
;
9167 zfree(context
->long_message
);
9168 if (!box
|| !since
) return IE_INVAL
;
9172 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
9174 isds_printf_message(context
,
9175 _("Could not build %s request"), "DisableDataBoxExternally");
9179 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9181 isds_log_message(context
, _("Could not create ISDS name space"));
9185 xmlSetNs(request
, isds_ns
);
9188 /* Add @box identification */
9189 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
9190 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
9191 if (err
) goto leave
;
9193 /* Add @since date */
9194 err
= tm2datestring(since
, &string
);
9196 isds_log_message(context
,
9197 _("Could not convert `since' argument to ISO date string"));
9200 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
9204 err
= insert_GExtApproval(context
, approval
, request
);
9205 if (err
) goto leave
;
9207 /* Send it to server and process response */
9208 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
9209 BAD_CAST
"DisableDataBoxExternally", &request
,
9210 (xmlChar
**) refnumber
);
9214 xmlFreeNode(request
);
9215 free(service_name_locale
);
9216 #else /* not HAVE_LIBCURL */
9225 /* Insert struct isds_message data (envelope (recipient data optional) and
9226 * documents into XML tree
9227 * @context is session context
9228 * @outgoing_message is libisds structure with message data
9229 * @create_message is XML CreateMessage or CreateMultipleMessage element
9230 * @process_recipient true for recipient data serialization, false for no
9232 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
9233 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
9234 const _Bool process_recipient
) {
9236 isds_error err
= IE_SUCCESS
;
9237 xmlNodePtr envelope
, dm_files
, node
;
9238 xmlChar
*string
= NULL
;
9240 if (!context
) return IE_INVALID_CONTEXT
;
9241 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
9244 /* Build envelope */
9245 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
9247 isds_printf_message(context
, _("Could not add dmEnvelope child to "
9248 "%s element"), create_message
->name
);
9252 if (!outgoing_message
->envelope
) {
9253 isds_log_message(context
, _("Outgoing message is missing envelope"));
9258 /* Insert optional message type */
9259 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
9261 if (err
) goto leave
;
9263 INSERT_STRING(envelope
, "dmSenderOrgUnit",
9264 outgoing_message
->envelope
->dmSenderOrgUnit
);
9265 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
9266 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
9268 if (process_recipient
) {
9269 if (!outgoing_message
->envelope
->dbIDRecipient
) {
9270 isds_log_message(context
,
9271 _("Outgoing message is missing recipient box identifier"));
9275 INSERT_STRING(envelope
, "dbIDRecipient",
9276 outgoing_message
->envelope
->dbIDRecipient
);
9278 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
9279 outgoing_message
->envelope
->dmRecipientOrgUnit
);
9280 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
9281 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
9282 INSERT_STRING(envelope
, "dmToHands",
9283 outgoing_message
->envelope
->dmToHands
);
9286 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
9288 INSERT_STRING(envelope
, "dmAnnotation",
9289 outgoing_message
->envelope
->dmAnnotation
);
9291 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
9292 0, 50, "dmRecipientRefNumber");
9293 INSERT_STRING(envelope
, "dmRecipientRefNumber",
9294 outgoing_message
->envelope
->dmRecipientRefNumber
);
9296 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
9297 0, 50, "dmSenderRefNumber");
9298 INSERT_STRING(envelope
, "dmSenderRefNumber",
9299 outgoing_message
->envelope
->dmSenderRefNumber
);
9301 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
9302 0, 50, "dmRecipientIdent");
9303 INSERT_STRING(envelope
, "dmRecipientIdent",
9304 outgoing_message
->envelope
->dmRecipientIdent
);
9306 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
9307 0, 50, "dmSenderIdent");
9308 INSERT_STRING(envelope
, "dmSenderIdent",
9309 outgoing_message
->envelope
->dmSenderIdent
);
9311 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
9312 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
9313 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
9314 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
9315 INSERT_STRING(envelope
, "dmLegalTitleSect",
9316 outgoing_message
->envelope
->dmLegalTitleSect
);
9317 INSERT_STRING(envelope
, "dmLegalTitlePar",
9318 outgoing_message
->envelope
->dmLegalTitlePar
);
9319 INSERT_STRING(envelope
, "dmLegalTitlePoint",
9320 outgoing_message
->envelope
->dmLegalTitlePoint
);
9322 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
9323 outgoing_message
->envelope
->dmPersonalDelivery
);
9324 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
9325 outgoing_message
->envelope
->dmAllowSubstDelivery
);
9327 /* ???: Should we require value for dbEffectiveOVM sender?
9328 * ISDS has default as true */
9329 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
9330 INSERT_BOOLEAN(envelope
, "dmPublishOwnID",
9331 outgoing_message
->envelope
->dmPublishOwnID
);
9334 /* Append dmFiles */
9335 if (!outgoing_message
->documents
) {
9336 isds_log_message(context
,
9337 _("Outgoing message is missing list of documents"));
9341 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
9343 isds_printf_message(context
, _("Could not add dmFiles child to "
9344 "%s element"), create_message
->name
);
9349 /* Check for document hierarchy */
9350 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
9351 if (err
) goto leave
;
9353 /* Process each document */
9354 for (struct isds_list
*item
=
9355 (struct isds_list
*) outgoing_message
->documents
;
9356 item
; item
= item
->next
) {
9358 isds_log_message(context
,
9359 _("List of documents contains empty item"));
9363 /* FIXME: Check for dmFileMetaType and for document references.
9364 * Only first document can be of MAIN type */
9365 err
= insert_document(context
, (struct isds_document
*) item
->data
,
9368 if (err
) goto leave
;
9375 #endif /* HAVE_LIBCURL */
9378 /* Send a message via ISDS to a recipient
9379 * @context is session context
9380 * @outgoing_message is message to send; Some members are mandatory (like
9381 * dbIDRecipient), some are optional and some are irrelevant (especially data
9382 * about sender). Included pointer to isds_list documents must contain at
9383 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9384 * members will be filled with valid data from ISDS. Exact list of write
9385 * members is subject to change. Currently dmID is changed.
9386 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9387 isds_error
isds_send_message(struct isds_ctx
*context
,
9388 struct isds_message
*outgoing_message
) {
9390 isds_error err
= IE_SUCCESS
;
9392 xmlNsPtr isds_ns
= NULL
;
9393 xmlNodePtr request
= NULL
;
9394 xmlDocPtr response
= NULL
;
9395 xmlChar
*code
= NULL
, *message
= NULL
;
9396 xmlXPathContextPtr xpath_ctx
= NULL
;
9397 xmlXPathObjectPtr result
= NULL
;
9398 /*_Bool message_is_complete = 0;*/
9401 if (!context
) return IE_INVALID_CONTEXT
;
9402 zfree(context
->long_message
);
9403 if (!outgoing_message
) return IE_INVAL
;
9406 /* Check if connection is established
9407 * TODO: This check should be done downstairs. */
9408 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9411 /* Build CreateMessage request */
9412 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
9414 isds_log_message(context
,
9415 _("Could not build CreateMessage request"));
9418 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9420 isds_log_message(context
, _("Could not create ISDS name space"));
9421 xmlFreeNode(request
);
9424 xmlSetNs(request
, isds_ns
);
9426 /* Append envelope and files */
9427 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
9428 if (err
) goto leave
;
9431 /* Signal we can serialize message since now */
9432 /*message_is_complete = 1;*/
9435 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
9438 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9440 /* Don't' destroy request, we want to provide it to application later */
9443 isds_log(ILF_ISDS
, ILL_DEBUG
,
9444 _("Processing ISDS response on CreateMessage "
9445 "request failed\n"));
9449 /* Check for response status */
9450 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9451 &code
, &message
, NULL
);
9453 isds_log(ILF_ISDS
, ILL_DEBUG
,
9454 _("ISDS response on CreateMessage request "
9455 "is missing status\n"));
9459 /* Request processed, but refused by server or server failed */
9460 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9461 char *box_id_locale
=
9462 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9463 char *code_locale
= _isds_utf82locale((char*)code
);
9464 char *message_locale
= _isds_utf82locale((char*)message
);
9465 isds_log(ILF_ISDS
, ILL_DEBUG
,
9466 _("Server did not accept message for %s on CreateMessage "
9467 "request (code=%s, message=%s)\n"),
9468 box_id_locale
, code_locale
, message_locale
);
9469 isds_log_message(context
, message_locale
);
9470 free(box_id_locale
);
9472 free(message_locale
);
9479 xpath_ctx
= xmlXPathNewContext(response
);
9484 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9488 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
9494 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9495 isds_log_message(context
, _("Missing CreateMessageResponse element"));
9499 if (result
->nodesetval
->nodeNr
> 1) {
9500 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
9504 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9505 xmlXPathFreeObject(result
); result
= NULL
;
9507 if (outgoing_message
->envelope
->dmID
) {
9508 free(outgoing_message
->envelope
->dmID
);
9509 outgoing_message
->envelope
->dmID
= NULL
;
9511 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
9512 if (!outgoing_message
->envelope
->dmID
) {
9513 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
9514 "but did not return assigned message ID\n"));
9518 /* TODO: Serialize message into structure member raw */
9519 /* XXX: Each web service transport message in different format.
9520 * Therefore it's not possible to save them directly.
9521 * To save them, one must figure out common format.
9522 * We can leave it on application, or we can implement the ESS format. */
9523 /*if (message_is_complete) {
9524 if (outgoing_message->envelope->dmID) {
9526 /* Add assigned message ID as first child*/
9527 /*xmlNodePtr dmid_text = xmlNewText(
9528 (xmlChar *) outgoing_message->envelope->dmID);
9529 if (!dmid_text) goto serialization_failed;
9531 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9533 if (!dmid_element) {
9534 xmlFreeNode(dmid_text);
9535 goto serialization_failed;
9538 xmlNodePtr dmid_element_with_text =
9539 xmlAddChild(dmid_element, dmid_text);
9540 if (!dmid_element_with_text) {
9541 xmlFreeNode(dmid_element);
9542 xmlFreeNode(dmid_text);
9543 goto serialization_failed;
9546 node = xmlAddPrevSibling(envelope->childern,
9547 dmid_element_with_text);
9549 xmlFreeNodeList(dmid_element_with_text);
9550 goto serialization_failed;
9554 /* Serialize message with ID into raw */
9555 /*buffer = serialize_element(envelope)*/
9558 serialization_failed:
9563 xmlXPathFreeObject(result
);
9564 xmlXPathFreeContext(xpath_ctx
);
9568 xmlFreeDoc(response
);
9569 xmlFreeNode(request
);
9572 isds_log(ILF_ISDS
, ILL_DEBUG
,
9573 _("CreateMessage request processed by server "
9574 "successfully.\n"));
9575 #else /* not HAVE_LIBCURL */
9583 /* Send a message via ISDS to a multiple recipients
9584 * @context is session context
9585 * @outgoing_message is message to send; Some members are mandatory,
9586 * some are optional and some are irrelevant (especially data
9587 * about sender). Data about recipient will be substituted by ISDS from
9588 * @copies. Included pointer to isds_list documents must
9589 * contain at least one document of FILEMETATYPE_MAIN.
9590 * @copies is list of isds_message_copy structures addressing all desired
9591 * recipients. This is read-write structure, some members will be filled with
9592 * valid data from ISDS (message IDs, error codes, error descriptions).
9594 * ISDS_SUCCESS if all messages have been sent
9595 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9596 * succeeded messages can be identified by copies->data->error),
9597 * or other error code if something other goes wrong. */
9598 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
9599 const struct isds_message
*outgoing_message
,
9600 struct isds_list
*copies
) {
9602 isds_error err
= IE_SUCCESS
;
9604 isds_error append_err
;
9605 xmlNsPtr isds_ns
= NULL
;
9606 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
9607 struct isds_list
*item
;
9608 struct isds_message_copy
*copy
;
9609 xmlDocPtr response
= NULL
;
9610 xmlChar
*code
= NULL
, *message
= NULL
;
9611 xmlXPathContextPtr xpath_ctx
= NULL
;
9612 xmlXPathObjectPtr result
= NULL
;
9613 xmlChar
*string
= NULL
;
9617 if (!context
) return IE_INVALID_CONTEXT
;
9618 zfree(context
->long_message
);
9619 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9622 /* Check if connection is established
9623 * TODO: This check should be done downstairs. */
9624 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9627 /* Build CreateMultipleMessage request */
9628 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9630 isds_log_message(context
,
9631 _("Could not build CreateMultipleMessage request"));
9634 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9636 isds_log_message(context
, _("Could not create ISDS name space"));
9637 xmlFreeNode(request
);
9640 xmlSetNs(request
, isds_ns
);
9643 /* Build recipients */
9644 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9646 isds_log_message(context
, _("Could not add dmRecipients child to "
9647 "CreateMultipleMessage element"));
9648 xmlFreeNode(request
);
9652 /* Insert each recipient */
9653 for (item
= copies
; item
; item
= item
->next
) {
9654 copy
= (struct isds_message_copy
*) item
->data
;
9656 isds_log_message(context
,
9657 _("`copies' list item contains empty data"));
9662 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9664 isds_log_message(context
, _("Could not add dmRecipient child to "
9665 "dmRecipients element"));
9670 if (!copy
->dbIDRecipient
) {
9671 isds_log_message(context
,
9672 _("Message copy is missing recipient box identifier"));
9676 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9677 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9678 copy
->dmRecipientOrgUnit
);
9679 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9680 copy
->dmRecipientOrgUnitNum
, string
);
9681 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9684 /* Append envelope and files */
9685 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9686 if (err
) goto leave
;
9689 isds_log(ILF_ISDS
, ILL_DEBUG
,
9690 _("Sending CreateMultipleMessage request to ISDS\n"));
9693 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9695 isds_log(ILF_ISDS
, ILL_DEBUG
,
9696 _("Processing ISDS response on CreateMultipleMessage "
9697 "request failed\n"));
9701 /* Check for response status */
9702 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9703 &code
, &message
, NULL
);
9705 isds_log(ILF_ISDS
, ILL_DEBUG
,
9706 _("ISDS response on CreateMultipleMessage request "
9707 "is missing status\n"));
9711 /* Request processed, but some copies failed */
9712 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9713 char *box_id_locale
=
9714 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9715 char *code_locale
= _isds_utf82locale((char*)code
);
9716 char *message_locale
= _isds_utf82locale((char*)message
);
9717 isds_log(ILF_ISDS
, ILL_DEBUG
,
9718 _("Server did accept message for multiple recipients "
9719 "on CreateMultipleMessage request but delivery to "
9720 "some of them failed (code=%s, message=%s)\n"),
9721 box_id_locale
, code_locale
, message_locale
);
9722 isds_log_message(context
, message_locale
);
9723 free(box_id_locale
);
9725 free(message_locale
);
9726 err
= IE_PARTIAL_SUCCESS
;
9729 /* Request refused by server as whole */
9730 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9731 char *box_id_locale
=
9732 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9733 char *code_locale
= _isds_utf82locale((char*)code
);
9734 char *message_locale
= _isds_utf82locale((char*)message
);
9735 isds_log(ILF_ISDS
, ILL_DEBUG
,
9736 _("Server did not accept message for multiple recipients "
9737 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9738 box_id_locale
, code_locale
, message_locale
);
9739 isds_log_message(context
, message_locale
);
9740 free(box_id_locale
);
9742 free(message_locale
);
9749 xpath_ctx
= xmlXPathNewContext(response
);
9754 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9758 result
= xmlXPathEvalExpression(
9759 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9760 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9766 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9767 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9772 /* Extract message ID and delivery status for each copy */
9773 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9774 item
= item
->next
, i
++) {
9775 copy
= (struct isds_message_copy
*) item
->data
;
9776 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9778 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9784 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9785 isds_printf_message(context
, _("ISDS returned unexpected number of "
9786 "message copy delivery states: %d"),
9787 result
->nodesetval
->nodeNr
);
9796 xmlXPathFreeObject(result
);
9797 xmlXPathFreeContext(xpath_ctx
);
9801 xmlFreeDoc(response
);
9802 xmlFreeNode(request
);
9805 isds_log(ILF_ISDS
, ILL_DEBUG
,
9806 _("CreateMultipleMessageResponse request processed by server "
9807 "successfully.\n"));
9808 #else /* not HAVE_LIBCURL */
9816 /* Get list of messages. This is common core for getting sent or received
9818 * Any criterion argument can be NULL, if you don't care about it.
9819 * @context is session context. Must not be NULL.
9820 * @outgoing_direction is true if you want list of outgoing messages,
9821 * it's false if you want incoming messages.
9822 * @from_time is minimal time and date of message sending inclusive.
9823 * @to_time is maximal time and date of message sending inclusive
9824 * @organization_unit_number is number of sender/recipient respectively.
9825 * @status_filter is bit field of isds_message_status values. Use special
9826 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9827 * all values, you can use bit-wise arithmetic if you want.)
9828 * @offset is index of first message we are interested in. First message is 1.
9829 * Set to 0 (or 1) if you don't care.
9830 * @number is maximal length of list you want to get as input value, outputs
9831 * number of messages matching these criteria. Can be NULL if you don't care
9832 * (applies to output value either).
9833 * @messages is automatically reallocated list of isds_message's. Be ware that
9834 * it returns only brief overview (envelope and some other fields) about each
9835 * message, not the complete message. FIXME: Specify exact fields.
9836 * The list is sorted by delivery time in ascending order.
9837 * Use NULL if you don't care about don't need the data (useful if you want to
9838 * know only the @number). If you provide &NULL, list will be allocated on
9839 * heap, if you provide pointer to non-NULL, list will be freed automatically
9840 * at first. Also in case of error the list will be NULLed.
9841 * @return IE_SUCCESS or appropriate error code. */
9842 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9843 _Bool outgoing_direction
,
9844 const struct timeval
*from_time
, const struct timeval
*to_time
,
9845 const long int *organization_unit_number
,
9846 const unsigned int status_filter
,
9847 const unsigned long int offset
, unsigned long int *number
,
9848 struct isds_list
**messages
) {
9850 isds_error err
= IE_SUCCESS
;
9852 xmlNsPtr isds_ns
= NULL
;
9853 xmlNodePtr request
= NULL
, node
;
9854 xmlDocPtr response
= NULL
;
9855 xmlChar
*code
= NULL
, *message
= NULL
;
9856 xmlXPathContextPtr xpath_ctx
= NULL
;
9857 xmlXPathObjectPtr result
= NULL
;
9858 xmlChar
*string
= NULL
;
9862 if (!context
) return IE_INVALID_CONTEXT
;
9863 zfree(context
->long_message
);
9865 /* Free former message list if any */
9866 if (messages
) isds_list_free(messages
);
9869 /* Check if connection is established
9870 * TODO: This check should be done downstairs. */
9871 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9873 /* Build GetListOf*Messages request */
9874 request
= xmlNewNode(NULL
,
9875 (outgoing_direction
) ?
9876 BAD_CAST
"GetListOfSentMessages" :
9877 BAD_CAST
"GetListOfReceivedMessages"
9880 isds_log_message(context
,
9881 (outgoing_direction
) ?
9882 _("Could not build GetListOfSentMessages request") :
9883 _("Could not build GetListOfReceivedMessages request")
9887 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9889 isds_log_message(context
, _("Could not create ISDS name space"));
9890 xmlFreeNode(request
);
9893 xmlSetNs(request
, isds_ns
);
9897 err
= timeval2timestring(from_time
, &string
);
9898 if (err
) goto leave
;
9900 INSERT_STRING(request
, "dmFromTime", string
);
9901 free(string
); string
= NULL
;
9904 err
= timeval2timestring(to_time
, &string
);
9905 if (err
) goto leave
;
9907 INSERT_STRING(request
, "dmToTime", string
);
9908 free(string
); string
= NULL
;
9910 if (outgoing_direction
) {
9911 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9912 organization_unit_number
, string
);
9914 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9915 organization_unit_number
, string
);
9918 if (status_filter
> MESSAGESTATE_ANY
) {
9919 isds_printf_message(context
,
9920 _("Invalid message state filter value: %ld"), status_filter
);
9924 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9927 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9929 INSERT_STRING(request
, "dmOffset", "1");
9932 /* number 0 means no limit */
9933 if (number
&& *number
== 0) {
9934 INSERT_STRING(request
, "dmLimit", NULL
);
9936 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9940 isds_log(ILF_ISDS
, ILL_DEBUG
,
9941 (outgoing_direction
) ?
9942 _("Sending GetListOfSentMessages request to ISDS\n") :
9943 _("Sending GetListOfReceivedMessages request to ISDS\n")
9947 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9948 xmlFreeNode(request
); request
= NULL
;
9951 isds_log(ILF_ISDS
, ILL_DEBUG
,
9952 (outgoing_direction
) ?
9953 _("Processing ISDS response on GetListOfSentMessages "
9954 "request failed\n") :
9955 _("Processing ISDS response on GetListOfReceivedMessages "
9961 /* Check for response status */
9962 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9963 &code
, &message
, NULL
);
9965 isds_log(ILF_ISDS
, ILL_DEBUG
,
9966 (outgoing_direction
) ?
9967 _("ISDS response on GetListOfSentMessages request "
9968 "is missing status\n") :
9969 _("ISDS response on GetListOfReceivedMessages request "
9970 "is missing status\n")
9975 /* Request processed, but nothing found */
9976 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9977 char *code_locale
= _isds_utf82locale((char*)code
);
9978 char *message_locale
= _isds_utf82locale((char*)message
);
9979 isds_log(ILF_ISDS
, ILL_DEBUG
,
9980 (outgoing_direction
) ?
9981 _("Server refused GetListOfSentMessages request "
9982 "(code=%s, message=%s)\n") :
9983 _("Server refused GetListOfReceivedMessages request "
9984 "(code=%s, message=%s)\n"),
9985 code_locale
, message_locale
);
9986 isds_log_message(context
, message_locale
);
9988 free(message_locale
);
9995 xpath_ctx
= xmlXPathNewContext(response
);
10000 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10004 result
= xmlXPathEvalExpression(
10005 (outgoing_direction
) ?
10006 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
10007 "isds:dmRecords/isds:dmRecord" :
10008 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
10009 "isds:dmRecords/isds:dmRecord",
10016 /* Fill output arguments in */
10017 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10018 struct isds_envelope
*envelope
;
10019 struct isds_list
*item
= NULL
, *last_item
= NULL
;
10021 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
10022 /* Create new message */
10023 item
= calloc(1, sizeof(*item
));
10028 item
->destructor
= (void(*)(void**)) &isds_message_free
;
10029 item
->data
= calloc(1, sizeof(struct isds_message
));
10031 isds_list_free(&item
);
10036 /* Extract envelope data */
10037 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
10039 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
10041 isds_list_free(&item
);
10045 /* Attach extracted envelope */
10046 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
10048 /* Append new message into the list */
10050 *messages
= last_item
= item
;
10052 last_item
->next
= item
;
10057 if (number
) *number
= count
;
10061 isds_list_free(messages
);
10065 xmlXPathFreeObject(result
);
10066 xmlXPathFreeContext(xpath_ctx
);
10070 xmlFreeDoc(response
);
10071 xmlFreeNode(request
);
10074 isds_log(ILF_ISDS
, ILL_DEBUG
,
10075 (outgoing_direction
) ?
10076 _("GetListOfSentMessages request processed by server "
10077 "successfully.\n") :
10078 _("GetListOfReceivedMessages request processed by server "
10081 #else /* not HAVE_LIBCURL */
10088 /* Get list of outgoing (already sent) messages.
10089 * Any criterion argument can be NULL, if you don't care about it.
10090 * @context is session context. Must not be NULL.
10091 * @from_time is minimal time and date of message sending inclusive.
10092 * @to_time is maximal time and date of message sending inclusive
10093 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
10094 * @status_filter is bit field of isds_message_status values. Use special
10095 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10096 * all values, you can use bit-wise arithmetic if you want.)
10097 * @offset is index of first message we are interested in. First message is 1.
10098 * Set to 0 (or 1) if you don't care.
10099 * @number is maximal length of list you want to get as input value, outputs
10100 * number of messages matching these criteria. Can be NULL if you don't care
10101 * (applies to output value either).
10102 * @messages is automatically reallocated list of isds_message's. Be ware that
10103 * it returns only brief overview (envelope and some other fields) about each
10104 * message, not the complete message. FIXME: Specify exact fields.
10105 * The list is sorted by delivery time in ascending order.
10106 * Use NULL if you don't care about the meta data (useful if you want to know
10107 * only the @number). If you provide &NULL, list will be allocated on heap,
10108 * if you provide pointer to non-NULL, list will be freed automatically at
10109 * first. Also in case of error the list will be NULLed.
10110 * @return IE_SUCCESS or appropriate error code. */
10111 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
10112 const struct timeval
*from_time
, const struct timeval
*to_time
,
10113 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
10114 const unsigned long int offset
, unsigned long int *number
,
10115 struct isds_list
**messages
) {
10117 return isds_get_list_of_messages(
10119 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
10125 /* Get list of incoming (addressed to you) messages.
10126 * Any criterion argument can be NULL, if you don't care about it.
10127 * @context is session context. Must not be NULL.
10128 * @from_time is minimal time and date of message sending inclusive.
10129 * @to_time is maximal time and date of message sending inclusive
10130 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
10131 * @status_filter is bit field of isds_message_status values. Use special
10132 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10133 * all values, you can use bit-wise arithmetic if you want.)
10134 * @offset is index of first message we are interested in. First message is 1.
10135 * Set to 0 (or 1) if you don't care.
10136 * @number is maximal length of list you want to get as input value, outputs
10137 * number of messages matching these criteria. Can be NULL if you don't care
10138 * (applies to output value either).
10139 * @messages is automatically reallocated list of isds_message's. Be ware that
10140 * it returns only brief overview (envelope and some other fields) about each
10141 * message, not the complete message. FIXME: Specify exact fields.
10142 * Use NULL if you don't care about the meta data (useful if you want to know
10143 * only the @number). If you provide &NULL, list will be allocated on heap,
10144 * if you provide pointer to non-NULL, list will be freed automatically at
10145 * first. Also in case of error the list will be NULLed.
10146 * @return IE_SUCCESS or appropriate error code. */
10147 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
10148 const struct timeval
*from_time
, const struct timeval
*to_time
,
10149 const long int *dmRecipientOrgUnitNum
,
10150 const unsigned int status_filter
,
10151 const unsigned long int offset
, unsigned long int *number
,
10152 struct isds_list
**messages
) {
10154 return isds_get_list_of_messages(
10156 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
10162 /* Get list of sent message state changes.
10163 * Any criterion argument can be NULL, if you don't care about it.
10164 * @context is session context. Must not be NULL.
10165 * @from_time is minimal time and date of status changes inclusive
10166 * @to_time is maximal time and date of status changes inclusive
10167 * @changed_states is automatically reallocated list of
10168 * isds_message_status_change's. If you provide &NULL, list will be allocated
10169 * on heap, if you provide pointer to non-NULL, list will be freed
10170 * automatically at first. Also in case of error the list will be NULLed.
10171 * XXX: The list item ordering is not specified.
10172 * XXX: Server provides only `recent' changes.
10173 * @return IE_SUCCESS or appropriate error code. */
10174 isds_error
isds_get_list_of_sent_message_state_changes(
10175 struct isds_ctx
*context
,
10176 const struct timeval
*from_time
, const struct timeval
*to_time
,
10177 struct isds_list
**changed_states
) {
10179 isds_error err
= IE_SUCCESS
;
10181 xmlNsPtr isds_ns
= NULL
;
10182 xmlNodePtr request
= NULL
, node
;
10183 xmlDocPtr response
= NULL
;
10184 xmlXPathContextPtr xpath_ctx
= NULL
;
10185 xmlXPathObjectPtr result
= NULL
;
10186 xmlChar
*string
= NULL
;
10190 if (!context
) return IE_INVALID_CONTEXT
;
10191 zfree(context
->long_message
);
10193 /* Free former message list if any */
10194 isds_list_free(changed_states
);
10197 /* Check if connection is established
10198 * TODO: This check should be done downstairs. */
10199 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10201 /* Build GetMessageStateChanges request */
10202 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
10204 isds_log_message(context
,
10205 _("Could not build GetMessageStateChanges request"));
10208 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10210 isds_log_message(context
, _("Could not create ISDS name space"));
10211 xmlFreeNode(request
);
10214 xmlSetNs(request
, isds_ns
);
10218 err
= timeval2timestring(from_time
, &string
);
10219 if (err
) goto leave
;
10221 INSERT_STRING(request
, "dmFromTime", string
);
10225 err
= timeval2timestring(to_time
, &string
);
10226 if (err
) goto leave
;
10228 INSERT_STRING(request
, "dmToTime", string
);
10233 err
= send_destroy_request_check_response(context
,
10234 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
10235 &response
, NULL
, NULL
);
10236 if (err
) goto leave
;
10240 xpath_ctx
= xmlXPathNewContext(response
);
10245 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10249 result
= xmlXPathEvalExpression(
10250 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
10251 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
10257 /* Fill output arguments in */
10258 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10259 struct isds_list
*item
= NULL
, *last_item
= NULL
;
10261 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
10262 /* Create new status change */
10263 item
= calloc(1, sizeof(*item
));
10269 (void(*)(void**)) &isds_message_status_change_free
;
10271 /* Extract message status change */
10272 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
10273 err
= extract_StateChangesRecord(context
,
10274 (struct isds_message_status_change
**) &item
->data
,
10277 isds_list_free(&item
);
10281 /* Append new message status change into the list */
10282 if (!*changed_states
) {
10283 *changed_states
= last_item
= item
;
10285 last_item
->next
= item
;
10293 isds_list_free(changed_states
);
10297 xmlXPathFreeObject(result
);
10298 xmlXPathFreeContext(xpath_ctx
);
10299 xmlFreeDoc(response
);
10300 xmlFreeNode(request
);
10303 isds_log(ILF_ISDS
, ILL_DEBUG
,
10304 _("GetMessageStateChanges request processed by server "
10305 "successfully.\n"));
10306 #else /* not HAVE_LIBCURL */
10314 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10316 * @context is session context
10317 * @service is ISDS WS service handler
10318 * @service_name is name of SERVICE_DM_OPERATIONS
10319 * @message_id is message ID to send as service argument to ISDS
10320 * @response is reallocated server SOAP body response as XML document
10321 * @raw_response is reallocated bit stream with response body. Use
10322 * NULL if you don't care
10323 * @raw_response_length is size of @raw_response in bytes
10324 * @code is reallocated ISDS status code
10325 * @status_message is reallocated ISDS status message
10326 * @return error coded from lower layer, context message will be set up
10327 * appropriately. */
10328 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
10329 const isds_service service
, const xmlChar
*service_name
,
10330 const char *message_id
,
10331 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
10332 xmlChar
**code
, xmlChar
**status_message
) {
10334 isds_error err
= IE_SUCCESS
;
10335 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
10336 xmlNodePtr request
= NULL
, node
;
10337 xmlNsPtr isds_ns
= NULL
;
10339 if (!context
) return IE_INVALID_CONTEXT
;
10340 if (!service_name
|| !message_id
) return IE_INVAL
;
10341 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
10342 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
10344 /* Free output argument */
10345 xmlFreeDoc(*response
); *response
= NULL
;
10346 if (raw_response
) zfree(*raw_response
);
10348 zfree(*status_message
);
10351 /* Check if connection is established
10352 * TODO: This check should be done downstairs. */
10353 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10355 service_name_locale
= _isds_utf82locale((char*)service_name
);
10356 message_id_locale
= _isds_utf82locale(message_id
);
10357 if (!service_name_locale
|| !message_id_locale
) {
10362 /* Build request */
10363 request
= xmlNewNode(NULL
, service_name
);
10365 isds_printf_message(context
,
10366 _("Could not build %s request for %s message ID"),
10367 service_name_locale
, message_id_locale
);
10371 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10373 isds_log_message(context
, _("Could not create ISDS name space"));
10377 xmlSetNs(request
, isds_ns
);
10380 /* Add requested ID */
10381 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
10382 if (err
) goto leave
;
10383 INSERT_STRING(request
, "dmID", message_id
);
10386 isds_log(ILF_ISDS
, ILL_DEBUG
,
10387 _("Sending %s request for %s message ID to ISDS\n"),
10388 service_name_locale
, message_id_locale
);
10391 err
= _isds(context
, service
, request
, response
,
10392 raw_response
, raw_response_length
);
10393 xmlFreeNode(request
); request
= NULL
;
10396 isds_log(ILF_ISDS
, ILL_DEBUG
,
10397 _("Processing ISDS response on %s request failed\n"),
10398 service_name_locale
);
10402 /* Check for response status */
10403 err
= isds_response_status(context
, service
, *response
,
10404 code
, status_message
, NULL
);
10406 isds_log(ILF_ISDS
, ILL_DEBUG
,
10407 _("ISDS response on %s request is missing status\n"),
10408 service_name_locale
);
10412 /* Request processed, but nothing found */
10413 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
10414 char *code_locale
= _isds_utf82locale((char*) *code
);
10415 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
10416 isds_log(ILF_ISDS
, ILL_DEBUG
,
10417 _("Server refused %s request for %s message ID "
10418 "(code=%s, message=%s)\n"),
10419 service_name_locale
, message_id_locale
,
10420 code_locale
, status_message_locale
);
10421 isds_log_message(context
, status_message_locale
);
10423 free(status_message_locale
);
10429 free(message_id_locale
);
10430 free(service_name_locale
);
10431 xmlFreeNode(request
);
10436 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10437 * signed data and free ISDS response.
10438 * @context is session context
10439 * @message_id is UTF-8 encoded message ID for logging purpose
10440 * @response is parsed XML document. It will be freed and NULLed in the middle
10441 * of function run to save memory. This is not guaranteed in case of error.
10442 * @request_name is name of ISDS request used to construct response root
10443 * element name and for logging purpose.
10444 * @raw is reallocated output buffer with DER encoded CMS data
10445 * @raw_length is size of @raw buffer in bytes
10446 * @returns standard error codes, in case of error, @raw will be freed and
10447 * NULLed, @response sometimes. */
10448 static isds_error
find_extract_signed_data_free_response(
10449 struct isds_ctx
*context
, const xmlChar
*message_id
,
10450 xmlDocPtr
*response
, const xmlChar
*request_name
,
10451 void **raw
, size_t *raw_length
) {
10453 isds_error err
= IE_SUCCESS
;
10454 char *xpath_expression
= NULL
;
10455 xmlXPathContextPtr xpath_ctx
= NULL
;
10456 xmlXPathObjectPtr result
= NULL
;
10457 char *encoded_structure
= NULL
;
10459 if (!context
) return IE_INVALID_CONTEXT
;
10460 if (!raw
) return IE_INVAL
;
10462 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
10465 /* Build XPath expression */
10466 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
10467 "Response/isds:dmSignature");
10468 if (!xpath_expression
) return IE_NOMEM
;
10471 xpath_ctx
= xmlXPathNewContext(*response
);
10476 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10480 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
10485 /* Empty response */
10486 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10487 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10488 isds_printf_message(context
,
10489 _("Server did not return any signed data for message ID `%s' "
10491 message_id_locale
, request_name
);
10492 free(message_id_locale
);
10496 /* More responses */
10497 if (result
->nodesetval
->nodeNr
> 1) {
10498 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10499 isds_printf_message(context
,
10500 _("Server did return more signed data for message ID `%s' "
10502 message_id_locale
, request_name
);
10503 free(message_id_locale
);
10508 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10510 /* Extract PKCS#7 structure */
10511 EXTRACT_STRING(".", encoded_structure
);
10512 if (!encoded_structure
) {
10513 isds_log_message(context
, _("dmSignature element is empty"));
10516 /* Here we have delivery info as standalone CMS in encoded_structure.
10517 * We don't need any other data, free them: */
10518 xmlXPathFreeObject(result
); result
= NULL
;
10519 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
10520 xmlFreeDoc(*response
); *response
= NULL
;
10523 /* Decode PKCS#7 to DER format */
10524 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
10525 if (*raw_length
== (size_t) -1) {
10526 isds_log_message(context
,
10527 _("Error while Base64-decoding PKCS#7 structure"));
10538 free(encoded_structure
);
10539 xmlXPathFreeObject(result
);
10540 xmlXPathFreeContext(xpath_ctx
);
10541 free(xpath_expression
);
10545 #endif /* HAVE_LIBCURL */
10548 /* Download incoming message envelope identified by ID.
10549 * @context is session context
10550 * @message_id is message identifier (you can get them from
10551 * isds_get_list_of_received_messages())
10552 * @message is automatically reallocated message retrieved from ISDS.
10553 * It will miss documents per se. Use isds_get_received_message(), if you are
10554 * interested in documents (content) too.
10555 * Returned hash and timestamp require documents to be verifiable. */
10556 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
10557 const char *message_id
, struct isds_message
**message
) {
10559 isds_error err
= IE_SUCCESS
;
10561 xmlDocPtr response
= NULL
;
10562 xmlChar
*code
= NULL
, *status_message
= NULL
;
10563 xmlXPathContextPtr xpath_ctx
= NULL
;
10564 xmlXPathObjectPtr result
= NULL
;
10567 if (!context
) return IE_INVALID_CONTEXT
;
10568 zfree(context
->long_message
);
10570 /* Free former message if any */
10571 if (!message
) return IE_INVAL
;
10572 isds_message_free(message
);
10575 /* Do request and check for success */
10576 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10577 BAD_CAST
"MessageEnvelopeDownload", message_id
,
10578 &response
, NULL
, NULL
, &code
, &status_message
);
10579 if (err
) goto leave
;
10582 xpath_ctx
= xmlXPathNewContext(response
);
10587 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10591 result
= xmlXPathEvalExpression(
10592 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
10593 "isds:dmReturnedMessageEnvelope",
10599 /* Empty response */
10600 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10601 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10602 isds_printf_message(context
,
10603 _("Server did not return any envelope for ID `%s' "
10604 "on MessageEnvelopeDownload request"), message_id_locale
);
10605 free(message_id_locale
);
10609 /* More envelops */
10610 if (result
->nodesetval
->nodeNr
> 1) {
10611 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10612 isds_printf_message(context
,
10613 _("Server did return more envelopes for ID `%s' "
10614 "on MessageEnvelopeDownload request"), message_id_locale
);
10615 free(message_id_locale
);
10620 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10622 /* Extract the envelope (= message without documents, hence 0) */
10623 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10624 if (err
) goto leave
;
10626 /* Save XML blob */
10627 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10628 &(*message
)->raw_length
);
10632 isds_message_free(message
);
10635 xmlXPathFreeObject(result
);
10636 xmlXPathFreeContext(xpath_ctx
);
10639 free(status_message
);
10640 if (!*message
|| !(*message
)->xml
) {
10641 xmlFreeDoc(response
);
10645 isds_log(ILF_ISDS
, ILL_DEBUG
,
10646 _("MessageEnvelopeDownload request processed by server "
10649 #else /* not HAVE_LIBCURL */
10656 /* Load delivery info of any format from buffer.
10657 * @context is session context
10658 * @raw_type advertises format of @buffer content. Only delivery info types
10660 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10661 * retrieve such data from message->raw after calling
10662 * isds_get_signed_delivery_info().
10663 * @length is length of buffer in bytes.
10664 * @message is automatically reallocated message parsed from @buffer.
10665 * @strategy selects how buffer will be attached into raw isds_message member.
10667 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10668 const isds_raw_type raw_type
,
10669 const void *buffer
, const size_t length
,
10670 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10672 isds_error err
= IE_SUCCESS
;
10673 message_ns_type message_ns
;
10674 xmlDocPtr message_doc
= NULL
;
10675 xmlXPathContextPtr xpath_ctx
= NULL
;
10676 xmlXPathObjectPtr result
= NULL
;
10677 void *xml_stream
= NULL
;
10678 size_t xml_stream_length
= 0;
10680 if (!context
) return IE_INVALID_CONTEXT
;
10681 zfree(context
->long_message
);
10682 if (!message
) return IE_INVAL
;
10683 isds_message_free(message
);
10684 if (!buffer
) return IE_INVAL
;
10687 /* Select buffer format and extract XML from CMS*/
10688 switch (raw_type
) {
10689 case RAWTYPE_DELIVERYINFO
:
10690 message_ns
= MESSAGE_NS_UNSIGNED
;
10691 xml_stream
= (void *) buffer
;
10692 xml_stream_length
= length
;
10695 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10696 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10697 xml_stream
= (void *) buffer
;
10698 xml_stream_length
= length
;
10701 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10702 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10703 err
= _isds_extract_cms_data(context
, buffer
, length
,
10704 &xml_stream
, &xml_stream_length
);
10705 if (err
) goto leave
;
10709 isds_log_message(context
, _("Bad raw delivery representation type"));
10714 if (_isds_sizet2int(xml_stream_length
) >= 0) {
10715 isds_log(ILF_ISDS
, ILL_DEBUG
,
10716 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10717 _isds_sizet2int(xml_stream_length
), xml_stream
);
10720 /* Convert delivery info XML stream into XPath context */
10721 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10722 if (!message_doc
) {
10726 xpath_ctx
= xmlXPathNewContext(message_doc
);
10731 /* XXX: Name spaces mangled for signed delivery info:
10732 * http://isds.czechpoint.cz/v20/delivery:
10734 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10736 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10737 * <p:dmID>170272</p:dmID>
10740 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10742 * </q:dmEvents>...</q:dmEvents>
10744 * </q:GetDeliveryInfoResponse>
10746 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10750 result
= xmlXPathEvalExpression(
10751 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10757 /* Empty delivery info */
10758 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10759 isds_printf_message(context
,
10760 _("XML document is not sisds:dmDelivery document"));
10764 /* More delivery info's */
10765 if (result
->nodesetval
->nodeNr
> 1) {
10766 isds_printf_message(context
,
10767 _("XML document has more sisds:dmDelivery elements"));
10771 /* One delivery info */
10772 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10774 /* Extract the envelope (= message without documents, hence 0).
10775 * XXX: extract_TReturnedMessage() can obtain attachments size,
10776 * but delivery info carries none. It's coded as option elements,
10777 * so it should work. */
10778 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10779 if (err
) goto leave
;
10781 /* Extract events */
10782 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10783 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10784 if (err
) { err
= IE_ERROR
; goto leave
; }
10785 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10786 if (err
) goto leave
;
10788 /* Append raw CMS structure into message */
10789 (*message
)->raw_type
= raw_type
;
10790 switch (strategy
) {
10791 case BUFFER_DONT_STORE
:
10794 (*message
)->raw
= malloc(length
);
10795 if (!(*message
)->raw
) {
10799 memcpy((*message
)->raw
, buffer
, length
);
10800 (*message
)->raw_length
= length
;
10803 (*message
)->raw
= (void *) buffer
;
10804 (*message
)->raw_length
= length
;
10813 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10814 isds_message_free(message
);
10817 xmlXPathFreeObject(result
);
10818 xmlXPathFreeContext(xpath_ctx
);
10819 if (!*message
|| !(*message
)->xml
) {
10820 xmlFreeDoc(message_doc
);
10822 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10825 isds_log(ILF_ISDS
, ILL_DEBUG
,
10826 _("Delivery info loaded successfully.\n"));
10831 /* Download signed delivery info-sheet of given message identified by ID.
10832 * @context is session context
10833 * @message_id is message identifier (you can get them from
10834 * isds_get_list_of_{sent,received}_messages())
10835 * @message is automatically reallocated message retrieved from ISDS.
10836 * It will miss documents per se. Use isds_get_signed_received_message(),
10837 * if you are interested in documents (content). OTOH, only this function
10838 * can get list events message has gone through. */
10839 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10840 const char *message_id
, struct isds_message
**message
) {
10842 isds_error err
= IE_SUCCESS
;
10844 xmlDocPtr response
= NULL
;
10845 xmlChar
*code
= NULL
, *status_message
= NULL
;
10847 size_t raw_length
= 0;
10850 if (!context
) return IE_INVALID_CONTEXT
;
10851 zfree(context
->long_message
);
10853 /* Free former message if any */
10854 if (!message
) return IE_INVAL
;
10855 isds_message_free(message
);
10858 /* Do request and check for success */
10859 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10860 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10861 &response
, NULL
, NULL
, &code
, &status_message
);
10862 if (err
) goto leave
;
10864 /* Find signed delivery info, extract it into raw and maybe free
10866 err
= find_extract_signed_data_free_response(context
,
10867 (xmlChar
*)message_id
, &response
,
10868 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10869 if (err
) goto leave
;
10871 /* Parse delivery info */
10872 err
= isds_load_delivery_info(context
,
10873 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10874 message
, BUFFER_MOVE
);
10875 if (err
) goto leave
;
10881 isds_message_free(message
);
10886 free(status_message
);
10887 xmlFreeDoc(response
);
10890 isds_log(ILF_ISDS
, ILL_DEBUG
,
10891 _("GetSignedDeliveryInfo request processed by server "
10894 #else /* not HAVE_LIBCURL */
10901 /* Download delivery info-sheet of given message identified by ID.
10902 * @context is session context
10903 * @message_id is message identifier (you can get them from
10904 * isds_get_list_of_{sent,received}_messages())
10905 * @message is automatically reallocated message retrieved from ISDS.
10906 * It will miss documents per se. Use isds_get_received_message(), if you are
10907 * interested in documents (content). OTOH, only this function can get list
10908 * of events message has gone through. */
10909 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10910 const char *message_id
, struct isds_message
**message
) {
10912 isds_error err
= IE_SUCCESS
;
10914 xmlDocPtr response
= NULL
;
10915 xmlChar
*code
= NULL
, *status_message
= NULL
;
10916 xmlNodePtr delivery_node
= NULL
;
10918 size_t raw_length
= 0;
10921 if (!context
) return IE_INVALID_CONTEXT
;
10922 zfree(context
->long_message
);
10924 /* Free former message if any */
10925 if (!message
) return IE_INVAL
;
10926 isds_message_free(message
);
10929 /* Do request and check for success */
10930 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10931 BAD_CAST
"GetDeliveryInfo", message_id
,
10932 &response
, NULL
, NULL
, &code
, &status_message
);
10933 if (err
) goto leave
;
10936 /* Serialize delivery info */
10937 delivery_node
= xmlDocGetRootElement(response
);
10938 if (!delivery_node
) {
10939 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10940 isds_printf_message(context
,
10941 _("Server did not return any delivery info for ID `%s' "
10942 "on GetDeliveryInfo request"), message_id_locale
);
10943 free(message_id_locale
);
10947 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10948 if (err
) goto leave
;
10950 /* Parse delivery info */
10951 /* TODO: Here we parse the response second time. We could single delivery
10952 * parser from isds_load_delivery_info() to make things faster. */
10953 err
= isds_load_delivery_info(context
,
10954 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10955 message
, BUFFER_MOVE
);
10956 if (err
) goto leave
;
10963 isds_message_free(message
);
10968 free(status_message
);
10969 xmlFreeDoc(response
);
10972 isds_log(ILF_ISDS
, ILL_DEBUG
,
10973 _("GetDeliveryInfo request processed by server "
10976 #else /* not HAVE_LIBCURL */
10983 /* Download incoming message identified by ID.
10984 * @context is session context
10985 * @message_id is message identifier (you can get them from
10986 * isds_get_list_of_received_messages())
10987 * @message is automatically reallocated message retrieved from ISDS */
10988 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10989 const char *message_id
, struct isds_message
**message
) {
10991 isds_error err
= IE_SUCCESS
;
10993 xmlDocPtr response
= NULL
;
10994 void *xml_stream
= NULL
;
10995 size_t xml_stream_length
;
10996 xmlChar
*code
= NULL
, *status_message
= NULL
;
10997 xmlXPathContextPtr xpath_ctx
= NULL
;
10998 xmlXPathObjectPtr result
= NULL
;
10999 char *phys_path
= NULL
;
11000 size_t phys_start
, phys_end
;
11003 if (!context
) return IE_INVALID_CONTEXT
;
11004 zfree(context
->long_message
);
11006 /* Free former message if any */
11007 if (NULL
== message
) return IE_INVAL
;
11008 if (message
) isds_message_free(message
);
11011 /* Do request and check for success */
11012 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
11013 BAD_CAST
"MessageDownload", message_id
,
11014 &response
, &xml_stream
, &xml_stream_length
,
11015 &code
, &status_message
);
11016 if (err
) goto leave
;
11019 xpath_ctx
= xmlXPathNewContext(response
);
11024 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11028 result
= xmlXPathEvalExpression(
11029 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
11035 /* Empty response */
11036 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11037 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11038 isds_printf_message(context
,
11039 _("Server did not return any message for ID `%s' "
11040 "on MessageDownload request"), message_id_locale
);
11041 free(message_id_locale
);
11045 /* More messages */
11046 if (result
->nodesetval
->nodeNr
> 1) {
11047 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11048 isds_printf_message(context
,
11049 _("Server did return more messages for ID `%s' "
11050 "on MessageDownload request"), message_id_locale
);
11051 free(message_id_locale
);
11056 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11058 /* Extract the message */
11059 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
11060 if (err
) goto leave
;
11062 /* Locate raw XML blob */
11063 phys_path
= strdup(
11064 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
11065 PHYSXML_ELEMENT_SEPARATOR
11066 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
11067 PHYSXML_ELEMENT_SEPARATOR
11068 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11074 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11075 phys_path
, &phys_start
, &phys_end
);
11078 isds_log_message(context
,
11079 _("Substring with isds:MessageDownloadResponse element "
11080 "could not be located in raw SOAP message"));
11083 /* Save XML blob */
11084 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
11085 &(*message)->raw_length);*/
11086 /* TODO: Store name space declarations from ancestors */
11087 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
11088 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
11089 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
11090 (*message
)->raw
= malloc((*message
)->raw_length
);
11091 if (!(*message
)->raw
) {
11095 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
11100 isds_message_free(message
);
11105 xmlXPathFreeObject(result
);
11106 xmlXPathFreeContext(xpath_ctx
);
11109 free(status_message
);
11111 if (!*message
|| !(*message
)->xml
) {
11112 xmlFreeDoc(response
);
11116 isds_log(ILF_ISDS
, ILL_DEBUG
,
11117 _("MessageDownload request processed by server "
11120 #else /* not HAVE_LIBCURL */
11127 /* Load message of any type from buffer.
11128 * @context is session context
11129 * @raw_type defines content type of @buffer. Only message types are allowed.
11130 * @buffer is message raw representation. Format (CMS, plain signed,
11131 * message direction) is defined in @raw_type. You can retrieve such data
11132 * from message->raw after calling isds_get_[signed]{received,sent}_message().
11133 * @length is length of buffer in bytes.
11134 * @message is automatically reallocated message parsed from @buffer.
11135 * @strategy selects how buffer will be attached into raw isds_message member.
11137 isds_error
isds_load_message(struct isds_ctx
*context
,
11138 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
11139 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
11141 isds_error err
= IE_SUCCESS
;
11142 void *xml_stream
= NULL
;
11143 size_t xml_stream_length
= 0;
11144 message_ns_type message_ns
;
11145 xmlDocPtr message_doc
= NULL
;
11146 xmlXPathContextPtr xpath_ctx
= NULL
;
11147 xmlXPathObjectPtr result
= NULL
;
11149 if (!context
) return IE_INVALID_CONTEXT
;
11150 zfree(context
->long_message
);
11151 if (!message
) return IE_INVAL
;
11152 isds_message_free(message
);
11153 if (!buffer
) return IE_INVAL
;
11156 /* Select buffer format and extract XML from CMS*/
11157 switch (raw_type
) {
11158 case RAWTYPE_INCOMING_MESSAGE
:
11159 message_ns
= MESSAGE_NS_UNSIGNED
;
11160 xml_stream
= (void *) buffer
;
11161 xml_stream_length
= length
;
11164 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11165 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
11166 xml_stream
= (void *) buffer
;
11167 xml_stream_length
= length
;
11170 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11171 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
11172 err
= _isds_extract_cms_data(context
, buffer
, length
,
11173 &xml_stream
, &xml_stream_length
);
11174 if (err
) goto leave
;
11177 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11178 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
11179 xml_stream
= (void *) buffer
;
11180 xml_stream_length
= length
;
11183 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11184 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
11185 err
= _isds_extract_cms_data(context
, buffer
, length
,
11186 &xml_stream
, &xml_stream_length
);
11187 if (err
) goto leave
;
11191 isds_log_message(context
, _("Bad raw message representation type"));
11196 if (_isds_sizet2int(xml_stream_length
) >= 0) {
11197 isds_log(ILF_ISDS
, ILL_DEBUG
,
11198 _("Loading message:\n%.*s\nEnd of message\n"),
11199 _isds_sizet2int(xml_stream_length
), xml_stream
);
11202 /* Convert messages XML stream into XPath context */
11203 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
11204 if (!message_doc
) {
11208 xpath_ctx
= xmlXPathNewContext(message_doc
);
11213 /* XXX: Standard name space for unsigned incoming direction:
11214 * http://isds.czechpoint.cz/v20/
11216 * XXX: Name spaces mangled for signed outgoing direction:
11217 * http://isds.czechpoint.cz/v20/SentMessage:
11219 * <q:MessageDownloadResponse
11220 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11221 * <q:dmReturnedMessage>
11222 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11223 * <p:dmID>151916</p:dmID>
11226 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11228 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11229 * </q:dmReturnedMessage>
11230 * </q:MessageDownloadResponse>
11232 * XXX: Name spaces mangled for signed incoming direction:
11233 * http://isds.czechpoint.cz/v20/message:
11235 * <q:MessageDownloadResponse
11236 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11237 * <q:dmReturnedMessage>
11238 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11239 * <p:dmID>151916</p:dmID>
11242 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11244 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11245 * </q:dmReturnedMessage>
11246 * </q:MessageDownloadResponse>
11248 * Stupidity of ISDS developers is unlimited */
11249 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
11253 result
= xmlXPathEvalExpression(
11254 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11260 /* Empty message */
11261 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11262 isds_printf_message(context
,
11263 _("XML document does not contain "
11264 "sisds:dmReturnedMessage element"));
11268 /* More messages */
11269 if (result
->nodesetval
->nodeNr
> 1) {
11270 isds_printf_message(context
,
11271 _("XML document has more sisds:dmReturnedMessage elements"));
11276 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11278 /* Extract the message */
11279 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
11280 if (err
) goto leave
;
11282 /* Append raw buffer into message */
11283 (*message
)->raw_type
= raw_type
;
11284 switch (strategy
) {
11285 case BUFFER_DONT_STORE
:
11288 (*message
)->raw
= malloc(length
);
11289 if (!(*message
)->raw
) {
11293 memcpy((*message
)->raw
, buffer
, length
);
11294 (*message
)->raw_length
= length
;
11297 (*message
)->raw
= (void *) buffer
;
11298 (*message
)->raw_length
= length
;
11308 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
11309 isds_message_free(message
);
11312 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11313 xmlXPathFreeObject(result
);
11314 xmlXPathFreeContext(xpath_ctx
);
11315 if (!*message
|| !(*message
)->xml
) {
11316 xmlFreeDoc(message_doc
);
11320 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
11325 /* Determine type of raw message or delivery info according some heuristics.
11326 * It does not validate the raw blob.
11327 * @context is session context
11328 * @raw_type returns content type of @buffer. Valid only if exit code of this
11329 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11330 * reallocated memory.
11331 * @buffer is message raw representation.
11332 * @length is length of buffer in bytes. */
11333 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
11334 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
11336 void *xml_stream
= NULL
;
11337 size_t xml_stream_length
= 0;
11338 xmlDocPtr document
= NULL
;
11339 xmlNodePtr root
= NULL
;
11341 if (!context
) return IE_INVALID_CONTEXT
;
11342 zfree(context
->long_message
);
11343 if (length
== 0 || !buffer
) return IE_INVAL
;
11344 if (!raw_type
) return IE_INVAL
;
11347 err
= _isds_extract_cms_data(context
, buffer
, length
,
11348 &xml_stream
, &xml_stream_length
);
11350 xml_stream
= (void *) buffer
;
11351 xml_stream_length
= (size_t) length
;
11356 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
11358 isds_printf_message(context
,
11359 _("Could not parse data as XML document"));
11364 /* Get root element */
11365 root
= xmlDocGetRootElement(document
);
11367 isds_printf_message(context
,
11368 _("XML document is missing root element"));
11373 if (!root
->ns
|| !root
->ns
->href
) {
11374 isds_printf_message(context
,
11375 _("Root element does not belong to any name space"));
11380 /* Test name space */
11381 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
11382 if (xml_stream
== buffer
)
11383 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
11385 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
11386 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
11387 if (xml_stream
== buffer
)
11388 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
11390 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
11391 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
11392 if (xml_stream
== buffer
)
11393 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
11395 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
11396 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
11397 if (xml_stream
!= buffer
) {
11398 isds_printf_message(context
,
11399 _("Document in ISDS name space is encapsulated into CMS" ));
11401 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
11402 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
11403 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
11404 *raw_type
= RAWTYPE_DELIVERYINFO
;
11406 isds_printf_message(context
,
11407 _("Unknown root element in ISDS name space"));
11411 isds_printf_message(context
,
11412 _("Unknown name space"));
11417 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11418 xmlFreeDoc(document
);
11423 /* Download signed incoming/outgoing message identified by ID.
11424 * @context is session context
11425 * @output is true for outgoing message, false for incoming message
11426 * @message_id is message identifier (you can get them from
11427 * isds_get_list_of_{sent,received}_messages())
11428 * @message is automatically reallocated message retrieved from ISDS. The raw
11429 * member will be filled with PKCS#7 structure in DER format. */
11430 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
11431 const _Bool outgoing
, const char *message_id
,
11432 struct isds_message
**message
) {
11434 isds_error err
= IE_SUCCESS
;
11436 xmlDocPtr response
= NULL
;
11437 xmlChar
*code
= NULL
, *status_message
= NULL
;
11438 xmlXPathContextPtr xpath_ctx
= NULL
;
11439 xmlXPathObjectPtr result
= NULL
;
11440 char *encoded_structure
= NULL
;
11442 size_t raw_length
= 0;
11445 if (!context
) return IE_INVALID_CONTEXT
;
11446 zfree(context
->long_message
);
11447 if (!message
) return IE_INVAL
;
11448 isds_message_free(message
);
11451 /* Do request and check for success */
11452 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
11453 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11454 BAD_CAST
"SignedMessageDownload",
11455 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11456 if (err
) goto leave
;
11458 /* Find signed message, extract it into raw and maybe free
11460 err
= find_extract_signed_data_free_response(context
,
11461 (xmlChar
*)message_id
, &response
,
11462 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11463 BAD_CAST
"SignedMessageDownload",
11464 &raw
, &raw_length
);
11465 if (err
) goto leave
;
11467 /* Parse message */
11468 err
= isds_load_message(context
,
11469 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11470 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
11471 raw
, raw_length
, message
, BUFFER_MOVE
);
11472 if (err
) goto leave
;
11478 isds_message_free(message
);
11481 free(encoded_structure
);
11482 xmlXPathFreeObject(result
);
11483 xmlXPathFreeContext(xpath_ctx
);
11487 free(status_message
);
11488 xmlFreeDoc(response
);
11491 isds_log(ILF_ISDS
, ILL_DEBUG
,
11493 _("SignedSentMessageDownload request processed by server "
11494 "successfully.\n") :
11495 _("SignedMessageDownload request processed by server "
11498 #else /* not HAVE_LIBCURL */
11505 /* Download signed incoming message identified by ID.
11506 * @context is session context
11507 * @message_id is message identifier (you can get them from
11508 * isds_get_list_of_received_messages())
11509 * @message is automatically reallocated message retrieved from ISDS. The raw
11510 * member will be filled with PKCS#7 structure in DER format. */
11511 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
11512 const char *message_id
, struct isds_message
**message
) {
11513 return isds_get_signed_message(context
, 0, message_id
, message
);
11517 /* Download signed outgoing message identified by ID.
11518 * @context is session context
11519 * @message_id is message identifier (you can get them from
11520 * isds_get_list_of_sent_messages())
11521 * @message is automatically reallocated message retrieved from ISDS. The raw
11522 * member will be filled with PKCS#7 structure in DER format. */
11523 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
11524 const char *message_id
, struct isds_message
**message
) {
11525 return isds_get_signed_message(context
, 1, message_id
, message
);
11529 /* Get type and name of user who sent a message identified by ID.
11530 * @context is session context
11531 * @message_id is message identifier
11532 * @sender_type is pointer to automatically allocated type of sender detected
11533 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11534 * library or to the server, NULL will be returned. Pass NULL if you don't
11536 * @raw_sender_type is automatically reallocated UTF-8 string describing
11537 * sender type or NULL if not known to server. Pass NULL if you don't care.
11538 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11539 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11540 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
11541 const char *message_id
, isds_sender_type
**sender_type
,
11542 char **raw_sender_type
, char **sender_name
) {
11543 isds_error err
= IE_SUCCESS
;
11545 xmlDocPtr response
= NULL
;
11546 xmlChar
*code
= NULL
, *status_message
= NULL
;
11547 xmlXPathContextPtr xpath_ctx
= NULL
;
11548 xmlXPathObjectPtr result
= NULL
;
11549 char *type_string
= NULL
;
11552 if (!context
) return IE_INVALID_CONTEXT
;
11553 zfree(context
->long_message
);
11554 if (sender_type
) zfree(*sender_type
);
11555 if (raw_sender_type
) zfree(*raw_sender_type
);
11556 if (sender_name
) zfree(*sender_name
);
11557 if (!message_id
) return IE_INVAL
;
11560 /* Do request and check for success */
11561 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11562 BAD_CAST
"GetMessageAuthor",
11563 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11564 if (err
) goto leave
;
11567 xpath_ctx
= xmlXPathNewContext(response
);
11572 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11576 result
= xmlXPathEvalExpression(
11577 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
11582 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11583 isds_log_message(context
,
11584 _("Missing GetMessageAuthorResponse element"));
11588 if (result
->nodesetval
->nodeNr
> 1) {
11589 isds_log_message(context
,
11590 _("Multiple GetMessageAuthorResponse element"));
11594 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11595 xmlXPathFreeObject(result
); result
= NULL
;
11597 /* Fill output arguments in */
11598 EXTRACT_STRING("isds:userType", type_string
);
11599 if (NULL
!= type_string
) {
11600 if (NULL
!= sender_type
) {
11601 *sender_type
= calloc(1, sizeof(**sender_type
));
11602 if (NULL
== *sender_type
) {
11607 err
= string2isds_sender_type((xmlChar
*)type_string
,
11610 zfree(*sender_type
);
11611 if (err
== IE_ENUM
) {
11613 char *type_string_locale
= _isds_utf82locale(type_string
);
11614 isds_log(ILF_ISDS
, ILL_WARNING
,
11615 _("Unknown isds:userType value: %s"),
11616 type_string_locale
);
11617 free(type_string_locale
);
11622 if (NULL
== raw_sender_type
)
11623 zfree(type_string
);
11624 if (NULL
!= sender_name
)
11625 EXTRACT_STRING("isds:authorName", *sender_name
);
11629 if (NULL
!= sender_type
) zfree(*sender_type
);
11630 zfree(type_string
);
11631 if (NULL
!= sender_name
) zfree(*sender_name
);
11633 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11635 xmlXPathFreeObject(result
);
11636 xmlXPathFreeContext(xpath_ctx
);
11639 free(status_message
);
11640 xmlFreeDoc(response
);
11643 isds_log(ILF_ISDS
, ILL_DEBUG
,
11644 _("GetMessageAuthor request processed by server "
11645 "successfully.\n"));
11646 #else /* not HAVE_LIBCURL */
11653 /* Retrieve hash of message identified by ID stored in ISDS.
11654 * @context is session context
11655 * @message_id is message identifier
11656 * @hash is automatically reallocated message hash downloaded from ISDS.
11657 * Message must exist in system and must not be deleted. */
11658 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11659 const char *message_id
, struct isds_hash
**hash
) {
11661 isds_error err
= IE_SUCCESS
;
11663 xmlDocPtr response
= NULL
;
11664 xmlChar
*code
= NULL
, *status_message
= NULL
;
11665 xmlXPathContextPtr xpath_ctx
= NULL
;
11666 xmlXPathObjectPtr result
= NULL
;
11669 if (!context
) return IE_INVALID_CONTEXT
;
11670 zfree(context
->long_message
);
11672 isds_hash_free(hash
);
11675 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11676 BAD_CAST
"VerifyMessage", message_id
,
11677 &response
, NULL
, NULL
, &code
, &status_message
);
11678 if (err
) goto leave
;
11682 xpath_ctx
= xmlXPathNewContext(response
);
11687 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11691 result
= xmlXPathEvalExpression(
11692 BAD_CAST
"/isds:VerifyMessageResponse",
11698 /* Empty response */
11699 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11700 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11701 isds_printf_message(context
,
11702 _("Server did not return any response for ID `%s' "
11703 "on VerifyMessage request"), message_id_locale
);
11704 free(message_id_locale
);
11708 /* More responses */
11709 if (result
->nodesetval
->nodeNr
> 1) {
11710 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11711 isds_printf_message(context
,
11712 _("Server did return more responses for ID `%s' "
11713 "on VerifyMessage request"), message_id_locale
);
11714 free(message_id_locale
);
11719 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11721 /* Extract the hash */
11722 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11726 isds_hash_free(hash
);
11729 xmlXPathFreeObject(result
);
11730 xmlXPathFreeContext(xpath_ctx
);
11733 free(status_message
);
11734 xmlFreeDoc(response
);
11737 isds_log(ILF_ISDS
, ILL_DEBUG
,
11738 _("VerifyMessage request processed by server "
11741 #else /* not HAVE_LIBCURL */
11748 /* Erase message specified by @message_id from long term storage. Other
11749 * message cannot be erased on user request.
11750 * @context is session context
11751 * @message_id is message identifier.
11752 * @incoming is true for incoming message, false for outgoing message.
11754 * IE_SUCCESS if message has ben removed
11755 * IE_INVAL if message does not exist in long term storage or message
11756 * belongs to different box
11757 * TODO: IE_NOEPRM if user has no permission to erase a message */
11758 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11759 const char *message_id
, _Bool incoming
) {
11760 isds_error err
= IE_SUCCESS
;
11762 xmlNodePtr request
= NULL
, node
;
11763 xmlNsPtr isds_ns
= NULL
;
11764 xmlDocPtr response
= NULL
;
11765 xmlChar
*code
= NULL
, *status_message
= NULL
;
11768 if (!context
) return IE_INVALID_CONTEXT
;
11769 zfree(context
->long_message
);
11770 if (NULL
== message_id
) return IE_INVAL
;
11773 /* Check if connection is established
11774 * TODO: This check should be done downstairs. */
11775 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11777 /* Build request */
11778 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11780 isds_log_message(context
,
11781 _("Could build EraseMessage request"));
11784 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11786 isds_log_message(context
, _("Could not create ISDS name space"));
11787 xmlFreeNode(request
);
11790 xmlSetNs(request
, isds_ns
);
11792 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11793 if (err
) goto leave
;
11794 INSERT_STRING(request
, "dmID", message_id
);
11796 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11800 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11801 "message ID %s to ISDS\n"), message_id
);
11802 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11803 xmlFreeNode(request
); request
= NULL
;
11806 isds_log(ILF_ISDS
, ILL_DEBUG
,
11807 _("Processing ISDS response on EraseMessage request "
11812 /* Check for response status */
11813 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11814 &code
, &status_message
, NULL
);
11816 isds_log(ILF_ISDS
, ILL_DEBUG
,
11817 _("ISDS response on EraseMessage request is missing "
11822 /* Check server status code */
11823 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11824 isds_log_message(context
, _("Message to erase belongs to other box"));
11826 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11827 isds_log_message(context
, _("Message to erase is not saved in "
11828 "long term storage or the direction does not match"));
11830 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11831 char *code_locale
= _isds_utf82locale((char*) code
);
11832 char *message_locale
= _isds_utf82locale((char*) status_message
);
11833 isds_log(ILF_ISDS
, ILL_DEBUG
,
11834 _("Server refused EraseMessage request "
11835 "(code=%s, message=%s)\n"),
11836 code_locale
, message_locale
);
11837 isds_log_message(context
, message_locale
);
11839 free(message_locale
);
11846 free(status_message
);
11847 xmlFreeDoc(response
);
11848 xmlFreeNode(request
);
11851 isds_log(ILF_ISDS
, ILL_DEBUG
,
11852 _("EraseMessage request processed by server "
11855 #else /* not HAVE_LIBCURL */
11862 /* Mark message as read. This is a transactional commit function to acknowledge
11863 * to ISDS the message has been downloaded and processed by client properly.
11864 * @context is session context
11865 * @message_id is message identifier. */
11866 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11867 const char *message_id
) {
11869 isds_error err
= IE_SUCCESS
;
11871 xmlDocPtr response
= NULL
;
11872 xmlChar
*code
= NULL
, *status_message
= NULL
;
11875 if (!context
) return IE_INVALID_CONTEXT
;
11876 zfree(context
->long_message
);
11879 /* Do request and check for success */
11880 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11881 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11882 &response
, NULL
, NULL
, &code
, &status_message
);
11885 free(status_message
);
11886 xmlFreeDoc(response
);
11889 isds_log(ILF_ISDS
, ILL_DEBUG
,
11890 _("MarkMessageAsDownloaded request processed by server "
11893 #else /* not HAVE_LIBCURL */
11900 /* Mark message as received by recipient. This is applicable only to
11901 * commercial message. Use envelope->dmType message member to distinguish
11902 * commercial message from government message. Government message is
11903 * received automatically (by law), commercial message on recipient request.
11904 * @context is session context
11905 * @message_id is message identifier. */
11906 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11907 const char *message_id
) {
11909 isds_error err
= IE_SUCCESS
;
11911 xmlDocPtr response
= NULL
;
11912 xmlChar
*code
= NULL
, *status_message
= NULL
;
11915 if (!context
) return IE_INVALID_CONTEXT
;
11916 zfree(context
->long_message
);
11919 /* Do request and check for success */
11920 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11921 BAD_CAST
"ConfirmDelivery", message_id
,
11922 &response
, NULL
, NULL
, &code
, &status_message
);
11925 free(status_message
);
11926 xmlFreeDoc(response
);
11929 isds_log(ILF_ISDS
, ILL_DEBUG
,
11930 _("ConfirmDelivery request processed by server "
11933 #else /* not HAVE_LIBCURL */
11940 /* Send document for authorized conversion into Czech POINT system.
11941 * This is public anonymous service, no log-in necessary. Special context is
11942 * used to reuse keep-a-live HTTPS connection.
11943 * @context is Czech POINT session context. DO NOT use context connected to
11944 * ISDS server. Use new context or context used by this function previously.
11945 * @document is document to convert. Only data, data_length, dmFileDescr and
11946 * is_xml members are significant. Be ware that not all document formats can be
11947 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11948 * @id is reallocated identifier assigned by Czech POINT system to
11949 * your document on submit. Use is to tell it to Czech POINT officer.
11950 * @date is reallocated document submit date (submitted documents
11951 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11953 isds_error
czp_convert_document(struct isds_ctx
*context
,
11954 const struct isds_document
*document
,
11955 char **id
, struct tm
**date
) {
11956 isds_error err
= IE_SUCCESS
;
11958 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11959 xmlNodePtr request
= NULL
, node
;
11960 xmlDocPtr response
= NULL
;
11962 xmlXPathContextPtr xpath_ctx
= NULL
;
11963 xmlXPathObjectPtr result
= NULL
;
11964 long int status
= -1;
11965 long int *status_ptr
= &status
;
11966 char *string
= NULL
;
11970 if (!context
) return IE_INVALID_CONTEXT
;
11971 zfree(context
->long_message
);
11972 if (!document
|| !id
|| !date
) return IE_INVAL
;
11974 if (document
->is_xml
) {
11975 isds_log_message(context
,
11976 _("XML documents cannot be submitted to conversion"));
11980 /* Free output arguments */
11985 /* Store configuration */
11986 context
->type
= CTX_TYPE_CZP
;
11987 free(context
->url
);
11988 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11989 if (!(context
->url
))
11992 /* Prepare CURL handle if not yet connected */
11993 if (!context
->curl
) {
11994 context
->curl
= curl_easy_init();
11995 if (!(context
->curl
))
11999 /* Build conversion request */
12000 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
12002 isds_log_message(context
,
12003 _("Could not build Czech POINT conversion request"));
12006 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
12008 isds_log_message(context
,
12009 _("Could not create Czech POINT deposit name space"));
12010 xmlFreeNode(request
);
12013 xmlSetNs(request
, deposit_ns
);
12015 /* Insert children. They are in empty namespace! */
12016 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
12018 isds_log_message(context
, _("Could not create empty name space"));
12022 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
12023 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
12024 document
->dmFileDescr
);
12026 /* Document encoded in Base64 */
12027 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
12028 document
->data
, document
->data_length
);
12029 if (err
) goto leave
;
12031 isds_log(ILF_ISDS
, ILL_DEBUG
,
12032 _("Submitting document for conversion into Czech POINT deposit"));
12034 /* Send conversion request */
12035 err
= _czp_czpdeposit(context
, request
, &response
);
12036 xmlFreeNode(request
); request
= NULL
;
12039 czp_do_close_connection(context
);
12044 /* Extract response */
12045 xpath_ctx
= xmlXPathNewContext(response
);
12050 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12054 result
= xmlXPathEvalExpression(
12055 BAD_CAST
"/deposit:saveDocumentResponse/return",
12061 /* Empty response */
12062 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
12063 isds_printf_message(context
,
12064 _("Missing `return' element in Czech POINT deposit response"));
12068 /* More responses */
12069 if (result
->nodesetval
->nodeNr
> 1) {
12070 isds_printf_message(context
,
12071 _("Multiple `return' element in Czech POINT deposit response"));
12076 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
12079 EXTRACT_LONGINT("status", status_ptr
, 1);
12081 EXTRACT_STRING("statusMsg", string
);
12082 char *string_locale
= _isds_utf82locale(string
);
12083 isds_printf_message(context
,
12084 _("Czech POINT deposit refused document for conversion "
12085 "(code=%ld, message=%s)"),
12086 status
, string_locale
);
12087 free(string_locale
);
12092 /* Get document ID */
12093 EXTRACT_STRING("documentID", *id
);
12095 /* Get submit date */
12096 EXTRACT_STRING("dateInserted", string
);
12098 *date
= calloc(1, sizeof(**date
));
12103 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
12105 if (err
== IE_NOTSUP
) {
12107 char *string_locale
= _isds_utf82locale(string
);
12108 isds_printf_message(context
,
12109 _("Invalid dateInserted value: %s"), string_locale
);
12110 free(string_locale
);
12118 xmlXPathFreeObject(result
);
12119 xmlXPathFreeContext(xpath_ctx
);
12121 xmlFreeDoc(response
);
12122 xmlFreeNode(request
);
12125 char *id_locale
= _isds_utf82locale((char *) *id
);
12126 isds_log(ILF_ISDS
, ILL_DEBUG
,
12127 _("Document %s has been submitted for conversion "
12128 "to server successfully\n"), id_locale
);
12131 #else /* not HAVE_LIBCURL */
12138 /* Close possibly opened connection to Czech POINT document deposit.
12139 * @context is Czech POINT session context. */
12140 isds_error
czp_close_connection(struct isds_ctx
*context
) {
12141 if (!context
) return IE_INVALID_CONTEXT
;
12142 zfree(context
->long_message
);
12144 return czp_do_close_connection(context
);
12151 /* Send request for new box creation in testing ISDS instance.
12152 * It's not possible to request for a production box currently, as it
12153 * communicates via e-mail.
12154 * XXX: This function does not work either. Server complains about invalid
12156 * XXX: Remove context->type hacks in isds.c and validator.c when removing
12158 * @context is special session context for box creation request. DO NOT use
12159 * standard context as it could reveal your password. Use fresh new context or
12160 * context previously used by this function.
12161 * @box is box description to create including single primary user (in case of
12162 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
12163 * ignored. It outputs box ID assigned by ISDS in dbID element.
12164 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
12165 * box, or contact address of PFO box owner). The email member is mandatory as
12166 * it will be used to deliver credentials.
12167 * @former_names is former name of box owner. Pass NULL if you don't care.
12168 * @approval is optional external approval of box manipulation
12169 * @refnumber is reallocated serial number of request assigned by ISDS. Use
12170 * NULL, if you don't care.*/
12171 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
12172 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
12173 const char *former_names
, const struct isds_approval
*approval
,
12174 char **refnumber
) {
12175 isds_error err
= IE_SUCCESS
;
12177 xmlNodePtr request
= NULL
;
12178 xmlDocPtr response
= NULL
;
12179 xmlXPathContextPtr xpath_ctx
= NULL
;
12180 xmlXPathObjectPtr result
= NULL
;
12184 if (!context
) return IE_INVALID_CONTEXT
;
12185 zfree(context
->long_message
);
12186 if (!box
) return IE_INVAL
;
12189 if (!box
->email
|| box
->email
[0] == '\0') {
12190 isds_log_message(context
, _("E-mail field is mandatory"));
12194 /* Scratch box ID */
12197 /* Store configuration */
12198 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
12199 free(context
->url
);
12200 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
12201 if (!(context
->url
))
12204 /* Prepare CURL handle if not yet connected */
12205 if (!context
->curl
) {
12206 context
->curl
= curl_easy_init();
12207 if (!(context
->curl
))
12211 /* Build CreateDataBox request */
12212 err
= build_CreateDBInput_request(context
,
12213 &request
, BAD_CAST
"CreateDataBox",
12214 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
12215 if (err
) goto leave
;
12217 /* Send it to server and process response */
12218 err
= send_destroy_request_check_response(context
,
12219 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
12220 &response
, (xmlChar
**) refnumber
, NULL
);
12221 if (err
) goto leave
;
12223 /* Extract box ID */
12224 xpath_ctx
= xmlXPathNewContext(response
);
12229 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12233 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
12236 xmlXPathFreeObject(result
);
12237 xmlXPathFreeContext(xpath_ctx
);
12238 xmlFreeDoc(response
);
12239 xmlFreeNode(request
);
12242 isds_log(ILF_ISDS
, ILL_DEBUG
,
12243 _("CreateDataBox request processed by server successfully.\n"));
12245 #else /* not HAVE_LIBCURL */
12253 /* Submit CMS signed message to ISDS to verify its originality. This is
12254 * stronger form of isds_verify_message_hash() because ISDS does more checks
12255 * than simple one (potentialy old weak) hash comparison.
12256 * @context is session context
12257 * @message is memory with raw CMS signed message bit stream
12258 * @length is @message size in bytes
12260 * IE_SUCCESS if message originates in ISDS
12261 * IE_NOTEQUAL if message is unknown to ISDS
12262 * other code for other errors */
12263 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
12264 const void *message
, size_t length
) {
12265 isds_error err
= IE_SUCCESS
;
12267 xmlNsPtr isds_ns
= NULL
;
12268 xmlNodePtr request
= NULL
;
12269 xmlDocPtr response
= NULL
;
12270 xmlXPathContextPtr xpath_ctx
= NULL
;
12271 xmlXPathObjectPtr result
= NULL
;
12272 _Bool
*authentic
= NULL
;
12275 if (!context
) return IE_INVALID_CONTEXT
;
12276 zfree(context
->long_message
);
12277 if (!message
|| length
== 0) return IE_INVAL
;
12280 /* Check if connection is established
12281 * TODO: This check should be done downstairs. */
12282 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12285 /* Build AuthenticateMessage request */
12286 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
12288 isds_log_message(context
,
12289 _("Could not build AuthenticateMessage request"));
12292 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12294 isds_log_message(context
, _("Could not create ISDS name space"));
12295 xmlFreeNode(request
);
12298 xmlSetNs(request
, isds_ns
);
12300 /* Insert Base64 encoded message */
12301 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
12303 if (err
) goto leave
;
12305 /* Send request to server and process response */
12306 err
= send_destroy_request_check_response(context
,
12307 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
12308 &response
, NULL
, NULL
);
12309 if (err
) goto leave
;
12312 /* ISDS has decided */
12313 xpath_ctx
= xmlXPathNewContext(response
);
12318 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12323 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
12326 isds_log_message(context
,
12327 _("Server did not return any response on "
12328 "AuthenticateMessage request"));
12333 isds_log(ILF_ISDS
, ILL_DEBUG
,
12334 _("ISDS authenticated the message successfully\n"));
12336 isds_log_message(context
, _("ISDS does not know the message"));
12343 xmlXPathFreeObject(result
);
12344 xmlXPathFreeContext(xpath_ctx
);
12346 xmlFreeDoc(response
);
12347 xmlFreeNode(request
);
12348 #else /* not HAVE_LIBCURL */
12356 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12357 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12359 * @context is session context
12360 * @input_data is memory with raw CMS signed message or delivery info bit
12361 * stream to re-sign
12362 * @input_length is @input_data size in bytes
12363 * @output_data is pointer to auto-allocated memory where to store re-signed
12364 * input data blob. Caller must free it.
12365 * @output_data is pointer where to store @output_data size in bytes
12366 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12367 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12369 * IE_SUCCESS if CMS blob has been re-signed successfully
12370 * other code for other errors */
12371 isds_error
isds_resign_message(struct isds_ctx
*context
,
12372 const void *input_data
, size_t input_length
,
12373 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
12374 isds_error err
= IE_SUCCESS
;
12376 xmlNsPtr isds_ns
= NULL
;
12377 xmlNodePtr request
= NULL
;
12378 xmlDocPtr response
= NULL
;
12379 xmlXPathContextPtr xpath_ctx
= NULL
;
12380 xmlXPathObjectPtr result
= NULL
;
12381 char *string
= NULL
;
12382 const xmlChar
*codes
[] = {
12389 const char *meanings
[] = {
12391 "Message is not original",
12392 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12393 "Time stamp could not been generated in time"
12395 const isds_error errors
[] = {
12401 struct code_map_isds_error map
= {
12403 .meanings
= meanings
,
12408 if (NULL
!= output_data
) *output_data
= NULL
;
12409 if (NULL
!= output_length
) *output_length
= 0;
12410 if (NULL
!= valid_to
) *valid_to
= NULL
;
12412 if (NULL
== context
) return IE_INVALID_CONTEXT
;
12413 zfree(context
->long_message
);
12414 if (NULL
== input_data
|| 0 == input_length
) {
12415 isds_log_message(context
, _("Empty CMS blob on input"));
12418 if (NULL
== output_data
|| NULL
== output_length
) {
12419 isds_log_message(context
,
12420 _("NULL pointer provided for output CMS blob"));
12425 /* Check if connection is established
12426 * TODO: This check should be done downstairs. */
12427 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12430 /* Build Re-signISDSDocument request */
12431 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
12433 isds_log_message(context
,
12434 _("Could not build Re-signISDSDocument request"));
12437 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12439 isds_log_message(context
, _("Could not create ISDS name space"));
12440 xmlFreeNode(request
);
12443 xmlSetNs(request
, isds_ns
);
12445 /* Insert Base64 encoded CMS blob */
12446 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
12447 input_data
, input_length
);
12448 if (err
) goto leave
;
12450 /* Send request to server and process response */
12451 err
= send_destroy_request_check_response(context
,
12452 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
12453 &response
, NULL
, &map
);
12454 if (err
) goto leave
;
12457 /* Extract re-signed data */
12458 xpath_ctx
= xmlXPathNewContext(response
);
12463 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12467 result
= xmlXPathEvalExpression(
12468 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
12473 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
12474 isds_log_message(context
,
12475 _("Missing Re-signISDSDocumentResponse element"));
12479 if (result
->nodesetval
->nodeNr
> 1) {
12480 isds_log_message(context
,
12481 _("Multiple Re-signISDSDocumentResponse element"));
12485 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
12486 xmlXPathFreeObject(result
); result
= NULL
;
12488 EXTRACT_STRING("isds:dmResultDoc", string
);
12489 /* Decode non-empty data */
12490 if (NULL
!= string
&& string
[0] != '\0') {
12491 *output_length
= _isds_b64decode(string
, output_data
);
12492 if (*output_length
== (size_t) -1) {
12493 isds_log_message(context
,
12494 _("Error while Base64-decoding re-signed data"));
12499 isds_log_message(context
, _("Server did not send re-signed data"));
12505 if (NULL
!= valid_to
) {
12506 /* Get time stamp expiration date */
12507 EXTRACT_STRING("isds:dmValidTo", string
);
12508 if (NULL
!= string
) {
12509 *valid_to
= calloc(1, sizeof(**valid_to
));
12514 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
12516 if (err
== IE_NOTSUP
) {
12518 char *string_locale
= _isds_utf82locale(string
);
12519 isds_printf_message(context
,
12520 _("Invalid dmValidTo value: %s"), string_locale
);
12521 free(string_locale
);
12531 xmlXPathFreeObject(result
);
12532 xmlXPathFreeContext(xpath_ctx
);
12534 xmlFreeDoc(response
);
12535 xmlFreeNode(request
);
12536 #else /* not HAVE_LIBCURL */
12543 #undef INSERT_ELEMENT
12544 #undef CHECK_FOR_STRING_LENGTH
12545 #undef INSERT_STRING_ATTRIBUTE
12546 #undef INSERT_ULONGINTNOPTR
12547 #undef INSERT_ULONGINT
12548 #undef INSERT_LONGINT
12549 #undef INSERT_BOOLEAN
12550 #undef INSERT_SCALAR_BOOLEAN
12551 #undef INSERT_STRING
12552 #undef INSERT_STRING_WITH_NS
12553 #undef EXTRACT_STRING_ATTRIBUTE
12554 #undef EXTRACT_ULONGINT
12555 #undef EXTRACT_LONGINT
12556 #undef EXTRACT_BOOLEAN
12557 #undef EXTRACT_STRING
12560 /* Compute hash of message from raw representation and store it into envelope.
12561 * Original hash structure will be destroyed in envelope.
12562 * @context is session context
12563 * @message is message carrying raw XML message blob
12564 * @algorithm is desired hash algorithm to use */
12565 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
12566 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
12567 isds_error err
= IE_SUCCESS
;
12569 void *xml_stream
= NULL
;
12570 size_t xml_stream_length
;
12571 size_t phys_start
, phys_end
;
12572 char *phys_path
= NULL
;
12573 struct isds_hash
*new_hash
= NULL
;
12576 if (!context
) return IE_INVALID_CONTEXT
;
12577 zfree(context
->long_message
);
12578 if (!message
) return IE_INVAL
;
12580 if (!message
->raw
) {
12581 isds_log_message(context
,
12582 _("Message does not carry raw representation"));
12586 switch (message
->raw_type
) {
12587 case RAWTYPE_INCOMING_MESSAGE
:
12589 xml_stream
= message
->raw
;
12590 xml_stream_length
= message
->raw_length
;
12593 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
12594 nsuri
= SISDS_INCOMING_NS
;
12595 xml_stream
= message
->raw
;
12596 xml_stream_length
= message
->raw_length
;
12599 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
12600 nsuri
= SISDS_INCOMING_NS
;
12601 err
= _isds_extract_cms_data(context
,
12602 message
->raw
, message
->raw_length
,
12603 &xml_stream
, &xml_stream_length
);
12604 if (err
) goto leave
;
12607 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
12608 nsuri
= SISDS_OUTGOING_NS
;
12609 xml_stream
= message
->raw
;
12610 xml_stream_length
= message
->raw_length
;
12613 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
12614 nsuri
= SISDS_OUTGOING_NS
;
12615 err
= _isds_extract_cms_data(context
,
12616 message
->raw
, message
->raw_length
,
12617 &xml_stream
, &xml_stream_length
);
12618 if (err
) goto leave
;
12622 isds_log_message(context
, _("Bad raw representation type"));
12628 /* XXX: Hash is computed from original string representing isds:dmDm
12629 * subtree. That means no encoding, white space, xmlns attributes changes.
12630 * In other words, input for hash can be invalid XML stream. */
12631 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12632 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12633 PHYSXML_ELEMENT_SEPARATOR
,
12634 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12635 PHYSXML_ELEMENT_SEPARATOR
12636 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12640 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12641 phys_path
, &phys_start
, &phys_end
);
12644 isds_log_message(context
,
12645 _("Substring with isds:dmDM element could not be located "
12646 "in raw message"));
12652 new_hash
= calloc(1, sizeof(*new_hash
));
12657 new_hash
->algorithm
= algorithm
;
12658 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12661 isds_log_message(context
, _("Could not compute message hash"));
12665 /* Save computed hash */
12666 if (!message
->envelope
) {
12667 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12668 if (!message
->envelope
) {
12673 isds_hash_free(&message
->envelope
->hash
);
12674 message
->envelope
->hash
= new_hash
;
12678 isds_hash_free(&new_hash
);
12682 if (xml_stream
!= message
->raw
) free(xml_stream
);
12687 /* Compare two hashes.
12688 * @h1 is first hash
12689 * @h2 is another hash
12691 * IE_SUCCESS if hashes equal
12692 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12693 * IE_ENUM if not comparable, but both structures defined
12694 * IE_INVAL if some of the structures are undefined (NULL)
12695 * IE_ERROR if internal error occurs */
12696 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12697 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12698 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12699 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12700 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12701 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12703 for (size_t i
= 0; i
< h1
->length
; i
++) {
12704 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12705 return IE_NOTEQUAL
;
12711 /* Check message has gone through ISDS by comparing message hash stored in
12712 * ISDS and locally computed hash. You must provide message with valid raw
12713 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12714 * This is convenient wrapper for isds_download_message_hash(),
12715 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12716 * @context is session context
12717 * @message is message with valid raw and envelope member; envelope->hash
12718 * member will be changed during function run. Use envelope on heap only.
12720 * IE_SUCCESS if message originates in ISDS
12721 * IE_NOTEQUAL if message is unknown to ISDS
12722 * other code for other errors */
12723 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12724 struct isds_message
*message
) {
12725 isds_error err
= IE_SUCCESS
;
12726 struct isds_hash
*downloaded_hash
= NULL
;
12728 if (!context
) return IE_INVALID_CONTEXT
;
12729 zfree(context
->long_message
);
12730 if (!message
) return IE_INVAL
;
12732 if (!message
->envelope
) {
12733 isds_log_message(context
,
12734 _("Given message structure is missing envelope"));
12737 if (!message
->raw
) {
12738 isds_log_message(context
,
12739 _("Given message structure is missing raw representation"));
12743 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12745 if (err
) goto leave
;
12747 err
= isds_compute_message_hash(context
, message
,
12748 downloaded_hash
->algorithm
);
12749 if (err
) goto leave
;
12751 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12754 isds_hash_free(&downloaded_hash
);
12759 /* Search for document by document ID in list of documents. IDs are compared
12761 * @documents is list of isds_documents
12762 * @id is document identifier
12763 * @return first matching document or NULL. */
12764 const struct isds_document
*isds_find_document_by_id(
12765 const struct isds_list
*documents
, const char *id
) {
12766 const struct isds_list
*item
;
12767 const struct isds_document
*document
;
12769 for (item
= documents
; item
; item
= item
->next
) {
12770 document
= (struct isds_document
*) item
->data
;
12771 if (!document
) continue;
12773 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12781 /* Normalize @mime_type to be proper MIME type.
12782 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12783 * guess regular MIME type (e.g. "application/pdf").
12784 * @mime_type is UTF-8 encoded MIME type to fix
12785 * @return original @mime_type if no better interpretation exists, or
12786 * constant static UTF-8 encoded string with proper MIME type. */
12787 const char *isds_normalize_mime_type(const char *mime_type
) {
12788 if (!mime_type
) return NULL
;
12790 for (size_t offset
= 0;
12791 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12793 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12794 extension_map_mime
[offset
]))
12795 return (const char *) extension_map_mime
[offset
+ 1];
12802 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12803 struct isds_message **message);
12804 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12805 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12806 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12807 struct isds_address **address);
12809 int isds_message_free(struct isds_message **message);
12810 int isds_address_free(struct isds_address **address);
12814 /* Makes known all relevant namespaces to given XPath context
12815 * @xpath_ctx is XPath context
12816 * @message_ns selects proper message name space. Unsigned and signed
12817 * messages and delivery info's differ in prefix and URI. */
12818 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12819 const message_ns_type message_ns
) {
12820 const xmlChar
*message_namespace
= NULL
;
12822 if (!xpath_ctx
) return IE_ERROR
;
12824 switch(message_ns
) {
12826 message_namespace
= BAD_CAST ISDS1_NS
; break;
12827 case MESSAGE_NS_UNSIGNED
:
12828 message_namespace
= BAD_CAST ISDS_NS
; break;
12829 case MESSAGE_NS_SIGNED_INCOMING
:
12830 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12831 case MESSAGE_NS_SIGNED_OUTGOING
:
12832 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12833 case MESSAGE_NS_SIGNED_DELIVERY
:
12834 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12839 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12841 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12843 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12845 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12847 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12849 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))