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
) {
5179 request
= xmlNewNode(NULL
, service_name
);
5181 isds_printf_message(context
,
5182 _("Could not build %s request"), service_name_locale
);
5186 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5188 isds_log_message(context
, _("Could not create ISDS name space"));
5192 xmlSetNs(request
, isds_ns
);
5195 /* Add XSD:tDummyInput child */
5196 INSERT_STRING(request
, "dbDummy", NULL
);
5199 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5200 service_name_locale
);
5203 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
5204 raw_response
, raw_response_length
);
5205 xmlFreeNode(request
); request
= NULL
;
5208 isds_log(ILF_ISDS
, ILL_DEBUG
,
5209 _("Processing ISDS response on %s request failed\n"),
5210 service_name_locale
);
5214 /* Check for response status */
5215 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
5216 code
, status_message
, NULL
);
5218 isds_log(ILF_ISDS
, ILL_DEBUG
,
5219 _("ISDS response on %s request is missing status\n"),
5220 service_name_locale
);
5224 /* Request processed, but nothing found */
5225 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
5226 char *code_locale
= _isds_utf82locale((char*) *code
);
5227 char *status_message_locale
=
5228 _isds_utf82locale((char*) *status_message
);
5229 isds_log(ILF_ISDS
, ILL_DEBUG
,
5230 _("Server refused %s request (code=%s, message=%s)\n"),
5231 service_name_locale
, code_locale
, status_message_locale
);
5232 isds_log_message(context
, status_message_locale
);
5234 free(status_message_locale
);
5240 free(service_name_locale
);
5241 xmlFreeNode(request
);
5247 /* Get data about logged in user and his box.
5248 * @context is session context
5249 * @db_owner_info is reallocated box owner description. It will be freed on
5251 * @return error code from lower layer, context message will be set up
5253 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
5254 struct isds_DbOwnerInfo
**db_owner_info
) {
5255 isds_error err
= IE_SUCCESS
;
5257 xmlDocPtr response
= NULL
;
5258 xmlChar
*code
= NULL
, *message
= NULL
;
5259 xmlXPathContextPtr xpath_ctx
= NULL
;
5260 xmlXPathObjectPtr result
= NULL
;
5261 char *string
= NULL
;
5264 if (!context
) return IE_INVALID_CONTEXT
;
5265 zfree(context
->long_message
);
5266 if (!db_owner_info
) return IE_INVAL
;
5267 isds_DbOwnerInfo_free(db_owner_info
);
5270 /* Check if connection is established */
5271 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5274 /* Do request and check for success */
5275 err
= build_send_check_dbdummy_request(context
,
5276 BAD_CAST
"GetOwnerInfoFromLogin",
5277 &response
, NULL
, NULL
, &code
, &message
);
5278 if (err
) goto leave
;
5282 /* Prepare structure */
5283 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5284 if (!*db_owner_info
) {
5288 xpath_ctx
= xmlXPathNewContext(response
);
5293 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5298 /* Set context node */
5299 result
= xmlXPathEvalExpression(BAD_CAST
5300 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5305 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5306 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5310 if (result
->nodesetval
->nodeNr
> 1) {
5311 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5315 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5316 xmlXPathFreeObject(result
); result
= NULL
;
5319 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5324 isds_DbOwnerInfo_free(db_owner_info
);
5328 xmlXPathFreeObject(result
);
5329 xmlXPathFreeContext(xpath_ctx
);
5333 xmlFreeDoc(response
);
5336 isds_log(ILF_ISDS
, ILL_DEBUG
,
5337 _("GetOwnerInfoFromLogin request processed by server "
5338 "successfully.\n"));
5339 #else /* not HAVE_LIBCURL */
5347 /* Get data about logged in user. */
5348 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5349 struct isds_DbUserInfo
**db_user_info
) {
5350 isds_error err
= IE_SUCCESS
;
5352 xmlDocPtr response
= NULL
;
5353 xmlChar
*code
= NULL
, *message
= NULL
;
5354 xmlXPathContextPtr xpath_ctx
= NULL
;
5355 xmlXPathObjectPtr result
= NULL
;
5358 if (!context
) return IE_INVALID_CONTEXT
;
5359 zfree(context
->long_message
);
5360 if (!db_user_info
) return IE_INVAL
;
5361 isds_DbUserInfo_free(db_user_info
);
5364 /* Check if connection is established */
5365 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5368 /* Do request and check for success */
5369 err
= build_send_check_dbdummy_request(context
,
5370 BAD_CAST
"GetUserInfoFromLogin",
5371 &response
, NULL
, NULL
, &code
, &message
);
5372 if (err
) goto leave
;
5376 /* Prepare structure */
5377 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5378 if (!*db_user_info
) {
5382 xpath_ctx
= xmlXPathNewContext(response
);
5387 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5392 /* Set context node */
5393 result
= xmlXPathEvalExpression(BAD_CAST
5394 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5399 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5400 isds_log_message(context
, _("Missing dbUserInfo element"));
5404 if (result
->nodesetval
->nodeNr
> 1) {
5405 isds_log_message(context
, _("Multiple dbUserInfo element"));
5409 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5410 xmlXPathFreeObject(result
); result
= NULL
;
5413 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5417 isds_DbUserInfo_free(db_user_info
);
5420 xmlXPathFreeObject(result
);
5421 xmlXPathFreeContext(xpath_ctx
);
5425 xmlFreeDoc(response
);
5428 isds_log(ILF_ISDS
, ILL_DEBUG
,
5429 _("GetUserInfoFromLogin request processed by server "
5430 "successfully.\n"));
5431 #else /* not HAVE_LIBCURL */
5439 /* Get expiration time of current password
5440 * @context is session context
5441 * @expiration is automatically reallocated time when password expires. If
5442 * password expiration is disabled, NULL will be returned. In case of error
5443 * it will be nulled too. */
5444 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5445 struct timeval
**expiration
) {
5446 isds_error err
= IE_SUCCESS
;
5448 xmlDocPtr response
= NULL
;
5449 xmlChar
*code
= NULL
, *message
= NULL
;
5450 xmlXPathContextPtr xpath_ctx
= NULL
;
5451 xmlXPathObjectPtr result
= NULL
;
5452 char *string
= NULL
;
5455 if (!context
) return IE_INVALID_CONTEXT
;
5456 zfree(context
->long_message
);
5457 if (!expiration
) return IE_INVAL
;
5461 /* Check if connection is established */
5462 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5465 /* Do request and check for success */
5466 err
= build_send_check_dbdummy_request(context
,
5467 BAD_CAST
"GetPasswordInfo",
5468 &response
, NULL
, NULL
, &code
, &message
);
5469 if (err
) goto leave
;
5473 xpath_ctx
= xmlXPathNewContext(response
);
5478 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5483 /* Set context node */
5484 result
= xmlXPathEvalExpression(BAD_CAST
5485 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5490 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5491 isds_log_message(context
,
5492 _("Missing GetPasswordInfoResponse element"));
5496 if (result
->nodesetval
->nodeNr
> 1) {
5497 isds_log_message(context
,
5498 _("Multiple GetPasswordInfoResponse element"));
5502 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5503 xmlXPathFreeObject(result
); result
= NULL
;
5505 /* Extract expiration date */
5506 EXTRACT_STRING("isds:pswExpDate", string
);
5508 /* And convert it if any returned. Otherwise expiration is disabled. */
5509 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5511 char *string_locale
= _isds_utf82locale(string
);
5512 if (err
== IE_DATE
) err
= IE_ISDS
;
5513 isds_printf_message(context
,
5514 _("Could not convert pswExpDate as ISO time: %s"),
5516 free(string_locale
);
5529 xmlXPathFreeObject(result
);
5530 xmlXPathFreeContext(xpath_ctx
);
5534 xmlFreeDoc(response
);
5537 isds_log(ILF_ISDS
, ILL_DEBUG
,
5538 _("GetPasswordInfo request processed by server "
5539 "successfully.\n"));
5540 #else /* not HAVE_LIBCURL */
5549 /* Request delivering new TOTP code from ISDS through side channel before
5550 * changing password.
5551 * @context is session context
5552 * @password is current password.
5553 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5554 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5555 * function for more details.
5556 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5557 * NULL, if you don't care.
5558 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5560 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5561 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5562 isds_error err
= IE_SUCCESS
;
5563 char *saved_url
= NULL
; /* No copy */
5564 #if HAVE_CURL_REAUTHORIZATION_BUG
5565 CURL
*saved_curl
= NULL
; /* No copy */
5567 xmlNsPtr isds_ns
= NULL
;
5568 xmlNodePtr request
= NULL
;
5569 xmlDocPtr response
= NULL
;
5570 xmlChar
*code
= NULL
, *message
= NULL
;
5571 const xmlChar
*codes
[] = {
5576 const char *meanings
[] = {
5577 N_("Unexpected error"),
5578 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5579 N_("One-time code could not been sent. Try later again.")
5581 const isds_otp_resolution resolutions
[] = {
5582 OTP_RESOLUTION_UNKNOWN
,
5583 OTP_RESOLUTION_TO_FAST
,
5584 OTP_RESOLUTION_TOTP_NOT_SENT
5587 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5588 zfree(context
->long_message
);
5589 if (NULL
== password
) {
5590 isds_log_message(context
,
5591 _("Second argument (password) of isds_change_password() "
5596 /* Check if connection is established
5597 * TODO: This check should be done downstairs. */
5598 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5600 if (!context
->otp
) {
5601 isds_log_message(context
, _("This function requires OTP-authenticated "
5603 return IE_INVALID_CONTEXT
;
5606 isds_log_message(context
, _("If one-time password authentication "
5607 "method is in use, requesting new OTP code requires "
5608 "one-time credentials argument either"));
5611 if (otp
->method
!= OTP_TIME
) {
5612 isds_log_message(context
, _("Requesting new time-based OTP code from "
5613 "server requires one-time password authentication "
5617 if (otp
->otp_code
!= NULL
) {
5618 isds_log_message(context
, _("Requesting new time-based OTP code from "
5619 "server requires undefined OTP code member in "
5620 "one-time credentials argument"));
5626 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5628 isds_log_message(context
, _("Could not build SendSMSCode request"));
5631 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5633 isds_log_message(context
, _("Could not create ISDS name space"));
5634 xmlFreeNode(request
);
5637 xmlSetNs(request
, isds_ns
);
5639 /* Change URL temporarily for sending this request only */
5641 char *new_url
= NULL
;
5642 if ((err
= _isds_build_url_from_context(context
,
5643 "%.*sasws/changePassword", &new_url
))) {
5646 saved_url
= context
->url
;
5647 context
->url
= new_url
;
5650 /* Store credentials for sending this request only */
5651 context
->otp_credentials
= otp
;
5652 _isds_discard_credentials(context
, 0);
5653 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5655 _isds_discard_credentials(context
, 0);
5658 #if HAVE_CURL_REAUTHORIZATION_BUG
5659 saved_curl
= context
->curl
;
5660 context
->curl
= curl_easy_init();
5661 if (NULL
== context
->curl
) {
5665 if (context
->timeout
) {
5666 err
= isds_set_timeout(context
, context
->timeout
);
5667 if (err
) goto leave
;
5671 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5674 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5676 /* Remove temporal credentials */
5677 _isds_discard_credentials(context
, 0);
5678 /* Detach pointer to OTP credentials from context */
5679 context
->otp_credentials
= NULL
;
5680 /* Keep context->otp true to keep signaling this is OTP session */
5682 /* Destroy request */
5683 xmlFreeNode(request
); request
= NULL
;
5686 isds_log(ILF_ISDS
, ILL_DEBUG
,
5687 _("Processing ISDS response on SendSMSCode request failed\n"));
5691 /* Check for response status */
5692 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5693 &code
, &message
, (xmlChar
**)refnumber
);
5695 isds_log(ILF_ISDS
, ILL_DEBUG
,
5696 _("ISDS response on SendSMSCode request is missing "
5701 /* Check for error */
5702 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5703 char *code_locale
= _isds_utf82locale((char*)code
);
5704 char *message_locale
= _isds_utf82locale((char*)message
);
5706 isds_log(ILF_ISDS
, ILL_DEBUG
,
5707 _("Server refused to send new code on SendSMSCode "
5708 "request (code=%s, message=%s)\n"),
5709 code_locale
, message_locale
);
5711 /* Check for known error codes */
5712 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5713 if (!xmlStrcmp(code
, codes
[i
])) break;
5715 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5716 isds_log_message(context
, _(meanings
[i
]));
5717 /* Mimic otp->resolution according to the code, specification does
5718 * prescribe OTP header to be available. */
5719 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5720 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5721 otp
->resolution
= resolutions
[i
];
5723 isds_log_message(context
, message_locale
);
5726 free(message_locale
);
5732 /* Otherwise new code sent successfully */
5733 /* Mimic otp->resolution according to the code, specification does
5734 * prescribe OTP header to be available. */
5735 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5736 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5739 if (NULL
!= saved_url
) {
5740 /* Revert URL to original one */
5741 zfree(context
->url
);
5742 context
->url
= saved_url
;
5744 #if HAVE_CURL_REAUTHORIZATION_BUG
5745 if (NULL
!= saved_curl
) {
5746 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5747 context
->curl
= saved_curl
;
5753 xmlFreeDoc(response
);
5754 xmlFreeNode(request
);
5757 isds_log(ILF_ISDS
, ILL_DEBUG
,
5758 _("New OTP code has been sent successfully on SendSMSCode "
5764 /* Convert response status code to isds_error code and set long message
5765 * @context is context to save long message to
5766 * @map is mapping from codes to errors and messages. Pass NULL for generic
5768 * @code is status code to translate
5769 * @message is non-localized status message to put into long message in case
5770 * of uknown error. It can be NULL if server did not provide any.
5771 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5772 * invalid invocation. */
5773 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5774 const struct code_map_isds_error
*map
,
5775 const xmlChar
*code
, const xmlChar
*message
) {
5777 isds_log_message(context
,
5778 _("NULL status code passed to statuscode2isds_error()"));
5783 /* Check for known error codes */
5784 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5785 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5786 isds_log_message(context
, _(map
->meanings
[i
]));
5787 return map
->errors
[i
];
5793 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5794 char *message_locale
= _isds_utf82locale((char*)message
);
5795 if (NULL
== message_locale
)
5796 isds_log_message(context
, _("ISDS server returned unknown error"));
5798 isds_log_message(context
, message_locale
);
5799 free(message_locale
);
5808 /* Change user password in ISDS.
5809 * User must supply old password, new password will takes effect after some
5810 * time, current session can continue. Password must fulfill some constraints.
5811 * @context is session context
5812 * @old_password is current password.
5813 * @new_password is requested new password
5814 * @otp auxiliary data required if one-time password authentication is in use,
5815 * defines OTP code (if known) and returns fine grade resolution of OTP
5816 * procedure. Pass NULL, if one-time password authentication is not needed.
5817 * Please note the @otp argument must match OTP method used at log-in time. See
5818 * isds_login() function for more details.
5819 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5820 * NULL, if you don't care.
5821 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5822 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5823 * awaiting OTP code that has been delivered by side channel to the user. */
5824 isds_error
isds_change_password(struct isds_ctx
*context
,
5825 const char *old_password
, const char *new_password
,
5826 struct isds_otp
*otp
, char **refnumber
) {
5827 isds_error err
= IE_SUCCESS
;
5829 char *saved_url
= NULL
; /* No copy */
5830 #if HAVE_CURL_REAUTHORIZATION_BUG
5831 CURL
*saved_curl
= NULL
; /* No copy */
5833 xmlNsPtr isds_ns
= NULL
;
5834 xmlNodePtr request
= NULL
, node
;
5835 xmlDocPtr response
= NULL
;
5836 xmlChar
*code
= NULL
, *message
= NULL
;
5837 const xmlChar
*codes
[] = {
5850 const char *meanings
[] = {
5851 N_("Password length must be between 8 and 32 characters"),
5852 N_("Password cannot be reused"), /* Server does not distinguish 1067
5853 and 1091 on ChangePasswordOTP */
5854 N_("Password contains forbidden character"),
5855 N_("Password must contain at least one upper-case letter, "
5856 "one lower-case, and one digit"),
5857 N_("Password cannot contain sequence of three identical characters"),
5858 N_("Password cannot contain user identifier"),
5859 N_("Password is too simmple"),
5860 N_("Old password is not valid"),
5861 N_("Password cannot be reused"),
5862 N_("Unexpected error"),
5863 N_("LDAP update error")
5867 if (!context
) return IE_INVALID_CONTEXT
;
5868 zfree(context
->long_message
);
5869 if (NULL
!= refnumber
)
5871 if (NULL
== old_password
) {
5872 isds_log_message(context
,
5873 _("Second argument (old password) of isds_change_password() "
5877 if (NULL
== otp
&& NULL
== new_password
) {
5878 isds_log_message(context
,
5879 _("Third argument (new password) of isds_change_password() "
5885 /* Check if connection is established
5886 * TODO: This check should be done downstairs. */
5887 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5889 if (context
->otp
&& NULL
== otp
) {
5890 isds_log_message(context
, _("If one-time password authentication "
5891 "method is in use, changing password requires one-time "
5892 "credentials either"));
5896 /* Build ChangeISDSPassword request */
5897 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5898 BAD_CAST
"ChangePasswordOTP");
5900 isds_log_message(context
, (NULL
== otp
) ?
5901 _("Could not build ChangeISDSPassword request") :
5902 _("Could not build ChangePasswordOTP request"));
5905 isds_ns
= xmlNewNs(request
,
5906 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5909 isds_log_message(context
, _("Could not create ISDS name space"));
5910 xmlFreeNode(request
);
5913 xmlSetNs(request
, isds_ns
);
5915 INSERT_STRING(request
, "dbOldPassword", old_password
);
5916 INSERT_STRING(request
, "dbNewPassword", new_password
);
5919 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5920 switch (otp
->method
) {
5922 isds_log(ILF_SEC
, ILL_INFO
,
5923 _("Selected authentication method: "
5924 "HMAC-based one-time password\n"));
5925 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5928 isds_log(ILF_SEC
, ILL_INFO
,
5929 _("Selected authentication method: "
5930 "Time-based one-time password\n"));
5931 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5932 if (otp
->otp_code
== NULL
) {
5933 isds_log(ILF_SEC
, ILL_INFO
,
5934 _("OTP code has not been provided by "
5935 "application, requesting server for "
5937 err
= _isds_request_totp_code(context
, old_password
, otp
,
5939 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5943 isds_log(ILF_SEC
, ILL_INFO
,
5944 _("OTP code has been provided by "
5945 "application, not requesting server "
5950 isds_log_message(context
,
5951 _("Unknown one-time password authentication "
5952 "method requested by application"));
5957 /* Change URL temporarily for sending this request only */
5959 char *new_url
= NULL
;
5960 if ((err
= _isds_build_url_from_context(context
,
5961 "%.*sasws/changePassword", &new_url
))) {
5964 saved_url
= context
->url
;
5965 context
->url
= new_url
;
5968 /* Store credentials for sending this request only */
5969 context
->otp_credentials
= otp
;
5970 _isds_discard_credentials(context
, 0);
5971 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5972 old_password
, NULL
))) {
5973 _isds_discard_credentials(context
, 0);
5976 #if HAVE_CURL_REAUTHORIZATION_BUG
5977 saved_curl
= context
->curl
;
5978 context
->curl
= curl_easy_init();
5979 if (NULL
== context
->curl
) {
5983 if (context
->timeout
) {
5984 err
= isds_set_timeout(context
, context
->timeout
);
5985 if (err
) goto leave
;
5990 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5991 _("Sending ChangeISDSPassword request to ISDS\n") :
5992 _("Sending ChangePasswordOTP request to ISDS\n"));
5995 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5996 request
, &response
, NULL
, NULL
);
5999 /* Remove temporal credentials */
6000 _isds_discard_credentials(context
, 0);
6001 /* Detach pointer to OTP credentials from context */
6002 context
->otp_credentials
= NULL
;
6003 /* Keep context->otp true to keep signaling this is OTP session */
6006 /* Destroy request */
6007 xmlFreeNode(request
); request
= NULL
;
6010 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6011 _("Processing ISDS response on ChangeISDSPassword "
6012 "request failed\n") :
6013 _("Processing ISDS response on ChangePasswordOTP "
6014 "request failed\n"));
6018 /* Check for response status */
6019 err
= isds_response_status(context
,
6020 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
6021 &code
, &message
, (xmlChar
**)refnumber
);
6023 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6024 _("ISDS response on ChangeISDSPassword request is missing "
6026 _("ISDS response on ChangePasswordOTP request is missing "
6031 /* Check for known error codes */
6032 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
6033 if (!xmlStrcmp(code
, codes
[i
])) {
6034 char *code_locale
= _isds_utf82locale((char*)code
);
6035 char *message_locale
= _isds_utf82locale((char*)message
);
6036 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6037 _("Server refused to change password on ChangeISDSPassword "
6038 "request (code=%s, message=%s)\n") :
6039 _("Server refused to change password on ChangePasswordOTP "
6040 "request (code=%s, message=%s)\n"),
6041 code_locale
, message_locale
);
6043 free(message_locale
);
6044 isds_log_message(context
, _(meanings
[i
]));
6051 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6052 char *code_locale
= _isds_utf82locale((char*)code
);
6053 char *message_locale
= _isds_utf82locale((char*)message
);
6054 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6055 _("Server refused to change password on ChangeISDSPassword "
6056 "request (code=%s, message=%s)\n") :
6057 _("Server refused to change password on ChangePasswordOTP "
6058 "request (code=%s, message=%s)\n"),
6059 code_locale
, message_locale
);
6060 isds_log_message(context
, message_locale
);
6062 free(message_locale
);
6067 /* Otherwise password changed successfully */
6070 if (NULL
!= saved_url
) {
6071 /* Revert URL to original one */
6072 zfree(context
->url
);
6073 context
->url
= saved_url
;
6075 #if HAVE_CURL_REAUTHORIZATION_BUG
6076 if (NULL
!= saved_curl
) {
6077 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
6078 context
->curl
= saved_curl
;
6084 xmlFreeDoc(response
);
6085 xmlFreeNode(request
);
6088 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
6089 _("Password changed successfully on ChangeISDSPassword "
6091 _("Password changed successfully on ChangePasswordOTP "
6093 #else /* not HAVE_LIBCURL */
6102 /* Generic middle part with request sending and response check.
6103 * It sends prepared request and checks for error code.
6104 * @context is ISDS session context.
6105 * @service is ISDS service handler
6106 * @service_name is name in scope of given @service
6107 * @request is XML tree with request. Will be freed to save memory.
6108 * @response is XML document outputting ISDS response.
6109 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6110 * @map is mapping from status code to library error. Pass NULL if no special
6111 * handling is requested.
6112 * NULL, if you don't care. */
6113 static isds_error
send_destroy_request_check_response(
6114 struct isds_ctx
*context
,
6115 const isds_service service
, const xmlChar
*service_name
,
6116 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
6117 const struct code_map_isds_error
*map
) {
6118 isds_error err
= IE_SUCCESS
;
6119 char *service_name_locale
= NULL
;
6120 xmlChar
*code
= NULL
, *message
= NULL
;
6123 if (!context
) return IE_INVALID_CONTEXT
;
6124 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
6128 /* Check if connection is established
6129 * TODO: This check should be done downstairs. */
6130 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6132 service_name_locale
= _isds_utf82locale((char*) service_name
);
6133 if (!service_name_locale
) {
6138 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
6139 service_name_locale
);
6142 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
6143 xmlFreeNode(*request
); *request
= NULL
;
6146 isds_log(ILF_ISDS
, ILL_DEBUG
,
6147 _("Processing ISDS response on %s request failed\n"),
6148 service_name_locale
);
6152 /* Check for response status */
6153 err
= isds_response_status(context
, service
, *response
,
6154 &code
, &message
, refnumber
);
6156 isds_log(ILF_ISDS
, ILL_DEBUG
,
6157 _("ISDS response on %s request is missing status\n"),
6158 service_name_locale
);
6162 err
= statuscode2isds_error(context
, map
, code
, message
);
6164 /* Request processed, but server failed */
6165 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6166 char *code_locale
= _isds_utf82locale((char*) code
);
6167 char *message_locale
= _isds_utf82locale((char*) message
);
6168 isds_log(ILF_ISDS
, ILL_DEBUG
,
6169 _("Server refused %s request (code=%s, message=%s)\n"),
6170 service_name_locale
, code_locale
, message_locale
);
6172 free(message_locale
);
6180 if (err
&& *response
) {
6181 xmlFreeDoc(*response
);
6185 xmlFreeNode(*request
);
6188 free(service_name_locale
);
6194 /* Generic bottom half with request sending.
6195 * It sends prepared request, checks for error code, destroys response and
6196 * request and log success or failure.
6197 * @context is ISDS session context.
6198 * @service is ISDS service handler
6199 * @service_name is name in scope of given @service
6200 * @request is XML tree with request. Will be freed to save memory.
6201 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6202 * NULL, if you don't care. */
6203 static isds_error
send_request_check_drop_response(
6204 struct isds_ctx
*context
,
6205 const isds_service service
, const xmlChar
*service_name
,
6206 xmlNodePtr
*request
, xmlChar
**refnumber
) {
6207 isds_error err
= IE_SUCCESS
;
6208 xmlDocPtr response
= NULL
;
6211 if (!context
) return IE_INVALID_CONTEXT
;
6212 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
6215 /* Send request and check response*/
6216 err
= send_destroy_request_check_response(context
,
6217 service
, service_name
, request
, &response
, refnumber
, NULL
);
6219 xmlFreeDoc(response
);
6222 xmlFreeNode(*request
);
6227 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6228 isds_log(ILF_ISDS
, ILL_DEBUG
,
6229 _("%s request processed by server successfully.\n"),
6230 service_name_locale
);
6231 free(service_name_locale
);
6238 /* Insert isds_credentials_delivery structure into XML request if not NULL
6239 * @context is session context
6240 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6241 * credentials delivery. The email field is passed.
6242 * @parent is XML element where to insert */
6243 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
6244 const struct isds_credentials_delivery
*credentials_delivery
,
6245 xmlNodePtr parent
) {
6246 isds_error err
= IE_SUCCESS
;
6249 if (!context
) return IE_INVALID_CONTEXT
;
6250 if (!parent
) return IE_INVAL
;
6252 if (credentials_delivery
) {
6253 /* Following elements are valid only for services:
6254 * NewAccessData, AddDataBoxUser, CreateDataBox */
6255 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
6256 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
6264 /* Extract credentials delivery from ISDS response.
6265 * @context is session context
6266 * @credentials_delivery is pointer to valid structure to fill in returned
6267 * user's password (and new log-in name). If NULL, do not extract the data.
6268 * @response is pointer to XML document with ISDS response
6269 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6270 * @return IE_SUCCESS even if new user name has not been found because it's not
6271 * clear whether it's returned always. */
6272 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
6273 struct isds_credentials_delivery
*credentials_delivery
,
6274 xmlDocPtr response
, const char *request_name
) {
6275 isds_error err
= IE_SUCCESS
;
6276 xmlXPathContextPtr xpath_ctx
= NULL
;
6277 xmlXPathObjectPtr result
= NULL
;
6278 char *xpath_query
= NULL
;
6280 if (!context
) return IE_INVALID_CONTEXT
;
6281 if (credentials_delivery
) {
6282 zfree(credentials_delivery
->token
);
6283 zfree(credentials_delivery
->new_user_name
);
6285 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6288 /* Extract optional token */
6289 if (credentials_delivery
) {
6290 xpath_ctx
= xmlXPathNewContext(response
);
6295 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6300 /* Verify root element */
6301 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6306 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6311 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6312 char *request_name_locale
= _isds_utf82locale(request_name
);
6313 isds_log(ILF_ISDS
, ILL_WARNING
,
6314 _("Wrong element in ISDS response for %s request "
6315 "while extracting credentials delivery details\n"),
6316 request_name_locale
);
6317 free(request_name_locale
);
6321 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6324 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6326 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6328 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6329 if (!credentials_delivery
->token
) {
6330 char *request_name_locale
= _isds_utf82locale(request_name
);
6331 isds_log(ILF_ISDS
, ILL_ERR
,
6332 _("ISDS did not return token on %s request "
6333 "even if requested\n"), request_name_locale
);
6334 free(request_name_locale
);
6341 xmlXPathFreeObject(result
);
6342 xmlXPathFreeContext(xpath_ctx
);
6348 /* Build XSD:tCreateDBInput request type for box creating.
6349 * @context is session context
6350 * @request outputs built XML tree
6351 * @service_name is request name of SERVICE_DB_MANIPULATION service
6352 * @box is box description to create including single primary user (in case of
6353 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6355 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6356 * box, or contact address of PFO box owner)
6357 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6358 * @upper_box_id is optional ID of supper box if currently created box is
6360 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6362 * @credentials_delivery is valid pointer if ISDS should return token that box
6363 * owner can use to obtain his new credentials in on-line way. Then valid email
6364 * member value should be supplied.
6365 * @approval is optional external approval of box manipulation */
6366 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6367 xmlNodePtr
*request
, const xmlChar
*service_name
,
6368 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6369 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6370 const xmlChar
*ceo_label
,
6371 const struct isds_credentials_delivery
*credentials_delivery
,
6372 const struct isds_approval
*approval
) {
6373 isds_error err
= IE_SUCCESS
;
6374 xmlNsPtr isds_ns
= NULL
;
6375 xmlNodePtr node
, dbPrimaryUsers
;
6376 xmlChar
*string
= NULL
;
6377 const struct isds_list
*item
;
6380 if (!context
) return IE_INVALID_CONTEXT
;
6381 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6385 /* Build CreateDataBox-similar request */
6386 *request
= xmlNewNode(NULL
, service_name
);
6388 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6389 isds_printf_message(context
, _("Could build %s request"),
6390 service_name_locale
);
6391 free(service_name_locale
);
6394 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6395 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6397 isds_log_message(context
, _("Could not create ISDS1 name space"));
6398 xmlFreeNode(*request
);
6402 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6404 isds_log_message(context
, _("Could not create ISDS name space"));
6405 xmlFreeNode(*request
);
6409 xmlSetNs(*request
, isds_ns
);
6411 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6412 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6413 if (err
) goto leave
;
6416 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6417 * verbose documentation allows none dbUserInfo */
6418 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6419 for (item
= users
; item
; item
= item
->next
) {
6421 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6422 err
= insert_DbUserInfo(context
,
6423 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6424 if (err
) goto leave
;
6428 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6429 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6430 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6432 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6433 if (err
) goto leave
;
6435 err
= insert_GExtApproval(context
, approval
, *request
);
6436 if (err
) goto leave
;
6440 xmlFreeNode(*request
);
6446 #endif /* HAVE_LIBCURL */
6450 * @context is session context
6451 * @box is box description to create including single primary user (in case of
6452 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6453 * ignored. It outputs box ID assigned by ISDS in dbID element.
6454 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6455 * box, or contact address of PFO box owner)
6456 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6457 * @upper_box_id is optional ID of supper box if currently created box is
6459 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6460 * @credentials_delivery is NULL if new password should be delivered off-line
6461 * to box owner. It is valid pointer if owner should obtain new password on-line
6462 * on dedicated web server. Then input @credentials_delivery.email value is
6463 * his e-mail address he must provide to dedicated web server together
6464 * with output reallocated @credentials_delivery.token member. Output
6465 * member @credentials_delivery.new_user_name is unused up on this call.
6466 * @approval is optional external approval of box manipulation
6467 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6468 * NULL, if you don't care.*/
6469 isds_error
isds_add_box(struct isds_ctx
*context
,
6470 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6471 const char *former_names
, const char *upper_box_id
,
6472 const char *ceo_label
,
6473 struct isds_credentials_delivery
*credentials_delivery
,
6474 const struct isds_approval
*approval
, char **refnumber
) {
6475 isds_error err
= IE_SUCCESS
;
6477 xmlNodePtr request
= NULL
;
6478 xmlDocPtr response
= NULL
;
6479 xmlXPathContextPtr xpath_ctx
= NULL
;
6480 xmlXPathObjectPtr result
= NULL
;
6484 if (!context
) return IE_INVALID_CONTEXT
;
6485 zfree(context
->long_message
);
6486 if (credentials_delivery
) {
6487 zfree(credentials_delivery
->token
);
6488 zfree(credentials_delivery
->new_user_name
);
6490 if (!box
) return IE_INVAL
;
6493 /* Scratch box ID */
6496 /* Build CreateDataBox request */
6497 err
= build_CreateDBInput_request(context
,
6498 &request
, BAD_CAST
"CreateDataBox",
6499 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6500 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6501 if (err
) goto leave
;
6503 /* Send it to server and process response */
6504 err
= send_destroy_request_check_response(context
,
6505 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6506 &response
, (xmlChar
**) refnumber
, NULL
);
6508 /* Extract box ID */
6509 xpath_ctx
= xmlXPathNewContext(response
);
6514 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6518 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6520 /* Extract optional token */
6521 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6525 xmlXPathFreeObject(result
);
6526 xmlXPathFreeContext(xpath_ctx
);
6527 xmlFreeDoc(response
);
6528 xmlFreeNode(request
);
6531 isds_log(ILF_ISDS
, ILL_DEBUG
,
6532 _("CreateDataBox request processed by server successfully.\n"));
6534 #else /* not HAVE_LIBCURL */
6542 /* Notify ISDS about new PFO entity.
6543 * This function has no real effect.
6544 * @context is session context
6545 * @box is PFO description including single primary user. aifoIsds,
6546 * address->adCode, address->adDistrict members are ignored.
6547 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6548 * @former_names is optional undocumented string. Pass NULL if you don't care.
6549 * @upper_box_id is optional ID of supper box if currently created box is
6551 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6552 * @approval is optional external approval of box manipulation
6553 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6554 * NULL, if you don't care.*/
6555 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6556 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6557 const char *former_names
, const char *upper_box_id
,
6558 const char *ceo_label
, const struct isds_approval
*approval
,
6560 isds_error err
= IE_SUCCESS
;
6562 xmlNodePtr request
= NULL
;
6565 if (!context
) return IE_INVALID_CONTEXT
;
6566 zfree(context
->long_message
);
6567 if (!box
) return IE_INVAL
;
6570 /* Build CreateDataBoxPFOInfo request */
6571 err
= build_CreateDBInput_request(context
,
6572 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6573 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6574 (xmlChar
*) ceo_label
, NULL
, approval
);
6575 if (err
) goto leave
;
6577 /* Send it to server and process response */
6578 err
= send_request_check_drop_response(context
,
6579 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6580 (xmlChar
**) refnumber
);
6581 /* XXX: XML Schema names output dbID element but textual documentation
6582 * states no box identifier is returned. */
6584 xmlFreeNode(request
);
6585 #else /* not HAVE_LIBCURL */
6592 /* Common implementation for removing given box.
6593 * @context is session context
6594 * @service_name is UTF-8 encoded name fo ISDS service
6595 * @box is box description to delete. aifoIsds, address->adCode,
6596 * address->adDistrict members are ignored.
6597 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6598 * carry sane value. If NULL, do not inject this information into request.
6599 * @approval is optional external approval of box manipulation
6600 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6601 * NULL, if you don't care.*/
6602 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6603 const xmlChar
*service_name
,
6604 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6605 const struct isds_approval
*approval
, char **refnumber
) {
6606 isds_error err
= IE_SUCCESS
;
6608 xmlNsPtr isds_ns
= NULL
;
6609 xmlNodePtr request
= NULL
;
6611 xmlChar
*string
= NULL
;
6615 if (!context
) return IE_INVALID_CONTEXT
;
6616 zfree(context
->long_message
);
6617 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6621 /* Build DeleteDataBox(Promptly) request */
6622 request
= xmlNewNode(NULL
, service_name
);
6624 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6625 isds_printf_message(context
,
6626 _("Could build %s request"), service_name_locale
);
6627 free(service_name_locale
);
6630 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6632 isds_log_message(context
, _("Could not create ISDS name space"));
6633 xmlFreeNode(request
);
6636 xmlSetNs(request
, isds_ns
);
6638 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6639 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6640 if (err
) goto leave
;
6643 err
= tm2datestring(since
, &string
);
6645 isds_log_message(context
,
6646 _("Could not convert `since' argument to ISO date string"));
6649 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6653 err
= insert_GExtApproval(context
, approval
, request
);
6654 if (err
) goto leave
;
6657 /* Send it to server and process response */
6658 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6659 service_name
, &request
, (xmlChar
**) refnumber
);
6662 xmlFreeNode(request
);
6664 #else /* not HAVE_LIBCURL */
6671 /* Remove given box permanently.
6672 * @context is session context
6673 * @box is box description to delete. aifoIsds, address->adCode,
6674 * address->adDistrict members are ignored.
6675 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6677 * @approval is optional external approval of box manipulation
6678 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6679 * NULL, if you don't care.*/
6680 isds_error
isds_delete_box(struct isds_ctx
*context
,
6681 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6682 const struct isds_approval
*approval
, char **refnumber
) {
6683 if (!context
) return IE_INVALID_CONTEXT
;
6684 zfree(context
->long_message
);
6685 if (!box
|| !since
) return IE_INVAL
;
6687 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6688 box
, since
, approval
, refnumber
);
6692 /* Undocumented function.
6693 * @context is session context
6694 * @box is box description to delete. aifoIsds, address->adCode,
6695 * address->adDistrict members are ignored.
6696 * @approval is optional external approval of box manipulation
6697 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6698 * NULL, if you don't care.*/
6699 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6700 const struct isds_DbOwnerInfo
*box
,
6701 const struct isds_approval
*approval
, char **refnumber
) {
6702 if (!context
) return IE_INVALID_CONTEXT
;
6703 zfree(context
->long_message
);
6704 if (!box
) return IE_INVAL
;
6706 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6707 box
, NULL
, approval
, refnumber
);
6711 /* Update data about given box.
6712 * @context is session context
6713 * @old_box current box description. aifoIsds, address->adCode,
6714 * address->adDistrict members are ignored.
6715 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6716 * address->adDistrict members are ignored.
6717 * @approval is optional external approval of box manipulation
6718 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6719 * NULL, if you don't care.*/
6720 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6721 const struct isds_DbOwnerInfo
*old_box
,
6722 const struct isds_DbOwnerInfo
*new_box
,
6723 const struct isds_approval
*approval
, char **refnumber
) {
6724 isds_error err
= IE_SUCCESS
;
6726 xmlNsPtr isds_ns
= NULL
;
6727 xmlNodePtr request
= NULL
;
6732 if (!context
) return IE_INVALID_CONTEXT
;
6733 zfree(context
->long_message
);
6734 if (!old_box
|| !new_box
) return IE_INVAL
;
6738 /* Build UpdateDataBoxDescr request */
6739 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6741 isds_log_message(context
,
6742 _("Could build UpdateDataBoxDescr request"));
6745 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6747 isds_log_message(context
, _("Could not create ISDS name space"));
6748 xmlFreeNode(request
);
6751 xmlSetNs(request
, isds_ns
);
6753 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6754 err
= insert_DbOwnerInfo(context
, old_box
, 0, node
);
6755 if (err
) goto leave
;
6757 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6758 err
= insert_DbOwnerInfo(context
, new_box
, 0, node
);
6759 if (err
) goto leave
;
6761 err
= insert_GExtApproval(context
, approval
, request
);
6762 if (err
) goto leave
;
6765 /* Send it to server and process response */
6766 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6767 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6770 xmlFreeNode(request
);
6771 #else /* not HAVE_LIBCURL */
6780 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6782 * @context is session context
6783 * @service is SOAP service
6784 * @service_name is name of request in @service
6785 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6786 * @box_id is box ID of interest
6787 * @approval is optional external approval of box manipulation
6788 * @response is server SOAP body response as XML document
6789 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6790 * NULL, if you don't care.
6791 * @return error coded from lower layer, context message will be set up
6793 static isds_error
build_send_dbid_request_check_response(
6794 struct isds_ctx
*context
, const isds_service service
,
6795 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6796 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6797 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6799 isds_error err
= IE_SUCCESS
;
6800 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6801 xmlNodePtr request
= NULL
, node
;
6802 xmlNsPtr isds_ns
= NULL
;
6804 if (!context
) return IE_INVALID_CONTEXT
;
6805 if (!service_name
|| !box_id
) return IE_INVAL
;
6806 if (!response
) return IE_INVAL
;
6808 /* Free output argument */
6809 xmlFreeDoc(*response
); *response
= NULL
;
6811 /* Prepare strings */
6812 service_name_locale
= _isds_utf82locale((char*)service_name
);
6813 if (!service_name_locale
) {
6817 box_id_locale
= _isds_utf82locale((char*)box_id
);
6818 if (!box_id_locale
) {
6824 request
= xmlNewNode(NULL
, service_name
);
6826 isds_printf_message(context
,
6827 _("Could not build %s request for %s box"), service_name_locale
,
6832 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6834 isds_log_message(context
, _("Could not create ISDS name space"));
6838 xmlSetNs(request
, isds_ns
);
6840 /* Add XSD:tIdDbInput children */
6841 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6842 INSERT_STRING(request
, box_id_element
, box_id
);
6843 err
= insert_GExtApproval(context
, approval
, request
);
6844 if (err
) goto leave
;
6846 /* Send request and check response*/
6847 err
= send_destroy_request_check_response(context
,
6848 service
, service_name
, &request
, response
, refnumber
, NULL
);
6851 free(service_name_locale
);
6852 free(box_id_locale
);
6853 xmlFreeNode(request
);
6856 #endif /* HAVE_LIBCURL */
6859 /* Get data about all users assigned to given box.
6860 * @context is session context
6862 * @users is automatically reallocated list of struct isds_DbUserInfo */
6863 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6864 struct isds_list
**users
) {
6865 isds_error err
= IE_SUCCESS
;
6867 xmlDocPtr response
= NULL
;
6868 xmlXPathContextPtr xpath_ctx
= NULL
;
6869 xmlXPathObjectPtr result
= NULL
;
6871 struct isds_list
*item
, *prev_item
= NULL
;
6874 if (!context
) return IE_INVALID_CONTEXT
;
6875 zfree(context
->long_message
);
6876 if (!users
|| !box_id
) return IE_INVAL
;
6877 isds_list_free(users
);
6881 /* Do request and check for success */
6882 err
= build_send_dbid_request_check_response(context
,
6883 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6884 BAD_CAST box_id
, NULL
, &response
, NULL
);
6885 if (err
) goto leave
;
6889 /* Prepare structure */
6890 xpath_ctx
= xmlXPathNewContext(response
);
6895 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6900 /* Set context node */
6901 result
= xmlXPathEvalExpression(BAD_CAST
6902 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6908 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6909 /* Iterate over all users */
6910 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6912 /* Prepare structure */
6913 item
= calloc(1, sizeof(*item
));
6918 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6919 if (i
== 0) *users
= item
;
6920 else prev_item
->next
= item
;
6924 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6925 err
= extract_DbUserInfo(context
,
6926 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6927 if (err
) goto leave
;
6933 isds_list_free(users
);
6936 xmlXPathFreeObject(result
);
6937 xmlXPathFreeContext(xpath_ctx
);
6938 xmlFreeDoc(response
);
6941 isds_log(ILF_ISDS
, ILL_DEBUG
,
6942 _("GetDataBoxUsers request processed by server "
6943 "successfully.\n"));
6944 #else /* not HAVE_LIBCURL */
6952 /* Update data about user assigned to given box.
6953 * @context is session context
6954 * @box is box identification. aifoIsds, address->adCode,
6955 * address->adDistrict members are ignored.
6956 * @old_user identifies user to update, aifo_ticket member is ignored
6957 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6958 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6959 * NULL, if you don't care.*/
6960 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6961 const struct isds_DbOwnerInfo
*box
,
6962 const struct isds_DbUserInfo
*old_user
,
6963 const struct isds_DbUserInfo
*new_user
,
6965 isds_error err
= IE_SUCCESS
;
6967 xmlNsPtr isds_ns
= NULL
;
6968 xmlNodePtr request
= NULL
;
6973 if (!context
) return IE_INVALID_CONTEXT
;
6974 zfree(context
->long_message
);
6975 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6979 /* Build UpdateDataBoxUser request */
6980 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6982 isds_log_message(context
,
6983 _("Could build UpdateDataBoxUser request"));
6986 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6988 isds_log_message(context
, _("Could not create ISDS name space"));
6989 xmlFreeNode(request
);
6992 xmlSetNs(request
, isds_ns
);
6994 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6995 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6996 if (err
) goto leave
;
6998 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6999 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
7000 if (err
) goto leave
;
7002 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
7003 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
7004 if (err
) goto leave
;
7006 /* Send it to server and process response */
7007 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7008 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
7011 xmlFreeNode(request
);
7012 #else /* not HAVE_LIBCURL */
7020 /* Undocumented function.
7021 * @context is session context
7022 * @box_id is UTF-8 encoded box identifier
7023 * @token is UTF-8 encoded temporary password
7024 * @user_id outputs UTF-8 encoded reallocated user identifier
7025 * @password outpus UTF-8 encoded reallocated user password
7026 * Output arguments will be nulled in case of error */
7027 isds_error
isds_activate(struct isds_ctx
*context
,
7028 const char *box_id
, const char *token
,
7029 char **user_id
, char **password
) {
7030 isds_error err
= IE_SUCCESS
;
7032 xmlNsPtr isds_ns
= NULL
;
7033 xmlNodePtr request
= NULL
, node
;
7034 xmlDocPtr response
= NULL
;
7035 xmlXPathContextPtr xpath_ctx
= NULL
;
7036 xmlXPathObjectPtr result
= NULL
;
7040 if (!context
) return IE_INVALID_CONTEXT
;
7041 zfree(context
->long_message
);
7043 if (user_id
) zfree(*user_id
);
7044 if (password
) zfree(*password
);
7046 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
7050 /* Build Activate request */
7051 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
7053 isds_log_message(context
, _("Could build Activate request"));
7056 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7058 isds_log_message(context
, _("Could not create ISDS name space"));
7059 xmlFreeNode(request
);
7062 xmlSetNs(request
, isds_ns
);
7064 INSERT_STRING(request
, "dbAccessDataId", token
);
7065 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
7066 INSERT_STRING(request
, "dbID", box_id
);
7069 /* Send request and check response*/
7070 err
= send_destroy_request_check_response(context
,
7071 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
7072 &response
, NULL
, NULL
);
7073 if (err
) goto leave
;
7077 xpath_ctx
= xmlXPathNewContext(response
);
7082 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7086 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
7092 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7093 isds_log_message(context
, _("Missing ActivateResponse element"));
7097 if (result
->nodesetval
->nodeNr
> 1) {
7098 isds_log_message(context
, _("Multiple ActivateResponse element"));
7102 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7103 xmlXPathFreeObject(result
); result
= NULL
;
7105 EXTRACT_STRING("isds:userId", *user_id
);
7107 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
7108 "but did not return `userId' element.\n"));
7110 EXTRACT_STRING("isds:password", *password
);
7112 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
7113 "but did not return `password' element.\n"));
7116 xmlXPathFreeObject(result
);
7117 xmlXPathFreeContext(xpath_ctx
);
7118 xmlFreeDoc(response
);
7119 xmlFreeNode(request
);
7122 isds_log(ILF_ISDS
, ILL_DEBUG
,
7123 _("Activate request processed by server successfully.\n"));
7124 #else /* not HAVE_LIBCURL */
7132 /* Reset credentials of user assigned to given box.
7133 * @context is session context
7134 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7135 * members are ignored.
7136 * @user identifies user to reset password, aifo_ticket member is ignored
7137 * @fee_paid is true if fee has been paid, false otherwise
7138 * @approval is optional external approval of box manipulation
7139 * @credentials_delivery is NULL if new password should be delivered off-line
7140 * to the user. It is valid pointer if user should obtain new password on-line
7141 * on dedicated web server. Then input @credentials_delivery.email value is
7142 * user's e-mail address user must provide to dedicated web server together
7143 * with @credentials_delivery.token. The output reallocated token user needs
7144 * to use to authorize on the web server to view his new password. Output
7145 * reallocated @credentials_delivery.new_user_name is user's log-in name that
7146 * ISDS changed up on this call. (No reason why server could change the name
7148 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7149 * NULL, if you don't care.*/
7150 isds_error
isds_reset_password(struct isds_ctx
*context
,
7151 const struct isds_DbOwnerInfo
*box
,
7152 const struct isds_DbUserInfo
*user
,
7153 const _Bool fee_paid
, const struct isds_approval
*approval
,
7154 struct isds_credentials_delivery
*credentials_delivery
,
7156 isds_error err
= IE_SUCCESS
;
7158 xmlNsPtr isds_ns
= NULL
;
7159 xmlNodePtr request
= NULL
, node
;
7160 xmlDocPtr response
= NULL
;
7164 if (!context
) return IE_INVALID_CONTEXT
;
7165 zfree(context
->long_message
);
7167 if (credentials_delivery
) {
7168 zfree(credentials_delivery
->token
);
7169 zfree(credentials_delivery
->new_user_name
);
7171 if (!box
|| !user
) return IE_INVAL
;
7175 /* Build NewAccessData request */
7176 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
7178 isds_log_message(context
,
7179 _("Could build NewAccessData request"));
7182 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7184 isds_log_message(context
, _("Could not create ISDS name space"));
7185 xmlFreeNode(request
);
7188 xmlSetNs(request
, isds_ns
);
7190 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7191 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7192 if (err
) goto leave
;
7194 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7195 err
= insert_DbUserInfo(context
, user
, 0, node
);
7196 if (err
) goto leave
;
7198 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
7200 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7201 if (err
) goto leave
;
7203 err
= insert_GExtApproval(context
, approval
, request
);
7204 if (err
) goto leave
;
7206 /* Send request and check response*/
7207 err
= send_destroy_request_check_response(context
,
7208 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
7209 &response
, (xmlChar
**) refnumber
, NULL
);
7210 if (err
) goto leave
;
7213 /* Extract optional token */
7214 err
= extract_credentials_delivery(context
, credentials_delivery
,
7215 response
, "NewAccessData");
7218 xmlFreeDoc(response
);
7219 xmlFreeNode(request
);
7222 isds_log(ILF_ISDS
, ILL_DEBUG
,
7223 _("NewAccessData request processed by server "
7224 "successfully.\n"));
7225 #else /* not HAVE_LIBCURL */
7233 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7234 * code, destroy response and log success.
7235 * @context is ISDS session context.
7236 * @service_name is name of SERVICE_DB_MANIPULATION service
7237 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7238 * members are ignored.
7239 * @user identifies user to remove
7240 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7241 * @credentials_delivery is NULL if new user's password should be delivered
7242 * off-line to the user. It is valid pointer if user should obtain new
7243 * password on-line on dedicated web server. Then input
7244 * @credentials_delivery.email value is user's e-mail address user must
7245 * provide to dedicated web server together with @credentials_delivery.token.
7246 * The output reallocated token user needs to use to authorize on the web
7247 * server to view his new password. Output reallocated
7248 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7249 * assingned or changed up on this call.
7250 * @approval is optional external approval of box manipulation
7251 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7252 * NULL, if you don't care. */
7253 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
7254 struct isds_ctx
*context
, const xmlChar
*service_name
,
7255 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7256 _Bool honor_aifo_ticket
,
7257 struct isds_credentials_delivery
*credentials_delivery
,
7258 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7259 isds_error err
= IE_SUCCESS
;
7261 xmlNsPtr isds_ns
= NULL
;
7262 xmlNodePtr request
= NULL
, node
;
7263 xmlDocPtr response
= NULL
;
7267 if (!context
) return IE_INVALID_CONTEXT
;
7268 zfree(context
->long_message
);
7269 if (credentials_delivery
) {
7270 zfree(credentials_delivery
->token
);
7271 zfree(credentials_delivery
->new_user_name
);
7273 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7278 /* Build NewAccessData or similar request */
7279 request
= xmlNewNode(NULL
, service_name
);
7281 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7282 isds_printf_message(context
, _("Could not build %s request"),
7283 service_name_locale
);
7284 free(service_name_locale
);
7287 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7289 isds_log_message(context
, _("Could not create ISDS name space"));
7290 xmlFreeNode(request
);
7293 xmlSetNs(request
, isds_ns
);
7295 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7296 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7297 if (err
) goto leave
;
7299 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7300 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7301 if (err
) goto leave
;
7303 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7304 if (err
) goto leave
;
7306 err
= insert_GExtApproval(context
, approval
, request
);
7307 if (err
) goto leave
;
7310 /* Send request and check response*/
7311 err
= send_destroy_request_check_response(context
,
7312 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7315 xmlFreeNode(request
);
7318 /* Pick up credentials_delivery if requested */
7319 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7320 (char *)service_name
);
7323 xmlFreeDoc(response
);
7324 if (request
) xmlFreeNode(request
);
7327 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7328 isds_log(ILF_ISDS
, ILL_DEBUG
,
7329 _("%s request processed by server successfully.\n"),
7330 service_name_locale
);
7331 free(service_name_locale
);
7333 #else /* not HAVE_LIBCURL */
7341 /* Assign new user to given box.
7342 * @context is session context
7343 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7344 * members are ignored.
7345 * @user defines new user to add
7346 * @credentials_delivery is NULL if new user's password should be delivered
7347 * off-line to the user. It is valid pointer if user should obtain new
7348 * password on-line on dedicated web server. Then input
7349 * @credentials_delivery.email value is user's e-mail address user must
7350 * provide to dedicated web server together with @credentials_delivery.token.
7351 * The output reallocated token user needs to use to authorize on the web
7352 * server to view his new password. Output reallocated
7353 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7354 * assingned up on this call.
7355 * @approval is optional external approval of box manipulation
7356 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7357 * NULL, if you don't care.*/
7358 isds_error
isds_add_user(struct isds_ctx
*context
,
7359 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7360 struct isds_credentials_delivery
*credentials_delivery
,
7361 const struct isds_approval
*approval
, char **refnumber
) {
7362 return build_send_manipulationboxuser_request_check_drop_response(context
,
7363 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7364 approval
, (xmlChar
**) refnumber
);
7368 /* Remove user assigned to given box.
7369 * @context is session context
7370 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7371 * members are ignored.
7372 * @user identifies user to remove, aifo_ticket member is ignored
7373 * @approval is optional external approval of box manipulation
7374 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7375 * NULL, if you don't care.*/
7376 isds_error
isds_delete_user(struct isds_ctx
*context
,
7377 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7378 const struct isds_approval
*approval
, char **refnumber
) {
7379 return build_send_manipulationboxuser_request_check_drop_response(context
,
7380 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7381 (xmlChar
**) refnumber
);
7385 /* Get list of boxes in ZIP archive.
7386 * @context is session context
7387 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7388 * System recognizes following values currently: ALL (all boxes), UPG
7389 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7390 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7391 * commercial messages). This argument is a string because specification
7392 * states new values can appear in the future. Not all list types are
7393 * available to all users.
7394 * @buffer is automatically reallocated memory to store the list of boxes. The
7395 * list is zipped CSV file.
7396 * @buffer_length is size of @buffer data in bytes.
7397 * In case of error @buffer will be freed and @buffer_length will be
7399 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7400 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7401 isds_error err
= IE_SUCCESS
;
7403 xmlNsPtr isds_ns
= NULL
;
7404 xmlNodePtr request
= NULL
, node
;
7405 xmlDocPtr response
= NULL
;
7406 xmlXPathContextPtr xpath_ctx
= NULL
;
7407 xmlXPathObjectPtr result
= NULL
;
7408 char *string
= NULL
;
7412 if (!context
) return IE_INVALID_CONTEXT
;
7413 zfree(context
->long_message
);
7414 if (buffer
) zfree(*buffer
);
7415 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7419 /* Check if connection is established
7420 * TODO: This check should be done downstairs. */
7421 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7424 /* Build AuthenticateMessage request */
7425 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7427 isds_log_message(context
,
7428 _("Could not build GetDataBoxList request"));
7431 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7433 isds_log_message(context
, _("Could not create ISDS name space"));
7434 xmlFreeNode(request
);
7437 xmlSetNs(request
, isds_ns
);
7438 INSERT_STRING(request
, "dblType", list_identifier
);
7440 /* Send request to server and process response */
7441 err
= send_destroy_request_check_response(context
,
7442 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7443 &response
, NULL
, NULL
);
7444 if (err
) goto leave
;
7447 /* Extract Base-64 encoded ZIP file */
7448 xpath_ctx
= xmlXPathNewContext(response
);
7453 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7457 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7459 /* Decode non-empty archive */
7460 if (string
&& string
[0] != '\0') {
7461 *buffer_length
= _isds_b64decode(string
, buffer
);
7462 if (*buffer_length
== (size_t) -1) {
7463 isds_printf_message(context
,
7464 _("Error while Base64-decoding box list archive"));
7473 xmlXPathFreeObject(result
);
7474 xmlXPathFreeContext(xpath_ctx
);
7475 xmlFreeDoc(response
);
7476 xmlFreeNode(request
);
7479 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7480 "processed by server successfully.\n"));
7482 #else /* not HAVE_LIBCURL */
7490 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7491 * send it, check for error code, extract list of results, destroy response
7493 * @context is ISDS session context.
7494 * @service_name is name of SERVICE_DB_SEARCH service
7495 * @pfo_service is false if tDbOwnerInfo request should be built from
7496 * @criteria and corresponding result extracted. It is true if
7497 * tDbPersonalOwnerInfoRequest request should be built. The request and
7498 * response differ subset of significant isds_DbOwnerInfo structure members.
7499 * @criteria is filter. You should fill in at least some members.
7500 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7501 * members will be ignored. If @pfo_service is true, dbType, ic,
7502 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7503 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7505 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7506 * possibly empty. Input NULL or valid old structure. The same memebers as
7507 * in described for @criteria argument will be NULL according to @pfo_service
7510 * IE_SUCCESS if search succeeded, @boxes contains useful data
7511 * IE_NOEXIST if no such box exists, @boxes will be NULL
7512 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7513 * contains still valid data
7514 * other code if something bad happens. @boxes will be NULL. */
7515 static isds_error
build_send_findbox_request_check_parse_drop_response(
7516 struct isds_ctx
*context
, const xmlChar
*service_name
,
7517 _Bool pfo_service
, const struct isds_DbOwnerInfo
*criteria
,
7518 struct isds_list
**boxes
) {
7519 isds_error err
= IE_SUCCESS
;
7521 char *service_name_locale
= NULL
;
7522 _Bool truncated
= 0;
7523 xmlNsPtr isds_ns
= NULL
;
7524 xmlNodePtr request
= NULL
;
7525 xmlDocPtr response
= NULL
;
7526 xmlChar
*code
= NULL
, *message
= NULL
;
7527 xmlNodePtr db_owner_info
;
7528 xmlXPathContextPtr xpath_ctx
= NULL
;
7529 xmlXPathObjectPtr result
= NULL
;
7530 xmlChar
*string
= NULL
;
7534 if (!context
) return IE_INVALID_CONTEXT
;
7535 zfree(context
->long_message
);
7536 if (!boxes
) return IE_INVAL
;
7537 isds_list_free(boxes
);
7544 /* Check if connection is established
7545 * TODO: This check should be done downstairs. */
7546 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7547 service_name_locale
= _isds_utf82locale((char *) service_name
);
7550 request
= xmlNewNode(NULL
, service_name
);
7552 isds_printf_message(context
, _("Could not build %s request"),
7553 service_name_locale
);
7554 free(service_name_locale
);
7557 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7559 isds_log_message(context
, _("Could not create ISDS name space"));
7560 free(service_name_locale
);
7561 xmlFreeNode(request
);
7564 xmlSetNs(request
, isds_ns
);
7565 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7566 if (!db_owner_info
) {
7567 isds_printf_message(context
,
7568 _("Could not add dbOwnerInfo child to %s element"),
7569 service_name_locale
);
7570 free(service_name_locale
);
7571 xmlFreeNode(request
);
7575 err
= insert_DbOwnerInfo(context
, criteria
, pfo_service
, db_owner_info
);
7576 if (err
) goto leave
;
7580 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
7581 service_name_locale
);
7582 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7584 /* Destroy request */
7585 xmlFreeNode(request
); request
= NULL
;
7588 isds_log(ILF_ISDS
, ILL_DEBUG
,
7589 _("Processing ISDS response on %s request failed\n"),
7590 service_name_locale
);
7594 /* Check for response status */
7595 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7596 &code
, &message
, NULL
);
7598 isds_log(ILF_ISDS
, ILL_DEBUG
,
7599 _("ISDS response on %s request is missing status\n"),
7600 service_name_locale
);
7604 /* Request processed, but nothing found */
7605 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7606 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7607 char *code_locale
= _isds_utf82locale((char*)code
);
7608 char *message_locale
= _isds_utf82locale((char*)message
);
7609 isds_log(ILF_ISDS
, ILL_DEBUG
,
7610 _("Server did not find any box on %s request "
7611 "(code=%s, message=%s)\n"), service_name_locale
,
7612 code_locale
, message_locale
);
7613 isds_log_message(context
, message_locale
);
7615 free(message_locale
);
7620 /* Warning, not an error */
7621 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7622 char *code_locale
= _isds_utf82locale((char*)code
);
7623 char *message_locale
= _isds_utf82locale((char*)message
);
7624 isds_log(ILF_ISDS
, ILL_DEBUG
,
7625 _("Server truncated response on %s request "
7626 "(code=%s, message=%s)\n"), service_name_locale
,
7627 code_locale
, message_locale
);
7628 isds_log_message(context
, message_locale
);
7630 free(message_locale
);
7635 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7636 char *code_locale
= _isds_utf82locale((char*)code
);
7637 char *message_locale
= _isds_utf82locale((char*)message
);
7638 isds_log(ILF_ISDS
, ILL_DEBUG
,
7639 _("Server refused %s request (code=%s, message=%s)\n"),
7640 service_name_locale
, code_locale
, message_locale
);
7641 isds_log_message(context
, message_locale
);
7643 free(message_locale
);
7648 xpath_ctx
= xmlXPathNewContext(response
);
7653 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7658 /* Extract boxes if they present */
7659 if (-1 == isds_asprintf((char **)&string
,
7660 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7665 result
= xmlXPathEvalExpression(string
, xpath_ctx
);
7671 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7672 struct isds_list
*item
, *prev_item
= NULL
;
7673 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7674 item
= calloc(1, sizeof(*item
));
7680 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7681 if (i
== 0) *boxes
= item
;
7682 else prev_item
->next
= item
;
7685 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7686 err
= extract_DbOwnerInfo(context
,
7687 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7688 if (err
) goto leave
;
7694 isds_list_free(boxes
);
7696 if (truncated
) err
= IE_2BIG
;
7700 xmlFreeNode(request
);
7701 xmlXPathFreeObject(result
);
7702 xmlXPathFreeContext(xpath_ctx
);
7706 xmlFreeDoc(response
);
7709 isds_log(ILF_ISDS
, ILL_DEBUG
,
7710 _("%s request processed by server successfully.\n"),
7711 service_name_locale
);
7712 free(service_name_locale
);
7713 #else /* not HAVE_LIBCURL */
7721 /* Find boxes suiting given criteria.
7722 * @criteria is filter. You should fill in at least some members. aifoIsds,
7723 * address->adCode, address->adDistrict members are ignored.
7724 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7725 * possibly empty. Input NULL or valid old structure.
7727 * IE_SUCCESS if search succeeded, @boxes contains useful data
7728 * IE_NOEXIST if no such box exists, @boxes will be NULL
7729 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7730 * contains still valid data
7731 * other code if something bad happens. @boxes will be NULL. */
7732 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7733 const struct isds_DbOwnerInfo
*criteria
,
7734 struct isds_list
**boxes
) {
7735 return build_send_findbox_request_check_parse_drop_response(context
,
7736 BAD_CAST
"FindDataBox", 0, criteria
, boxes
);
7740 /* Find accessible FO-type boxes suiting given criteria.
7741 * @criteria is filter. You should fill in at least some members. dbType, ic,
7742 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7743 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members are ignored.
7744 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7745 * possibly empty. Input NULL or valid old structure.
7747 * IE_SUCCESS if search succeeded, @boxes contains useful data
7748 * IE_NOEXIST if no such box exists, @boxes will be NULL
7749 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7750 * contains still valid data
7751 * other code if something bad happens. @boxes will be NULL. */
7752 isds_error
isds_FindPersonalDataBox(struct isds_ctx
*context
,
7753 const struct isds_DbOwnerInfo
*criteria
,
7754 struct isds_list
**boxes
) {
7755 return build_send_findbox_request_check_parse_drop_response(context
,
7756 BAD_CAST
"FindPersonalDataBox", 1, criteria
, boxes
);
7761 /* Convert a string with match markers into a plain string with list of
7762 * pointers to the matches
7763 * @string is an UTF-8 encoded non-constant string with match markers
7764 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7765 * The markers will be removed from the string.
7766 * @starts is a reallocated list of static pointers into the @string pointing
7767 * to places where match start markers occured.
7768 * @ends is a reallocated list of static pointers into the @string pointing
7769 * to places where match end markers occured.
7770 * @return IE_SUCCESS in case of no failure. */
7771 static isds_error
interpret_matches(xmlChar
*string
,
7772 struct isds_list
**starts
, struct isds_list
**ends
) {
7773 isds_error err
= IE_SUCCESS
;
7774 xmlChar
*pointer
, *destination
, *source
;
7775 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7777 isds_list_free(starts
);
7778 isds_list_free(ends
);
7779 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7780 if (NULL
== string
) return IE_SUCCESS
;
7782 for (pointer
= string
; *pointer
!= '\0';) {
7783 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7784 /* Remove the start marker */
7785 for (source
= pointer
+ 14, destination
= pointer
;
7786 *source
!= '\0'; source
++, destination
++) {
7787 *destination
= *source
;
7789 *destination
= '\0';
7790 /* Append the pointer into the list */
7791 item
= calloc(1, sizeof(*item
));
7796 item
->destructor
= (void (*)(void **))NULL
;
7797 item
->data
= pointer
;
7798 if (NULL
== prev_start
) *starts
= item
;
7799 else prev_start
->next
= item
;
7801 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7802 /* Remove the end marker */
7803 for (source
= pointer
+ 12, destination
= pointer
;
7804 *source
!= '\0'; source
++, destination
++) {
7805 *destination
= *source
;
7807 *destination
= '\0';
7808 /* Append the pointer into the list */
7809 item
= calloc(1, sizeof(*item
));
7814 item
->destructor
= (void (*)(void **))NULL
;
7815 item
->data
= pointer
;
7816 if (NULL
== prev_end
) *ends
= item
;
7817 else prev_end
->next
= item
;
7826 isds_list_free(starts
);
7827 isds_list_free(ends
);
7833 /* Convert isds:dbResult XML tree into structure
7834 * @context is ISDS context.
7835 * @fulltext_result is automatically reallocated found box structure.
7836 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7837 * @collect_matches is true to interpret match markers.
7838 * In case of error @result will be freed. */
7839 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7840 struct isds_fulltext_result
**fulltext_result
,
7841 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7842 isds_error err
= IE_SUCCESS
;
7843 xmlXPathObjectPtr result
= NULL
;
7844 char *string
= NULL
;
7846 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7847 if (NULL
== fulltext_result
) return IE_INVAL
;
7848 isds_fulltext_result_free(fulltext_result
);
7849 if (!xpath_ctx
) return IE_INVAL
;
7852 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7853 if (NULL
== *fulltext_result
) {
7859 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7861 EXTRACT_STRING("isds:dbType", string
);
7862 if (NULL
== string
) {
7864 isds_log_message(context
, _("Empty isds:dbType element"));
7867 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7869 if (err
== IE_ENUM
) {
7871 char *string_locale
= _isds_utf82locale(string
);
7872 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7874 free(string_locale
);
7880 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7881 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7883 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7884 if (err
) goto leave
;
7886 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7887 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7888 (*fulltext_result
)->dbEffectiveOVM
);
7890 EXTRACT_STRING("isds:dbSendOptions", string
);
7891 if (NULL
== string
) {
7893 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7896 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7897 (*fulltext_result
)->active
= 1;
7898 (*fulltext_result
)->public_sending
= 1;
7899 (*fulltext_result
)->commercial_sending
= 0;
7900 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7901 (*fulltext_result
)->active
= 1;
7902 (*fulltext_result
)->public_sending
= 1;
7903 (*fulltext_result
)->commercial_sending
= 1;
7904 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7905 (*fulltext_result
)->active
= 1;
7906 (*fulltext_result
)->public_sending
= 0;
7907 (*fulltext_result
)->commercial_sending
= 1;
7908 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7909 (*fulltext_result
)->active
= 1;
7910 (*fulltext_result
)->public_sending
= 0;
7911 (*fulltext_result
)->commercial_sending
= 0;
7912 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7913 (*fulltext_result
)->active
= 0;
7914 (*fulltext_result
)->public_sending
= 0;
7915 (*fulltext_result
)->commercial_sending
= 0;
7918 char *string_locale
= _isds_utf82locale(string
);
7919 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7921 free(string_locale
);
7926 /* Interpret match marks */
7927 if (collect_matches
) {
7928 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7929 &((*fulltext_result
)->name_match_start
),
7930 &((*fulltext_result
)->name_match_end
));
7931 if (err
) goto leave
;
7932 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7933 &((*fulltext_result
)->address_match_start
),
7934 &((*fulltext_result
)->address_match_end
));
7935 if (err
) goto leave
;
7939 if (err
) isds_fulltext_result_free(fulltext_result
);
7941 xmlXPathFreeObject(result
);
7944 #endif /* HAVE_LIBCURL */
7947 /* Find boxes matching a given full-text criteria.
7948 * @context is a session context
7949 * @query is a non-empty string which consists of words to search
7950 * @target selects box attributes to search for @query words. Pass NULL if you
7952 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7953 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7954 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7955 * which is DBTYPE_SYSTEM.
7956 * @page_size defines count of boxes to constitute a response page. It counts
7957 * from zero. Pass NULL to let server to use a default value (50 now).
7958 * @page_number defines ordinar number of the response page to return. It
7959 * counts from zero. Pass NULL to let server to use a default value (0 now).
7960 * @track_matches points to true for marking @query words found in the box
7961 * attributes. It points to false for not marking. Pass NULL to let the server
7962 * to use default value (false now).
7963 * @total_matching_boxes outputs reallocated number of all boxes matching the
7964 * query. Will be pointer to NULL if server did not provide the value.
7965 * Pass NULL if you don't care.
7966 * @current_page_beginning outputs reallocated ordinar number of the first box
7967 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7968 * server did not provide the value. Pass NULL if you don't care.
7969 * @current_page_size outputs reallocated count of boxes in the this @boxes
7970 * page. It will be pointer to NULL if the server did not provide the value.
7971 * Pass NULL if you don't care.
7972 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7973 * is the last one, false if more boxes match, NULL if the server did not
7974 * provude the value. Pass NULL if you don't care.
7975 * @boxes outputs reallocated list of isds_fulltext_result structures,
7978 * IE_SUCCESS if search succeeded
7979 * IE_2BIG if @page_size is too large
7980 * other code if something bad happens; output arguments will be NULL. */
7981 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7983 const isds_fulltext_target
*target
,
7984 const isds_DbType
*box_type
,
7985 const unsigned long int *page_size
,
7986 const unsigned long int *page_number
,
7987 const _Bool
*track_matches
,
7988 unsigned long int **total_matching_boxes
,
7989 unsigned long int **current_page_beginning
,
7990 unsigned long int **current_page_size
,
7992 struct isds_list
**boxes
) {
7993 isds_error err
= IE_SUCCESS
;
7995 xmlNsPtr isds_ns
= NULL
;
7996 xmlNodePtr request
= NULL
;
7997 xmlDocPtr response
= NULL
;
7999 xmlXPathContextPtr xpath_ctx
= NULL
;
8000 xmlXPathObjectPtr result
= NULL
;
8001 const xmlChar
*static_string
= NULL
;
8002 xmlChar
*string
= NULL
;
8004 const xmlChar
*codes
[] = {
8014 const char *meanings
[] = {
8015 N_("You are not allowed to perform the search"),
8016 N_("The query string is empty"),
8017 N_("Searched box ID is malformed"),
8018 N_("Searched organization ID is malformed"),
8019 N_("Invalid input"),
8020 N_("Requested page size is too large"),
8021 N_("Search engine internal error")
8023 const isds_error errors
[] = {
8032 struct code_map_isds_error map
= {
8034 .meanings
= meanings
,
8040 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
8041 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
8042 if (NULL
!= current_page_size
) zfree(*current_page_size
);
8043 if (NULL
!= last_page
) zfree(*last_page
);
8044 isds_list_free(boxes
);
8046 if (NULL
== context
) return IE_INVALID_CONTEXT
;
8047 zfree(context
->long_message
);
8049 if (NULL
== boxes
) return IE_INVAL
;
8051 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
8052 isds_log_message(context
, _("Query string must be non-empty"));
8057 /* Check if connection is established
8058 * TODO: This check should be done downstairs. */
8059 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8061 /* Build FindDataBox request */
8062 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
8063 if (NULL
== request
) {
8064 isds_log_message(context
,
8065 _("Could not build ISDSSearch2 request"));
8068 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8069 if(NULL
== isds_ns
) {
8070 isds_log_message(context
, _("Could not create ISDS name space"));
8071 xmlFreeNode(request
);
8074 xmlSetNs(request
, isds_ns
);
8076 INSERT_STRING(request
, "searchText", query
);
8078 if (NULL
!= target
) {
8079 static_string
= isds_fulltext_target2string(*(target
));
8080 if (NULL
== static_string
) {
8081 isds_printf_message(context
, _("Invalid target value: %d"),
8087 INSERT_STRING(request
, "searchType", static_string
);
8088 static_string
= NULL
;
8090 if (NULL
!= box_type
) {
8091 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
8092 if (DBTYPE_SYSTEM
== *box_type
) {
8093 static_string
= BAD_CAST
"ALL";
8094 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
8095 static_string
= BAD_CAST
"OVM_MAIN";
8097 static_string
= isds_DbType2string(*(box_type
));
8098 if (NULL
== static_string
) {
8099 isds_printf_message(context
, _("Invalid box type value: %d"),
8106 INSERT_STRING(request
, "searchScope", static_string
);
8107 static_string
= NULL
;
8109 INSERT_ULONGINT(request
, "page", page_number
, string
);
8110 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
8111 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
8113 /* Send request and check response */
8114 err
= send_destroy_request_check_response(context
,
8115 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
8116 &request
, &response
, NULL
, &map
);
8117 if (err
) goto leave
;
8119 /* Parse response */
8120 xpath_ctx
= xmlXPathNewContext(response
);
8121 if (NULL
== xpath_ctx
) {
8125 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8129 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
8135 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8136 isds_log_message(context
, _("Missing ISDSSearch2 element"));
8140 if (result
->nodesetval
->nodeNr
> 1) {
8141 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
8145 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8146 xmlXPathFreeObject(result
); result
= NULL
;
8149 /* Extract counters */
8150 if (NULL
!= total_matching_boxes
) {
8151 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
8153 if (NULL
!= current_page_size
) {
8154 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
8156 if (NULL
!= current_page_beginning
) {
8157 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
8159 if (NULL
!= last_page
) {
8160 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
8162 xmlXPathFreeObject(result
); result
= NULL
;
8164 /* Extract boxes if they present */
8165 result
= xmlXPathEvalExpression(BAD_CAST
8166 "isds:dbResults/isds:dbResult", xpath_ctx
);
8167 if (NULL
== result
) {
8171 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8172 struct isds_list
*item
, *prev_item
= NULL
;
8173 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8174 item
= calloc(1, sizeof(*item
));
8180 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
8181 if (i
== 0) *boxes
= item
;
8182 else prev_item
->next
= item
;
8185 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8186 err
= extract_dbResult(context
,
8187 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
8188 (NULL
== track_matches
) ? 0 : *track_matches
);
8189 if (err
) goto leave
;
8195 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
8196 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
8197 if (NULL
!= current_page_size
) zfree(*current_page_size
);
8198 if (NULL
!= last_page
) zfree(*last_page
);
8199 isds_list_free(boxes
);
8203 xmlFreeNode(request
);
8204 xmlXPathFreeObject(result
);
8205 xmlXPathFreeContext(xpath_ctx
);
8206 xmlFreeDoc(response
);
8209 isds_log(ILF_ISDS
, ILL_DEBUG
,
8210 _("ISDSSearch2 request processed by server successfully.\n"));
8211 #else /* not HAVE_LIBCURL */
8219 /* Get status of a box.
8220 * @context is ISDS session context.
8221 * @box_id is UTF-8 encoded box identifier as zero terminated string
8222 * @box_status is return value of box status.
8224 * IE_SUCCESS if box has been found and its status retrieved
8225 * IE_NOEXIST if box is not known to ISDS server
8226 * or other appropriate error.
8227 * You can use isds_DbState to enumerate box status. However out of enum
8228 * range value can be returned too. This is feature because ISDS
8229 * specification leaves the set of values open.
8230 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
8231 * the box has been deleted, but ISDS still lists its former existence. */
8232 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
8233 long int *box_status
) {
8234 isds_error err
= IE_SUCCESS
;
8236 xmlNsPtr isds_ns
= NULL
;
8237 xmlNodePtr request
= NULL
, db_id
;
8238 xmlDocPtr response
= NULL
;
8239 xmlXPathContextPtr xpath_ctx
= NULL
;
8240 xmlXPathObjectPtr result
= NULL
;
8241 xmlChar
*string
= NULL
;
8243 const xmlChar
*codes
[] = {
8249 const char *meanings
[] = {
8250 "The box does not exist",
8251 "Box ID is malformed",
8254 const isds_error errors
[] = {
8259 struct code_map_isds_error map
= {
8261 .meanings
= meanings
,
8266 if (!context
) return IE_INVALID_CONTEXT
;
8267 zfree(context
->long_message
);
8268 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
8271 /* Check if connection is established
8272 * TODO: This check should be done downstairs. */
8273 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8276 /* Build CheckDataBox request */
8277 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
8279 isds_log_message(context
,
8280 _("Could build CheckDataBox request"));
8283 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8285 isds_log_message(context
, _("Could not create ISDS name space"));
8286 xmlFreeNode(request
);
8289 xmlSetNs(request
, isds_ns
);
8290 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
8292 isds_log_message(context
, _("Could not add dbID child to "
8293 "CheckDataBox element"));
8294 xmlFreeNode(request
);
8299 /* Send request and check response*/
8300 err
= send_destroy_request_check_response(context
,
8301 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
8302 &request
, &response
, NULL
, &map
);
8303 if (err
) goto leave
;
8307 xpath_ctx
= xmlXPathNewContext(response
);
8312 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8316 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
8322 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8323 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
8327 if (result
->nodesetval
->nodeNr
> 1) {
8328 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
8332 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8333 xmlXPathFreeObject(result
); result
= NULL
;
8335 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
8340 xmlXPathFreeObject(result
);
8341 xmlXPathFreeContext(xpath_ctx
);
8343 xmlFreeDoc(response
);
8346 isds_log(ILF_ISDS
, ILL_DEBUG
,
8347 _("CheckDataBox request processed by server successfully.\n"));
8348 #else /* not HAVE_LIBCURL */
8357 /* Convert XSD:tdbPeriod XML tree into structure
8358 * @context is ISDS context.
8359 * @period is automatically reallocated found box status period structure.
8360 * @xpath_ctx is XPath context with current node as element of
8361 * XSD:tDbPeriod type.
8362 * In case of error @period will be freed. */
8363 static isds_error
extract_Period(struct isds_ctx
*context
,
8364 struct isds_box_state_period
**period
, xmlXPathContextPtr xpath_ctx
) {
8365 isds_error err
= IE_SUCCESS
;
8366 xmlXPathObjectPtr result
= NULL
;
8367 char *string
= NULL
;
8368 long int *dbState_ptr
;
8370 if (NULL
== context
) return IE_INVALID_CONTEXT
;
8371 if (NULL
== period
) return IE_INVAL
;
8372 isds_box_state_period_free(period
);
8373 if (!xpath_ctx
) return IE_INVAL
;
8376 *period
= calloc(1, sizeof(**period
));
8377 if (NULL
== *period
) {
8383 EXTRACT_STRING("isds:PeriodFrom", string
);
8384 if (NULL
== string
) {
8386 isds_log_message(context
,
8387 _("Could not find PeriodFrom element value"));
8390 err
= timestring2static_timeval((xmlChar
*) string
,
8391 &((*period
)->from
));
8393 char *string_locale
= _isds_utf82locale(string
);
8394 if (err
== IE_DATE
) err
= IE_ISDS
;
8395 isds_printf_message(context
,
8396 _("Could not convert PeriodFrom as ISO time: %s"),
8398 free(string_locale
);
8403 EXTRACT_STRING("isds:PeriodTo", string
);
8404 if (NULL
== string
) {
8406 isds_log_message(context
,
8407 _("Could not find PeriodTo element value"));
8410 err
= timestring2static_timeval((xmlChar
*) string
,
8413 char *string_locale
= _isds_utf82locale(string
);
8414 if (err
== IE_DATE
) err
= IE_ISDS
;
8415 isds_printf_message(context
,
8416 _("Could not convert PeriodTo as ISO time: %s"),
8418 free(string_locale
);
8423 dbState_ptr
= &((*period
)->dbState
);
8424 EXTRACT_LONGINT("isds:DbState", dbState_ptr
, 1);
8427 if (err
) isds_box_state_period_free(period
);
8429 xmlXPathFreeObject(result
);
8432 #endif /* HAVE_LIBCURL */
8435 /* Get history of box state changes.
8436 * @context is ISDS session context.
8437 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8438 * @from_time is first second of history to return in @history. Server ignores
8439 * subseconds. NULL means time of creating the box.
8440 * @to_time is last second of history to return in @history. Server ignores
8441 * subseconds. It's valid to have the @from_time equaled to the @to_time. The
8442 * interval is closed from both ends. NULL means now.
8443 * @history outputs auto-reallocated list of pointers to struct
8444 * isds_box_state_period. Each item describes a continues time when the box
8445 * was in one state. The state is 1 for accessible box. Otherwise the box
8446 * is inaccessible (priviledged users will get exact box state as enumerated
8447 * in isds_DbState, other users 0).
8449 * IE_SUCCESS if the history has been obtained correctly,
8450 * or other appropriate error. Please note that server allows to retrieve
8451 * the history only to some users. */
8452 isds_error
isds_get_box_state_history(struct isds_ctx
*context
,
8454 const struct timeval
*from_time
, const struct timeval
*to_time
,
8455 struct isds_list
**history
) {
8456 isds_error err
= IE_SUCCESS
;
8458 char *box_id_locale
= NULL
;
8459 xmlNodePtr request
= NULL
, node
;
8460 xmlNsPtr isds_ns
= NULL
;
8461 xmlChar
*string
= NULL
;
8463 xmlDocPtr response
= NULL
;
8464 xmlXPathContextPtr xpath_ctx
= NULL
;
8465 xmlXPathObjectPtr result
= NULL
;
8468 if (!context
) return IE_INVALID_CONTEXT
;
8469 zfree(context
->long_message
);
8471 /* Free output argument */
8472 isds_list_free(history
);
8475 /* Check if connection is established */
8476 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8478 /* ??? XML schema allows empty box ID, textual documentation
8479 * requries the value. */
8480 /* Allow undefined box_id */
8481 if (NULL
!= box_id
) {
8482 box_id_locale
= _isds_utf82locale((char*)box_id
);
8483 if (NULL
== box_id_locale
) {
8490 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxActivityStatus");
8491 if (NULL
== request
) {
8492 isds_printf_message(context
,
8493 _("Could not build GetDataBoxActivityStatus request "
8499 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8501 isds_log_message(context
, _("Could not create ISDS name space"));
8505 xmlSetNs(request
, isds_ns
);
8507 /* Add mandatory XSD:tIdDbInput child */
8508 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8509 /* Add times elements only when defined */
8510 /* ???: XML schema requires the values, textual documentation does not. */
8512 err
= timeval2timestring(from_time
, &string
);
8514 isds_log_message(context
,
8515 _("Could not convert `from_time' argument to ISO time "
8519 INSERT_STRING(request
, "baFrom", string
);
8523 err
= timeval2timestring(to_time
, &string
);
8525 isds_log_message(context
,
8526 _("Could not convert `to_time' argument to ISO time "
8530 INSERT_STRING(request
, "baTo", string
);
8534 /* Send request and check response*/
8535 err
= send_destroy_request_check_response(context
,
8536 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxActivityStatus",
8537 &request
, &response
, NULL
, NULL
);
8538 if (err
) goto leave
;
8542 /* Set context to the root */
8543 xpath_ctx
= xmlXPathNewContext(response
);
8548 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8552 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:GetDataBoxActivityStatusResponse",
8558 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8559 isds_log_message(context
, _("Missing GetDataBoxActivityStatusResponse element"));
8563 if (result
->nodesetval
->nodeNr
> 1) {
8564 isds_log_message(context
, _("Multiple GetDataBoxActivityStatusResponse element"));
8568 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8569 xmlXPathFreeObject(result
); result
= NULL
;
8571 /* Ignore dbID, it's the same as the input argument. */
8573 /* Extract records */
8574 if (NULL
== history
) goto leave
;
8575 result
= xmlXPathEvalExpression(BAD_CAST
"isds:Periods/isds:Period",
8581 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8582 struct isds_list
*prev_item
= NULL
;
8584 /* Iterate over all records */
8585 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8586 struct isds_list
*item
;
8588 /* Prepare structure */
8589 item
= calloc(1, sizeof(*item
));
8594 item
->destructor
= (void(*)(void**))isds_box_state_period_free
;
8595 if (i
== 0) *history
= item
;
8596 else prev_item
->next
= item
;
8600 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8601 err
= extract_Period(context
,
8602 (struct isds_box_state_period
**) (&item
->data
),
8604 if (err
) goto leave
;
8610 isds_log(ILF_ISDS
, ILL_DEBUG
,
8611 _("GetDataBoxActivityStatus request for %s box "
8612 "processed by server successfully.\n"), box_id_locale
);
8615 isds_list_free(history
);
8618 free(box_id_locale
);
8619 xmlXPathFreeObject(result
);
8620 xmlXPathFreeContext(xpath_ctx
);
8621 xmlFreeDoc(response
);
8623 #else /* not HAVE_LIBCURL */
8631 /* Get list of permissions to send commercial messages.
8632 * @context is ISDS session context.
8633 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8634 * @permissions is a reallocated list of permissions (struct
8635 * isds_commercial_permission*) to send commercial messages from @box_id. The
8636 * order of permissions is significant as the server applies the permissions
8637 * and associated pre-paid credits in the order. Empty list means no
8640 * IE_SUCCESS if the list has been obtained correctly,
8641 * or other appropriate error. */
8642 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8643 const char *box_id
, struct isds_list
**permissions
) {
8644 isds_error err
= IE_SUCCESS
;
8646 xmlDocPtr response
= NULL
;
8647 xmlXPathContextPtr xpath_ctx
= NULL
;
8648 xmlXPathObjectPtr result
= NULL
;
8651 if (!context
) return IE_INVALID_CONTEXT
;
8652 zfree(context
->long_message
);
8653 if (NULL
== permissions
) return IE_INVAL
;
8654 isds_list_free(permissions
);
8655 if (NULL
== box_id
) return IE_INVAL
;
8658 /* Check if connection is established */
8659 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8661 /* Do request and check for success */
8662 err
= build_send_dbid_request_check_response(context
,
8663 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8664 BAD_CAST box_id
, NULL
, &response
, NULL
);
8666 isds_log(ILF_ISDS
, ILL_DEBUG
,
8667 _("PDZInfo request processed by server successfully.\n"));
8671 /* Prepare structure */
8672 xpath_ctx
= xmlXPathNewContext(response
);
8677 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8682 /* Set context node */
8683 result
= xmlXPathEvalExpression(BAD_CAST
8684 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8690 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8691 struct isds_list
*prev_item
= NULL
;
8693 /* Iterate over all permission records */
8694 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8695 struct isds_list
*item
;
8697 /* Prepare structure */
8698 item
= calloc(1, sizeof(*item
));
8703 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8704 if (i
== 0) *permissions
= item
;
8705 else prev_item
->next
= item
;
8709 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8710 err
= extract_DbPDZRecord(context
,
8711 (struct isds_commercial_permission
**) (&item
->data
),
8713 if (err
) goto leave
;
8719 isds_list_free(permissions
);
8722 xmlXPathFreeObject(result
);
8723 xmlXPathFreeContext(xpath_ctx
);
8724 xmlFreeDoc(response
);
8726 #else /* not HAVE_LIBCURL */
8734 /* Get details about credit for sending pre-paid commercial messages.
8735 * @context is ISDS session context.
8736 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8737 * @from_date is first day of credit history to return in @history. Only
8738 * tm_year, tm_mon and tm_mday carry sane value.
8739 * @to_date is last day of credit history to return in @history. Only
8740 * tm_year, tm_mon and tm_mday carry sane value.
8741 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8742 * if you don't care. This and all other credit values are integers in
8743 * hundredths of Czech Crowns.
8744 * @email outputs notification e-mail address where notifications about credit
8745 * are sent. This is automatically reallocated string. Pass NULL if you don't
8746 * care. It can return NULL if no address is defined.
8747 * @history outputs auto-reallocated list of pointers to struct
8748 * isds_credit_event. Events in closed interval @from_time to @to_time are
8749 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8750 * are sorted by time.
8752 * IE_SUCCESS if the credit details have been obtained correctly,
8753 * or other appropriate error. Please note that server allows to retrieve
8754 * only limited history of events. */
8755 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8757 const struct tm
*from_date
, const struct tm
*to_date
,
8758 long int *credit
, char **email
, struct isds_list
**history
) {
8759 isds_error err
= IE_SUCCESS
;
8761 char *box_id_locale
= NULL
;
8762 xmlNodePtr request
= NULL
, node
;
8763 xmlNsPtr isds_ns
= NULL
;
8764 xmlChar
*string
= NULL
;
8766 xmlDocPtr response
= NULL
;
8767 xmlXPathContextPtr xpath_ctx
= NULL
;
8768 xmlXPathObjectPtr result
= NULL
;
8770 const xmlChar
*codes
[] = {
8778 const char *meanings
[] = {
8779 "Insufficient priviledges for the box",
8780 "The box does not exist",
8781 "Date is too long (history is not available after 15 months)",
8782 "Interval is too long (limit is 3 months)",
8785 const isds_error errors
[] = {
8792 struct code_map_isds_error map
= {
8794 .meanings
= meanings
,
8799 if (!context
) return IE_INVALID_CONTEXT
;
8800 zfree(context
->long_message
);
8802 /* Free output argument */
8803 if (NULL
!= credit
) *credit
= 0;
8804 if (NULL
!= email
) zfree(*email
);
8805 isds_list_free(history
);
8807 if (NULL
== box_id
) return IE_INVAL
;
8810 /* Check if connection is established */
8811 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8813 box_id_locale
= _isds_utf82locale((char*)box_id
);
8814 if (NULL
== box_id_locale
) {
8820 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8821 if (NULL
== request
) {
8822 isds_printf_message(context
,
8823 _("Could not build DataBoxCreditInfo request for %s box"),
8828 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8830 isds_log_message(context
, _("Could not create ISDS name space"));
8834 xmlSetNs(request
, isds_ns
);
8836 /* Add mandatory XSD:tIdDbInput child */
8837 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8838 /* Add mandatory dates elements with optional values */
8840 err
= tm2datestring(from_date
, &string
);
8842 isds_log_message(context
,
8843 _("Could not convert `from_date' argument to ISO date "
8847 INSERT_STRING(request
, "ciFromDate", string
);
8850 INSERT_STRING(request
, "ciFromDate", NULL
);
8853 err
= tm2datestring(to_date
, &string
);
8855 isds_log_message(context
,
8856 _("Could not convert `to_date' argument to ISO date "
8860 INSERT_STRING(request
, "ciTodate", string
);
8863 INSERT_STRING(request
, "ciTodate", NULL
);
8866 /* Send request and check response*/
8867 err
= send_destroy_request_check_response(context
,
8868 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8869 &request
, &response
, NULL
, &map
);
8870 if (err
) goto leave
;
8874 /* Set context to the root */
8875 xpath_ctx
= xmlXPathNewContext(response
);
8880 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8884 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8890 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8891 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8895 if (result
->nodesetval
->nodeNr
> 1) {
8896 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8900 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8901 xmlXPathFreeObject(result
); result
= NULL
;
8903 /* Extract common data */
8904 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8905 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8907 /* result gets overwritten in next step */
8908 xmlXPathFreeObject(result
); result
= NULL
;
8910 /* Extract records */
8911 if (NULL
== history
) goto leave
;
8912 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8918 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8919 struct isds_list
*prev_item
= NULL
;
8921 /* Iterate over all records */
8922 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8923 struct isds_list
*item
;
8925 /* Prepare structure */
8926 item
= calloc(1, sizeof(*item
));
8931 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8932 if (i
== 0) *history
= item
;
8933 else prev_item
->next
= item
;
8937 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8938 err
= extract_CiRecord(context
,
8939 (struct isds_credit_event
**) (&item
->data
),
8941 if (err
) goto leave
;
8947 isds_log(ILF_ISDS
, ILL_DEBUG
,
8948 _("DataBoxCreditInfo request processed by server successfully.\n"));
8951 isds_list_free(history
);
8952 if (NULL
!= email
) zfree(*email
)
8955 free(box_id_locale
);
8956 xmlXPathFreeObject(result
);
8957 xmlXPathFreeContext(xpath_ctx
);
8958 xmlFreeDoc(response
);
8960 #else /* not HAVE_LIBCURL */
8968 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8969 * code, destroy response and log success.
8970 * @context is ISDS session context.
8971 * @service_name is name of SERVICE_DB_MANIPULATION service
8972 * @box_id is UTF-8 encoded box identifier as zero terminated string
8973 * @approval is optional external approval of box manipulation
8974 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8975 * NULL, if you don't care. */
8976 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8977 struct isds_ctx
*context
, const xmlChar
*service_name
,
8978 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8979 xmlChar
**refnumber
) {
8980 isds_error err
= IE_SUCCESS
;
8982 xmlDocPtr response
= NULL
;
8985 if (!context
) return IE_INVALID_CONTEXT
;
8986 zfree(context
->long_message
);
8987 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8990 /* Check if connection is established */
8991 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8993 /* Do request and check for success */
8994 err
= build_send_dbid_request_check_response(context
,
8995 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8996 &response
, refnumber
);
8997 xmlFreeDoc(response
);
9000 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
9001 isds_log(ILF_ISDS
, ILL_DEBUG
,
9002 _("%s request processed by server successfully.\n"),
9003 service_name_locale
);
9004 free(service_name_locale
);
9006 #else /* not HAVE_LIBCURL */
9014 /* Switch box into state where box can receive commercial messages (off by
9016 * @context is ISDS session context.
9017 * @box_id is UTF-8 encoded box identifier as zero terminated string
9018 * @allow is true for enable, false for disable commercial messages income
9019 * @approval is optional external approval of box manipulation
9020 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9021 * NULL, if you don't care. */
9022 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
9023 const char *box_id
, const _Bool allow
,
9024 const struct isds_approval
*approval
, char **refnumber
) {
9025 return build_send_manipulationdbid_request_check_drop_response(context
,
9026 (allow
) ? BAD_CAST
"SetOpenAddressing" :
9027 BAD_CAST
"ClearOpenAddressing",
9028 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
9032 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
9033 * message acceptance). This is just a box permission. Sender must apply
9034 * such role by sending each message.
9035 * @context is ISDS session context.
9036 * @box_id is UTF-8 encoded box identifier as zero terminated string
9037 * @allow is true for enable, false for disable OVM role permission
9038 * @approval is optional external approval of box manipulation
9039 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9040 * NULL, if you don't care. */
9041 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
9042 const char *box_id
, const _Bool allow
,
9043 const struct isds_approval
*approval
, char **refnumber
) {
9044 return build_send_manipulationdbid_request_check_drop_response(context
,
9045 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
9046 BAD_CAST
"ClearEffectiveOVM",
9047 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
9051 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
9052 * code, destroy response and log success.
9053 * @context is ISDS session context.
9054 * @service_name is name of SERVICE_DB_MANIPULATION service
9055 * @owner is structure describing box. aifoIsds, address->adCode,
9056 * address->adDistrict members are ignored.
9057 * @approval is optional external approval of box manipulation
9058 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9059 * NULL, if you don't care. */
9060 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
9061 struct isds_ctx
*context
, const xmlChar
*service_name
,
9062 const struct isds_DbOwnerInfo
*owner
,
9063 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
9064 isds_error err
= IE_SUCCESS
;
9066 char *service_name_locale
= NULL
;
9067 xmlNodePtr request
= NULL
, db_owner_info
;
9068 xmlNsPtr isds_ns
= NULL
;
9072 if (!context
) return IE_INVALID_CONTEXT
;
9073 zfree(context
->long_message
);
9074 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
9077 service_name_locale
= _isds_utf82locale((char*)service_name
);
9078 if (!service_name_locale
) {
9084 request
= xmlNewNode(NULL
, service_name
);
9086 isds_printf_message(context
,
9087 _("Could not build %s request"), service_name_locale
);
9091 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9093 isds_log_message(context
, _("Could not create ISDS name space"));
9097 xmlSetNs(request
, isds_ns
);
9100 /* Add XSD:tOwnerInfoInput child*/
9101 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
9102 err
= insert_DbOwnerInfo(context
, owner
, 0, db_owner_info
);
9103 if (err
) goto leave
;
9105 /* Add XSD:gExtApproval*/
9106 err
= insert_GExtApproval(context
, approval
, request
);
9107 if (err
) goto leave
;
9109 /* Send it to server and process response */
9110 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
9111 service_name
, &request
, refnumber
);
9114 xmlFreeNode(request
);
9115 free(service_name_locale
);
9116 #else /* not HAVE_LIBCURL */
9124 /* Switch box accessibility state on request of box owner.
9125 * Despite the name, owner must do the request off-line. This function is
9126 * designed for such off-line meeting points (e.g. Czech POINT).
9127 * @context is ISDS session context.
9128 * @box identifies box to switch accessibility state. aifoIsds,
9129 * address->adCode, address->adDistrict members are ignored.
9130 * @allow is true for making accessible, false to disallow access.
9131 * @approval is optional external approval of box manipulation
9132 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9133 * NULL, if you don't care. */
9134 isds_error
isds_switch_box_accessibility_on_owner_request(
9135 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
9136 const _Bool allow
, const struct isds_approval
*approval
,
9138 return build_send_manipulationdbowner_request_check_drop_response(context
,
9139 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
9140 BAD_CAST
"DisableOwnDataBox",
9141 box
, approval
, (xmlChar
**) refnumber
);
9145 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
9147 * @context is ISDS session context.
9148 * @box identifies box to switch accessibility state. aifoIsds,
9149 * address->adCode, address->adDistrict members are ignored.
9150 * @since is date since accessibility has been denied. This can be past too.
9151 * Only tm_year, tm_mon and tm_mday carry sane value.
9152 * @approval is optional external approval of box manipulation
9153 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9154 * NULL, if you don't care. */
9155 isds_error
isds_disable_box_accessibility_externaly(
9156 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
9157 const struct tm
*since
, const struct isds_approval
*approval
,
9159 isds_error err
= IE_SUCCESS
;
9161 char *service_name_locale
= NULL
;
9162 xmlNodePtr request
= NULL
, node
;
9163 xmlNsPtr isds_ns
= NULL
;
9164 xmlChar
*string
= NULL
;
9168 if (!context
) return IE_INVALID_CONTEXT
;
9169 zfree(context
->long_message
);
9170 if (!box
|| !since
) return IE_INVAL
;
9174 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
9176 isds_printf_message(context
,
9177 _("Could not build %s request"), "DisableDataBoxExternally");
9181 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9183 isds_log_message(context
, _("Could not create ISDS name space"));
9187 xmlSetNs(request
, isds_ns
);
9190 /* Add @box identification */
9191 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
9192 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
9193 if (err
) goto leave
;
9195 /* Add @since date */
9196 err
= tm2datestring(since
, &string
);
9198 isds_log_message(context
,
9199 _("Could not convert `since' argument to ISO date string"));
9202 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
9206 err
= insert_GExtApproval(context
, approval
, request
);
9207 if (err
) goto leave
;
9209 /* Send it to server and process response */
9210 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
9211 BAD_CAST
"DisableDataBoxExternally", &request
,
9212 (xmlChar
**) refnumber
);
9216 xmlFreeNode(request
);
9217 free(service_name_locale
);
9218 #else /* not HAVE_LIBCURL */
9227 /* Insert struct isds_message data (envelope (recipient data optional) and
9228 * documents into XML tree
9229 * @context is session context
9230 * @outgoing_message is libisds structure with message data
9231 * @create_message is XML CreateMessage or CreateMultipleMessage element
9232 * @process_recipient true for recipient data serialization, false for no
9234 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
9235 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
9236 const _Bool process_recipient
) {
9238 isds_error err
= IE_SUCCESS
;
9239 xmlNodePtr envelope
, dm_files
, node
;
9240 xmlChar
*string
= NULL
;
9242 if (!context
) return IE_INVALID_CONTEXT
;
9243 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
9246 /* Build envelope */
9247 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
9249 isds_printf_message(context
, _("Could not add dmEnvelope child to "
9250 "%s element"), create_message
->name
);
9254 if (!outgoing_message
->envelope
) {
9255 isds_log_message(context
, _("Outgoing message is missing envelope"));
9260 /* Insert optional message type */
9261 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
9263 if (err
) goto leave
;
9265 INSERT_STRING(envelope
, "dmSenderOrgUnit",
9266 outgoing_message
->envelope
->dmSenderOrgUnit
);
9267 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
9268 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
9270 if (process_recipient
) {
9271 if (!outgoing_message
->envelope
->dbIDRecipient
) {
9272 isds_log_message(context
,
9273 _("Outgoing message is missing recipient box identifier"));
9277 INSERT_STRING(envelope
, "dbIDRecipient",
9278 outgoing_message
->envelope
->dbIDRecipient
);
9280 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
9281 outgoing_message
->envelope
->dmRecipientOrgUnit
);
9282 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
9283 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
9284 INSERT_STRING(envelope
, "dmToHands",
9285 outgoing_message
->envelope
->dmToHands
);
9288 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
9290 INSERT_STRING(envelope
, "dmAnnotation",
9291 outgoing_message
->envelope
->dmAnnotation
);
9293 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
9294 0, 50, "dmRecipientRefNumber");
9295 INSERT_STRING(envelope
, "dmRecipientRefNumber",
9296 outgoing_message
->envelope
->dmRecipientRefNumber
);
9298 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
9299 0, 50, "dmSenderRefNumber");
9300 INSERT_STRING(envelope
, "dmSenderRefNumber",
9301 outgoing_message
->envelope
->dmSenderRefNumber
);
9303 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
9304 0, 50, "dmRecipientIdent");
9305 INSERT_STRING(envelope
, "dmRecipientIdent",
9306 outgoing_message
->envelope
->dmRecipientIdent
);
9308 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
9309 0, 50, "dmSenderIdent");
9310 INSERT_STRING(envelope
, "dmSenderIdent",
9311 outgoing_message
->envelope
->dmSenderIdent
);
9313 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
9314 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
9315 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
9316 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
9317 INSERT_STRING(envelope
, "dmLegalTitleSect",
9318 outgoing_message
->envelope
->dmLegalTitleSect
);
9319 INSERT_STRING(envelope
, "dmLegalTitlePar",
9320 outgoing_message
->envelope
->dmLegalTitlePar
);
9321 INSERT_STRING(envelope
, "dmLegalTitlePoint",
9322 outgoing_message
->envelope
->dmLegalTitlePoint
);
9324 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
9325 outgoing_message
->envelope
->dmPersonalDelivery
);
9326 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
9327 outgoing_message
->envelope
->dmAllowSubstDelivery
);
9329 /* ???: Should we require value for dbEffectiveOVM sender?
9330 * ISDS has default as true */
9331 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
9332 INSERT_BOOLEAN(envelope
, "dmPublishOwnID",
9333 outgoing_message
->envelope
->dmPublishOwnID
);
9336 /* Append dmFiles */
9337 if (!outgoing_message
->documents
) {
9338 isds_log_message(context
,
9339 _("Outgoing message is missing list of documents"));
9343 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
9345 isds_printf_message(context
, _("Could not add dmFiles child to "
9346 "%s element"), create_message
->name
);
9351 /* Check for document hierarchy */
9352 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
9353 if (err
) goto leave
;
9355 /* Process each document */
9356 for (struct isds_list
*item
=
9357 (struct isds_list
*) outgoing_message
->documents
;
9358 item
; item
= item
->next
) {
9360 isds_log_message(context
,
9361 _("List of documents contains empty item"));
9365 /* FIXME: Check for dmFileMetaType and for document references.
9366 * Only first document can be of MAIN type */
9367 err
= insert_document(context
, (struct isds_document
*) item
->data
,
9370 if (err
) goto leave
;
9377 #endif /* HAVE_LIBCURL */
9380 /* Send a message via ISDS to a recipient
9381 * @context is session context
9382 * @outgoing_message is message to send; Some members are mandatory (like
9383 * dbIDRecipient), some are optional and some are irrelevant (especially data
9384 * about sender). Included pointer to isds_list documents must contain at
9385 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
9386 * members will be filled with valid data from ISDS. Exact list of write
9387 * members is subject to change. Currently dmID is changed.
9388 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
9389 isds_error
isds_send_message(struct isds_ctx
*context
,
9390 struct isds_message
*outgoing_message
) {
9392 isds_error err
= IE_SUCCESS
;
9394 xmlNsPtr isds_ns
= NULL
;
9395 xmlNodePtr request
= NULL
;
9396 xmlDocPtr response
= NULL
;
9397 xmlChar
*code
= NULL
, *message
= NULL
;
9398 xmlXPathContextPtr xpath_ctx
= NULL
;
9399 xmlXPathObjectPtr result
= NULL
;
9400 /*_Bool message_is_complete = 0;*/
9403 if (!context
) return IE_INVALID_CONTEXT
;
9404 zfree(context
->long_message
);
9405 if (!outgoing_message
) return IE_INVAL
;
9408 /* Check if connection is established
9409 * TODO: This check should be done downstairs. */
9410 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9413 /* Build CreateMessage request */
9414 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
9416 isds_log_message(context
,
9417 _("Could not build CreateMessage request"));
9420 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9422 isds_log_message(context
, _("Could not create ISDS name space"));
9423 xmlFreeNode(request
);
9426 xmlSetNs(request
, isds_ns
);
9428 /* Append envelope and files */
9429 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
9430 if (err
) goto leave
;
9433 /* Signal we can serialize message since now */
9434 /*message_is_complete = 1;*/
9437 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
9440 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9442 /* Don't' destroy request, we want to provide it to application later */
9445 isds_log(ILF_ISDS
, ILL_DEBUG
,
9446 _("Processing ISDS response on CreateMessage "
9447 "request failed\n"));
9451 /* Check for response status */
9452 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9453 &code
, &message
, NULL
);
9455 isds_log(ILF_ISDS
, ILL_DEBUG
,
9456 _("ISDS response on CreateMessage request "
9457 "is missing status\n"));
9461 /* Request processed, but refused by server or server failed */
9462 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9463 char *box_id_locale
=
9464 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9465 char *code_locale
= _isds_utf82locale((char*)code
);
9466 char *message_locale
= _isds_utf82locale((char*)message
);
9467 isds_log(ILF_ISDS
, ILL_DEBUG
,
9468 _("Server did not accept message for %s on CreateMessage "
9469 "request (code=%s, message=%s)\n"),
9470 box_id_locale
, code_locale
, message_locale
);
9471 isds_log_message(context
, message_locale
);
9472 free(box_id_locale
);
9474 free(message_locale
);
9481 xpath_ctx
= xmlXPathNewContext(response
);
9486 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9490 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
9496 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9497 isds_log_message(context
, _("Missing CreateMessageResponse element"));
9501 if (result
->nodesetval
->nodeNr
> 1) {
9502 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
9506 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9507 xmlXPathFreeObject(result
); result
= NULL
;
9509 if (outgoing_message
->envelope
->dmID
) {
9510 free(outgoing_message
->envelope
->dmID
);
9511 outgoing_message
->envelope
->dmID
= NULL
;
9513 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
9514 if (!outgoing_message
->envelope
->dmID
) {
9515 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
9516 "but did not return assigned message ID\n"));
9520 /* TODO: Serialize message into structure member raw */
9521 /* XXX: Each web service transport message in different format.
9522 * Therefore it's not possible to save them directly.
9523 * To save them, one must figure out common format.
9524 * We can leave it on application, or we can implement the ESS format. */
9525 /*if (message_is_complete) {
9526 if (outgoing_message->envelope->dmID) {
9528 /* Add assigned message ID as first child*/
9529 /*xmlNodePtr dmid_text = xmlNewText(
9530 (xmlChar *) outgoing_message->envelope->dmID);
9531 if (!dmid_text) goto serialization_failed;
9533 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9535 if (!dmid_element) {
9536 xmlFreeNode(dmid_text);
9537 goto serialization_failed;
9540 xmlNodePtr dmid_element_with_text =
9541 xmlAddChild(dmid_element, dmid_text);
9542 if (!dmid_element_with_text) {
9543 xmlFreeNode(dmid_element);
9544 xmlFreeNode(dmid_text);
9545 goto serialization_failed;
9548 node = xmlAddPrevSibling(envelope->childern,
9549 dmid_element_with_text);
9551 xmlFreeNodeList(dmid_element_with_text);
9552 goto serialization_failed;
9556 /* Serialize message with ID into raw */
9557 /*buffer = serialize_element(envelope)*/
9560 serialization_failed:
9565 xmlXPathFreeObject(result
);
9566 xmlXPathFreeContext(xpath_ctx
);
9570 xmlFreeDoc(response
);
9571 xmlFreeNode(request
);
9574 isds_log(ILF_ISDS
, ILL_DEBUG
,
9575 _("CreateMessage request processed by server "
9576 "successfully.\n"));
9577 #else /* not HAVE_LIBCURL */
9585 /* Send a message via ISDS to a multiple recipients
9586 * @context is session context
9587 * @outgoing_message is message to send; Some members are mandatory,
9588 * some are optional and some are irrelevant (especially data
9589 * about sender). Data about recipient will be substituted by ISDS from
9590 * @copies. Included pointer to isds_list documents must
9591 * contain at least one document of FILEMETATYPE_MAIN.
9592 * @copies is list of isds_message_copy structures addressing all desired
9593 * recipients. This is read-write structure, some members will be filled with
9594 * valid data from ISDS (message IDs, error codes, error descriptions).
9596 * ISDS_SUCCESS if all messages have been sent
9597 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9598 * succeeded messages can be identified by copies->data->error),
9599 * or other error code if something other goes wrong. */
9600 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
9601 const struct isds_message
*outgoing_message
,
9602 struct isds_list
*copies
) {
9604 isds_error err
= IE_SUCCESS
;
9606 isds_error append_err
;
9607 xmlNsPtr isds_ns
= NULL
;
9608 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
9609 struct isds_list
*item
;
9610 struct isds_message_copy
*copy
;
9611 xmlDocPtr response
= NULL
;
9612 xmlChar
*code
= NULL
, *message
= NULL
;
9613 xmlXPathContextPtr xpath_ctx
= NULL
;
9614 xmlXPathObjectPtr result
= NULL
;
9615 xmlChar
*string
= NULL
;
9619 if (!context
) return IE_INVALID_CONTEXT
;
9620 zfree(context
->long_message
);
9621 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9624 /* Check if connection is established
9625 * TODO: This check should be done downstairs. */
9626 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9629 /* Build CreateMultipleMessage request */
9630 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9632 isds_log_message(context
,
9633 _("Could not build CreateMultipleMessage request"));
9636 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9638 isds_log_message(context
, _("Could not create ISDS name space"));
9639 xmlFreeNode(request
);
9642 xmlSetNs(request
, isds_ns
);
9645 /* Build recipients */
9646 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9648 isds_log_message(context
, _("Could not add dmRecipients child to "
9649 "CreateMultipleMessage element"));
9650 xmlFreeNode(request
);
9654 /* Insert each recipient */
9655 for (item
= copies
; item
; item
= item
->next
) {
9656 copy
= (struct isds_message_copy
*) item
->data
;
9658 isds_log_message(context
,
9659 _("`copies' list item contains empty data"));
9664 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9666 isds_log_message(context
, _("Could not add dmRecipient child to "
9667 "dmRecipients element"));
9672 if (!copy
->dbIDRecipient
) {
9673 isds_log_message(context
,
9674 _("Message copy is missing recipient box identifier"));
9678 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9679 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9680 copy
->dmRecipientOrgUnit
);
9681 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9682 copy
->dmRecipientOrgUnitNum
, string
);
9683 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9686 /* Append envelope and files */
9687 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9688 if (err
) goto leave
;
9691 isds_log(ILF_ISDS
, ILL_DEBUG
,
9692 _("Sending CreateMultipleMessage request to ISDS\n"));
9695 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9697 isds_log(ILF_ISDS
, ILL_DEBUG
,
9698 _("Processing ISDS response on CreateMultipleMessage "
9699 "request failed\n"));
9703 /* Check for response status */
9704 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9705 &code
, &message
, NULL
);
9707 isds_log(ILF_ISDS
, ILL_DEBUG
,
9708 _("ISDS response on CreateMultipleMessage request "
9709 "is missing status\n"));
9713 /* Request processed, but some copies failed */
9714 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9715 char *box_id_locale
=
9716 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9717 char *code_locale
= _isds_utf82locale((char*)code
);
9718 char *message_locale
= _isds_utf82locale((char*)message
);
9719 isds_log(ILF_ISDS
, ILL_DEBUG
,
9720 _("Server did accept message for multiple recipients "
9721 "on CreateMultipleMessage request but delivery to "
9722 "some of them failed (code=%s, message=%s)\n"),
9723 box_id_locale
, code_locale
, message_locale
);
9724 isds_log_message(context
, message_locale
);
9725 free(box_id_locale
);
9727 free(message_locale
);
9728 err
= IE_PARTIAL_SUCCESS
;
9731 /* Request refused by server as whole */
9732 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9733 char *box_id_locale
=
9734 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9735 char *code_locale
= _isds_utf82locale((char*)code
);
9736 char *message_locale
= _isds_utf82locale((char*)message
);
9737 isds_log(ILF_ISDS
, ILL_DEBUG
,
9738 _("Server did not accept message for multiple recipients "
9739 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9740 box_id_locale
, code_locale
, message_locale
);
9741 isds_log_message(context
, message_locale
);
9742 free(box_id_locale
);
9744 free(message_locale
);
9751 xpath_ctx
= xmlXPathNewContext(response
);
9756 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9760 result
= xmlXPathEvalExpression(
9761 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9762 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9768 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9769 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9774 /* Extract message ID and delivery status for each copy */
9775 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9776 item
= item
->next
, i
++) {
9777 copy
= (struct isds_message_copy
*) item
->data
;
9778 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9780 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9786 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9787 isds_printf_message(context
, _("ISDS returned unexpected number of "
9788 "message copy delivery states: %d"),
9789 result
->nodesetval
->nodeNr
);
9798 xmlXPathFreeObject(result
);
9799 xmlXPathFreeContext(xpath_ctx
);
9803 xmlFreeDoc(response
);
9804 xmlFreeNode(request
);
9807 isds_log(ILF_ISDS
, ILL_DEBUG
,
9808 _("CreateMultipleMessageResponse request processed by server "
9809 "successfully.\n"));
9810 #else /* not HAVE_LIBCURL */
9818 /* Get list of messages. This is common core for getting sent or received
9820 * Any criterion argument can be NULL, if you don't care about it.
9821 * @context is session context. Must not be NULL.
9822 * @outgoing_direction is true if you want list of outgoing messages,
9823 * it's false if you want incoming messages.
9824 * @from_time is minimal time and date of message sending inclusive.
9825 * @to_time is maximal time and date of message sending inclusive
9826 * @organization_unit_number is number of sender/recipient respectively.
9827 * @status_filter is bit field of isds_message_status values. Use special
9828 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9829 * all values, you can use bit-wise arithmetic if you want.)
9830 * @offset is index of first message we are interested in. First message is 1.
9831 * Set to 0 (or 1) if you don't care.
9832 * @number is maximal length of list you want to get as input value, outputs
9833 * number of messages matching these criteria. Can be NULL if you don't care
9834 * (applies to output value either).
9835 * @messages is automatically reallocated list of isds_message's. Be ware that
9836 * it returns only brief overview (envelope and some other fields) about each
9837 * message, not the complete message. FIXME: Specify exact fields.
9838 * The list is sorted by delivery time in ascending order.
9839 * Use NULL if you don't care about don't need the data (useful if you want to
9840 * know only the @number). If you provide &NULL, list will be allocated on
9841 * heap, if you provide pointer to non-NULL, list will be freed automatically
9842 * at first. Also in case of error the list will be NULLed.
9843 * @return IE_SUCCESS or appropriate error code. */
9844 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9845 _Bool outgoing_direction
,
9846 const struct timeval
*from_time
, const struct timeval
*to_time
,
9847 const long int *organization_unit_number
,
9848 const unsigned int status_filter
,
9849 const unsigned long int offset
, unsigned long int *number
,
9850 struct isds_list
**messages
) {
9852 isds_error err
= IE_SUCCESS
;
9854 xmlNsPtr isds_ns
= NULL
;
9855 xmlNodePtr request
= NULL
, node
;
9856 xmlDocPtr response
= NULL
;
9857 xmlChar
*code
= NULL
, *message
= NULL
;
9858 xmlXPathContextPtr xpath_ctx
= NULL
;
9859 xmlXPathObjectPtr result
= NULL
;
9860 xmlChar
*string
= NULL
;
9864 if (!context
) return IE_INVALID_CONTEXT
;
9865 zfree(context
->long_message
);
9867 /* Free former message list if any */
9868 if (messages
) isds_list_free(messages
);
9871 /* Check if connection is established
9872 * TODO: This check should be done downstairs. */
9873 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9875 /* Build GetListOf*Messages request */
9876 request
= xmlNewNode(NULL
,
9877 (outgoing_direction
) ?
9878 BAD_CAST
"GetListOfSentMessages" :
9879 BAD_CAST
"GetListOfReceivedMessages"
9882 isds_log_message(context
,
9883 (outgoing_direction
) ?
9884 _("Could not build GetListOfSentMessages request") :
9885 _("Could not build GetListOfReceivedMessages request")
9889 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9891 isds_log_message(context
, _("Could not create ISDS name space"));
9892 xmlFreeNode(request
);
9895 xmlSetNs(request
, isds_ns
);
9899 err
= timeval2timestring(from_time
, &string
);
9900 if (err
) goto leave
;
9902 INSERT_STRING(request
, "dmFromTime", string
);
9903 free(string
); string
= NULL
;
9906 err
= timeval2timestring(to_time
, &string
);
9907 if (err
) goto leave
;
9909 INSERT_STRING(request
, "dmToTime", string
);
9910 free(string
); string
= NULL
;
9912 if (outgoing_direction
) {
9913 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9914 organization_unit_number
, string
);
9916 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9917 organization_unit_number
, string
);
9920 if (status_filter
> MESSAGESTATE_ANY
) {
9921 isds_printf_message(context
,
9922 _("Invalid message state filter value: %ld"), status_filter
);
9926 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9929 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9931 INSERT_STRING(request
, "dmOffset", "1");
9934 /* number 0 means no limit */
9935 if (number
&& *number
== 0) {
9936 INSERT_STRING(request
, "dmLimit", NULL
);
9938 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9942 isds_log(ILF_ISDS
, ILL_DEBUG
,
9943 (outgoing_direction
) ?
9944 _("Sending GetListOfSentMessages request to ISDS\n") :
9945 _("Sending GetListOfReceivedMessages request to ISDS\n")
9949 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9950 xmlFreeNode(request
); request
= NULL
;
9953 isds_log(ILF_ISDS
, ILL_DEBUG
,
9954 (outgoing_direction
) ?
9955 _("Processing ISDS response on GetListOfSentMessages "
9956 "request failed\n") :
9957 _("Processing ISDS response on GetListOfReceivedMessages "
9963 /* Check for response status */
9964 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9965 &code
, &message
, NULL
);
9967 isds_log(ILF_ISDS
, ILL_DEBUG
,
9968 (outgoing_direction
) ?
9969 _("ISDS response on GetListOfSentMessages request "
9970 "is missing status\n") :
9971 _("ISDS response on GetListOfReceivedMessages request "
9972 "is missing status\n")
9977 /* Request processed, but nothing found */
9978 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9979 char *code_locale
= _isds_utf82locale((char*)code
);
9980 char *message_locale
= _isds_utf82locale((char*)message
);
9981 isds_log(ILF_ISDS
, ILL_DEBUG
,
9982 (outgoing_direction
) ?
9983 _("Server refused GetListOfSentMessages request "
9984 "(code=%s, message=%s)\n") :
9985 _("Server refused GetListOfReceivedMessages request "
9986 "(code=%s, message=%s)\n"),
9987 code_locale
, message_locale
);
9988 isds_log_message(context
, message_locale
);
9990 free(message_locale
);
9997 xpath_ctx
= xmlXPathNewContext(response
);
10002 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10006 result
= xmlXPathEvalExpression(
10007 (outgoing_direction
) ?
10008 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
10009 "isds:dmRecords/isds:dmRecord" :
10010 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
10011 "isds:dmRecords/isds:dmRecord",
10018 /* Fill output arguments in */
10019 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10020 struct isds_envelope
*envelope
;
10021 struct isds_list
*item
= NULL
, *last_item
= NULL
;
10023 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
10024 /* Create new message */
10025 item
= calloc(1, sizeof(*item
));
10030 item
->destructor
= (void(*)(void**)) &isds_message_free
;
10031 item
->data
= calloc(1, sizeof(struct isds_message
));
10033 isds_list_free(&item
);
10038 /* Extract envelope data */
10039 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
10041 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
10043 isds_list_free(&item
);
10047 /* Attach extracted envelope */
10048 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
10050 /* Append new message into the list */
10052 *messages
= last_item
= item
;
10054 last_item
->next
= item
;
10059 if (number
) *number
= count
;
10063 isds_list_free(messages
);
10067 xmlXPathFreeObject(result
);
10068 xmlXPathFreeContext(xpath_ctx
);
10072 xmlFreeDoc(response
);
10073 xmlFreeNode(request
);
10076 isds_log(ILF_ISDS
, ILL_DEBUG
,
10077 (outgoing_direction
) ?
10078 _("GetListOfSentMessages request processed by server "
10079 "successfully.\n") :
10080 _("GetListOfReceivedMessages request processed by server "
10083 #else /* not HAVE_LIBCURL */
10090 /* Get list of outgoing (already sent) messages.
10091 * Any criterion argument can be NULL, if you don't care about it.
10092 * @context is session context. Must not be NULL.
10093 * @from_time is minimal time and date of message sending inclusive.
10094 * @to_time is maximal time and date of message sending inclusive
10095 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
10096 * @status_filter is bit field of isds_message_status values. Use special
10097 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10098 * all values, you can use bit-wise arithmetic if you want.)
10099 * @offset is index of first message we are interested in. First message is 1.
10100 * Set to 0 (or 1) if you don't care.
10101 * @number is maximal length of list you want to get as input value, outputs
10102 * number of messages matching these criteria. Can be NULL if you don't care
10103 * (applies to output value either).
10104 * @messages is automatically reallocated list of isds_message's. Be ware that
10105 * it returns only brief overview (envelope and some other fields) about each
10106 * message, not the complete message. FIXME: Specify exact fields.
10107 * The list is sorted by delivery time in ascending order.
10108 * Use NULL if you don't care about the meta data (useful if you want to know
10109 * only the @number). If you provide &NULL, list will be allocated on heap,
10110 * if you provide pointer to non-NULL, list will be freed automatically at
10111 * first. Also in case of error the list will be NULLed.
10112 * @return IE_SUCCESS or appropriate error code. */
10113 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
10114 const struct timeval
*from_time
, const struct timeval
*to_time
,
10115 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
10116 const unsigned long int offset
, unsigned long int *number
,
10117 struct isds_list
**messages
) {
10119 return isds_get_list_of_messages(
10121 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
10127 /* Get list of incoming (addressed to you) messages.
10128 * Any criterion argument can be NULL, if you don't care about it.
10129 * @context is session context. Must not be NULL.
10130 * @from_time is minimal time and date of message sending inclusive.
10131 * @to_time is maximal time and date of message sending inclusive
10132 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
10133 * @status_filter is bit field of isds_message_status values. Use special
10134 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
10135 * all values, you can use bit-wise arithmetic if you want.)
10136 * @offset is index of first message we are interested in. First message is 1.
10137 * Set to 0 (or 1) if you don't care.
10138 * @number is maximal length of list you want to get as input value, outputs
10139 * number of messages matching these criteria. Can be NULL if you don't care
10140 * (applies to output value either).
10141 * @messages is automatically reallocated list of isds_message's. Be ware that
10142 * it returns only brief overview (envelope and some other fields) about each
10143 * message, not the complete message. FIXME: Specify exact fields.
10144 * Use NULL if you don't care about the meta data (useful if you want to know
10145 * only the @number). If you provide &NULL, list will be allocated on heap,
10146 * if you provide pointer to non-NULL, list will be freed automatically at
10147 * first. Also in case of error the list will be NULLed.
10148 * @return IE_SUCCESS or appropriate error code. */
10149 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
10150 const struct timeval
*from_time
, const struct timeval
*to_time
,
10151 const long int *dmRecipientOrgUnitNum
,
10152 const unsigned int status_filter
,
10153 const unsigned long int offset
, unsigned long int *number
,
10154 struct isds_list
**messages
) {
10156 return isds_get_list_of_messages(
10158 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
10164 /* Get list of sent message state changes.
10165 * Any criterion argument can be NULL, if you don't care about it.
10166 * @context is session context. Must not be NULL.
10167 * @from_time is minimal time and date of status changes inclusive
10168 * @to_time is maximal time and date of status changes inclusive
10169 * @changed_states is automatically reallocated list of
10170 * isds_message_status_change's. If you provide &NULL, list will be allocated
10171 * on heap, if you provide pointer to non-NULL, list will be freed
10172 * automatically at first. Also in case of error the list will be NULLed.
10173 * XXX: The list item ordering is not specified.
10174 * XXX: Server provides only `recent' changes.
10175 * @return IE_SUCCESS or appropriate error code. */
10176 isds_error
isds_get_list_of_sent_message_state_changes(
10177 struct isds_ctx
*context
,
10178 const struct timeval
*from_time
, const struct timeval
*to_time
,
10179 struct isds_list
**changed_states
) {
10181 isds_error err
= IE_SUCCESS
;
10183 xmlNsPtr isds_ns
= NULL
;
10184 xmlNodePtr request
= NULL
, node
;
10185 xmlDocPtr response
= NULL
;
10186 xmlXPathContextPtr xpath_ctx
= NULL
;
10187 xmlXPathObjectPtr result
= NULL
;
10188 xmlChar
*string
= NULL
;
10192 if (!context
) return IE_INVALID_CONTEXT
;
10193 zfree(context
->long_message
);
10195 /* Free former message list if any */
10196 isds_list_free(changed_states
);
10199 /* Check if connection is established
10200 * TODO: This check should be done downstairs. */
10201 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10203 /* Build GetMessageStateChanges request */
10204 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
10206 isds_log_message(context
,
10207 _("Could not build GetMessageStateChanges request"));
10210 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10212 isds_log_message(context
, _("Could not create ISDS name space"));
10213 xmlFreeNode(request
);
10216 xmlSetNs(request
, isds_ns
);
10220 err
= timeval2timestring(from_time
, &string
);
10221 if (err
) goto leave
;
10223 INSERT_STRING(request
, "dmFromTime", string
);
10227 err
= timeval2timestring(to_time
, &string
);
10228 if (err
) goto leave
;
10230 INSERT_STRING(request
, "dmToTime", string
);
10235 err
= send_destroy_request_check_response(context
,
10236 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
10237 &response
, NULL
, NULL
);
10238 if (err
) goto leave
;
10242 xpath_ctx
= xmlXPathNewContext(response
);
10247 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10251 result
= xmlXPathEvalExpression(
10252 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
10253 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
10259 /* Fill output arguments in */
10260 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10261 struct isds_list
*item
= NULL
, *last_item
= NULL
;
10263 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
10264 /* Create new status change */
10265 item
= calloc(1, sizeof(*item
));
10271 (void(*)(void**)) &isds_message_status_change_free
;
10273 /* Extract message status change */
10274 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
10275 err
= extract_StateChangesRecord(context
,
10276 (struct isds_message_status_change
**) &item
->data
,
10279 isds_list_free(&item
);
10283 /* Append new message status change into the list */
10284 if (!*changed_states
) {
10285 *changed_states
= last_item
= item
;
10287 last_item
->next
= item
;
10295 isds_list_free(changed_states
);
10299 xmlXPathFreeObject(result
);
10300 xmlXPathFreeContext(xpath_ctx
);
10301 xmlFreeDoc(response
);
10302 xmlFreeNode(request
);
10305 isds_log(ILF_ISDS
, ILL_DEBUG
,
10306 _("GetMessageStateChanges request processed by server "
10307 "successfully.\n"));
10308 #else /* not HAVE_LIBCURL */
10316 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
10318 * @context is session context
10319 * @service is ISDS WS service handler
10320 * @service_name is name of SERVICE_DM_OPERATIONS
10321 * @message_id is message ID to send as service argument to ISDS
10322 * @response is reallocated server SOAP body response as XML document
10323 * @raw_response is reallocated bit stream with response body. Use
10324 * NULL if you don't care
10325 * @raw_response_length is size of @raw_response in bytes
10326 * @code is reallocated ISDS status code
10327 * @status_message is reallocated ISDS status message
10328 * @return error coded from lower layer, context message will be set up
10329 * appropriately. */
10330 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
10331 const isds_service service
, const xmlChar
*service_name
,
10332 const char *message_id
,
10333 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
10334 xmlChar
**code
, xmlChar
**status_message
) {
10336 isds_error err
= IE_SUCCESS
;
10337 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
10338 xmlNodePtr request
= NULL
, node
;
10339 xmlNsPtr isds_ns
= NULL
;
10341 if (!context
) return IE_INVALID_CONTEXT
;
10342 if (!service_name
|| !message_id
) return IE_INVAL
;
10343 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
10344 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
10346 /* Free output argument */
10347 xmlFreeDoc(*response
); *response
= NULL
;
10348 if (raw_response
) zfree(*raw_response
);
10350 zfree(*status_message
);
10353 /* Check if connection is established
10354 * TODO: This check should be done downstairs. */
10355 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10357 service_name_locale
= _isds_utf82locale((char*)service_name
);
10358 message_id_locale
= _isds_utf82locale(message_id
);
10359 if (!service_name_locale
|| !message_id_locale
) {
10364 /* Build request */
10365 request
= xmlNewNode(NULL
, service_name
);
10367 isds_printf_message(context
,
10368 _("Could not build %s request for %s message ID"),
10369 service_name_locale
, message_id_locale
);
10373 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10375 isds_log_message(context
, _("Could not create ISDS name space"));
10379 xmlSetNs(request
, isds_ns
);
10382 /* Add requested ID */
10383 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
10384 if (err
) goto leave
;
10385 INSERT_STRING(request
, "dmID", message_id
);
10388 isds_log(ILF_ISDS
, ILL_DEBUG
,
10389 _("Sending %s request for %s message ID to ISDS\n"),
10390 service_name_locale
, message_id_locale
);
10393 err
= _isds(context
, service
, request
, response
,
10394 raw_response
, raw_response_length
);
10395 xmlFreeNode(request
); request
= NULL
;
10398 isds_log(ILF_ISDS
, ILL_DEBUG
,
10399 _("Processing ISDS response on %s request failed\n"),
10400 service_name_locale
);
10404 /* Check for response status */
10405 err
= isds_response_status(context
, service
, *response
,
10406 code
, status_message
, NULL
);
10408 isds_log(ILF_ISDS
, ILL_DEBUG
,
10409 _("ISDS response on %s request is missing status\n"),
10410 service_name_locale
);
10414 /* Request processed, but nothing found */
10415 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
10416 char *code_locale
= _isds_utf82locale((char*) *code
);
10417 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
10418 isds_log(ILF_ISDS
, ILL_DEBUG
,
10419 _("Server refused %s request for %s message ID "
10420 "(code=%s, message=%s)\n"),
10421 service_name_locale
, message_id_locale
,
10422 code_locale
, status_message_locale
);
10423 isds_log_message(context
, status_message_locale
);
10425 free(status_message_locale
);
10431 free(message_id_locale
);
10432 free(service_name_locale
);
10433 xmlFreeNode(request
);
10438 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
10439 * signed data and free ISDS response.
10440 * @context is session context
10441 * @message_id is UTF-8 encoded message ID for logging purpose
10442 * @response is parsed XML document. It will be freed and NULLed in the middle
10443 * of function run to save memory. This is not guaranteed in case of error.
10444 * @request_name is name of ISDS request used to construct response root
10445 * element name and for logging purpose.
10446 * @raw is reallocated output buffer with DER encoded CMS data
10447 * @raw_length is size of @raw buffer in bytes
10448 * @returns standard error codes, in case of error, @raw will be freed and
10449 * NULLed, @response sometimes. */
10450 static isds_error
find_extract_signed_data_free_response(
10451 struct isds_ctx
*context
, const xmlChar
*message_id
,
10452 xmlDocPtr
*response
, const xmlChar
*request_name
,
10453 void **raw
, size_t *raw_length
) {
10455 isds_error err
= IE_SUCCESS
;
10456 char *xpath_expression
= NULL
;
10457 xmlXPathContextPtr xpath_ctx
= NULL
;
10458 xmlXPathObjectPtr result
= NULL
;
10459 char *encoded_structure
= NULL
;
10461 if (!context
) return IE_INVALID_CONTEXT
;
10462 if (!raw
) return IE_INVAL
;
10464 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
10467 /* Build XPath expression */
10468 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
10469 "Response/isds:dmSignature");
10470 if (!xpath_expression
) return IE_NOMEM
;
10473 xpath_ctx
= xmlXPathNewContext(*response
);
10478 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10482 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
10487 /* Empty response */
10488 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10489 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10490 isds_printf_message(context
,
10491 _("Server did not return any signed data for message ID `%s' "
10493 message_id_locale
, request_name
);
10494 free(message_id_locale
);
10498 /* More responses */
10499 if (result
->nodesetval
->nodeNr
> 1) {
10500 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10501 isds_printf_message(context
,
10502 _("Server did return more signed data for message ID `%s' "
10504 message_id_locale
, request_name
);
10505 free(message_id_locale
);
10510 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10512 /* Extract PKCS#7 structure */
10513 EXTRACT_STRING(".", encoded_structure
);
10514 if (!encoded_structure
) {
10515 isds_log_message(context
, _("dmSignature element is empty"));
10518 /* Here we have delivery info as standalone CMS in encoded_structure.
10519 * We don't need any other data, free them: */
10520 xmlXPathFreeObject(result
); result
= NULL
;
10521 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
10522 xmlFreeDoc(*response
); *response
= NULL
;
10525 /* Decode PKCS#7 to DER format */
10526 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
10527 if (*raw_length
== (size_t) -1) {
10528 isds_log_message(context
,
10529 _("Error while Base64-decoding PKCS#7 structure"));
10540 free(encoded_structure
);
10541 xmlXPathFreeObject(result
);
10542 xmlXPathFreeContext(xpath_ctx
);
10543 free(xpath_expression
);
10547 #endif /* HAVE_LIBCURL */
10550 /* Download incoming message envelope identified by ID.
10551 * @context is session context
10552 * @message_id is message identifier (you can get them from
10553 * isds_get_list_of_received_messages())
10554 * @message is automatically reallocated message retrieved from ISDS.
10555 * It will miss documents per se. Use isds_get_received_message(), if you are
10556 * interested in documents (content) too.
10557 * Returned hash and timestamp require documents to be verifiable. */
10558 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
10559 const char *message_id
, struct isds_message
**message
) {
10561 isds_error err
= IE_SUCCESS
;
10563 xmlDocPtr response
= NULL
;
10564 xmlChar
*code
= NULL
, *status_message
= NULL
;
10565 xmlXPathContextPtr xpath_ctx
= NULL
;
10566 xmlXPathObjectPtr result
= NULL
;
10569 if (!context
) return IE_INVALID_CONTEXT
;
10570 zfree(context
->long_message
);
10572 /* Free former message if any */
10573 if (!message
) return IE_INVAL
;
10574 isds_message_free(message
);
10577 /* Do request and check for success */
10578 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10579 BAD_CAST
"MessageEnvelopeDownload", message_id
,
10580 &response
, NULL
, NULL
, &code
, &status_message
);
10581 if (err
) goto leave
;
10584 xpath_ctx
= xmlXPathNewContext(response
);
10589 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10593 result
= xmlXPathEvalExpression(
10594 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
10595 "isds:dmReturnedMessageEnvelope",
10601 /* Empty response */
10602 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10603 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10604 isds_printf_message(context
,
10605 _("Server did not return any envelope for ID `%s' "
10606 "on MessageEnvelopeDownload request"), message_id_locale
);
10607 free(message_id_locale
);
10611 /* More envelops */
10612 if (result
->nodesetval
->nodeNr
> 1) {
10613 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10614 isds_printf_message(context
,
10615 _("Server did return more envelopes for ID `%s' "
10616 "on MessageEnvelopeDownload request"), message_id_locale
);
10617 free(message_id_locale
);
10622 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10624 /* Extract the envelope (= message without documents, hence 0) */
10625 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10626 if (err
) goto leave
;
10628 /* Save XML blob */
10629 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10630 &(*message
)->raw_length
);
10634 isds_message_free(message
);
10637 xmlXPathFreeObject(result
);
10638 xmlXPathFreeContext(xpath_ctx
);
10641 free(status_message
);
10642 if (!*message
|| !(*message
)->xml
) {
10643 xmlFreeDoc(response
);
10647 isds_log(ILF_ISDS
, ILL_DEBUG
,
10648 _("MessageEnvelopeDownload request processed by server "
10651 #else /* not HAVE_LIBCURL */
10658 /* Load delivery info of any format from buffer.
10659 * @context is session context
10660 * @raw_type advertises format of @buffer content. Only delivery info types
10662 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10663 * retrieve such data from message->raw after calling
10664 * isds_get_signed_delivery_info().
10665 * @length is length of buffer in bytes.
10666 * @message is automatically reallocated message parsed from @buffer.
10667 * @strategy selects how buffer will be attached into raw isds_message member.
10669 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10670 const isds_raw_type raw_type
,
10671 const void *buffer
, const size_t length
,
10672 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10674 isds_error err
= IE_SUCCESS
;
10675 message_ns_type message_ns
;
10676 xmlDocPtr message_doc
= NULL
;
10677 xmlXPathContextPtr xpath_ctx
= NULL
;
10678 xmlXPathObjectPtr result
= NULL
;
10679 void *xml_stream
= NULL
;
10680 size_t xml_stream_length
= 0;
10682 if (!context
) return IE_INVALID_CONTEXT
;
10683 zfree(context
->long_message
);
10684 if (!message
) return IE_INVAL
;
10685 isds_message_free(message
);
10686 if (!buffer
) return IE_INVAL
;
10689 /* Select buffer format and extract XML from CMS*/
10690 switch (raw_type
) {
10691 case RAWTYPE_DELIVERYINFO
:
10692 message_ns
= MESSAGE_NS_UNSIGNED
;
10693 xml_stream
= (void *) buffer
;
10694 xml_stream_length
= length
;
10697 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10698 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10699 xml_stream
= (void *) buffer
;
10700 xml_stream_length
= length
;
10703 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10704 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10705 err
= _isds_extract_cms_data(context
, buffer
, length
,
10706 &xml_stream
, &xml_stream_length
);
10707 if (err
) goto leave
;
10711 isds_log_message(context
, _("Bad raw delivery representation type"));
10716 if (_isds_sizet2int(xml_stream_length
) >= 0) {
10717 isds_log(ILF_ISDS
, ILL_DEBUG
,
10718 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10719 _isds_sizet2int(xml_stream_length
), xml_stream
);
10722 /* Convert delivery info XML stream into XPath context */
10723 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10724 if (!message_doc
) {
10728 xpath_ctx
= xmlXPathNewContext(message_doc
);
10733 /* XXX: Name spaces mangled for signed delivery info:
10734 * http://isds.czechpoint.cz/v20/delivery:
10736 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10738 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10739 * <p:dmID>170272</p:dmID>
10742 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10744 * </q:dmEvents>...</q:dmEvents>
10746 * </q:GetDeliveryInfoResponse>
10748 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10752 result
= xmlXPathEvalExpression(
10753 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10759 /* Empty delivery info */
10760 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10761 isds_printf_message(context
,
10762 _("XML document is not sisds:dmDelivery document"));
10766 /* More delivery info's */
10767 if (result
->nodesetval
->nodeNr
> 1) {
10768 isds_printf_message(context
,
10769 _("XML document has more sisds:dmDelivery elements"));
10773 /* One delivery info */
10774 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10776 /* Extract the envelope (= message without documents, hence 0).
10777 * XXX: extract_TReturnedMessage() can obtain attachments size,
10778 * but delivery info carries none. It's coded as option elements,
10779 * so it should work. */
10780 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10781 if (err
) goto leave
;
10783 /* Extract events */
10784 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10785 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10786 if (err
) { err
= IE_ERROR
; goto leave
; }
10787 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10788 if (err
) goto leave
;
10790 /* Append raw CMS structure into message */
10791 (*message
)->raw_type
= raw_type
;
10792 switch (strategy
) {
10793 case BUFFER_DONT_STORE
:
10796 (*message
)->raw
= malloc(length
);
10797 if (!(*message
)->raw
) {
10801 memcpy((*message
)->raw
, buffer
, length
);
10802 (*message
)->raw_length
= length
;
10805 (*message
)->raw
= (void *) buffer
;
10806 (*message
)->raw_length
= length
;
10815 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10816 isds_message_free(message
);
10819 xmlXPathFreeObject(result
);
10820 xmlXPathFreeContext(xpath_ctx
);
10821 if (!*message
|| !(*message
)->xml
) {
10822 xmlFreeDoc(message_doc
);
10824 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10827 isds_log(ILF_ISDS
, ILL_DEBUG
,
10828 _("Delivery info loaded successfully.\n"));
10833 /* Download signed delivery info-sheet of given message identified by ID.
10834 * @context is session context
10835 * @message_id is message identifier (you can get them from
10836 * isds_get_list_of_{sent,received}_messages())
10837 * @message is automatically reallocated message retrieved from ISDS.
10838 * It will miss documents per se. Use isds_get_signed_received_message(),
10839 * if you are interested in documents (content). OTOH, only this function
10840 * can get list events message has gone through. */
10841 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10842 const char *message_id
, struct isds_message
**message
) {
10844 isds_error err
= IE_SUCCESS
;
10846 xmlDocPtr response
= NULL
;
10847 xmlChar
*code
= NULL
, *status_message
= NULL
;
10849 size_t raw_length
= 0;
10852 if (!context
) return IE_INVALID_CONTEXT
;
10853 zfree(context
->long_message
);
10855 /* Free former message if any */
10856 if (!message
) return IE_INVAL
;
10857 isds_message_free(message
);
10860 /* Do request and check for success */
10861 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10862 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10863 &response
, NULL
, NULL
, &code
, &status_message
);
10864 if (err
) goto leave
;
10866 /* Find signed delivery info, extract it into raw and maybe free
10868 err
= find_extract_signed_data_free_response(context
,
10869 (xmlChar
*)message_id
, &response
,
10870 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10871 if (err
) goto leave
;
10873 /* Parse delivery info */
10874 err
= isds_load_delivery_info(context
,
10875 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10876 message
, BUFFER_MOVE
);
10877 if (err
) goto leave
;
10883 isds_message_free(message
);
10888 free(status_message
);
10889 xmlFreeDoc(response
);
10892 isds_log(ILF_ISDS
, ILL_DEBUG
,
10893 _("GetSignedDeliveryInfo request processed by server "
10896 #else /* not HAVE_LIBCURL */
10903 /* Download delivery info-sheet of given message identified by ID.
10904 * @context is session context
10905 * @message_id is message identifier (you can get them from
10906 * isds_get_list_of_{sent,received}_messages())
10907 * @message is automatically reallocated message retrieved from ISDS.
10908 * It will miss documents per se. Use isds_get_received_message(), if you are
10909 * interested in documents (content). OTOH, only this function can get list
10910 * of events message has gone through. */
10911 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10912 const char *message_id
, struct isds_message
**message
) {
10914 isds_error err
= IE_SUCCESS
;
10916 xmlDocPtr response
= NULL
;
10917 xmlChar
*code
= NULL
, *status_message
= NULL
;
10918 xmlNodePtr delivery_node
= NULL
;
10920 size_t raw_length
= 0;
10923 if (!context
) return IE_INVALID_CONTEXT
;
10924 zfree(context
->long_message
);
10926 /* Free former message if any */
10927 if (!message
) return IE_INVAL
;
10928 isds_message_free(message
);
10931 /* Do request and check for success */
10932 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10933 BAD_CAST
"GetDeliveryInfo", message_id
,
10934 &response
, NULL
, NULL
, &code
, &status_message
);
10935 if (err
) goto leave
;
10938 /* Serialize delivery info */
10939 delivery_node
= xmlDocGetRootElement(response
);
10940 if (!delivery_node
) {
10941 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10942 isds_printf_message(context
,
10943 _("Server did not return any delivery info for ID `%s' "
10944 "on GetDeliveryInfo request"), message_id_locale
);
10945 free(message_id_locale
);
10949 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10950 if (err
) goto leave
;
10952 /* Parse delivery info */
10953 /* TODO: Here we parse the response second time. We could single delivery
10954 * parser from isds_load_delivery_info() to make things faster. */
10955 err
= isds_load_delivery_info(context
,
10956 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10957 message
, BUFFER_MOVE
);
10958 if (err
) goto leave
;
10965 isds_message_free(message
);
10970 free(status_message
);
10971 xmlFreeDoc(response
);
10974 isds_log(ILF_ISDS
, ILL_DEBUG
,
10975 _("GetDeliveryInfo request processed by server "
10978 #else /* not HAVE_LIBCURL */
10985 /* Download incoming message identified by ID.
10986 * @context is session context
10987 * @message_id is message identifier (you can get them from
10988 * isds_get_list_of_received_messages())
10989 * @message is automatically reallocated message retrieved from ISDS */
10990 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10991 const char *message_id
, struct isds_message
**message
) {
10993 isds_error err
= IE_SUCCESS
;
10995 xmlDocPtr response
= NULL
;
10996 void *xml_stream
= NULL
;
10997 size_t xml_stream_length
;
10998 xmlChar
*code
= NULL
, *status_message
= NULL
;
10999 xmlXPathContextPtr xpath_ctx
= NULL
;
11000 xmlXPathObjectPtr result
= NULL
;
11001 char *phys_path
= NULL
;
11002 size_t phys_start
, phys_end
;
11005 if (!context
) return IE_INVALID_CONTEXT
;
11006 zfree(context
->long_message
);
11008 /* Free former message if any */
11009 if (NULL
== message
) return IE_INVAL
;
11010 if (message
) isds_message_free(message
);
11013 /* Do request and check for success */
11014 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
11015 BAD_CAST
"MessageDownload", message_id
,
11016 &response
, &xml_stream
, &xml_stream_length
,
11017 &code
, &status_message
);
11018 if (err
) goto leave
;
11021 xpath_ctx
= xmlXPathNewContext(response
);
11026 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11030 result
= xmlXPathEvalExpression(
11031 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
11037 /* Empty response */
11038 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11039 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11040 isds_printf_message(context
,
11041 _("Server did not return any message for ID `%s' "
11042 "on MessageDownload request"), message_id_locale
);
11043 free(message_id_locale
);
11047 /* More messages */
11048 if (result
->nodesetval
->nodeNr
> 1) {
11049 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11050 isds_printf_message(context
,
11051 _("Server did return more messages for ID `%s' "
11052 "on MessageDownload request"), message_id_locale
);
11053 free(message_id_locale
);
11058 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11060 /* Extract the message */
11061 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
11062 if (err
) goto leave
;
11064 /* Locate raw XML blob */
11065 phys_path
= strdup(
11066 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
11067 PHYSXML_ELEMENT_SEPARATOR
11068 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
11069 PHYSXML_ELEMENT_SEPARATOR
11070 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11076 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11077 phys_path
, &phys_start
, &phys_end
);
11080 isds_log_message(context
,
11081 _("Substring with isds:MessageDownloadResponse element "
11082 "could not be located in raw SOAP message"));
11085 /* Save XML blob */
11086 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
11087 &(*message)->raw_length);*/
11088 /* TODO: Store name space declarations from ancestors */
11089 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
11090 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
11091 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
11092 (*message
)->raw
= malloc((*message
)->raw_length
);
11093 if (!(*message
)->raw
) {
11097 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
11102 isds_message_free(message
);
11107 xmlXPathFreeObject(result
);
11108 xmlXPathFreeContext(xpath_ctx
);
11111 free(status_message
);
11113 if (!*message
|| !(*message
)->xml
) {
11114 xmlFreeDoc(response
);
11118 isds_log(ILF_ISDS
, ILL_DEBUG
,
11119 _("MessageDownload request processed by server "
11122 #else /* not HAVE_LIBCURL */
11129 /* Load message of any type from buffer.
11130 * @context is session context
11131 * @raw_type defines content type of @buffer. Only message types are allowed.
11132 * @buffer is message raw representation. Format (CMS, plain signed,
11133 * message direction) is defined in @raw_type. You can retrieve such data
11134 * from message->raw after calling isds_get_[signed]{received,sent}_message().
11135 * @length is length of buffer in bytes.
11136 * @message is automatically reallocated message parsed from @buffer.
11137 * @strategy selects how buffer will be attached into raw isds_message member.
11139 isds_error
isds_load_message(struct isds_ctx
*context
,
11140 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
11141 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
11143 isds_error err
= IE_SUCCESS
;
11144 void *xml_stream
= NULL
;
11145 size_t xml_stream_length
= 0;
11146 message_ns_type message_ns
;
11147 xmlDocPtr message_doc
= NULL
;
11148 xmlXPathContextPtr xpath_ctx
= NULL
;
11149 xmlXPathObjectPtr result
= NULL
;
11151 if (!context
) return IE_INVALID_CONTEXT
;
11152 zfree(context
->long_message
);
11153 if (!message
) return IE_INVAL
;
11154 isds_message_free(message
);
11155 if (!buffer
) return IE_INVAL
;
11158 /* Select buffer format and extract XML from CMS*/
11159 switch (raw_type
) {
11160 case RAWTYPE_INCOMING_MESSAGE
:
11161 message_ns
= MESSAGE_NS_UNSIGNED
;
11162 xml_stream
= (void *) buffer
;
11163 xml_stream_length
= length
;
11166 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11167 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
11168 xml_stream
= (void *) buffer
;
11169 xml_stream_length
= length
;
11172 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11173 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
11174 err
= _isds_extract_cms_data(context
, buffer
, length
,
11175 &xml_stream
, &xml_stream_length
);
11176 if (err
) goto leave
;
11179 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11180 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
11181 xml_stream
= (void *) buffer
;
11182 xml_stream_length
= length
;
11185 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11186 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
11187 err
= _isds_extract_cms_data(context
, buffer
, length
,
11188 &xml_stream
, &xml_stream_length
);
11189 if (err
) goto leave
;
11193 isds_log_message(context
, _("Bad raw message representation type"));
11198 if (_isds_sizet2int(xml_stream_length
) >= 0) {
11199 isds_log(ILF_ISDS
, ILL_DEBUG
,
11200 _("Loading message:\n%.*s\nEnd of message\n"),
11201 _isds_sizet2int(xml_stream_length
), xml_stream
);
11204 /* Convert messages XML stream into XPath context */
11205 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
11206 if (!message_doc
) {
11210 xpath_ctx
= xmlXPathNewContext(message_doc
);
11215 /* XXX: Standard name space for unsigned incoming direction:
11216 * http://isds.czechpoint.cz/v20/
11218 * XXX: Name spaces mangled for signed outgoing direction:
11219 * http://isds.czechpoint.cz/v20/SentMessage:
11221 * <q:MessageDownloadResponse
11222 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
11223 * <q:dmReturnedMessage>
11224 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11225 * <p:dmID>151916</p:dmID>
11228 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11230 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11231 * </q:dmReturnedMessage>
11232 * </q:MessageDownloadResponse>
11234 * XXX: Name spaces mangled for signed incoming direction:
11235 * http://isds.czechpoint.cz/v20/message:
11237 * <q:MessageDownloadResponse
11238 * xmlns:q="http://isds.czechpoint.cz/v20/message">
11239 * <q:dmReturnedMessage>
11240 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
11241 * <p:dmID>151916</p:dmID>
11244 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
11246 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
11247 * </q:dmReturnedMessage>
11248 * </q:MessageDownloadResponse>
11250 * Stupidity of ISDS developers is unlimited */
11251 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
11255 result
= xmlXPathEvalExpression(
11256 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
11262 /* Empty message */
11263 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11264 isds_printf_message(context
,
11265 _("XML document does not contain "
11266 "sisds:dmReturnedMessage element"));
11270 /* More messages */
11271 if (result
->nodesetval
->nodeNr
> 1) {
11272 isds_printf_message(context
,
11273 _("XML document has more sisds:dmReturnedMessage elements"));
11278 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11280 /* Extract the message */
11281 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
11282 if (err
) goto leave
;
11284 /* Append raw buffer into message */
11285 (*message
)->raw_type
= raw_type
;
11286 switch (strategy
) {
11287 case BUFFER_DONT_STORE
:
11290 (*message
)->raw
= malloc(length
);
11291 if (!(*message
)->raw
) {
11295 memcpy((*message
)->raw
, buffer
, length
);
11296 (*message
)->raw_length
= length
;
11299 (*message
)->raw
= (void *) buffer
;
11300 (*message
)->raw_length
= length
;
11310 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
11311 isds_message_free(message
);
11314 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11315 xmlXPathFreeObject(result
);
11316 xmlXPathFreeContext(xpath_ctx
);
11317 if (!*message
|| !(*message
)->xml
) {
11318 xmlFreeDoc(message_doc
);
11322 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
11327 /* Determine type of raw message or delivery info according some heuristics.
11328 * It does not validate the raw blob.
11329 * @context is session context
11330 * @raw_type returns content type of @buffer. Valid only if exit code of this
11331 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
11332 * reallocated memory.
11333 * @buffer is message raw representation.
11334 * @length is length of buffer in bytes. */
11335 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
11336 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
11338 void *xml_stream
= NULL
;
11339 size_t xml_stream_length
= 0;
11340 xmlDocPtr document
= NULL
;
11341 xmlNodePtr root
= NULL
;
11343 if (!context
) return IE_INVALID_CONTEXT
;
11344 zfree(context
->long_message
);
11345 if (length
== 0 || !buffer
) return IE_INVAL
;
11346 if (!raw_type
) return IE_INVAL
;
11349 err
= _isds_extract_cms_data(context
, buffer
, length
,
11350 &xml_stream
, &xml_stream_length
);
11352 xml_stream
= (void *) buffer
;
11353 xml_stream_length
= (size_t) length
;
11358 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
11360 isds_printf_message(context
,
11361 _("Could not parse data as XML document"));
11366 /* Get root element */
11367 root
= xmlDocGetRootElement(document
);
11369 isds_printf_message(context
,
11370 _("XML document is missing root element"));
11375 if (!root
->ns
|| !root
->ns
->href
) {
11376 isds_printf_message(context
,
11377 _("Root element does not belong to any name space"));
11382 /* Test name space */
11383 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
11384 if (xml_stream
== buffer
)
11385 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
11387 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
11388 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
11389 if (xml_stream
== buffer
)
11390 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
11392 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
11393 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
11394 if (xml_stream
== buffer
)
11395 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
11397 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
11398 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
11399 if (xml_stream
!= buffer
) {
11400 isds_printf_message(context
,
11401 _("Document in ISDS name space is encapsulated into CMS" ));
11403 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
11404 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
11405 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
11406 *raw_type
= RAWTYPE_DELIVERYINFO
;
11408 isds_printf_message(context
,
11409 _("Unknown root element in ISDS name space"));
11413 isds_printf_message(context
,
11414 _("Unknown name space"));
11419 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
11420 xmlFreeDoc(document
);
11425 /* Download signed incoming/outgoing message identified by ID.
11426 * @context is session context
11427 * @output is true for outgoing message, false for incoming message
11428 * @message_id is message identifier (you can get them from
11429 * isds_get_list_of_{sent,received}_messages())
11430 * @message is automatically reallocated message retrieved from ISDS. The raw
11431 * member will be filled with PKCS#7 structure in DER format. */
11432 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
11433 const _Bool outgoing
, const char *message_id
,
11434 struct isds_message
**message
) {
11436 isds_error err
= IE_SUCCESS
;
11438 xmlDocPtr response
= NULL
;
11439 xmlChar
*code
= NULL
, *status_message
= NULL
;
11440 xmlXPathContextPtr xpath_ctx
= NULL
;
11441 xmlXPathObjectPtr result
= NULL
;
11442 char *encoded_structure
= NULL
;
11444 size_t raw_length
= 0;
11447 if (!context
) return IE_INVALID_CONTEXT
;
11448 zfree(context
->long_message
);
11449 if (!message
) return IE_INVAL
;
11450 isds_message_free(message
);
11453 /* Do request and check for success */
11454 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
11455 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11456 BAD_CAST
"SignedMessageDownload",
11457 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11458 if (err
) goto leave
;
11460 /* Find signed message, extract it into raw and maybe free
11462 err
= find_extract_signed_data_free_response(context
,
11463 (xmlChar
*)message_id
, &response
,
11464 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
11465 BAD_CAST
"SignedMessageDownload",
11466 &raw
, &raw_length
);
11467 if (err
) goto leave
;
11469 /* Parse message */
11470 err
= isds_load_message(context
,
11471 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11472 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
11473 raw
, raw_length
, message
, BUFFER_MOVE
);
11474 if (err
) goto leave
;
11480 isds_message_free(message
);
11483 free(encoded_structure
);
11484 xmlXPathFreeObject(result
);
11485 xmlXPathFreeContext(xpath_ctx
);
11489 free(status_message
);
11490 xmlFreeDoc(response
);
11493 isds_log(ILF_ISDS
, ILL_DEBUG
,
11495 _("SignedSentMessageDownload request processed by server "
11496 "successfully.\n") :
11497 _("SignedMessageDownload request processed by server "
11500 #else /* not HAVE_LIBCURL */
11507 /* Download signed incoming message identified by ID.
11508 * @context is session context
11509 * @message_id is message identifier (you can get them from
11510 * isds_get_list_of_received_messages())
11511 * @message is automatically reallocated message retrieved from ISDS. The raw
11512 * member will be filled with PKCS#7 structure in DER format. */
11513 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
11514 const char *message_id
, struct isds_message
**message
) {
11515 return isds_get_signed_message(context
, 0, message_id
, message
);
11519 /* Download signed outgoing message identified by ID.
11520 * @context is session context
11521 * @message_id is message identifier (you can get them from
11522 * isds_get_list_of_sent_messages())
11523 * @message is automatically reallocated message retrieved from ISDS. The raw
11524 * member will be filled with PKCS#7 structure in DER format. */
11525 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
11526 const char *message_id
, struct isds_message
**message
) {
11527 return isds_get_signed_message(context
, 1, message_id
, message
);
11531 /* Get type and name of user who sent a message identified by ID.
11532 * @context is session context
11533 * @message_id is message identifier
11534 * @sender_type is pointer to automatically allocated type of sender detected
11535 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11536 * library or to the server, NULL will be returned. Pass NULL if you don't
11538 * @raw_sender_type is automatically reallocated UTF-8 string describing
11539 * sender type or NULL if not known to server. Pass NULL if you don't care.
11540 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11541 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11542 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
11543 const char *message_id
, isds_sender_type
**sender_type
,
11544 char **raw_sender_type
, char **sender_name
) {
11545 isds_error err
= IE_SUCCESS
;
11547 xmlDocPtr response
= NULL
;
11548 xmlChar
*code
= NULL
, *status_message
= NULL
;
11549 xmlXPathContextPtr xpath_ctx
= NULL
;
11550 xmlXPathObjectPtr result
= NULL
;
11551 char *type_string
= NULL
;
11554 if (!context
) return IE_INVALID_CONTEXT
;
11555 zfree(context
->long_message
);
11556 if (sender_type
) zfree(*sender_type
);
11557 if (raw_sender_type
) zfree(*raw_sender_type
);
11558 if (sender_name
) zfree(*sender_name
);
11559 if (!message_id
) return IE_INVAL
;
11562 /* Do request and check for success */
11563 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11564 BAD_CAST
"GetMessageAuthor",
11565 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11566 if (err
) goto leave
;
11569 xpath_ctx
= xmlXPathNewContext(response
);
11574 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11578 result
= xmlXPathEvalExpression(
11579 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
11584 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11585 isds_log_message(context
,
11586 _("Missing GetMessageAuthorResponse element"));
11590 if (result
->nodesetval
->nodeNr
> 1) {
11591 isds_log_message(context
,
11592 _("Multiple GetMessageAuthorResponse element"));
11596 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11597 xmlXPathFreeObject(result
); result
= NULL
;
11599 /* Fill output arguments in */
11600 EXTRACT_STRING("isds:userType", type_string
);
11601 if (NULL
!= type_string
) {
11602 if (NULL
!= sender_type
) {
11603 *sender_type
= calloc(1, sizeof(**sender_type
));
11604 if (NULL
== *sender_type
) {
11609 err
= string2isds_sender_type((xmlChar
*)type_string
,
11612 zfree(*sender_type
);
11613 if (err
== IE_ENUM
) {
11615 char *type_string_locale
= _isds_utf82locale(type_string
);
11616 isds_log(ILF_ISDS
, ILL_WARNING
,
11617 _("Unknown isds:userType value: %s"),
11618 type_string_locale
);
11619 free(type_string_locale
);
11624 if (NULL
== raw_sender_type
)
11625 zfree(type_string
);
11626 if (NULL
!= sender_name
)
11627 EXTRACT_STRING("isds:authorName", *sender_name
);
11631 if (NULL
!= sender_type
) zfree(*sender_type
);
11632 zfree(type_string
);
11633 if (NULL
!= sender_name
) zfree(*sender_name
);
11635 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11637 xmlXPathFreeObject(result
);
11638 xmlXPathFreeContext(xpath_ctx
);
11641 free(status_message
);
11642 xmlFreeDoc(response
);
11645 isds_log(ILF_ISDS
, ILL_DEBUG
,
11646 _("GetMessageAuthor request processed by server "
11647 "successfully.\n"));
11648 #else /* not HAVE_LIBCURL */
11655 /* Retrieve hash of message identified by ID stored in ISDS.
11656 * @context is session context
11657 * @message_id is message identifier
11658 * @hash is automatically reallocated message hash downloaded from ISDS.
11659 * Message must exist in system and must not be deleted. */
11660 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11661 const char *message_id
, struct isds_hash
**hash
) {
11663 isds_error err
= IE_SUCCESS
;
11665 xmlDocPtr response
= NULL
;
11666 xmlChar
*code
= NULL
, *status_message
= NULL
;
11667 xmlXPathContextPtr xpath_ctx
= NULL
;
11668 xmlXPathObjectPtr result
= NULL
;
11671 if (!context
) return IE_INVALID_CONTEXT
;
11672 zfree(context
->long_message
);
11674 isds_hash_free(hash
);
11677 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11678 BAD_CAST
"VerifyMessage", message_id
,
11679 &response
, NULL
, NULL
, &code
, &status_message
);
11680 if (err
) goto leave
;
11684 xpath_ctx
= xmlXPathNewContext(response
);
11689 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11693 result
= xmlXPathEvalExpression(
11694 BAD_CAST
"/isds:VerifyMessageResponse",
11700 /* Empty response */
11701 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11702 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11703 isds_printf_message(context
,
11704 _("Server did not return any response for ID `%s' "
11705 "on VerifyMessage request"), message_id_locale
);
11706 free(message_id_locale
);
11710 /* More responses */
11711 if (result
->nodesetval
->nodeNr
> 1) {
11712 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11713 isds_printf_message(context
,
11714 _("Server did return more responses for ID `%s' "
11715 "on VerifyMessage request"), message_id_locale
);
11716 free(message_id_locale
);
11721 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11723 /* Extract the hash */
11724 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11728 isds_hash_free(hash
);
11731 xmlXPathFreeObject(result
);
11732 xmlXPathFreeContext(xpath_ctx
);
11735 free(status_message
);
11736 xmlFreeDoc(response
);
11739 isds_log(ILF_ISDS
, ILL_DEBUG
,
11740 _("VerifyMessage request processed by server "
11743 #else /* not HAVE_LIBCURL */
11750 /* Erase message specified by @message_id from long term storage. Other
11751 * message cannot be erased on user request.
11752 * @context is session context
11753 * @message_id is message identifier.
11754 * @incoming is true for incoming message, false for outgoing message.
11756 * IE_SUCCESS if message has ben removed
11757 * IE_INVAL if message does not exist in long term storage or message
11758 * belongs to different box
11759 * TODO: IE_NOEPRM if user has no permission to erase a message */
11760 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11761 const char *message_id
, _Bool incoming
) {
11762 isds_error err
= IE_SUCCESS
;
11764 xmlNodePtr request
= NULL
, node
;
11765 xmlNsPtr isds_ns
= NULL
;
11766 xmlDocPtr response
= NULL
;
11767 xmlChar
*code
= NULL
, *status_message
= NULL
;
11770 if (!context
) return IE_INVALID_CONTEXT
;
11771 zfree(context
->long_message
);
11772 if (NULL
== message_id
) return IE_INVAL
;
11775 /* Check if connection is established
11776 * TODO: This check should be done downstairs. */
11777 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11779 /* Build request */
11780 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11782 isds_log_message(context
,
11783 _("Could build EraseMessage request"));
11786 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11788 isds_log_message(context
, _("Could not create ISDS name space"));
11789 xmlFreeNode(request
);
11792 xmlSetNs(request
, isds_ns
);
11794 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11795 if (err
) goto leave
;
11796 INSERT_STRING(request
, "dmID", message_id
);
11798 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11802 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11803 "message ID %s to ISDS\n"), message_id
);
11804 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11805 xmlFreeNode(request
); request
= NULL
;
11808 isds_log(ILF_ISDS
, ILL_DEBUG
,
11809 _("Processing ISDS response on EraseMessage request "
11814 /* Check for response status */
11815 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11816 &code
, &status_message
, NULL
);
11818 isds_log(ILF_ISDS
, ILL_DEBUG
,
11819 _("ISDS response on EraseMessage request is missing "
11824 /* Check server status code */
11825 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11826 isds_log_message(context
, _("Message to erase belongs to other box"));
11828 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11829 isds_log_message(context
, _("Message to erase is not saved in "
11830 "long term storage or the direction does not match"));
11832 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11833 char *code_locale
= _isds_utf82locale((char*) code
);
11834 char *message_locale
= _isds_utf82locale((char*) status_message
);
11835 isds_log(ILF_ISDS
, ILL_DEBUG
,
11836 _("Server refused EraseMessage request "
11837 "(code=%s, message=%s)\n"),
11838 code_locale
, message_locale
);
11839 isds_log_message(context
, message_locale
);
11841 free(message_locale
);
11848 free(status_message
);
11849 xmlFreeDoc(response
);
11850 xmlFreeNode(request
);
11853 isds_log(ILF_ISDS
, ILL_DEBUG
,
11854 _("EraseMessage request processed by server "
11857 #else /* not HAVE_LIBCURL */
11864 /* Mark message as read. This is a transactional commit function to acknowledge
11865 * to ISDS the message has been downloaded and processed by client properly.
11866 * @context is session context
11867 * @message_id is message identifier. */
11868 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11869 const char *message_id
) {
11871 isds_error err
= IE_SUCCESS
;
11873 xmlDocPtr response
= NULL
;
11874 xmlChar
*code
= NULL
, *status_message
= NULL
;
11877 if (!context
) return IE_INVALID_CONTEXT
;
11878 zfree(context
->long_message
);
11881 /* Do request and check for success */
11882 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11883 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11884 &response
, NULL
, NULL
, &code
, &status_message
);
11887 free(status_message
);
11888 xmlFreeDoc(response
);
11891 isds_log(ILF_ISDS
, ILL_DEBUG
,
11892 _("MarkMessageAsDownloaded request processed by server "
11895 #else /* not HAVE_LIBCURL */
11902 /* Mark message as received by recipient. This is applicable only to
11903 * commercial message. Use envelope->dmType message member to distinguish
11904 * commercial message from government message. Government message is
11905 * received automatically (by law), commercial message on recipient request.
11906 * @context is session context
11907 * @message_id is message identifier. */
11908 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11909 const char *message_id
) {
11911 isds_error err
= IE_SUCCESS
;
11913 xmlDocPtr response
= NULL
;
11914 xmlChar
*code
= NULL
, *status_message
= NULL
;
11917 if (!context
) return IE_INVALID_CONTEXT
;
11918 zfree(context
->long_message
);
11921 /* Do request and check for success */
11922 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11923 BAD_CAST
"ConfirmDelivery", message_id
,
11924 &response
, NULL
, NULL
, &code
, &status_message
);
11927 free(status_message
);
11928 xmlFreeDoc(response
);
11931 isds_log(ILF_ISDS
, ILL_DEBUG
,
11932 _("ConfirmDelivery request processed by server "
11935 #else /* not HAVE_LIBCURL */
11942 /* Send document for authorized conversion into Czech POINT system.
11943 * This is public anonymous service, no log-in necessary. Special context is
11944 * used to reuse keep-a-live HTTPS connection.
11945 * @context is Czech POINT session context. DO NOT use context connected to
11946 * ISDS server. Use new context or context used by this function previously.
11947 * @document is document to convert. Only data, data_length, dmFileDescr and
11948 * is_xml members are significant. Be ware that not all document formats can be
11949 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11950 * @id is reallocated identifier assigned by Czech POINT system to
11951 * your document on submit. Use is to tell it to Czech POINT officer.
11952 * @date is reallocated document submit date (submitted documents
11953 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11955 isds_error
czp_convert_document(struct isds_ctx
*context
,
11956 const struct isds_document
*document
,
11957 char **id
, struct tm
**date
) {
11958 isds_error err
= IE_SUCCESS
;
11960 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11961 xmlNodePtr request
= NULL
, node
;
11962 xmlDocPtr response
= NULL
;
11964 xmlXPathContextPtr xpath_ctx
= NULL
;
11965 xmlXPathObjectPtr result
= NULL
;
11966 long int status
= -1;
11967 long int *status_ptr
= &status
;
11968 char *string
= NULL
;
11972 if (!context
) return IE_INVALID_CONTEXT
;
11973 zfree(context
->long_message
);
11974 if (!document
|| !id
|| !date
) return IE_INVAL
;
11976 if (document
->is_xml
) {
11977 isds_log_message(context
,
11978 _("XML documents cannot be submitted to conversion"));
11982 /* Free output arguments */
11987 /* Store configuration */
11988 context
->type
= CTX_TYPE_CZP
;
11989 free(context
->url
);
11990 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11991 if (!(context
->url
))
11994 /* Prepare CURL handle if not yet connected */
11995 if (!context
->curl
) {
11996 context
->curl
= curl_easy_init();
11997 if (!(context
->curl
))
12001 /* Build conversion request */
12002 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
12004 isds_log_message(context
,
12005 _("Could not build Czech POINT conversion request"));
12008 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
12010 isds_log_message(context
,
12011 _("Could not create Czech POINT deposit name space"));
12012 xmlFreeNode(request
);
12015 xmlSetNs(request
, deposit_ns
);
12017 /* Insert children. They are in empty namespace! */
12018 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
12020 isds_log_message(context
, _("Could not create empty name space"));
12024 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
12025 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
12026 document
->dmFileDescr
);
12028 /* Document encoded in Base64 */
12029 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
12030 document
->data
, document
->data_length
);
12031 if (err
) goto leave
;
12033 isds_log(ILF_ISDS
, ILL_DEBUG
,
12034 _("Submitting document for conversion into Czech POINT deposit"));
12036 /* Send conversion request */
12037 err
= _czp_czpdeposit(context
, request
, &response
);
12038 xmlFreeNode(request
); request
= NULL
;
12041 czp_do_close_connection(context
);
12046 /* Extract response */
12047 xpath_ctx
= xmlXPathNewContext(response
);
12052 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12056 result
= xmlXPathEvalExpression(
12057 BAD_CAST
"/deposit:saveDocumentResponse/return",
12063 /* Empty response */
12064 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
12065 isds_printf_message(context
,
12066 _("Missing `return' element in Czech POINT deposit response"));
12070 /* More responses */
12071 if (result
->nodesetval
->nodeNr
> 1) {
12072 isds_printf_message(context
,
12073 _("Multiple `return' element in Czech POINT deposit response"));
12078 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
12081 EXTRACT_LONGINT("status", status_ptr
, 1);
12083 EXTRACT_STRING("statusMsg", string
);
12084 char *string_locale
= _isds_utf82locale(string
);
12085 isds_printf_message(context
,
12086 _("Czech POINT deposit refused document for conversion "
12087 "(code=%ld, message=%s)"),
12088 status
, string_locale
);
12089 free(string_locale
);
12094 /* Get document ID */
12095 EXTRACT_STRING("documentID", *id
);
12097 /* Get submit date */
12098 EXTRACT_STRING("dateInserted", string
);
12100 *date
= calloc(1, sizeof(**date
));
12105 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
12107 if (err
== IE_NOTSUP
) {
12109 char *string_locale
= _isds_utf82locale(string
);
12110 isds_printf_message(context
,
12111 _("Invalid dateInserted value: %s"), string_locale
);
12112 free(string_locale
);
12120 xmlXPathFreeObject(result
);
12121 xmlXPathFreeContext(xpath_ctx
);
12123 xmlFreeDoc(response
);
12124 xmlFreeNode(request
);
12127 char *id_locale
= _isds_utf82locale((char *) *id
);
12128 isds_log(ILF_ISDS
, ILL_DEBUG
,
12129 _("Document %s has been submitted for conversion "
12130 "to server successfully\n"), id_locale
);
12133 #else /* not HAVE_LIBCURL */
12140 /* Close possibly opened connection to Czech POINT document deposit.
12141 * @context is Czech POINT session context. */
12142 isds_error
czp_close_connection(struct isds_ctx
*context
) {
12143 if (!context
) return IE_INVALID_CONTEXT
;
12144 zfree(context
->long_message
);
12146 return czp_do_close_connection(context
);
12153 /* Send request for new box creation in testing ISDS instance.
12154 * It's not possible to request for a production box currently, as it
12155 * communicates via e-mail.
12156 * XXX: This function does not work either. Server complains about invalid
12158 * XXX: Remove context->type hacks in isds.c and validator.c when removing
12160 * @context is special session context for box creation request. DO NOT use
12161 * standard context as it could reveal your password. Use fresh new context or
12162 * context previously used by this function.
12163 * @box is box description to create including single primary user (in case of
12164 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
12165 * ignored. It outputs box ID assigned by ISDS in dbID element.
12166 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
12167 * box, or contact address of PFO box owner). The email member is mandatory as
12168 * it will be used to deliver credentials.
12169 * @former_names is former name of box owner. Pass NULL if you don't care.
12170 * @approval is optional external approval of box manipulation
12171 * @refnumber is reallocated serial number of request assigned by ISDS. Use
12172 * NULL, if you don't care.*/
12173 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
12174 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
12175 const char *former_names
, const struct isds_approval
*approval
,
12176 char **refnumber
) {
12177 isds_error err
= IE_SUCCESS
;
12179 xmlNodePtr request
= NULL
;
12180 xmlDocPtr response
= NULL
;
12181 xmlXPathContextPtr xpath_ctx
= NULL
;
12182 xmlXPathObjectPtr result
= NULL
;
12186 if (!context
) return IE_INVALID_CONTEXT
;
12187 zfree(context
->long_message
);
12188 if (!box
) return IE_INVAL
;
12191 if (!box
->email
|| box
->email
[0] == '\0') {
12192 isds_log_message(context
, _("E-mail field is mandatory"));
12196 /* Scratch box ID */
12199 /* Store configuration */
12200 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
12201 free(context
->url
);
12202 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
12203 if (!(context
->url
))
12206 /* Prepare CURL handle if not yet connected */
12207 if (!context
->curl
) {
12208 context
->curl
= curl_easy_init();
12209 if (!(context
->curl
))
12213 /* Build CreateDataBox request */
12214 err
= build_CreateDBInput_request(context
,
12215 &request
, BAD_CAST
"CreateDataBox",
12216 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
12217 if (err
) goto leave
;
12219 /* Send it to server and process response */
12220 err
= send_destroy_request_check_response(context
,
12221 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
12222 &response
, (xmlChar
**) refnumber
, NULL
);
12223 if (err
) goto leave
;
12225 /* Extract box ID */
12226 xpath_ctx
= xmlXPathNewContext(response
);
12231 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12235 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
12238 xmlXPathFreeObject(result
);
12239 xmlXPathFreeContext(xpath_ctx
);
12240 xmlFreeDoc(response
);
12241 xmlFreeNode(request
);
12244 isds_log(ILF_ISDS
, ILL_DEBUG
,
12245 _("CreateDataBox request processed by server successfully.\n"));
12247 #else /* not HAVE_LIBCURL */
12255 /* Submit CMS signed message to ISDS to verify its originality. This is
12256 * stronger form of isds_verify_message_hash() because ISDS does more checks
12257 * than simple one (potentialy old weak) hash comparison.
12258 * @context is session context
12259 * @message is memory with raw CMS signed message bit stream
12260 * @length is @message size in bytes
12262 * IE_SUCCESS if message originates in ISDS
12263 * IE_NOTEQUAL if message is unknown to ISDS
12264 * other code for other errors */
12265 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
12266 const void *message
, size_t length
) {
12267 isds_error err
= IE_SUCCESS
;
12269 xmlNsPtr isds_ns
= NULL
;
12270 xmlNodePtr request
= NULL
;
12271 xmlDocPtr response
= NULL
;
12272 xmlXPathContextPtr xpath_ctx
= NULL
;
12273 xmlXPathObjectPtr result
= NULL
;
12274 _Bool
*authentic
= NULL
;
12277 if (!context
) return IE_INVALID_CONTEXT
;
12278 zfree(context
->long_message
);
12279 if (!message
|| length
== 0) return IE_INVAL
;
12282 /* Check if connection is established
12283 * TODO: This check should be done downstairs. */
12284 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12287 /* Build AuthenticateMessage request */
12288 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
12290 isds_log_message(context
,
12291 _("Could not build AuthenticateMessage request"));
12294 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12296 isds_log_message(context
, _("Could not create ISDS name space"));
12297 xmlFreeNode(request
);
12300 xmlSetNs(request
, isds_ns
);
12302 /* Insert Base64 encoded message */
12303 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
12305 if (err
) goto leave
;
12307 /* Send request to server and process response */
12308 err
= send_destroy_request_check_response(context
,
12309 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
12310 &response
, NULL
, NULL
);
12311 if (err
) goto leave
;
12314 /* ISDS has decided */
12315 xpath_ctx
= xmlXPathNewContext(response
);
12320 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12325 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
12328 isds_log_message(context
,
12329 _("Server did not return any response on "
12330 "AuthenticateMessage request"));
12335 isds_log(ILF_ISDS
, ILL_DEBUG
,
12336 _("ISDS authenticated the message successfully\n"));
12338 isds_log_message(context
, _("ISDS does not know the message"));
12345 xmlXPathFreeObject(result
);
12346 xmlXPathFreeContext(xpath_ctx
);
12348 xmlFreeDoc(response
);
12349 xmlFreeNode(request
);
12350 #else /* not HAVE_LIBCURL */
12358 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
12359 * including adding new CMS time stamp. Only CMS blobs without time stamp can
12361 * @context is session context
12362 * @input_data is memory with raw CMS signed message or delivery info bit
12363 * stream to re-sign
12364 * @input_length is @input_data size in bytes
12365 * @output_data is pointer to auto-allocated memory where to store re-signed
12366 * input data blob. Caller must free it.
12367 * @output_data is pointer where to store @output_data size in bytes
12368 * @valid_to is pointer to auto-allocated date of time stamp expiration.
12369 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
12371 * IE_SUCCESS if CMS blob has been re-signed successfully
12372 * other code for other errors */
12373 isds_error
isds_resign_message(struct isds_ctx
*context
,
12374 const void *input_data
, size_t input_length
,
12375 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
12376 isds_error err
= IE_SUCCESS
;
12378 xmlNsPtr isds_ns
= NULL
;
12379 xmlNodePtr request
= NULL
;
12380 xmlDocPtr response
= NULL
;
12381 xmlXPathContextPtr xpath_ctx
= NULL
;
12382 xmlXPathObjectPtr result
= NULL
;
12383 char *string
= NULL
;
12384 const xmlChar
*codes
[] = {
12391 const char *meanings
[] = {
12393 "Message is not original",
12394 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
12395 "Time stamp could not been generated in time"
12397 const isds_error errors
[] = {
12403 struct code_map_isds_error map
= {
12405 .meanings
= meanings
,
12410 if (NULL
!= output_data
) *output_data
= NULL
;
12411 if (NULL
!= output_length
) *output_length
= 0;
12412 if (NULL
!= valid_to
) *valid_to
= NULL
;
12414 if (NULL
== context
) return IE_INVALID_CONTEXT
;
12415 zfree(context
->long_message
);
12416 if (NULL
== input_data
|| 0 == input_length
) {
12417 isds_log_message(context
, _("Empty CMS blob on input"));
12420 if (NULL
== output_data
|| NULL
== output_length
) {
12421 isds_log_message(context
,
12422 _("NULL pointer provided for output CMS blob"));
12427 /* Check if connection is established
12428 * TODO: This check should be done downstairs. */
12429 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
12432 /* Build Re-signISDSDocument request */
12433 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
12435 isds_log_message(context
,
12436 _("Could not build Re-signISDSDocument request"));
12439 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
12441 isds_log_message(context
, _("Could not create ISDS name space"));
12442 xmlFreeNode(request
);
12445 xmlSetNs(request
, isds_ns
);
12447 /* Insert Base64 encoded CMS blob */
12448 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
12449 input_data
, input_length
);
12450 if (err
) goto leave
;
12452 /* Send request to server and process response */
12453 err
= send_destroy_request_check_response(context
,
12454 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
12455 &response
, NULL
, &map
);
12456 if (err
) goto leave
;
12459 /* Extract re-signed data */
12460 xpath_ctx
= xmlXPathNewContext(response
);
12465 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
12469 result
= xmlXPathEvalExpression(
12470 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
12475 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
12476 isds_log_message(context
,
12477 _("Missing Re-signISDSDocumentResponse element"));
12481 if (result
->nodesetval
->nodeNr
> 1) {
12482 isds_log_message(context
,
12483 _("Multiple Re-signISDSDocumentResponse element"));
12487 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
12488 xmlXPathFreeObject(result
); result
= NULL
;
12490 EXTRACT_STRING("isds:dmResultDoc", string
);
12491 /* Decode non-empty data */
12492 if (NULL
!= string
&& string
[0] != '\0') {
12493 *output_length
= _isds_b64decode(string
, output_data
);
12494 if (*output_length
== (size_t) -1) {
12495 isds_log_message(context
,
12496 _("Error while Base64-decoding re-signed data"));
12501 isds_log_message(context
, _("Server did not send re-signed data"));
12507 if (NULL
!= valid_to
) {
12508 /* Get time stamp expiration date */
12509 EXTRACT_STRING("isds:dmValidTo", string
);
12510 if (NULL
!= string
) {
12511 *valid_to
= calloc(1, sizeof(**valid_to
));
12516 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
12518 if (err
== IE_NOTSUP
) {
12520 char *string_locale
= _isds_utf82locale(string
);
12521 isds_printf_message(context
,
12522 _("Invalid dmValidTo value: %s"), string_locale
);
12523 free(string_locale
);
12533 xmlXPathFreeObject(result
);
12534 xmlXPathFreeContext(xpath_ctx
);
12536 xmlFreeDoc(response
);
12537 xmlFreeNode(request
);
12538 #else /* not HAVE_LIBCURL */
12545 #undef INSERT_ELEMENT
12546 #undef CHECK_FOR_STRING_LENGTH
12547 #undef INSERT_STRING_ATTRIBUTE
12548 #undef INSERT_ULONGINTNOPTR
12549 #undef INSERT_ULONGINT
12550 #undef INSERT_LONGINT
12551 #undef INSERT_BOOLEAN
12552 #undef INSERT_SCALAR_BOOLEAN
12553 #undef INSERT_STRING
12554 #undef INSERT_STRING_WITH_NS
12555 #undef EXTRACT_STRING_ATTRIBUTE
12556 #undef EXTRACT_ULONGINT
12557 #undef EXTRACT_LONGINT
12558 #undef EXTRACT_BOOLEAN
12559 #undef EXTRACT_STRING
12562 /* Compute hash of message from raw representation and store it into envelope.
12563 * Original hash structure will be destroyed in envelope.
12564 * @context is session context
12565 * @message is message carrying raw XML message blob
12566 * @algorithm is desired hash algorithm to use */
12567 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
12568 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
12569 isds_error err
= IE_SUCCESS
;
12571 void *xml_stream
= NULL
;
12572 size_t xml_stream_length
;
12573 size_t phys_start
, phys_end
;
12574 char *phys_path
= NULL
;
12575 struct isds_hash
*new_hash
= NULL
;
12578 if (!context
) return IE_INVALID_CONTEXT
;
12579 zfree(context
->long_message
);
12580 if (!message
) return IE_INVAL
;
12582 if (!message
->raw
) {
12583 isds_log_message(context
,
12584 _("Message does not carry raw representation"));
12588 switch (message
->raw_type
) {
12589 case RAWTYPE_INCOMING_MESSAGE
:
12591 xml_stream
= message
->raw
;
12592 xml_stream_length
= message
->raw_length
;
12595 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
12596 nsuri
= SISDS_INCOMING_NS
;
12597 xml_stream
= message
->raw
;
12598 xml_stream_length
= message
->raw_length
;
12601 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
12602 nsuri
= SISDS_INCOMING_NS
;
12603 err
= _isds_extract_cms_data(context
,
12604 message
->raw
, message
->raw_length
,
12605 &xml_stream
, &xml_stream_length
);
12606 if (err
) goto leave
;
12609 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
12610 nsuri
= SISDS_OUTGOING_NS
;
12611 xml_stream
= message
->raw
;
12612 xml_stream_length
= message
->raw_length
;
12615 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
12616 nsuri
= SISDS_OUTGOING_NS
;
12617 err
= _isds_extract_cms_data(context
,
12618 message
->raw
, message
->raw_length
,
12619 &xml_stream
, &xml_stream_length
);
12620 if (err
) goto leave
;
12624 isds_log_message(context
, _("Bad raw representation type"));
12630 /* XXX: Hash is computed from original string representing isds:dmDm
12631 * subtree. That means no encoding, white space, xmlns attributes changes.
12632 * In other words, input for hash can be invalid XML stream. */
12633 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12634 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12635 PHYSXML_ELEMENT_SEPARATOR
,
12636 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12637 PHYSXML_ELEMENT_SEPARATOR
12638 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12642 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12643 phys_path
, &phys_start
, &phys_end
);
12646 isds_log_message(context
,
12647 _("Substring with isds:dmDM element could not be located "
12648 "in raw message"));
12654 new_hash
= calloc(1, sizeof(*new_hash
));
12659 new_hash
->algorithm
= algorithm
;
12660 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12663 isds_log_message(context
, _("Could not compute message hash"));
12667 /* Save computed hash */
12668 if (!message
->envelope
) {
12669 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12670 if (!message
->envelope
) {
12675 isds_hash_free(&message
->envelope
->hash
);
12676 message
->envelope
->hash
= new_hash
;
12680 isds_hash_free(&new_hash
);
12684 if (xml_stream
!= message
->raw
) free(xml_stream
);
12689 /* Compare two hashes.
12690 * @h1 is first hash
12691 * @h2 is another hash
12693 * IE_SUCCESS if hashes equal
12694 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12695 * IE_ENUM if not comparable, but both structures defined
12696 * IE_INVAL if some of the structures are undefined (NULL)
12697 * IE_ERROR if internal error occurs */
12698 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12699 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12700 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12701 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12702 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12703 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12705 for (size_t i
= 0; i
< h1
->length
; i
++) {
12706 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12707 return IE_NOTEQUAL
;
12713 /* Check message has gone through ISDS by comparing message hash stored in
12714 * ISDS and locally computed hash. You must provide message with valid raw
12715 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12716 * This is convenient wrapper for isds_download_message_hash(),
12717 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12718 * @context is session context
12719 * @message is message with valid raw and envelope member; envelope->hash
12720 * member will be changed during function run. Use envelope on heap only.
12722 * IE_SUCCESS if message originates in ISDS
12723 * IE_NOTEQUAL if message is unknown to ISDS
12724 * other code for other errors */
12725 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12726 struct isds_message
*message
) {
12727 isds_error err
= IE_SUCCESS
;
12728 struct isds_hash
*downloaded_hash
= NULL
;
12730 if (!context
) return IE_INVALID_CONTEXT
;
12731 zfree(context
->long_message
);
12732 if (!message
) return IE_INVAL
;
12734 if (!message
->envelope
) {
12735 isds_log_message(context
,
12736 _("Given message structure is missing envelope"));
12739 if (!message
->raw
) {
12740 isds_log_message(context
,
12741 _("Given message structure is missing raw representation"));
12745 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12747 if (err
) goto leave
;
12749 err
= isds_compute_message_hash(context
, message
,
12750 downloaded_hash
->algorithm
);
12751 if (err
) goto leave
;
12753 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12756 isds_hash_free(&downloaded_hash
);
12761 /* Search for document by document ID in list of documents. IDs are compared
12763 * @documents is list of isds_documents
12764 * @id is document identifier
12765 * @return first matching document or NULL. */
12766 const struct isds_document
*isds_find_document_by_id(
12767 const struct isds_list
*documents
, const char *id
) {
12768 const struct isds_list
*item
;
12769 const struct isds_document
*document
;
12771 for (item
= documents
; item
; item
= item
->next
) {
12772 document
= (struct isds_document
*) item
->data
;
12773 if (!document
) continue;
12775 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12783 /* Normalize @mime_type to be proper MIME type.
12784 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12785 * guess regular MIME type (e.g. "application/pdf").
12786 * @mime_type is UTF-8 encoded MIME type to fix
12787 * @return original @mime_type if no better interpretation exists, or
12788 * constant static UTF-8 encoded string with proper MIME type. */
12789 const char *isds_normalize_mime_type(const char *mime_type
) {
12790 if (!mime_type
) return NULL
;
12792 for (size_t offset
= 0;
12793 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12795 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12796 extension_map_mime
[offset
]))
12797 return (const char *) extension_map_mime
[offset
+ 1];
12804 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12805 struct isds_message **message);
12806 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12807 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12808 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12809 struct isds_address **address);
12811 int isds_message_free(struct isds_message **message);
12812 int isds_address_free(struct isds_address **address);
12816 /* Makes known all relevant namespaces to given XPath context
12817 * @xpath_ctx is XPath context
12818 * @message_ns selects proper message name space. Unsigned and signed
12819 * messages and delivery info's differ in prefix and URI. */
12820 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12821 const message_ns_type message_ns
) {
12822 const xmlChar
*message_namespace
= NULL
;
12824 if (!xpath_ctx
) return IE_ERROR
;
12826 switch(message_ns
) {
12828 message_namespace
= BAD_CAST ISDS1_NS
; break;
12829 case MESSAGE_NS_UNSIGNED
:
12830 message_namespace
= BAD_CAST ISDS_NS
; break;
12831 case MESSAGE_NS_SIGNED_INCOMING
:
12832 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12833 case MESSAGE_NS_SIGNED_OUTGOING
:
12834 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12835 case MESSAGE_NS_SIGNED_DELIVERY
:
12836 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12841 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12843 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12845 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12847 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12849 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12851 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))