7 #include <stdint.h> /* For uint8_t and intmax_t */
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include <inttypes.h> /* For PRIdMAX formatting macro */
14 #include "validator.h"
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities
;
22 isds_log_level log_level
;
23 isds_log_callback log_callback
;
24 void *log_callback_data
;
25 const char *version_gpgme
= N_("n/a");
26 const char *version_gcrypt
= N_("n/a");
27 const char *version_openssl
= N_("n/a");
28 const char *version_expat
= N_("n/a");
31 /* Base URL of production ISDS instance */
32 const char isds_locator
[] = "https://ws1.mojedatovaschranka.cz/";
33 const char isds_cert_locator
[] = "https://ws1c.mojedatovaschranka.cz/";
34 const char isds_otp_locator
[] = "https://www.mojedatovaschranka.cz/";
36 /* Base URL of production ISDS instance */
37 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
38 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
39 const char isds_otp_testing_locator
[] = "https://www.czebox.cz/";
41 /* Extension to MIME type map */
42 static const xmlChar
*extension_map_mime
[] = {
43 BAD_CAST
"cer", BAD_CAST
"application/x-x509-ca-cert",
44 BAD_CAST
"crt", BAD_CAST
"application/x-x509-ca-cert",
45 BAD_CAST
"der", BAD_CAST
"application/x-x509-ca-cert",
46 BAD_CAST
"doc", BAD_CAST
"application/msword",
47 BAD_CAST
"docx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
48 "wordprocessingml.document",
49 BAD_CAST
"dbf", BAD_CAST
"application/octet-stream",
50 BAD_CAST
"prj", BAD_CAST
"application/octet-stream",
51 BAD_CAST
"qix", BAD_CAST
"application/octet-stream",
52 BAD_CAST
"sbn", BAD_CAST
"application/octet-stream",
53 BAD_CAST
"sbx", BAD_CAST
"application/octet-stream",
54 BAD_CAST
"shp", BAD_CAST
"application/octet-stream",
55 BAD_CAST
"shx", BAD_CAST
"application/octet-stream",
56 BAD_CAST
"dgn", BAD_CAST
"application/octet-stream",
57 BAD_CAST
"dwg", BAD_CAST
"image/vnd.dwg",
58 BAD_CAST
"edi", BAD_CAST
"application/edifact",
59 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.form+xml",
60 BAD_CAST
"gfs", BAD_CAST
"application/xml",
61 BAD_CAST
"gml", BAD_CAST
"application/xml",
62 BAD_CAST
"gif", BAD_CAST
"image/gif",
63 BAD_CAST
"htm", BAD_CAST
"text/html",
64 BAD_CAST
"html", BAD_CAST
"text/html",
65 BAD_CAST
"isdoc", BAD_CAST
"text/isdoc",
66 BAD_CAST
"isdocx", BAD_CAST
"text/isdocx",
67 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
68 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
69 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
70 BAD_CAST
"mpeg", BAD_CAST
"video/mpeg",
71 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
72 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg",
73 BAD_CAST
"mpg", BAD_CAST
"video/mpeg",
74 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
75 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
76 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
77 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
78 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
79 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
80 BAD_CAST
"p7b", BAD_CAST
"application/pkcs7-certificates",
81 BAD_CAST
"p7c", BAD_CAST
"application/pkcs7-mime",
82 BAD_CAST
"p7m", BAD_CAST
"application/pkcs7-mime",
83 BAD_CAST
"p7f", BAD_CAST
"application/pkcs7-signature",
84 BAD_CAST
"p7s", BAD_CAST
"application/pkcs7-signature",
85 BAD_CAST
"pk7", BAD_CAST
"application/pkcs7-mime",
86 BAD_CAST
"png", BAD_CAST
"image/png",
87 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
88 BAD_CAST
"pptx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
89 "presentationml.presentation",
90 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
91 BAD_CAST
"tif", BAD_CAST
"image/tiff",
92 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
93 BAD_CAST
"tsr", BAD_CAST
"application/timestamp-reply",
94 BAD_CAST
"tst", BAD_CAST
"application/timestamp-reply",
95 BAD_CAST
"txt", BAD_CAST
"text/plain",
96 BAD_CAST
"wav", BAD_CAST
"audio/wav",
97 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
98 BAD_CAST
"xlsx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
99 "spreadsheetml.sheet",
100 BAD_CAST
"xml", BAD_CAST
"application/xml",
101 BAD_CAST
"xsd", BAD_CAST
"application/xml",
102 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.form-xml-zip"
105 /* Structure type to hold conversion table from status code to isds_error and
107 struct code_map_isds_error
{
108 const xmlChar
**codes
; /* NULL terminated array of status codes */
109 const char **meanings
; /* Mapping to non-localized long messages */
110 const isds_error
*errors
; /* Mapping to isds_error code */
113 /* Deallocate structure isds_pki_credentials and NULL it.
114 * Pass-phrase is discarded.
115 * @pki credentials to to free */
116 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
117 if(!pki
|| !*pki
) return;
119 free((*pki
)->engine
);
120 free((*pki
)->certificate
);
123 if ((*pki
)->passphrase
) {
124 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
125 free((*pki
)->passphrase
);
132 /* Free isds_list with all member data.
133 * @list list to free, on return will be NULL */
134 void isds_list_free(struct isds_list
**list
) {
135 struct isds_list
*item
, *next_item
;
137 if (!list
|| !*list
) return;
139 for(item
= *list
; item
; item
= next_item
) {
140 if (item
->destructor
) (item
->destructor
)(&(item
->data
));
141 next_item
= item
->next
;
149 /* Deallocate structure isds_hash and NULL it.
150 * @hash hash to to free */
151 void isds_hash_free(struct isds_hash
**hash
) {
152 if(!hash
|| !*hash
) return;
153 free((*hash
)->value
);
158 /* Deallocate structure isds_PersonName recursively and NULL it */
159 void isds_PersonName_free(struct isds_PersonName
**person_name
) {
160 if (!person_name
|| !*person_name
) return;
162 free((*person_name
)->pnFirstName
);
163 free((*person_name
)->pnMiddleName
);
164 free((*person_name
)->pnLastName
);
165 free((*person_name
)->pnLastNameAtBirth
);
172 /* Deallocate structure isds_BirthInfo recursively and NULL it */
173 void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
174 if (!birth_info
|| !*birth_info
) return;
176 free((*birth_info
)->biDate
);
177 free((*birth_info
)->biCity
);
178 free((*birth_info
)->biCounty
);
179 free((*birth_info
)->biState
);
186 /* Deallocate structure isds_Address recursively and NULL it */
187 void isds_Address_free(struct isds_Address
**address
) {
188 if (!address
|| !*address
) return;
190 free((*address
)->adCode
);
191 free((*address
)->adCity
);
192 free((*address
)->adDistrict
);
193 free((*address
)->adStreet
);
194 free((*address
)->adNumberInStreet
);
195 free((*address
)->adNumberInMunicipality
);
196 free((*address
)->adZipCode
);
197 free((*address
)->adState
);
204 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
205 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
206 if (!db_owner_info
|| !*db_owner_info
) return;
208 free((*db_owner_info
)->dbID
);
209 free((*db_owner_info
)->dbType
);
210 free((*db_owner_info
)->ic
);
211 isds_PersonName_free(&((*db_owner_info
)->personName
));
212 free((*db_owner_info
)->firmName
);
213 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
214 isds_Address_free(&((*db_owner_info
)->address
));
215 free((*db_owner_info
)->nationality
);
216 free((*db_owner_info
)->email
);
217 free((*db_owner_info
)->telNumber
);
218 free((*db_owner_info
)->identifier
);
219 free((*db_owner_info
)->aifoIsds
);
220 free((*db_owner_info
)->registryCode
);
221 free((*db_owner_info
)->dbState
);
222 free((*db_owner_info
)->dbEffectiveOVM
);
223 free((*db_owner_info
)->dbOpenAddressing
);
225 free(*db_owner_info
);
226 *db_owner_info
= NULL
;
229 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
230 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
231 if (!db_user_info
|| !*db_user_info
) return;
233 free((*db_user_info
)->userID
);
234 free((*db_user_info
)->userType
);
235 free((*db_user_info
)->userPrivils
);
236 isds_PersonName_free(&((*db_user_info
)->personName
));
237 isds_Address_free(&((*db_user_info
)->address
));
238 free((*db_user_info
)->biDate
);
239 free((*db_user_info
)->ic
);
240 free((*db_user_info
)->firmName
);
241 free((*db_user_info
)->caStreet
);
242 free((*db_user_info
)->caCity
);
243 free((*db_user_info
)->caZipCode
);
244 free((*db_user_info
)->caState
);
245 free((*db_user_info
)->aifo_ticket
);
247 zfree(*db_user_info
);
251 /* Deallocate struct isds_event recursively and NULL it */
252 void isds_event_free(struct isds_event
**event
) {
253 if (!event
|| !*event
) return;
255 free((*event
)->time
);
256 free((*event
)->type
);
257 free((*event
)->description
);
262 /* Deallocate struct isds_envelope recursively and NULL it */
263 void isds_envelope_free(struct isds_envelope
**envelope
) {
264 if (!envelope
|| !*envelope
) return;
266 free((*envelope
)->dmID
);
267 free((*envelope
)->dbIDSender
);
268 free((*envelope
)->dmSender
);
269 free((*envelope
)->dmSenderAddress
);
270 free((*envelope
)->dmSenderType
);
271 free((*envelope
)->dmRecipient
);
272 free((*envelope
)->dmRecipientAddress
);
273 free((*envelope
)->dmAmbiguousRecipient
);
274 free((*envelope
)->dmType
);
276 free((*envelope
)->dmOrdinal
);
277 free((*envelope
)->dmMessageStatus
);
278 free((*envelope
)->dmDeliveryTime
);
279 free((*envelope
)->dmAcceptanceTime
);
280 isds_hash_free(&(*envelope
)->hash
);
281 free((*envelope
)->timestamp
);
282 isds_list_free(&(*envelope
)->events
);
284 free((*envelope
)->dmSenderOrgUnit
);
285 free((*envelope
)->dmSenderOrgUnitNum
);
286 free((*envelope
)->dbIDRecipient
);
287 free((*envelope
)->dmRecipientOrgUnit
);
288 free((*envelope
)->dmRecipientOrgUnitNum
);
289 free((*envelope
)->dmToHands
);
290 free((*envelope
)->dmAnnotation
);
291 free((*envelope
)->dmRecipientRefNumber
);
292 free((*envelope
)->dmSenderRefNumber
);
293 free((*envelope
)->dmRecipientIdent
);
294 free((*envelope
)->dmSenderIdent
);
296 free((*envelope
)->dmLegalTitleLaw
);
297 free((*envelope
)->dmLegalTitleYear
);
298 free((*envelope
)->dmLegalTitleSect
);
299 free((*envelope
)->dmLegalTitlePar
);
300 free((*envelope
)->dmLegalTitlePoint
);
302 free((*envelope
)->dmPersonalDelivery
);
303 free((*envelope
)->dmAllowSubstDelivery
);
305 free((*envelope
)->dmOVM
);
306 free((*envelope
)->dmPublishOwnID
);
313 /* Deallocate struct isds_message recursively and NULL it */
314 void isds_message_free(struct isds_message
**message
) {
315 if (!message
|| !*message
) return;
317 free((*message
)->raw
);
318 isds_envelope_free(&((*message
)->envelope
));
319 isds_list_free(&((*message
)->documents
));
320 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
327 /* Deallocate struct isds_document recursively and NULL it */
328 void isds_document_free(struct isds_document
**document
) {
329 if (!document
|| !*document
) return;
331 if (!(*document
)->is_xml
) {
332 free((*document
)->data
);
334 free((*document
)->dmMimeType
);
335 free((*document
)->dmFileGuid
);
336 free((*document
)->dmUpFileGuid
);
337 free((*document
)->dmFileDescr
);
338 free((*document
)->dmFormat
);
345 /* Deallocate struct isds_message_copy recursively and NULL it */
346 void isds_message_copy_free(struct isds_message_copy
**copy
) {
347 if (!copy
|| !*copy
) return;
349 free((*copy
)->dbIDRecipient
);
350 free((*copy
)->dmRecipientOrgUnit
);
351 free((*copy
)->dmRecipientOrgUnitNum
);
352 free((*copy
)->dmToHands
);
354 free((*copy
)->dmStatus
);
361 /* Deallocate struct isds_message_status_change recursively and NULL it */
362 void isds_message_status_change_free(
363 struct isds_message_status_change
**message_status_change
) {
364 if (!message_status_change
|| !*message_status_change
) return;
366 free((*message_status_change
)->dmID
);
367 free((*message_status_change
)->time
);
368 free((*message_status_change
)->dmMessageStatus
);
370 zfree(*message_status_change
);
374 /* Deallocate struct isds_approval recursively and NULL it */
375 void isds_approval_free(struct isds_approval
**approval
) {
376 if (!approval
|| !*approval
) return;
378 free((*approval
)->refference
);
384 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
385 * The email string is deallocated too. */
386 void isds_credentials_delivery_free(
387 struct isds_credentials_delivery
**credentials_delivery
) {
388 if (!credentials_delivery
|| !*credentials_delivery
) return;
390 free((*credentials_delivery
)->email
);
391 free((*credentials_delivery
)->token
);
392 free((*credentials_delivery
)->new_user_name
);
394 zfree(*credentials_delivery
);
398 /* Deallocate struct isds_commercial_permission recursively and NULL it */
399 void isds_commercial_permission_free(
400 struct isds_commercial_permission
**permission
) {
401 if (NULL
== permission
|| NULL
== *permission
) return;
403 free((*permission
)->recipient
);
404 free((*permission
)->payer
);
405 free((*permission
)->expiration
);
406 free((*permission
)->count
);
407 free((*permission
)->reply_identifier
);
413 /* Deallocate struct isds_credit_event recursively and NULL it */
414 void isds_credit_event_free(struct isds_credit_event
**event
) {
415 if (NULL
== event
|| NULL
== *event
) return;
417 free((*event
)->time
);
418 switch ((*event
)->type
) {
419 case ISDS_CREDIT_CHARGED
:
420 free((*event
)->details
.charged
.transaction
);
422 case ISDS_CREDIT_DISCHARGED
:
423 free((*event
)->details
.discharged
.transaction
);
425 case ISDS_CREDIT_MESSAGE_SENT
:
426 free((*event
)->details
.message_sent
.recipient
);
427 free((*event
)->details
.message_sent
.message_id
);
429 case ISDS_CREDIT_STORAGE_SET
:
430 free((*event
)->details
.storage_set
.new_valid_from
);
431 free((*event
)->details
.storage_set
.new_valid_to
);
432 free((*event
)->details
.storage_set
.old_capacity
);
433 free((*event
)->details
.storage_set
.old_valid_from
);
434 free((*event
)->details
.storage_set
.old_valid_to
);
435 free((*event
)->details
.storage_set
.initiator
);
437 case ISDS_CREDIT_EXPIRED
:
445 /* Deallocate struct isds_fulltext_result recursively and NULL it */
446 void isds_fulltext_result_free(
447 struct isds_fulltext_result
**result
) {
448 if (NULL
== result
|| NULL
== *result
) return;
450 free((*result
)->dbID
);
451 free((*result
)->name
);
452 isds_list_free(&((*result
)->name_match_start
));
453 isds_list_free(&((*result
)->name_match_end
));
454 free((*result
)->address
);
455 isds_list_free(&((*result
)->address_match_start
));
456 isds_list_free(&((*result
)->address_match_end
));
458 free((*result
)->biDate
);
464 /* *DUP_OR_ERROR macros needs error label */
465 #define STRDUP_OR_ERROR(new, template) { \
469 (new) = strdup(template); \
470 if (!new) goto error; \
474 #define FLATDUP_OR_ERROR(new, template) { \
478 (new) = malloc(sizeof(*(new))); \
479 if (!new) goto error; \
480 memcpy((new), (template), sizeof(*(template))); \
484 /* Copy structure isds_pki_credentials recursively. */
485 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
486 const struct isds_pki_credentials
*template) {
487 struct isds_pki_credentials
*new = NULL
;
489 if(!template) return NULL
;
491 new = calloc(1, sizeof(*new));
492 if (!new) return NULL
;
494 STRDUP_OR_ERROR(new->engine
, template->engine
);
495 new->certificate_format
= template->certificate_format
;
496 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
497 new->key_format
= template->key_format
;
498 STRDUP_OR_ERROR(new->key
, template->key
);
499 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
504 isds_pki_credentials_free(&new);
509 /* Copy structure isds_PersonName recursively */
510 struct isds_PersonName
*isds_PersonName_duplicate(
511 const struct isds_PersonName
*src
) {
512 struct isds_PersonName
*new = NULL
;
514 if (!src
) return NULL
;
516 new = calloc(1, sizeof(*new));
517 if (!new) return NULL
;
519 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
520 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
521 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
522 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
527 isds_PersonName_free(&new);
532 /* Copy structure isds_BirthInfo recursively */
533 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
534 const struct isds_BirthInfo
*template) {
535 struct isds_BirthInfo
*new = NULL
;
537 if (!template) return NULL
;
539 new = calloc(1, sizeof(*new));
540 if (!new) return NULL
;
542 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
543 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
544 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
545 STRDUP_OR_ERROR(new->biState
, template->biState
);
550 isds_BirthInfo_free(&new);
555 /* Copy structure isds_Address recursively */
556 struct isds_Address
*isds_Address_duplicate(
557 const struct isds_Address
*src
) {
558 struct isds_Address
*new = NULL
;
560 if (!src
) return NULL
;
562 new = calloc(1, sizeof(*new));
563 if (!new) return NULL
;
565 FLATDUP_OR_ERROR(new->adCode
, src
->adCode
);
566 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
567 STRDUP_OR_ERROR(new->adDistrict
, src
->adDistrict
);
568 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
569 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
570 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
571 src
->adNumberInMunicipality
);
572 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
573 STRDUP_OR_ERROR(new->adState
, src
->adState
);
578 isds_Address_free(&new);
583 /* Copy structure isds_DbOwnerInfo recursively */
584 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
585 const struct isds_DbOwnerInfo
*src
) {
586 struct isds_DbOwnerInfo
*new = NULL
;
587 if (!src
) return NULL
;
589 new = calloc(1, sizeof(*new));
590 if (!new) return NULL
;
592 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
593 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
594 STRDUP_OR_ERROR(new->ic
, src
->ic
);
596 if (src
->personName
) {
597 if (!(new->personName
=
598 isds_PersonName_duplicate(src
->personName
)))
602 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
604 if (src
->birthInfo
) {
605 if (!(new->birthInfo
=
606 isds_BirthInfo_duplicate(src
->birthInfo
)))
611 if (!(new->address
= isds_Address_duplicate(src
->address
)))
615 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
616 STRDUP_OR_ERROR(new->email
, src
->email
);
617 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
618 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
619 FLATDUP_OR_ERROR(new->aifoIsds
, src
->aifoIsds
);
620 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
621 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
622 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
623 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
628 isds_DbOwnerInfo_free(&new);
633 /* Copy structure isds_DbUserInfo recursively */
634 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
635 const struct isds_DbUserInfo
*src
) {
636 struct isds_DbUserInfo
*new = NULL
;
637 if (!src
) return NULL
;
639 new = calloc(1, sizeof(*new));
640 if (!new) return NULL
;
642 STRDUP_OR_ERROR(new->userID
, src
->userID
);
643 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
644 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
646 if (src
->personName
) {
647 if (!(new->personName
=
648 isds_PersonName_duplicate(src
->personName
)))
653 if (!(new->address
= isds_Address_duplicate(src
->address
)))
657 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
658 STRDUP_OR_ERROR(new->ic
, src
->ic
);
659 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
660 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
661 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
662 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
663 STRDUP_OR_ERROR(new->caState
, src
->caState
);
664 STRDUP_OR_ERROR(new->aifo_ticket
, src
->aifo_ticket
);
669 isds_DbUserInfo_free(&new);
673 #undef FLATDUP_OR_ERROR
674 #undef STRDUP_OR_ERROR
677 /* Logs libxml2 errors. Should be registered to libxml2 library.
678 * @ctx is unused currently
679 * @msg is printf-like formated message from libxml2 (UTF-8?)
680 * @... are variadic arguments for @msg */
681 static void log_xml(void *ctx
, const char *msg
, ...) {
685 /* Silent warning for unused function argument.
686 * The prototype is an API of libxml2's xmlSetGenericErrorFunc(). */
692 isds_vasprintf(&text
, msg
, ap
);
696 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
701 /* Initialize ISDS library.
702 * Global function, must be called before other functions.
703 * If it fails you can not use ISDS library and must call isds_cleanup() to
704 * free partially initialized global variables. */
705 isds_error
isds_init(void) {
706 /* NULL global variables */
707 log_facilities
= ILF_ALL
;
708 log_level
= ILL_WARNING
;
710 log_callback_data
= NULL
;
713 /* Initialize gettext */
714 bindtextdomain(PACKAGE
, LOCALEDIR
);
718 /* Initialize CURL */
719 if (curl_global_init(CURL_GLOBAL_ALL
)) {
720 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
723 #endif /* HAVE_LIBCURL */
725 /* Initialise cryptographic back-ends. */
726 if (IE_SUCCESS
!= _isds_init_crypto()) {
727 isds_log(ILF_ISDS
, ILL_CRIT
,
728 _("Initialization of cryptographic back-end failed\n"));
732 /* This can _exit() current program. Find not so assertive check. */
734 xmlSetGenericErrorFunc(NULL
, log_xml
);
737 if (_isds_init_expat(&version_expat
)) {
738 isds_log(ILF_ISDS
, ILL_CRIT
,
739 _("expat library initialization failed\n"));
743 /* Allocate global variables */
750 /* Deinitialize ISDS library.
751 * Global function, must be called as last library function. */
752 isds_error
isds_cleanup(void) {
758 curl_global_cleanup();
765 /* Return version string of this library. Version of dependencies can be
766 * embedded. Do no try to parse it. You must free it. */
767 char *isds_version(void) {
770 isds_asprintf(&buffer
,
772 # ifndef USE_OPENSSL_BACKEND
773 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
775 _("%s (%s, %s, %s, libxml2 %s)"),
778 # ifndef USE_OPENSSL_BACKEND
779 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
781 _("%s (%s, %s, libxml2 %s)"),
788 #ifndef USE_OPENSSL_BACKEND
789 version_gpgme
, version_gcrypt
,
793 version_expat
, xmlParserVersion
);
798 /* Return text description of ISDS error */
799 const char *isds_strerror(const isds_error error
) {
802 return(_("Success")); break;
804 return(_("Unspecified error")); break;
806 return(_("Not supported")); break;
808 return(_("Invalid value")); break;
809 case IE_INVALID_CONTEXT
:
810 return(_("Invalid context")); break;
811 case IE_NOT_LOGGED_IN
:
812 return(_("Not logged in")); break;
813 case IE_CONNECTION_CLOSED
:
814 return(_("Connection closed")); break;
816 return(_("Timed out")); break;
818 return(_("Not exist")); break;
820 return(_("Out of memory")); break;
822 return(_("Network problem")); break;
824 return(_("HTTP problem")); break;
826 return(_("SOAP problem")); break;
828 return(_("XML problem")); break;
830 return(_("ISDS server problem")); break;
832 return(_("Invalid enum value")); break;
834 return(_("Invalid date value")); break;
836 return(_("Too big")); break;
838 return(_("Too small")); break;
840 return(_("Value not unique")); break;
842 return(_("Values not equal")); break;
843 case IE_PARTIAL_SUCCESS
:
844 return(_("Some suboperations failed")); break;
846 return(_("Operation aborted")); break;
848 return(_("Security problem")); break;
850 return(_("Unknown error"));
855 /* Create ISDS context.
856 * Each context can be used for different sessions to (possibly) different
857 * ISDS server with different credentials. */
858 struct isds_ctx
*isds_ctx_create(void) {
859 struct isds_ctx
*context
;
860 context
= malloc(sizeof(*context
));
861 if (context
) memset(context
, 0, sizeof(*context
));
866 /* Close possibly opened connection to Czech POINT document deposit without
867 * resetting long_message buffer.
868 * XXX: Do not use czp_close_connection() if you do not want to destroy log
870 * @context is Czech POINT session context. */
871 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
872 if (!context
) return IE_INVALID_CONTEXT
;
873 _isds_close_connection(context
);
878 /* Discard credentials.
879 * @context is ISDS context
880 * @discard_saved_username is true for removing saved username, false for
882 * Only that. It does not cause log out, connection close or similar. */
883 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
884 _Bool discard_saved_username
) {
885 if(!context
) return IE_INVALID_CONTEXT
;
887 if (context
->username
) {
888 memset(context
->username
, 0, strlen(context
->username
));
889 zfree(context
->username
);
891 if (context
->password
) {
892 memset(context
->password
, 0, strlen(context
->password
));
893 zfree(context
->password
);
895 isds_pki_credentials_free(&context
->pki_credentials
);
896 if (discard_saved_username
&& context
->saved_username
) {
897 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
898 zfree(context
->saved_username
);
903 #endif /* HAVE_LIBCURL */
906 /* Destroy ISDS context and free memory.
907 * @context will be NULLed on success. */
908 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
909 if (!context
|| !*context
) {
910 return IE_INVALID_CONTEXT
;
914 /* Discard credentials and close connection */
915 switch ((*context
)->type
) {
916 case CTX_TYPE_NONE
: break;
917 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
919 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
920 czp_do_close_connection(*context
); break;
924 _isds_discard_credentials(*context
, 1);
926 /* Free other structures */
927 free((*context
)->url
);
928 free((*context
)->tls_verify_server
);
929 free((*context
)->tls_ca_file
);
930 free((*context
)->tls_ca_dir
);
931 free((*context
)->tls_crl_file
);
932 #endif /* HAVE_LIBCURL */
933 free((*context
)->long_message
);
941 /* Return long message text produced by library function, e.g. detailed error
942 * message. Returned pointer is only valid until new library function is
943 * called for the same context. Could be NULL, especially if NULL context is
944 * supplied. Return string is locale encoded. */
945 char *isds_long_message(const struct isds_ctx
*context
) {
946 if (!context
) return NULL
;
947 return context
->long_message
;
951 /* Stores message into context' long_message buffer.
952 * Application can pick the message up using isds_long_message().
953 * NULL @message truncates the buffer but does not deallocate it.
954 * @message is coded in locale encoding */
955 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
956 const char *message
) {
960 if (!context
) return IE_INVALID_CONTEXT
;
962 /* FIXME: Check for integer overflow */
963 length
= 1 + ((message
) ? strlen(message
) : 0);
964 buffer
= realloc(context
->long_message
, length
);
965 if (!buffer
) return IE_NOMEM
;
968 strcpy(buffer
, message
);
972 context
->long_message
= buffer
;
977 /* Appends message into context' long_message buffer.
978 * Application can pick the message up using isds_long_message().
979 * NULL message has void effect. */
980 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
981 const char *message
) {
983 size_t old_length
, length
;
985 if (!context
) return IE_INVALID_CONTEXT
;
986 if (!message
) return IE_SUCCESS
;
987 if (!context
->long_message
)
988 return isds_log_message(context
, message
);
990 old_length
= strlen(context
->long_message
);
991 /* FIXME: Check for integer overflow */
992 length
= 1 + old_length
+ strlen(message
);
993 buffer
= realloc(context
->long_message
, length
);
994 if (!buffer
) return IE_NOMEM
;
996 strcpy(buffer
+ old_length
, message
);
998 context
->long_message
= buffer
;
1003 /* Stores formatted message into context' long_message buffer.
1004 * Application can pick the message up using isds_long_message(). */
1005 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
1006 const char *format
, ...) {
1010 if (!context
) return IE_INVALID_CONTEXT
;
1011 va_start(ap
, format
);
1012 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
1015 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
1020 * @facilities is bit mask of isds_log_facility values,
1021 * @level is verbosity level. */
1022 void isds_set_logging(const unsigned int facilities
,
1023 const isds_log_level level
) {
1024 log_facilities
= facilities
;
1029 /* Register callback function libisds calls when new global log message is
1030 * produced by library. Library logs to stderr by default.
1031 * @callback is function provided by application libisds will call. See type
1032 * definition for @callback argument explanation. Pass NULL to revert logging to
1033 * default behaviour.
1034 * @data is application specific data @callback gets as last argument */
1035 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1036 log_callback
= callback
;
1037 log_callback_data
= data
;
1041 /* Log @message in class @facility with log @level into global log. @message
1042 * is printf(3) formatting string, variadic arguments may be necessary.
1043 * For debugging purposes. */
1044 _hidden isds_error
isds_log(const isds_log_facility facility
,
1045 const isds_log_level level
, const char *message
, ...) {
1047 char *buffer
= NULL
;
1050 if (level
> log_level
) return IE_SUCCESS
;
1051 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1052 if (!message
) return IE_INVAL
;
1055 /* Pass message to application supplied callback function */
1056 va_start(ap
, message
);
1057 length
= isds_vasprintf(&buffer
, message
, ap
);
1064 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1068 /* Default: Log it to stderr */
1069 va_start(ap
, message
);
1070 vfprintf(stderr
, message
, ap
);
1072 /* Line buffered printf is default.
1080 /* Set timeout in milliseconds for each network job like connecting to server
1081 * or sending message. Use 0 to disable timeout limits. */
1082 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1083 const unsigned int timeout
) {
1084 if (!context
) return IE_INVALID_CONTEXT
;
1085 zfree(context
->long_message
);
1088 context
->timeout
= timeout
;
1090 if (context
->curl
) {
1093 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1095 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1096 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1099 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1100 context
->timeout
/ 1000);
1101 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1102 if (curl_err
) return IE_ERROR
;
1106 #else /* not HAVE_LIBCURL */
1112 /* Register callback function libisds calls periodically during HTTP data
1114 * @context is session context
1115 * @callback is function provided by application libisds will call. See type
1116 * definition for @callback argument explanation.
1117 * @data is application specific data @callback gets as last argument */
1118 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1119 isds_progress_callback callback
, void *data
) {
1120 if (!context
) return IE_INVALID_CONTEXT
;
1121 zfree(context
->long_message
);
1124 context
->progress_callback
= callback
;
1125 context
->progress_callback_data
= data
;
1128 #else /* not HAVE_LIBCURL */
1134 /* Change context settings.
1135 * @context is context which setting will be applied to
1136 * @option is name of option. It determines the type of last argument. See
1137 * isds_option definition for more info.
1138 * @... is value of new setting. Type is determined by @option
1140 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1142 isds_error err
= IE_SUCCESS
;
1145 char *pointer
, *string
;
1148 if (!context
) return IE_INVALID_CONTEXT
;
1149 zfree(context
->long_message
);
1151 va_start(ap
, option
);
1153 #define REPLACE_VA_BOOLEAN(destination) { \
1154 if (!(destination)) { \
1155 (destination) = malloc(sizeof(*(destination))); \
1156 if (!(destination)) { \
1157 err = IE_NOMEM; goto leave; \
1160 *(destination) = (_Bool) !!va_arg(ap, int); \
1163 #define REPLACE_VA_STRING(destination) { \
1164 string = va_arg(ap, char *); \
1166 pointer = realloc((destination), 1 + strlen(string)); \
1167 if (!pointer) { err = IE_NOMEM; goto leave; } \
1168 strcpy(pointer, string); \
1169 (destination) = pointer; \
1171 free(destination); \
1172 (destination) = NULL; \
1177 case IOPT_TLS_VERIFY_SERVER
:
1179 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1181 err
= IE_NOTSUP
; goto leave
;
1184 case IOPT_TLS_CA_FILE
:
1186 REPLACE_VA_STRING(context
->tls_ca_file
);
1188 err
= IE_NOTSUP
; goto leave
;
1191 case IOPT_TLS_CA_DIRECTORY
:
1193 REPLACE_VA_STRING(context
->tls_ca_dir
);
1195 err
= IE_NOTSUP
; goto leave
;
1198 case IOPT_TLS_CRL_FILE
:
1200 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1201 REPLACE_VA_STRING(context
->tls_crl_file
);
1203 isds_log_message(context
,
1204 _("Curl library does not support CRL definition"));
1206 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1208 err
= IE_NOTSUP
; goto leave
;
1209 #endif /* not HAVE_LIBCURL */
1211 case IOPT_NORMALIZE_MIME_TYPE
:
1212 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1216 err
= IE_ENUM
; goto leave
;
1219 #undef REPLACE_VA_STRING
1220 #undef REPLACE_VA_BOOLEAN
1229 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1230 * Destination for NULL argument will not be touched.
1231 * Destination pointers must be freed before calling this function.
1232 * If @username is @context->saved_username, the saved_username will not be
1233 * replaced. The saved_username is clobbered only if context has set otp
1235 * Return IE_SUCCESS on success. */
1236 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1237 const char *username
, const char *password
,
1238 const struct isds_pki_credentials
*pki_credentials
) {
1239 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1241 /* FIXME: mlock password
1242 * (I have a library) */
1245 context
->username
= strdup(username
);
1246 if (context
->otp
&& context
->saved_username
!= username
)
1247 context
->saved_username
= strdup(username
);
1250 if (NULL
== context
->otp_credentials
)
1251 context
->password
= strdup(password
);
1253 context
->password
= _isds_astrcat(password
,
1254 context
->otp_credentials
->otp_code
);
1256 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1258 if ((NULL
!= username
&& NULL
== context
->username
) ||
1259 (NULL
!= password
&& NULL
== context
->password
) ||
1260 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1261 (context
->otp
&& NULL
!= context
->username
&&
1262 NULL
== context
->saved_username
)) {
1271 /* Connect and log into ISDS server.
1272 * All required arguments will be copied, you do not have to keep them after
1274 * ISDS supports six different authentication methods. Exact method is
1275 * selected on @username, @password, @pki_credentials, and @otp arguments:
1276 * - If @pki_credentials == NULL, @username and @password must be supplied
1278 * - If @otp == NULL, simple authentication by username and password will
1280 * - If @otp != NULL, authentication by username and password and OTP
1282 * - If @pki_credentials != NULL, then
1283 * - If @username == NULL, only certificate will be used
1284 * - If @username != NULL, then
1285 * - If @password == NULL, then certificate will be used and
1286 * @username shifts meaning to box ID. This is used for hosted
1288 * - Otherwise all three arguments will be used.
1289 * Please note, that different cases require different certificate type
1290 * (system qualified one or commercial non qualified one). This library
1291 * does not check such political issues. Please see ISDS Specification
1293 * @url is base address of ISDS web service. Pass extern isds_locator
1294 * variable to use production ISDS instance without client certificate
1295 * authentication (or extern isds_cert_locator with client certificate
1296 * authentication or extern isds_otp_locators with OTP authentication).
1297 * Passing NULL has the same effect, autoselection between isds_locator,
1298 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1299 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1300 * isds_otp_testing_locator) variable to select testing instance.
1301 * @username is user name of ISDS user or box ID
1302 * @password is user's secret password
1303 * @pki_credentials defines public key cryptographic material to use in client
1305 * @otp selects one-time password authentication method to use, defines OTP
1306 * code (if known) and returns fine grade resolution of OTP procedure.
1308 * IE_SUCCESS if authentication succeeds
1309 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1310 * requested, fine grade reason will be set into @otp->resolution. Error
1311 * message from server can be obtained by isds_long_message() call.
1312 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1313 * server has sent OTP code through side channel. Application is expected to
1314 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1315 * this call to complete second phase of TOTP authentication;
1316 * or other appropriate error. */
1317 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1318 const char *username
, const char *password
,
1319 const struct isds_pki_credentials
*pki_credentials
,
1320 struct isds_otp
*otp
) {
1322 isds_error err
= IE_NOT_LOGGED_IN
;
1323 isds_error soap_err
;
1324 xmlNsPtr isds_ns
= NULL
;
1325 xmlNodePtr request
= NULL
;
1326 #endif /* HAVE_LIBCURL */
1328 if (!context
) return IE_INVALID_CONTEXT
;
1329 zfree(context
->long_message
);
1332 /* Close connection if already logged in */
1333 if (context
->curl
) {
1334 _isds_close_connection(context
);
1337 /* Store configuration */
1338 context
->type
= CTX_TYPE_ISDS
;
1339 zfree(context
->url
);
1341 /* Mangle base URI according to requested authentication method */
1342 if (NULL
== pki_credentials
) {
1343 isds_log(ILF_SEC
, ILL_INFO
,
1344 _("Selected authentication method: no certificate, "
1345 "username and password\n"));
1346 if (!username
|| !password
) {
1347 isds_log_message(context
,
1348 _("Both username and password must be supplied"));
1351 context
->otp_credentials
= otp
;
1352 context
->otp
= (NULL
!= context
->otp_credentials
);
1354 if (!context
->otp
) {
1355 /* Default locator is official system (without certificate or
1357 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1359 const char *authenticator_uri
= NULL
;
1360 if (!url
) url
= isds_otp_locator
;
1361 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1362 switch (context
->otp_credentials
->method
) {
1364 isds_log(ILF_SEC
, ILL_INFO
,
1365 _("Selected authentication method: "
1366 "HMAC-based one-time password\n"));
1368 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1371 isds_log(ILF_SEC
, ILL_INFO
,
1372 _("Selected authentication method: "
1373 "Time-based one-time password\n"));
1374 if (context
->otp_credentials
->otp_code
== NULL
) {
1375 isds_log(ILF_SEC
, ILL_INFO
,
1376 _("OTP code has not been provided by "
1377 "application, requesting server for "
1380 "%1$sas/processLogin?type=totp&sendSms=true&"
1383 isds_log(ILF_SEC
, ILL_INFO
,
1384 _("OTP code has been provided by "
1385 "application, not requesting server "
1388 "%1$sas/processLogin?type=totp&"
1393 isds_log_message(context
,
1394 _("Unknown one-time password authentication "
1395 "method requested by application"));
1398 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1402 /* Default locator is official system (with client certificate) */
1404 context
->otp_credentials
= NULL
;
1405 if (!url
) url
= isds_cert_locator
;
1408 isds_log(ILF_SEC
, ILL_INFO
,
1409 _("Selected authentication method: system certificate, "
1410 "no username and no password\n"));
1412 context
->url
= _isds_astrcat(url
, "cert/");
1415 isds_log(ILF_SEC
, ILL_INFO
,
1416 _("Selected authentication method: system certificate, "
1417 "box ID and no password\n"));
1418 context
->url
= _isds_astrcat(url
, "hspis/");
1420 isds_log(ILF_SEC
, ILL_INFO
,
1421 _("Selected authentication method: commercial "
1422 "certificate, username and password\n"));
1423 context
->url
= _isds_astrcat(url
, "certds/");
1427 if (!(context
->url
))
1430 /* Prepare CURL handle */
1431 context
->curl
= curl_easy_init();
1432 if (!(context
->curl
))
1435 /* Build log-in request */
1436 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1438 isds_log_message(context
, _("Could not build ISDS log-in request"));
1441 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1443 isds_log_message(context
, _("Could not create ISDS name space"));
1444 xmlFreeNode(request
);
1447 xmlSetNs(request
, isds_ns
);
1449 /* Store credentials */
1450 _isds_discard_credentials(context
, 1);
1451 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1452 _isds_discard_credentials(context
, 1);
1453 xmlFreeNode(request
);
1457 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1460 /* XXX: ISDS documentation does not specify response body for
1461 * DummyOperation request. However real server sends back
1462 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1463 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1464 * SOAP body content, e.g. the dmStatus element. */
1466 /* Send log-in request */
1467 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1470 /* Revert context URL from OTP authentication service URL to OTP web
1471 * service base URL for subsequent calls. Potenial isds_login() retry
1472 * will re-set context URL again. */
1473 zfree(context
->url
);
1474 context
->url
= _isds_astrcat(url
, "apps/");
1475 if (context
->url
== NULL
) {
1476 soap_err
= IE_NOMEM
;
1478 /* Detach pointer to OTP credentials from context */
1479 context
->otp_credentials
= NULL
;
1482 /* Remove credentials */
1483 _isds_discard_credentials(context
, 0);
1485 /* Destroy log-in request */
1486 xmlFreeNode(request
);
1489 _isds_close_connection(context
);
1493 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1494 * authentication succeeded if soap_err == IE_SUCCESS */
1498 isds_log(ILF_ISDS
, ILL_DEBUG
,
1499 _("User %s has been logged into server %s successfully\n"),
1502 #else /* not HAVE_LIBCURL */
1508 /* Log out from ISDS server discards credentials and connection configuration. */
1509 isds_error
isds_logout(struct isds_ctx
*context
) {
1510 if (!context
) return IE_INVALID_CONTEXT
;
1511 zfree(context
->long_message
);
1514 if (context
->curl
) {
1516 isds_error err
= _isds_invalidate_otp_cookie(context
);
1517 if (err
) return err
;
1520 /* Close connection */
1521 _isds_close_connection(context
);
1523 /* Discard credentials for sure. They should not survive isds_login(),
1524 * even successful .*/
1525 _isds_discard_credentials(context
, 1);
1527 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1529 _isds_discard_credentials(context
, 1);
1531 zfree(context
->url
);
1533 #else /* not HAVE_LIBCURL */
1539 /* Verify connection to ISDS is alive and server is responding.
1540 * Send dummy request to ISDS and expect dummy response. */
1541 isds_error
isds_ping(struct isds_ctx
*context
) {
1543 isds_error soap_err
;
1544 xmlNsPtr isds_ns
= NULL
;
1545 xmlNodePtr request
= NULL
;
1546 #endif /* HAVE_LIBCURL */
1548 if (!context
) return IE_INVALID_CONTEXT
;
1549 zfree(context
->long_message
);
1552 /* Check if connection is established */
1553 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1556 /* Build dummy request */
1557 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1559 isds_log_message(context
, _("Could build ISDS dummy request"));
1562 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1564 isds_log_message(context
, _("Could not create ISDS name space"));
1565 xmlFreeNode(request
);
1568 xmlSetNs(request
, isds_ns
);
1570 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1572 /* XXX: ISDS documentation does not specify response body for
1573 * DummyOperation request. However real server sends back
1574 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1575 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1576 * SOAP body content, e.g. the dmStatus element. */
1578 /* Send dummy request */
1579 soap_err
= _isds_soap(context
, "DS/dz", request
, NULL
, NULL
, NULL
, NULL
);
1581 /* Destroy log-in request */
1582 xmlFreeNode(request
);
1585 isds_log(ILF_ISDS
, ILL_DEBUG
,
1586 _("ISDS server could not be contacted\n"));
1590 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1591 * authentication succeeded if soap_err == IE_SUCCESS */
1594 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1597 #else /* not HAVE_LIBCURL */
1603 /* Send bogus request to ISDS.
1604 * Just for test purposes */
1605 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1608 xmlNsPtr isds_ns
= NULL
;
1609 xmlNodePtr request
= NULL
;
1610 xmlDocPtr response
= NULL
;
1611 xmlChar
*code
= NULL
, *message
= NULL
;
1614 if (!context
) return IE_INVALID_CONTEXT
;
1615 zfree(context
->long_message
);
1618 /* Check if connection is established */
1619 if (!context
->curl
) {
1620 /* Testing printf message */
1621 isds_printf_message(context
, "%s", _("I said connection closed"));
1622 return IE_CONNECTION_CLOSED
;
1626 /* Build dummy request */
1627 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1629 isds_log_message(context
, _("Could build ISDS bogus request"));
1632 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1634 isds_log_message(context
, _("Could not create ISDS name space"));
1635 xmlFreeNode(request
);
1638 xmlSetNs(request
, isds_ns
);
1640 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1642 /* Sent bogus request */
1643 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1645 /* Destroy request */
1646 xmlFreeNode(request
);
1649 isds_log(ILF_ISDS
, ILL_DEBUG
,
1650 _("Processing ISDS response on bogus request failed\n"));
1651 xmlFreeDoc(response
);
1655 /* Check for response status */
1656 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1657 &code
, &message
, NULL
);
1659 isds_log(ILF_ISDS
, ILL_DEBUG
,
1660 _("ISDS response on bogus request is missing status\n"));
1663 xmlFreeDoc(response
);
1666 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1667 char *code_locale
= _isds_utf82locale((char*)code
);
1668 char *message_locale
= _isds_utf82locale((char*)message
);
1669 isds_log(ILF_ISDS
, ILL_DEBUG
,
1670 _("Server refused bogus request (code=%s, message=%s)\n"),
1671 code_locale
, message_locale
);
1672 /* XXX: Literal error messages from ISDS are Czech messages
1673 * (English sometimes) in UTF-8. It's hard to catch them for
1674 * translation. Successfully gettextized would return in locale
1675 * encoding, unsuccessfully translated would pass in UTF-8. */
1676 isds_log_message(context
, message_locale
);
1678 free(message_locale
);
1681 xmlFreeDoc(response
);
1688 xmlFreeDoc(response
);
1690 isds_log(ILF_ISDS
, ILL_DEBUG
,
1691 _("Bogus message accepted by server. This should not happen.\n"));
1694 #else /* not HAVE_LIBCURL */
1701 /* Serialize XML subtree to buffer preserving XML indentation.
1702 * @context is session context
1703 * @subtree is XML element to be serialized (with children)
1704 * @buffer is automatically reallocated buffer where serialize to
1705 * @length is size of serialized stream in bytes
1706 * @return standard error code, free @buffer in case of error */
1707 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1708 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1709 isds_error err
= IE_SUCCESS
;
1710 xmlBufferPtr xml_buffer
= NULL
;
1711 xmlSaveCtxtPtr save_ctx
= NULL
;
1712 xmlDocPtr subtree_doc
= NULL
;
1713 xmlNodePtr subtree_copy
;
1717 if (!context
) return IE_INVALID_CONTEXT
;
1718 if (!buffer
) return IE_INVAL
;
1720 if (!subtree
|| !length
) return IE_INVAL
;
1722 /* Make temporary XML document with @subtree root element */
1723 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1724 * It can result in not well-formed on invalid XML tree (e.g. name space
1725 * prefix definition can miss. */
1728 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1730 isds_log_message(context
, _("Could not build temporary document"));
1735 /* XXX: Copy subtree and attach the copy to document.
1736 * One node can not bee attached into more document at the same time.
1737 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1739 * XXX: Check xmlSaveTree() too. */
1740 subtree_copy
= xmlCopyNodeList(subtree
);
1741 if (!subtree_copy
) {
1742 isds_log_message(context
, _("Could not copy subtree"));
1746 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1748 /* Only this way we get namespace definition as @xmlns:isds,
1749 * otherwise we get namespace prefix without definition */
1750 /* FIXME: Don't overwrite original default namespace */
1751 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1753 isds_log_message(context
, _("Could not create ISDS name space"));
1757 xmlSetNs(subtree_copy
, isds_ns
);
1760 /* Serialize the document into buffer */
1761 xml_buffer
= xmlBufferCreate();
1763 isds_log_message(context
, _("Could not create xmlBuffer"));
1767 /* Last argument 0 means to not format the XML tree */
1768 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1770 isds_log_message(context
, _("Could not create XML serializer"));
1774 /* XXX: According LibXML documentation, this function does not return
1775 * meaningful value yet */
1776 xmlSaveDoc(save_ctx
, subtree_doc
);
1777 if (-1 == xmlSaveFlush(save_ctx
)) {
1778 isds_log_message(context
,
1779 _("Could not serialize XML subtree"));
1783 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1784 * even after xmlSaveFlush(). Thus close it here */
1785 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1788 /* Store and detach buffer from xml_buffer */
1789 *buffer
= xml_buffer
->content
;
1790 *length
= xml_buffer
->use
;
1791 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1794 new_buffer
= realloc(*buffer
, *length
);
1795 if (new_buffer
) *buffer
= new_buffer
;
1803 xmlSaveClose(save_ctx
);
1804 xmlBufferFree(xml_buffer
);
1805 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1808 #endif /* HAVE_LIBCURL */
1812 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1813 * @context is session context
1814 * @document is original document where @nodeset points to
1815 * @nodeset is XPath node set to dump (recursively)
1816 * @buffer is automatically reallocated buffer where serialize to
1817 * @length is size of serialized stream in bytes
1818 * @return standard error code, free @buffer in case of error */
1819 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1820 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1821 void **buffer
, size_t *length
) {
1822 isds_error err
= IE_SUCCESS
;
1823 xmlBufferPtr xml_buffer
= NULL
;
1826 if (!context
) return IE_INVALID_CONTEXT
;
1827 if (!buffer
) return IE_INVAL
;
1829 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1832 /* Empty node set results into NULL buffer */
1833 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1837 /* Resulting the document into buffer */
1838 xml_buffer
= xmlBufferCreate();
1840 isds_log_message(context
, _("Could not create xmlBuffer"));
1845 /* Iterate over all nodes */
1846 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1848 * XXX: xmlNodeDump() appends to xml_buffer. */
1850 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1851 isds_log_message(context
, _("Could not dump XML node"));
1857 /* Store and detach buffer from xml_buffer */
1858 *buffer
= xml_buffer
->content
;
1859 *length
= xml_buffer
->use
;
1860 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1863 new_buffer
= realloc(*buffer
, *length
);
1864 if (new_buffer
) *buffer
= new_buffer
;
1873 xmlBufferFree(xml_buffer
);
1879 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1880 * @context is session context
1881 * @document is original document where @nodeset points to
1882 * @nodeset is XPath node set to dump (recursively)
1883 * @buffer is automatically reallocated buffer where serialize to
1884 * @length is size of serialized stream in bytes
1885 * @return standard error code, free @buffer in case of error */
1886 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1887 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1888 void **buffer
, size_t *length
) {
1889 isds_error err
= IE_SUCCESS
;
1890 xmlBufferPtr xml_buffer
= NULL
;
1891 xmlSaveCtxtPtr save_ctx
= NULL
;
1894 if (!context
) return IE_INVALID_CONTEXT
;
1895 if (!buffer
) return IE_INVAL
;
1897 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1900 /* Empty node set results into NULL buffer */
1901 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1905 /* Resulting the document into buffer */
1906 xml_buffer
= xmlBufferCreate();
1908 isds_log_message(context
, _("Could not create xmlBuffer"));
1912 if (xmlSubstituteEntitiesDefault(1)) {
1913 isds_log_message(context
, _("Could not disable attribute escaping"));
1917 /* Last argument means:
1918 * 0 to not format the XML tree
1919 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1920 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1921 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1923 isds_log_message(context
, _("Could not create XML serializer"));
1927 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1928 isds_log_message(context, _("Could not disable attribute escaping"));
1934 /* Iterate over all nodes */
1935 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1937 * XXX: xmlNodeDump() appends to xml_buffer. */
1939 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1941 /* XXX: According LibXML documentation, this function does not return
1942 * meaningful value yet */
1943 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1944 if (-1 == xmlSaveFlush(save_ctx
)) {
1945 isds_log_message(context
,
1946 _("Could not serialize XML subtree"));
1952 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1953 * even after xmlSaveFlush(). Thus close it here */
1954 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1956 /* Store and detach buffer from xml_buffer */
1957 *buffer
= xml_buffer
->content
;
1958 *length
= xml_buffer
->use
;
1959 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1962 new_buffer
= realloc(*buffer
, *length
);
1963 if (new_buffer
) *buffer
= new_buffer
;
1971 xmlSaveClose(save_ctx
);
1972 xmlBufferFree(xml_buffer
);
1979 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1980 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1981 if (!string
|| !type
) return IE_INVAL
;
1983 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1985 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1987 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1988 *type
= DBTYPE_PFO_ADVOK
;
1989 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1990 *type
= DBTYPE_PFO_DANPOR
;
1991 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1992 *type
= DBTYPE_PFO_INSSPR
;
1993 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1995 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1996 *type
= DBTYPE_PO_ZAK
;
1997 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1998 *type
= DBTYPE_PO_REQ
;
1999 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
2001 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
2002 *type
= DBTYPE_OVM_NOTAR
;
2003 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
2004 *type
= DBTYPE_OVM_EXEKUT
;
2005 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
2006 *type
= DBTYPE_OVM_REQ
;
2013 /* Convert ISDS dbType enum @type to UTF-8 string.
2014 * @Return pointer to static string, or NULL if unknown enum value */
2015 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
2017 /* DBTYPE_SYSTEM and DBTYPE_OVM_MAIN are invalid values from point
2018 * of view of generic public SOAP interface. */
2019 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
2020 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
2021 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
2022 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
2023 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
2024 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2025 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2026 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2027 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2028 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2029 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2030 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2031 default: return NULL
; break;
2036 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2037 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2038 if (!string
|| !type
) return IE_INVAL
;
2040 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2041 *type
= USERTYPE_PRIMARY
;
2042 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2043 *type
= USERTYPE_ENTRUSTED
;
2044 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2045 *type
= USERTYPE_ADMINISTRATOR
;
2046 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2047 *type
= USERTYPE_OFFICIAL
;
2048 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2049 *type
= USERTYPE_OFFICIAL_CERT
;
2050 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2051 *type
= USERTYPE_LIQUIDATOR
;
2058 /* Convert ISDS userType enum @type to UTF-8 string.
2059 * @Return pointer to static string, or NULL if unknown enum value */
2060 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2062 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2063 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2064 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2065 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2066 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2067 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2068 default: return NULL
; break;
2073 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2074 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2075 isds_sender_type
*type
) {
2076 if (!string
|| !type
) return IE_INVAL
;
2078 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2079 *type
= SENDERTYPE_PRIMARY
;
2080 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2081 *type
= SENDERTYPE_ENTRUSTED
;
2082 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2083 *type
= SENDERTYPE_ADMINISTRATOR
;
2084 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2085 *type
= SENDERTYPE_OFFICIAL
;
2086 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2087 *type
= SENDERTYPE_VIRTUAL
;
2088 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2089 *type
= SENDERTYPE_OFFICIAL_CERT
;
2090 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2091 *type
= SENDERTYPE_LIQUIDATOR
;
2098 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2099 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2100 isds_payment_type
*type
) {
2101 if (!string
|| !type
) return IE_INVAL
;
2103 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2104 *type
= PAYMENT_SENDER
;
2105 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2106 *type
= PAYMENT_RESPONSE
;
2107 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2108 *type
= PAYMENT_SPONSOR
;
2109 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2110 *type
= PAYMENT_SPONSOR_LIMITED
;
2111 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2112 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2113 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2114 *type
= PAYMENT_STAMP
;
2121 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2122 * ciEventType is integer but we convert it from string representation
2124 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2125 isds_credit_event_type
*type
) {
2126 if (!string
|| !type
) return IE_INVAL
;
2128 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2129 *type
= ISDS_CREDIT_CHARGED
;
2130 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2131 *type
= ISDS_CREDIT_DISCHARGED
;
2132 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2133 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2134 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2135 *type
= ISDS_CREDIT_STORAGE_SET
;
2136 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2137 *type
= ISDS_CREDIT_EXPIRED
;
2144 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2145 * @Return pointer to static string, or NULL if unknown enum value */
2146 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2148 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2149 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2150 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2151 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2152 default: return NULL
; break;
2157 /* Convert isds_fulltext_target enum @type to UTF-8 string for
2158 * ISDSSearch2/searchType value.
2159 * @Return pointer to static string, or NULL if unknown enum value */
2160 static const xmlChar
*isds_fulltext_target2string(
2161 const isds_fulltext_target type
) {
2163 case FULLTEXT_ALL
: return(BAD_CAST
"GENERAL"); break;
2164 case FULLTEXT_ADDRESS
: return(BAD_CAST
"ADDRESS"); break;
2165 case FULLTEXT_IC
: return(BAD_CAST
"ICO"); break;
2166 case FULLTEXT_BOX_ID
: return(BAD_CAST
"DBID"); break;
2167 default: return NULL
; break;
2170 #endif /* HAVE_LIBCURL */
2173 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2174 * @Return IE_ENUM if @string is not valid enum member */
2175 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2176 isds_FileMetaType
*type
) {
2177 if (!string
|| !type
) return IE_INVAL
;
2179 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2180 *type
= FILEMETATYPE_MAIN
;
2181 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2182 *type
= FILEMETATYPE_ENCLOSURE
;
2183 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2184 *type
= FILEMETATYPE_SIGNATURE
;
2185 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2186 *type
= FILEMETATYPE_META
;
2193 /* Convert UTF-8 @string to ISDS hash @algorithm.
2194 * @Return IE_ENUM if @string is not valid enum member */
2195 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2196 isds_hash_algorithm
*algorithm
) {
2197 if (!string
|| !algorithm
) return IE_INVAL
;
2199 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2200 *algorithm
= HASH_ALGORITHM_MD5
;
2201 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2202 *algorithm
= HASH_ALGORITHM_SHA_1
;
2203 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2204 *algorithm
= HASH_ALGORITHM_SHA_224
;
2205 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2206 *algorithm
= HASH_ALGORITHM_SHA_256
;
2207 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2208 *algorithm
= HASH_ALGORITHM_SHA_384
;
2209 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2210 *algorithm
= HASH_ALGORITHM_SHA_512
;
2218 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2219 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2220 if (!time
|| !string
) return IE_INVAL
;
2222 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2223 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2230 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2231 * respects the @time microseconds too. */
2232 static isds_error
timeval2timestring(const struct timeval
*time
,
2235 time_t seconds_as_time_t
;
2237 if (!time
|| !string
) return IE_INVAL
;
2239 /* MinGW32 GCC 4.8+ uses 64-bit time_t but time->tv_sec is defined as
2240 * 32-bit long in Microsoft API. Convert value to the type expected by
2242 seconds_as_time_t
= time
->tv_sec
;
2243 if (!gmtime_r(&seconds_as_time_t
, &broken
)) return IE_DATE
;
2244 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2246 /* TODO: small negative year should be formatted as "-0012". This is not
2247 * true for glibc "%04d". We should implement it.
2248 * time->tv_usec type is su_seconds_t which is required to be signed
2249 * integer to accomodate values from range [-1, 1000000].
2250 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2251 /* XXX: Do not format time->tv_usec as intmax_t because %jd is not
2252 * supported and PRIdMAX is broken on MingGW. We can use int32_t because
2253 * of the range check above. */
2254 if (-1 == isds_asprintf((char **) string
,
2255 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRId32
,
2256 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2257 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2258 (int32_t)time
->tv_usec
))
2263 #endif /* HAVE_LIBCURL */
2266 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2267 * It respects microseconds too. Microseconds are rounded half up.
2268 * In case of error, @time will be freed. */
2269 static isds_error
timestring2timeval(const xmlChar
*string
,
2270 struct timeval
**time
) {
2272 char *offset
, *delim
, *endptr
;
2273 const int subsecond_resolution
= 6;
2274 char subseconds
[subsecond_resolution
+ 1];
2276 int offset_hours
, offset_minutes
;
2278 long int long_number
;
2283 if (!time
) return IE_INVAL
;
2289 memset(&broken
, 0, sizeof(broken
));
2292 *time
= calloc(1, sizeof(**time
));
2293 if (!*time
) return IE_NOMEM
;
2295 memset(*time
, 0, sizeof(**time
));
2299 /* xsd:date is ISO 8601 string, thus ASCII */
2300 /*TODO: negative year */
2304 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2305 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2306 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2312 broken
.tm_year
-= 1900;
2314 broken
.tm_isdst
= -1;
2315 offset
= (char*)string
+ i
;
2317 /* Parse date and time without subseconds and offset */
2318 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2325 /* Get subseconds */
2326 if (*offset
== '.' ) {
2329 /* Copy first 6 digits, pad it with zeros.
2330 * Current server implementation uses only millisecond resolution. */
2331 /* TODO: isdigit() is locale sensitive */
2333 i
< subsecond_resolution
&& isdigit(*offset
);
2335 subseconds
[i
] = *offset
;
2337 if (subsecond_resolution
== i
&& isdigit(*offset
)) {
2338 /* Check 7th digit for rounding */
2339 if (*offset
>= '5') round_up
= 1;
2342 for (; i
< subsecond_resolution
; i
++) {
2343 subseconds
[i
] = '0';
2345 subseconds
[subsecond_resolution
] = '\0';
2347 /* Convert it into integer */
2348 long_number
= strtol(subseconds
, &endptr
, 10);
2349 if (*endptr
!= '\0' || long_number
== LONG_MIN
||
2350 long_number
== LONG_MAX
) {
2354 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
2355 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
2356 * microseconds" and "the type shall be a signed integer capable of
2357 * storing values at least in the range [-1, 1000000]. */
2358 if (long_number
< -1 || long_number
>= 1000000) {
2362 (*time
)->tv_usec
= long_number
;
2364 /* Round the subseconds */
2366 if (999999 == (*time
)->tv_usec
) {
2367 (*time
)->tv_usec
= 0;
2374 /* move to the zone offset delimiter or signal NULL*/
2375 delim
= strchr(offset
, '-');
2377 delim
= strchr(offset
, '+');
2379 delim
= strchr(offset
, 'Z');
2383 /* Get zone offset */
2384 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2385 * "" equals to "Z" and it means UTC zone. */
2386 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2387 * colon separator */
2388 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2389 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2393 if (*offset
== '+') {
2394 broken
.tm_hour
-= offset_hours
;
2395 broken
.tm_min
-= offset_minutes
;
2397 broken
.tm_hour
+= offset_hours
;
2398 broken
.tm_min
+= offset_minutes
;
2402 /* Convert to time_t */
2403 (*time
)->tv_sec
= _isds_timegm(&broken
);
2404 if ((*time
)->tv_sec
== (time_t) -1) {
2413 /* Convert unsigned int into isds_message_status.
2414 * @context is session context
2415 * @number is pointer to number value. NULL will be treated as invalid value.
2416 * @status is automatically reallocated status
2417 * @return IE_SUCCESS, or error code and free status */
2418 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2419 const unsigned long int *number
, isds_message_status
**status
) {
2420 if (!context
) return IE_INVALID_CONTEXT
;
2421 if (!status
) return IE_INVAL
;
2423 free(*status
); *status
= NULL
;
2424 if (!number
) return IE_INVAL
;
2426 if (*number
< 1 || *number
> 10) {
2427 isds_printf_message(context
, _("Invalid message status value: %lu"),
2432 *status
= malloc(sizeof(**status
));
2433 if (!*status
) return IE_NOMEM
;
2435 **status
= 1 << *number
;
2440 /* Convert event description string into isds_event members type and
2442 * @string is raw event description starting with event prefix
2443 * @event is structure where to store type and stripped description to
2444 * @return standard error code, unknown prefix is not classified as an error.
2446 static isds_error
eventstring2event(const xmlChar
*string
,
2447 struct isds_event
* event
) {
2448 const xmlChar
*known_prefixes
[] = {
2459 const isds_event_type types
[] = {
2460 EVENT_ENTERED_SYSTEM
,
2461 EVENT_ACCEPTED_BY_RECIPIENT
,
2462 EVENT_ACCEPTED_BY_FICTION
,
2463 EVENT_UNDELIVERABLE
,
2464 EVENT_COMMERCIAL_ACCEPTED
,
2466 EVENT_PRIMARY_LOGIN
,
2467 EVENT_ENTRUSTED_LOGIN
,
2473 if (!string
|| !event
) return IE_INVAL
;
2476 event
->type
= malloc(sizeof(*event
->type
));
2477 if (!(event
->type
)) return IE_NOMEM
;
2479 zfree(event
->description
);
2481 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2483 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2485 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2486 /* Prefix is known */
2487 *event
->type
= types
[index
];
2489 /* Strip prefix from description and spaces */
2490 /* TODO: Recognize all white spaces from UCS blank class and
2491 * operate on UTF-8 chars. */
2492 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2493 event
->description
= strdup((char *) (string
+ length
));
2494 if (!(event
->description
)) return IE_NOMEM
;
2500 /* Unknown event prefix.
2501 * XSD allows any string */
2502 char *string_locale
= _isds_utf82locale((char *) string
);
2503 isds_log(ILF_ISDS
, ILL_WARNING
,
2504 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2505 free(string_locale
);
2507 *event
->type
= EVENT_UKNOWN
;
2508 event
->description
= strdup((char *) string
);
2509 if (!(event
->description
)) return IE_NOMEM
;
2515 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2516 * and leave label */
2517 #define EXTRACT_STRING(element, string) { \
2518 xmlXPathFreeObject(result); \
2519 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2520 if (NULL == (result)) { \
2524 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2525 if (result->nodesetval->nodeNr > 1) { \
2526 isds_printf_message(context, _("Multiple %s element"), element); \
2530 (string) = (char *) \
2531 xmlXPathCastNodeSetToString(result->nodesetval); \
2532 if (NULL == (string)) { \
2539 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2541 char *string = NULL; \
2542 EXTRACT_STRING(element, string); \
2545 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2546 if (!(booleanPtr)) { \
2552 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2553 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2554 *(booleanPtr) = 1; \
2555 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2556 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2557 *(booleanPtr) = 0; \
2559 char *string_locale = _isds_utf82locale((char*)string); \
2560 isds_printf_message(context, \
2561 _("%s value is not valid boolean: %s"), \
2562 element, string_locale); \
2563 free(string_locale); \
2573 #define EXTRACT_BOOLEANNOPTR(element, boolean) \
2575 char *string = NULL; \
2576 EXTRACT_STRING(element, string); \
2578 if (NULL == string) { \
2579 isds_printf_message(context, _("%s element is empty"), element); \
2583 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2584 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2586 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2587 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2590 char *string_locale = _isds_utf82locale((char*)string); \
2591 isds_printf_message(context, \
2592 _("%s value is not valid boolean: %s"), \
2593 element, string_locale); \
2594 free(string_locale); \
2603 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2605 char *string = NULL; \
2606 EXTRACT_STRING(element, string); \
2611 number = strtol((char*)string, &endptr, 10); \
2613 if (*endptr != '\0') { \
2614 char *string_locale = _isds_utf82locale((char *)string); \
2615 isds_printf_message(context, \
2616 _("%s is not valid integer: %s"), \
2617 element, string_locale); \
2618 free(string_locale); \
2624 if (number == LONG_MIN || number == LONG_MAX) { \
2625 char *string_locale = _isds_utf82locale((char *)string); \
2626 isds_printf_message(context, \
2627 _("%s value out of range of long int: %s"), \
2628 element, string_locale); \
2629 free(string_locale); \
2635 free(string); string = NULL; \
2637 if (!(preallocated)) { \
2638 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2639 if (!(longintPtr)) { \
2644 *(longintPtr) = number; \
2648 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2650 char *string = NULL; \
2651 EXTRACT_STRING(element, string); \
2656 number = strtol((char*)string, &endptr, 10); \
2658 if (*endptr != '\0') { \
2659 char *string_locale = _isds_utf82locale((char *)string); \
2660 isds_printf_message(context, \
2661 _("%s is not valid integer: %s"), \
2662 element, string_locale); \
2663 free(string_locale); \
2669 if (number == LONG_MIN || number == LONG_MAX) { \
2670 char *string_locale = _isds_utf82locale((char *)string); \
2671 isds_printf_message(context, \
2672 _("%s value out of range of long int: %s"), \
2673 element, string_locale); \
2674 free(string_locale); \
2680 free(string); string = NULL; \
2682 isds_printf_message(context, \
2683 _("%s value is negative: %ld"), element, number); \
2688 if (!(preallocated)) { \
2689 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2690 if (!(ulongintPtr)) { \
2695 *(ulongintPtr) = number; \
2699 #define EXTRACT_DATE(element, tmPtr) { \
2700 char *string = NULL; \
2701 EXTRACT_STRING(element, string); \
2702 if (NULL != string) { \
2703 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2704 if (NULL == (tmPtr)) { \
2709 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2711 if (err == IE_NOTSUP) { \
2713 char *string_locale = _isds_utf82locale(string); \
2714 char *element_locale = _isds_utf82locale(element); \
2715 isds_printf_message(context, _("Invalid %s value: %s"), \
2716 element_locale, string_locale); \
2717 free(string_locale); \
2718 free(element_locale); \
2727 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2728 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2730 if ((required) && (!string)) { \
2731 char *attribute_locale = _isds_utf82locale(attribute); \
2732 char *element_locale = \
2733 _isds_utf82locale((char *)xpath_ctx->node->name); \
2734 isds_printf_message(context, \
2735 _("Could not extract required %s attribute value from " \
2736 "%s element"), attribute_locale, element_locale); \
2737 free(element_locale); \
2738 free(attribute_locale); \
2745 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2747 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2748 (xmlChar *) (string)); \
2750 isds_printf_message(context, \
2751 _("Could not add %s child to %s element"), \
2752 element, (parent)->name); \
2758 #define INSERT_STRING(parent, element, string) \
2759 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2761 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2763 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2764 else { INSERT_STRING(parent, element, "false"); } \
2767 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2770 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2772 INSERT_STRING(parent, element, NULL); \
2776 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2777 if ((longintPtr)) { \
2778 /* FIXME: locale sensitive */ \
2779 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2783 INSERT_STRING(parent, element, buffer) \
2784 free(buffer); (buffer) = NULL; \
2785 } else { INSERT_STRING(parent, element, NULL) } \
2788 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2789 if ((ulongintPtr)) { \
2790 /* FIXME: locale sensitive */ \
2791 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2795 INSERT_STRING(parent, element, buffer) \
2796 free(buffer); (buffer) = NULL; \
2797 } else { INSERT_STRING(parent, element, NULL) } \
2800 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2802 /* FIXME: locale sensitive */ \
2803 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2807 INSERT_STRING(parent, element, buffer) \
2808 free(buffer); (buffer) = NULL; \
2811 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2813 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2815 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2816 (xmlChar *) (string)); \
2817 if (!attribute_node) { \
2818 isds_printf_message(context, _("Could not add %s " \
2819 "attribute to %s element"), \
2820 (attribute), (parent)->name); \
2826 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2828 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2829 if (length > (maximum)) { \
2830 isds_printf_message(context, \
2831 ngettext("%s has more than %d characters", \
2832 "%s has more than %d characters", (maximum)), \
2833 (name), (maximum)); \
2837 if (length < (minimum)) { \
2838 isds_printf_message(context, \
2839 ngettext("%s has less than %d characters", \
2840 "%s has less than %d characters", (minimum)), \
2841 (name), (minimum)); \
2848 #define INSERT_ELEMENT(child, parent, element) \
2850 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2852 isds_printf_message(context, \
2853 _("Could not add %s child to %s element"), \
2854 (element), (parent)->name); \
2861 /* Find child element by name in given XPath context and switch context onto
2862 * it. The child must be uniq and must exist. Otherwise fails.
2863 * @context is ISDS context
2864 * @child is child element name
2865 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2866 * into it child. In error case, the @xpath_ctx keeps original value. */
2867 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2868 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2869 isds_error err
= IE_SUCCESS
;
2870 xmlXPathObjectPtr result
= NULL
;
2872 if (!context
) return IE_INVALID_CONTEXT
;
2873 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2876 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2883 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2884 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2885 char *child_locale
= _isds_utf82locale((char*) child
);
2886 isds_printf_message(context
,
2887 _("%s element does not contain %s child"),
2888 parent_locale
, child_locale
);
2890 free(parent_locale
);
2896 if (result
->nodesetval
->nodeNr
> 1) {
2897 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2898 char *child_locale
= _isds_utf82locale((char*) child
);
2899 isds_printf_message(context
,
2900 _("%s element contains multiple %s children"),
2901 parent_locale
, child_locale
);
2903 free(parent_locale
);
2908 /* Switch context */
2909 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2912 xmlXPathFreeObject(result
);
2919 /* Find and convert XSD:gPersonName group in current node into structure
2920 * @context is ISDS context
2921 * @personName is automatically reallocated person name structure. If no member
2922 * value is found, will be freed.
2923 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2925 * In case of error @personName will be freed. */
2926 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2927 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2928 isds_error err
= IE_SUCCESS
;
2929 xmlXPathObjectPtr result
= NULL
;
2931 if (!context
) return IE_INVALID_CONTEXT
;
2932 if (!personName
) return IE_INVAL
;
2933 isds_PersonName_free(personName
);
2934 if (!xpath_ctx
) return IE_INVAL
;
2937 *personName
= calloc(1, sizeof(**personName
));
2943 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2944 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2945 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2946 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2948 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2949 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2950 isds_PersonName_free(personName
);
2953 if (err
) isds_PersonName_free(personName
);
2954 xmlXPathFreeObject(result
);
2959 /* Find and convert XSD:gAddress group in current node into structure
2960 * @context is ISDS context
2961 * @address is automatically reallocated address structure. If no member
2962 * value is found, will be freed.
2963 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2965 * In case of error @address will be freed. */
2966 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2967 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2968 isds_error err
= IE_SUCCESS
;
2969 xmlXPathObjectPtr result
= NULL
;
2971 if (!context
) return IE_INVALID_CONTEXT
;
2972 if (!address
) return IE_INVAL
;
2973 isds_Address_free(address
);
2974 if (!xpath_ctx
) return IE_INVAL
;
2977 *address
= calloc(1, sizeof(**address
));
2983 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2984 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2985 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2986 EXTRACT_STRING("isds:adNumberInMunicipality",
2987 (*address
)->adNumberInMunicipality
);
2988 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2989 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2991 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2992 !(*address
)->adNumberInStreet
&&
2993 !(*address
)->adNumberInMunicipality
&&
2994 !(*address
)->adZipCode
&& !(*address
)->adState
)
2995 isds_Address_free(address
);
2998 if (err
) isds_Address_free(address
);
2999 xmlXPathFreeObject(result
);
3004 /* Find and convert isds:biDate element in current node into structure
3005 * @context is ISDS context
3006 * @biDate is automatically reallocated birth date structure. If no member
3007 * value is found, will be freed.
3008 * @xpath_ctx is XPath context with current node as parent for isds:biDate
3010 * In case of error @biDate will be freed. */
3011 static isds_error
extract_BiDate(struct isds_ctx
*context
,
3012 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
3013 isds_error err
= IE_SUCCESS
;
3014 xmlXPathObjectPtr result
= NULL
;
3015 char *string
= NULL
;
3017 if (!context
) return IE_INVALID_CONTEXT
;
3018 if (!biDate
) return IE_INVAL
;
3020 if (!xpath_ctx
) return IE_INVAL
;
3022 EXTRACT_STRING("isds:biDate", string
);
3024 *biDate
= calloc(1, sizeof(**biDate
));
3029 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
3031 if (err
== IE_NOTSUP
) {
3033 char *string_locale
= _isds_utf82locale(string
);
3034 isds_printf_message(context
,
3035 _("Invalid isds:biDate value: %s"), string_locale
);
3036 free(string_locale
);
3043 if (err
) zfree(*biDate
);
3045 xmlXPathFreeObject(result
);
3050 /* Convert isds:dBOwnerInfo XML tree into structure
3051 * @context is ISDS context
3052 * @db_owner_info is automatically reallocated box owner info structure
3053 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
3054 * In case of error @db_owner_info will be freed. */
3055 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
3056 struct isds_DbOwnerInfo
**db_owner_info
,
3057 xmlXPathContextPtr xpath_ctx
) {
3058 isds_error err
= IE_SUCCESS
;
3059 xmlXPathObjectPtr result
= NULL
;
3060 char *string
= NULL
;
3062 if (!context
) return IE_INVALID_CONTEXT
;
3063 if (!db_owner_info
) return IE_INVAL
;
3064 isds_DbOwnerInfo_free(db_owner_info
);
3065 if (!xpath_ctx
) return IE_INVAL
;
3068 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
3069 if (!*db_owner_info
) {
3074 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
3076 EXTRACT_STRING("isds:dbType", string
);
3078 (*db_owner_info
)->dbType
=
3079 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
3080 if (!(*db_owner_info
)->dbType
) {
3084 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
3086 zfree((*db_owner_info
)->dbType
);
3087 if (err
== IE_ENUM
) {
3089 char *string_locale
= _isds_utf82locale(string
);
3090 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
3092 free(string_locale
);
3099 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
3101 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
3103 if (err
) goto leave
;
3105 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3107 (*db_owner_info
)->birthInfo
=
3108 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3109 if (!(*db_owner_info
)->birthInfo
) {
3113 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3115 if (err
) goto leave
;
3116 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3117 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3118 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3119 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3120 !(*db_owner_info
)->birthInfo
->biCity
&&
3121 !(*db_owner_info
)->birthInfo
->biCounty
&&
3122 !(*db_owner_info
)->birthInfo
->biState
)
3123 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3125 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3126 if (err
) goto leave
;
3128 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3129 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3130 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3131 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3132 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3134 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3136 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3137 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3138 (*db_owner_info
)->dbOpenAddressing
);
3141 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3143 xmlXPathFreeObject(result
);
3148 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3149 * @context is session context
3150 * @owner is libisds structure with box description.
3151 * If @pfo_subtype is false, aifoIsds, address->adCode, address->adDistrict
3152 * members will be ignored. If @pfo_subtype is true, dbType, ic,
3153 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
3154 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
3156 * @pfo_subtype is false if tDbOwnerInfo tree should be built from the @owner.
3157 * It is true if tDbPersonalOwnerInfo tree should be built from the @owner.
3158 * The tree differs in subset of significant isds_DbOwnerInfo structure members.
3159 * @db_owner_info is XML element of XSD:tDbOwnerInfo or XSD:tdbPersonalOnwerInfo
3161 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3162 const struct isds_DbOwnerInfo
*owner
, _Bool pfo_subtype
,
3163 xmlNodePtr db_owner_info
) {
3165 isds_error err
= IE_SUCCESS
;
3167 xmlChar
*string
= NULL
;
3168 const xmlChar
*type_string
= NULL
;
3170 if (!context
) return IE_INVALID_CONTEXT
;
3171 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3174 /* XXX: All the elements except email and telNumber are mandatory. */
3175 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3176 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3179 INSERT_BOOLEAN(db_owner_info
, "aifoIsds", owner
->aifoIsds
);
3184 if (owner
->dbType
) {
3185 type_string
= isds_DbType2string(*(owner
->dbType
));
3187 isds_printf_message(context
, _("Invalid dbType value: %d"),
3193 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3195 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3198 INSERT_STRING(db_owner_info
, "pnFirstName",
3199 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnFirstName
);
3200 INSERT_STRING(db_owner_info
, "pnMiddleName",
3201 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnMiddleName
);
3202 INSERT_STRING(db_owner_info
, "pnLastName",
3203 (NULL
== owner
->personName
) ? NULL
: owner
->personName
->pnLastName
);
3205 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3206 (NULL
== owner
->personName
) ? NULL
:
3207 owner
->personName
->pnLastNameAtBirth
);
3209 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3212 if (NULL
!= owner
->birthInfo
&& NULL
!= owner
->birthInfo
->biDate
) {
3213 err
= tm2datestring(owner
->birthInfo
->biDate
, &string
);
3214 if (err
) goto leave
;
3216 INSERT_STRING(db_owner_info
, "biDate", string
);
3219 INSERT_STRING(db_owner_info
, "biCity",
3220 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCity
);
3221 INSERT_STRING(db_owner_info
, "biCounty",
3222 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biCounty
);
3223 INSERT_STRING(db_owner_info
, "biState",
3224 (NULL
== owner
->birthInfo
) ? NULL
: owner
->birthInfo
->biState
);
3227 INSERT_LONGINT(db_owner_info
, "adCode",
3228 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCode
,
3231 INSERT_STRING(db_owner_info
, "adCity",
3232 (NULL
== owner
->address
) ? NULL
: owner
->address
->adCity
);
3234 INSERT_STRING(db_owner_info
, "adDistrict",
3235 (NULL
== owner
->address
) ? NULL
: owner
->address
->adDistrict
);
3237 INSERT_STRING(db_owner_info
, "adStreet",
3238 (NULL
== owner
->address
) ? NULL
: owner
->address
->adStreet
);
3239 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3240 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInStreet
);
3241 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3242 (NULL
== owner
->address
) ? NULL
: owner
->address
->adNumberInMunicipality
);
3243 INSERT_STRING(db_owner_info
, "adZipCode",
3244 (NULL
== owner
->address
) ? NULL
: owner
->address
->adZipCode
);
3245 INSERT_STRING(db_owner_info
, "adState",
3246 (NULL
== owner
->address
) ? NULL
: owner
->address
->adState
);
3248 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3251 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3252 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3254 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3255 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3257 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3258 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3260 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3262 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3263 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3264 owner
->dbOpenAddressing
);
3273 /* Convert XSD:tDbUserInfo XML tree into structure
3274 * @context is ISDS context
3275 * @db_user_info is automatically reallocated user info structure
3276 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3277 * In case of error @db_user_info will be freed. */
3278 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3279 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3280 isds_error err
= IE_SUCCESS
;
3281 xmlXPathObjectPtr result
= NULL
;
3282 char *string
= NULL
;
3284 if (!context
) return IE_INVALID_CONTEXT
;
3285 if (!db_user_info
) return IE_INVAL
;
3286 isds_DbUserInfo_free(db_user_info
);
3287 if (!xpath_ctx
) return IE_INVAL
;
3290 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3291 if (!*db_user_info
) {
3296 EXTRACT_STRING_ATTRIBUTE("AIFOTicket", (*db_user_info
)->aifo_ticket
, 0);
3298 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3300 EXTRACT_STRING("isds:userType", string
);
3302 (*db_user_info
)->userType
=
3303 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3304 if (!(*db_user_info
)->userType
) {
3308 err
= string2isds_UserType((xmlChar
*)string
,
3309 (*db_user_info
)->userType
);
3311 zfree((*db_user_info
)->userType
);
3312 if (err
== IE_ENUM
) {
3314 char *string_locale
= _isds_utf82locale(string
);
3315 isds_printf_message(context
,
3316 _("Unknown isds:userType value: %s"), string_locale
);
3317 free(string_locale
);
3324 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3326 (*db_user_info
)->personName
=
3327 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3328 if (!(*db_user_info
)->personName
) {
3333 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3335 if (err
) goto leave
;
3337 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3338 if (err
) goto leave
;
3340 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3341 if (err
) goto leave
;
3343 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3344 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3346 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3347 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3348 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3350 /* ???: Default value is "CZ" according specification. Should we provide
3352 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3355 if (err
) isds_DbUserInfo_free(db_user_info
);
3357 xmlXPathFreeObject(result
);
3362 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3363 * @context is session context
3364 * @user is libisds structure with user description
3365 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
3366 * @db_user_info is XML element of XSD:tDbUserInfo */
3367 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3368 const struct isds_DbUserInfo
*user
, _Bool honor_aifo_ticket
,
3369 xmlNodePtr db_user_info
) {
3371 isds_error err
= IE_SUCCESS
;
3373 xmlAttrPtr attribute_node
;
3374 xmlChar
*string
= NULL
;
3376 if (!context
) return IE_INVALID_CONTEXT
;
3377 if (!user
|| !db_user_info
) return IE_INVAL
;
3379 /* Build XSD:tDbUserInfo */
3381 /* XXX: Insert AIFOTicket attribute only when allowed. XML schema does not
3382 * allow it everywhere. */
3383 if (honor_aifo_ticket
&& user
->aifo_ticket
) {
3384 INSERT_STRING_ATTRIBUTE(db_user_info
, "AIFOTicket", user
->aifo_ticket
);
3387 if (user
->personName
) {
3388 INSERT_STRING(db_user_info
, "pnFirstName",
3389 user
->personName
->pnFirstName
);
3390 INSERT_STRING(db_user_info
, "pnMiddleName",
3391 user
->personName
->pnMiddleName
);
3392 INSERT_STRING(db_user_info
, "pnLastName",
3393 user
->personName
->pnLastName
);
3394 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3395 user
->personName
->pnLastNameAtBirth
);
3397 if (user
->address
) {
3398 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3399 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3400 INSERT_STRING(db_user_info
, "adNumberInStreet",
3401 user
->address
->adNumberInStreet
);
3402 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3403 user
->address
->adNumberInMunicipality
);
3404 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3405 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3408 if (!tm2datestring(user
->biDate
, &string
))
3409 INSERT_STRING(db_user_info
, "biDate", string
);
3412 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3413 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3416 if (user
->userType
) {
3417 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3419 isds_printf_message(context
, _("Invalid userType value: %d"),
3424 INSERT_STRING(db_user_info
, "userType", type_string
);
3427 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3428 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3429 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3430 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3431 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3432 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3433 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3434 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3435 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3443 /* Convert XSD:tPDZRec XML tree into structure
3444 * @context is ISDS context
3445 * @permission is automatically reallocated commercial permission structure
3446 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3447 * In case of error @permission will be freed. */
3448 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3449 struct isds_commercial_permission
**permission
,
3450 xmlXPathContextPtr xpath_ctx
) {
3451 isds_error err
= IE_SUCCESS
;
3452 xmlXPathObjectPtr result
= NULL
;
3453 char *string
= NULL
;
3455 if (!context
) return IE_INVALID_CONTEXT
;
3456 if (!permission
) return IE_INVAL
;
3457 isds_commercial_permission_free(permission
);
3458 if (!xpath_ctx
) return IE_INVAL
;
3461 *permission
= calloc(1, sizeof(**permission
));
3467 EXTRACT_STRING("isds:PDZType", string
);
3469 err
= string2isds_payment_type((xmlChar
*)string
,
3470 &(*permission
)->type
);
3472 if (err
== IE_ENUM
) {
3474 char *string_locale
= _isds_utf82locale(string
);
3475 isds_printf_message(context
,
3476 _("Unknown isds:PDZType value: %s"), string_locale
);
3477 free(string_locale
);
3484 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3485 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3487 EXTRACT_STRING("isds:PDZExpire", string
);
3489 err
= timestring2timeval((xmlChar
*) string
,
3490 &((*permission
)->expiration
));
3492 char *string_locale
= _isds_utf82locale(string
);
3493 if (err
== IE_DATE
) err
= IE_ISDS
;
3494 isds_printf_message(context
,
3495 _("Could not convert PDZExpire as ISO time: %s"),
3497 free(string_locale
);
3503 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3504 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3507 if (err
) isds_commercial_permission_free(permission
);
3509 xmlXPathFreeObject(result
);
3514 /* Convert XSD:tCiRecord XML tree into structure
3515 * @context is ISDS context
3516 * @event is automatically reallocated commercial credit event structure
3517 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3518 * In case of error @event will be freed. */
3519 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3520 struct isds_credit_event
**event
,
3521 xmlXPathContextPtr xpath_ctx
) {
3522 isds_error err
= IE_SUCCESS
;
3523 xmlXPathObjectPtr result
= NULL
;
3524 char *string
= NULL
;
3525 long int *number_ptr
;
3527 if (!context
) return IE_INVALID_CONTEXT
;
3528 if (!event
) return IE_INVAL
;
3529 isds_credit_event_free(event
);
3530 if (!xpath_ctx
) return IE_INVAL
;
3533 *event
= calloc(1, sizeof(**event
));
3539 EXTRACT_STRING("isds:ciEventTime", string
);
3541 err
= timestring2timeval((xmlChar
*) string
,
3544 char *string_locale
= _isds_utf82locale(string
);
3545 if (err
== IE_DATE
) err
= IE_ISDS
;
3546 isds_printf_message(context
,
3547 _("Could not convert ciEventTime as ISO time: %s"),
3549 free(string_locale
);
3555 EXTRACT_STRING("isds:ciEventType", string
);
3557 err
= string2isds_credit_event_type((xmlChar
*)string
,
3560 if (err
== IE_ENUM
) {
3562 char *string_locale
= _isds_utf82locale(string
);
3563 isds_printf_message(context
,
3564 _("Unknown isds:ciEventType value: %s"), string_locale
);
3565 free(string_locale
);
3572 number_ptr
= &((*event
)->credit_change
);
3573 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3574 number_ptr
= &(*event
)->new_credit
;
3575 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3577 switch((*event
)->type
) {
3578 case ISDS_CREDIT_CHARGED
:
3579 EXTRACT_STRING("isds:ciTransID",
3580 (*event
)->details
.charged
.transaction
);
3582 case ISDS_CREDIT_DISCHARGED
:
3583 EXTRACT_STRING("isds:ciTransID",
3584 (*event
)->details
.discharged
.transaction
);
3586 case ISDS_CREDIT_MESSAGE_SENT
:
3587 EXTRACT_STRING("isds:ciRecipientID",
3588 (*event
)->details
.message_sent
.recipient
);
3589 EXTRACT_STRING("isds:ciPDZID",
3590 (*event
)->details
.message_sent
.message_id
);
3592 case ISDS_CREDIT_STORAGE_SET
:
3593 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3594 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3595 EXTRACT_DATE("isds:ciNewFrom",
3596 (*event
)->details
.storage_set
.new_valid_from
);
3597 EXTRACT_DATE("isds:ciNewTo",
3598 (*event
)->details
.storage_set
.new_valid_to
);
3599 EXTRACT_LONGINT("isds:ciOldCapacity",
3600 (*event
)->details
.storage_set
.old_capacity
, 0);
3601 EXTRACT_DATE("isds:ciOldFrom",
3602 (*event
)->details
.storage_set
.old_valid_from
);
3603 EXTRACT_DATE("isds:ciOldTo",
3604 (*event
)->details
.storage_set
.old_valid_to
);
3605 EXTRACT_STRING("isds:ciDoneBy",
3606 (*event
)->details
.storage_set
.initiator
);
3608 case ISDS_CREDIT_EXPIRED
:
3613 if (err
) isds_credit_event_free(event
);
3615 xmlXPathFreeObject(result
);
3620 #endif /* HAVE_LIBCURL */
3623 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3624 * isds_envelope structure. The envelope is automatically allocated but not
3625 * reallocated. The date are just appended into envelope structure.
3626 * @context is ISDS context
3627 * @envelope is automatically allocated message envelope structure
3628 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3629 * In case of error @envelope will be freed. */
3630 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3631 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3632 isds_error err
= IE_SUCCESS
;
3633 xmlXPathObjectPtr result
= NULL
;
3635 if (!context
) return IE_INVALID_CONTEXT
;
3636 if (!envelope
) return IE_INVAL
;
3637 if (!xpath_ctx
) return IE_INVAL
;
3641 /* Allocate envelope */
3642 *envelope
= calloc(1, sizeof(**envelope
));
3648 /* Else free former data */
3649 zfree((*envelope
)->dmSenderOrgUnit
);
3650 zfree((*envelope
)->dmSenderOrgUnitNum
);
3651 zfree((*envelope
)->dbIDRecipient
);
3652 zfree((*envelope
)->dmRecipientOrgUnit
);
3653 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3654 zfree((*envelope
)->dmToHands
);
3655 zfree((*envelope
)->dmAnnotation
);
3656 zfree((*envelope
)->dmRecipientRefNumber
);
3657 zfree((*envelope
)->dmSenderRefNumber
);
3658 zfree((*envelope
)->dmRecipientIdent
);
3659 zfree((*envelope
)->dmSenderIdent
);
3660 zfree((*envelope
)->dmLegalTitleLaw
);
3661 zfree((*envelope
)->dmLegalTitleYear
);
3662 zfree((*envelope
)->dmLegalTitleSect
);
3663 zfree((*envelope
)->dmLegalTitlePar
);
3664 zfree((*envelope
)->dmLegalTitlePoint
);
3665 zfree((*envelope
)->dmPersonalDelivery
);
3666 zfree((*envelope
)->dmAllowSubstDelivery
);
3669 /* Extract envelope elements added by sender or ISDS
3670 * (XSD: gMessageEnvelopeSub type) */
3671 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3672 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3673 (*envelope
)->dmSenderOrgUnitNum
, 0);
3674 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3675 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3676 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3677 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3678 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3679 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3680 EXTRACT_STRING("isds:dmRecipientRefNumber",
3681 (*envelope
)->dmRecipientRefNumber
);
3682 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3683 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3684 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3686 /* Extract envelope elements regarding law reference */
3687 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3688 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3689 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3690 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3691 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3693 /* Extract envelope other elements */
3694 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3695 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3696 (*envelope
)->dmAllowSubstDelivery
);
3699 if (err
) isds_envelope_free(envelope
);
3700 xmlXPathFreeObject(result
);
3706 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3707 * isds_envelope structure. The envelope is automatically allocated but not
3708 * reallocated. The date are just appended into envelope structure.
3709 * @context is ISDS context
3710 * @envelope is automatically allocated message envelope structure
3711 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3712 * In case of error @envelope will be freed. */
3713 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3714 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3715 isds_error err
= IE_SUCCESS
;
3716 xmlXPathObjectPtr result
= NULL
;
3718 if (!context
) return IE_INVALID_CONTEXT
;
3719 if (!envelope
) return IE_INVAL
;
3720 if (!xpath_ctx
) return IE_INVAL
;
3724 /* Allocate envelope */
3725 *envelope
= calloc(1, sizeof(**envelope
));
3731 /* Else free former data */
3732 zfree((*envelope
)->dmID
);
3733 zfree((*envelope
)->dbIDSender
);
3734 zfree((*envelope
)->dmSender
);
3735 zfree((*envelope
)->dmSenderAddress
);
3736 zfree((*envelope
)->dmSenderType
);
3737 zfree((*envelope
)->dmRecipient
);
3738 zfree((*envelope
)->dmRecipientAddress
);
3739 zfree((*envelope
)->dmAmbiguousRecipient
);
3742 /* Extract envelope elements added by ISDS
3743 * (XSD: gMessageEnvelope type) */
3744 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3745 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3746 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3747 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3748 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3749 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3750 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3751 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3752 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3753 (*envelope
)->dmAmbiguousRecipient
);
3755 /* Extract envelope elements added by sender and ISDS
3756 * (XSD: gMessageEnvelope type) */
3757 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3758 if (err
) goto leave
;
3761 if (err
) isds_envelope_free(envelope
);
3762 xmlXPathFreeObject(result
);
3767 /* Convert other envelope elements from XML tree into isds_envelope structure:
3768 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3769 * The envelope is automatically allocated but not reallocated.
3770 * The data are just appended into envelope structure.
3771 * @context is ISDS context
3772 * @envelope is automatically allocated message envelope structure
3773 * @xpath_ctx is XPath context with current node as parent desired elements
3774 * In case of error @envelope will be freed. */
3775 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3776 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3777 isds_error err
= IE_SUCCESS
;
3778 xmlXPathObjectPtr result
= NULL
;
3779 char *string
= NULL
;
3780 unsigned long int *unumber
= NULL
;
3782 if (!context
) return IE_INVALID_CONTEXT
;
3783 if (!envelope
) return IE_INVAL
;
3784 if (!xpath_ctx
) return IE_INVAL
;
3789 *envelope
= calloc(1, sizeof(**envelope
));
3796 zfree((*envelope
)->dmMessageStatus
);
3797 zfree((*envelope
)->dmAttachmentSize
);
3798 zfree((*envelope
)->dmDeliveryTime
);
3799 zfree((*envelope
)->dmAcceptanceTime
);
3803 /* dmMessageStatus element is mandatory */
3804 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3806 isds_log_message(context
,
3807 _("Missing mandatory sisds:dmMessageStatus integer"));
3811 err
= uint2isds_message_status(context
, unumber
,
3812 &((*envelope
)->dmMessageStatus
));
3814 if (err
== IE_ENUM
) err
= IE_ISDS
;
3817 free(unumber
); unumber
= NULL
;
3819 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3822 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3824 err
= timestring2timeval((xmlChar
*) string
,
3825 &((*envelope
)->dmDeliveryTime
));
3827 char *string_locale
= _isds_utf82locale(string
);
3828 if (err
== IE_DATE
) err
= IE_ISDS
;
3829 isds_printf_message(context
,
3830 _("Could not convert dmDeliveryTime as ISO time: %s"),
3832 free(string_locale
);
3838 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3840 err
= timestring2timeval((xmlChar
*) string
,
3841 &((*envelope
)->dmAcceptanceTime
));
3843 char *string_locale
= _isds_utf82locale(string
);
3844 if (err
== IE_DATE
) err
= IE_ISDS
;
3845 isds_printf_message(context
,
3846 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3848 free(string_locale
);
3855 if (err
) isds_envelope_free(envelope
);
3858 xmlXPathFreeObject(result
);
3863 /* Convert message type attribute of current element into isds_envelope
3865 * TODO: This function can be incorporated into append_status_size_times() as
3866 * they are called always together.
3867 * The envelope is automatically allocated but not reallocated.
3868 * The data are just appended into envelope structure.
3869 * @context is ISDS context
3870 * @envelope is automatically allocated message envelope structure
3871 * @xpath_ctx is XPath context with current node as parent of attribute
3872 * carrying message type
3873 * In case of error @envelope will be freed. */
3874 static isds_error
append_message_type(struct isds_ctx
*context
,
3875 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3876 isds_error err
= IE_SUCCESS
;
3878 if (!context
) return IE_INVALID_CONTEXT
;
3879 if (!envelope
) return IE_INVAL
;
3880 if (!xpath_ctx
) return IE_INVAL
;
3885 *envelope
= calloc(1, sizeof(**envelope
));
3892 zfree((*envelope
)->dmType
);
3896 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3898 if (!(*envelope
)->dmType
) {
3899 /* Use default value */
3900 (*envelope
)->dmType
= strdup("V");
3901 if (!(*envelope
)->dmType
) {
3905 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3906 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3907 isds_printf_message(context
,
3908 _("Message type in dmType attribute is not 1 character long: "
3917 if (err
) isds_envelope_free(envelope
);
3923 /* Convert dmType isds_envelope member into XML attribute and append it to
3925 * @context is ISDS context
3926 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3927 * @dm_envelope is XML element the resulting attribute will be appended to.
3928 * @return error code, in case of error context' message is filled. */
3929 static isds_error
insert_message_type(struct isds_ctx
*context
,
3930 const char *type
, xmlNodePtr dm_envelope
) {
3931 isds_error err
= IE_SUCCESS
;
3932 xmlAttrPtr attribute_node
;
3934 if (!context
) return IE_INVALID_CONTEXT
;
3935 if (!dm_envelope
) return IE_INVAL
;
3937 /* Insert optional message type */
3939 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3940 char *type_locale
= _isds_utf82locale(type
);
3941 isds_printf_message(context
,
3942 _("Message type in envelope is not 1 character long: %s"),
3948 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3954 #endif /* HAVE_LIBCURL */
3957 /* Extract message document into reallocated document structure
3958 * @context is ISDS context
3959 * @document is automatically reallocated message documents structure
3960 * @xpath_ctx is XPath context with current node as isds:dmFile
3961 * In case of error @document will be freed. */
3962 static isds_error
extract_document(struct isds_ctx
*context
,
3963 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3964 isds_error err
= IE_SUCCESS
;
3965 xmlXPathObjectPtr result
= NULL
;
3966 xmlNodePtr file_node
;
3967 char *string
= NULL
;
3969 if (!context
) return IE_INVALID_CONTEXT
;
3970 if (!document
) return IE_INVAL
;
3971 isds_document_free(document
);
3972 if (!xpath_ctx
) return IE_INVAL
;
3973 file_node
= xpath_ctx
->node
;
3975 *document
= calloc(1, sizeof(**document
));
3981 /* Extract document meta data */
3982 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3983 if (context
->normalize_mime_type
) {
3984 const char *normalized_type
=
3985 isds_normalize_mime_type((*document
)->dmMimeType
);
3986 if (NULL
!= normalized_type
&&
3987 normalized_type
!= (*document
)->dmMimeType
) {
3988 char *new_type
= strdup(normalized_type
);
3989 if (NULL
== new_type
) {
3990 isds_printf_message(context
,
3991 _("Not enough memory to normalize document MIME type"));
3995 free((*document
)->dmMimeType
);
3996 (*document
)->dmMimeType
= new_type
;
4000 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
4001 err
= string2isds_FileMetaType((xmlChar
*)string
,
4002 &((*document
)->dmFileMetaType
));
4004 char *meta_type_locale
= _isds_utf82locale(string
);
4005 isds_printf_message(context
,
4006 _("Document has invalid dmFileMetaType attribute value: %s"),
4008 free(meta_type_locale
);
4014 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
4015 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
4016 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
4017 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
4020 /* Extract document data.
4021 * Base64 encoded blob or XML subtree must be presented. */
4023 /* Check for dmEncodedContent */
4024 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
4031 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4032 /* Here we have Base64 blob */
4033 (*document
)->is_xml
= 0;
4035 if (result
->nodesetval
->nodeNr
> 1) {
4036 isds_printf_message(context
,
4037 _("Document has more dmEncodedContent elements"));
4042 xmlXPathFreeObject(result
); result
= NULL
;
4043 EXTRACT_STRING("isds:dmEncodedContent", string
);
4045 /* Decode non-empty document */
4046 if (string
&& string
[0] != '\0') {
4047 (*document
)->data_length
=
4048 _isds_b64decode(string
, &((*document
)->data
));
4049 if ((*document
)->data_length
== (size_t) -1) {
4050 isds_printf_message(context
,
4051 _("Error while Base64-decoding document content"));
4057 /* No Base64 blob, try XML document */
4058 xmlXPathFreeObject(result
); result
= NULL
;
4059 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
4066 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4067 /* Here we have XML document */
4068 (*document
)->is_xml
= 1;
4070 if (result
->nodesetval
->nodeNr
> 1) {
4071 isds_printf_message(context
,
4072 _("Document has more dmXMLContent elements"));
4077 /* XXX: We cannot serialize the content simply because:
4078 * - XML document may point out of its scope (e.g. to message
4080 * - isds:dmXMLContent can contain more elements, no element,
4082 * - it's not the XML way
4083 * Thus we provide the only right solution: XML DOM. Let's
4084 * application to cope with this hot potato :) */
4085 (*document
)->xml_node_list
=
4086 result
->nodesetval
->nodeTab
[0]->children
;
4088 /* No base64 blob, nor XML document */
4089 isds_printf_message(context
,
4090 _("Document has no dmEncodedContent, nor dmXMLContent "
4099 if (err
) isds_document_free(document
);
4101 xmlXPathFreeObject(result
);
4102 xpath_ctx
->node
= file_node
;
4108 /* Extract message documents into reallocated list of documents
4109 * @context is ISDS context
4110 * @documents is automatically reallocated message documents list structure
4111 * @xpath_ctx is XPath context with current node as XSD tFilesArray
4112 * In case of error @documents will be freed. */
4113 static isds_error
extract_documents(struct isds_ctx
*context
,
4114 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
4115 isds_error err
= IE_SUCCESS
;
4116 xmlXPathObjectPtr result
= NULL
;
4117 xmlNodePtr files_node
;
4118 struct isds_list
*document
, *prev_document
= NULL
;
4120 if (!context
) return IE_INVALID_CONTEXT
;
4121 if (!documents
) return IE_INVAL
;
4122 isds_list_free(documents
);
4123 if (!xpath_ctx
) return IE_INVAL
;
4124 files_node
= xpath_ctx
->node
;
4126 /* Find documents */
4127 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
4134 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4135 isds_printf_message(context
,
4136 _("Message does not contain any document"));
4142 /* Iterate over documents */
4143 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4145 /* Allocate and append list item */
4146 document
= calloc(1, sizeof(*document
));
4151 document
->destructor
= (void (*)(void **))isds_document_free
;
4152 if (i
== 0) *documents
= document
;
4153 else prev_document
->next
= document
;
4154 prev_document
= document
;
4156 /* Extract document */
4157 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4158 err
= extract_document(context
,
4159 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4160 if (err
) goto leave
;
4165 if (err
) isds_list_free(documents
);
4166 xmlXPathFreeObject(result
);
4167 xpath_ctx
->node
= files_node
;
4173 /* Convert isds:dmRecord XML tree into structure
4174 * @context is ISDS context
4175 * @envelope is automatically reallocated message envelope structure
4176 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4177 * In case of error @envelope will be freed. */
4178 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4179 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4180 isds_error err
= IE_SUCCESS
;
4181 xmlXPathObjectPtr result
= NULL
;
4183 if (!context
) return IE_INVALID_CONTEXT
;
4184 if (!envelope
) return IE_INVAL
;
4185 isds_envelope_free(envelope
);
4186 if (!xpath_ctx
) return IE_INVAL
;
4189 *envelope
= calloc(1, sizeof(**envelope
));
4196 /* Extract tRecord data */
4197 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4199 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4200 * dmAcceptanceTime. */
4201 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4202 if (err
) goto leave
;
4204 /* Extract envelope elements added by sender and ISDS
4205 * (XSD: gMessageEnvelope type) */
4206 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4207 if (err
) goto leave
;
4209 /* Get message type */
4210 err
= append_message_type(context
, envelope
, xpath_ctx
);
4211 if (err
) goto leave
;
4215 if (err
) isds_envelope_free(envelope
);
4216 xmlXPathFreeObject(result
);
4221 /* Convert XSD:tStateChangesRecord type XML tree into structure
4222 * @context is ISDS context
4223 * @changed_status is automatically reallocated message state change structure
4224 * @xpath_ctx is XPath context with current node as element of
4225 * XSD:tStateChangesRecord type
4226 * In case of error @changed_status will be freed. */
4227 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4228 struct isds_message_status_change
**changed_status
,
4229 xmlXPathContextPtr xpath_ctx
) {
4230 isds_error err
= IE_SUCCESS
;
4231 xmlXPathObjectPtr result
= NULL
;
4232 unsigned long int *unumber
= NULL
;
4233 char *string
= NULL
;
4235 if (!context
) return IE_INVALID_CONTEXT
;
4236 if (!changed_status
) return IE_INVAL
;
4237 isds_message_status_change_free(changed_status
);
4238 if (!xpath_ctx
) return IE_INVAL
;
4241 *changed_status
= calloc(1, sizeof(**changed_status
));
4242 if (!*changed_status
) {
4248 /* Extract tGetStateChangesInput data */
4249 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4251 /* dmEventTime is mandatory */
4252 EXTRACT_STRING("isds:dmEventTime", string
);
4254 err
= timestring2timeval((xmlChar
*) string
,
4255 &((*changed_status
)->time
));
4257 char *string_locale
= _isds_utf82locale(string
);
4258 if (err
== IE_DATE
) err
= IE_ISDS
;
4259 isds_printf_message(context
,
4260 _("Could not convert dmEventTime as ISO time: %s"),
4262 free(string_locale
);
4268 /* dmMessageStatus element is mandatory */
4269 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4271 isds_log_message(context
,
4272 _("Missing mandatory isds:dmMessageStatus integer"));
4276 err
= uint2isds_message_status(context
, unumber
,
4277 &((*changed_status
)->dmMessageStatus
));
4279 if (err
== IE_ENUM
) err
= IE_ISDS
;
4288 if (err
) isds_message_status_change_free(changed_status
);
4289 xmlXPathFreeObject(result
);
4292 #endif /* HAVE_LIBCURL */
4295 /* Find and convert isds:dmHash XML tree into structure
4296 * @context is ISDS context
4297 * @envelope is automatically reallocated message hash structure
4298 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4299 * In case of error @hash will be freed. */
4300 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4301 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4302 isds_error err
= IE_SUCCESS
;
4303 xmlNodePtr old_ctx_node
;
4304 xmlXPathObjectPtr result
= NULL
;
4305 char *string
= NULL
;
4307 if (!context
) return IE_INVALID_CONTEXT
;
4308 if (!hash
) return IE_INVAL
;
4309 isds_hash_free(hash
);
4310 if (!xpath_ctx
) return IE_INVAL
;
4312 old_ctx_node
= xpath_ctx
->node
;
4314 *hash
= calloc(1, sizeof(**hash
));
4321 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4322 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4331 /* Get hash algorithm */
4332 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4333 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4335 if (err
== IE_ENUM
) {
4336 char *string_locale
= _isds_utf82locale(string
);
4337 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4339 free(string_locale
);
4345 /* Get hash value */
4346 EXTRACT_STRING(".", string
);
4348 isds_printf_message(context
,
4349 _("sisds:dmHash element is missing hash value"));
4353 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4354 if ((*hash
)->length
== (size_t) -1) {
4355 isds_printf_message(context
,
4356 _("Error while Base64-decoding hash value"));
4362 if (err
) isds_hash_free(hash
);
4364 xmlXPathFreeObject(result
);
4365 xpath_ctx
->node
= old_ctx_node
;
4370 /* Find and append isds:dmQTimestamp XML tree into envelope.
4371 * Because one service is allowed to miss time-stamp content, and we think
4372 * other could too (flaw in specification), this function is deliberated and
4373 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4374 * @context is ISDS context
4375 * @envelope is automatically allocated envelope structure
4376 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4378 * In case of error @envelope will be freed. */
4379 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4380 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4381 isds_error err
= IE_SUCCESS
;
4382 xmlXPathObjectPtr result
= NULL
;
4383 char *string
= NULL
;
4385 if (!context
) return IE_INVALID_CONTEXT
;
4386 if (!envelope
) return IE_INVAL
;
4388 isds_envelope_free(envelope
);
4393 *envelope
= calloc(1, sizeof(**envelope
));
4399 zfree((*envelope
)->timestamp
);
4400 (*envelope
)->timestamp_length
= 0;
4403 /* Get dmQTimestamp */
4404 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4406 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4409 (*envelope
)->timestamp_length
=
4410 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4411 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4412 isds_printf_message(context
,
4413 _("Error while Base64-decoding time stamp value"));
4419 if (err
) isds_envelope_free(envelope
);
4421 xmlXPathFreeObject(result
);
4426 /* Convert XSD tReturnedMessage XML tree into message structure.
4427 * It does not store serialized XML tree into message->raw.
4428 * It does store (pointer to) parsed XML tree into message->xml if needed.
4429 * @context is ISDS context
4430 * @include_documents Use true if documents must be extracted
4431 * (tReturnedMessage XSD type), use false if documents shall be omitted
4432 * (tReturnedMessageEnvelope).
4433 * @message is automatically reallocated message structure
4434 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4436 * In case of error @message will be freed. */
4437 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4438 const _Bool include_documents
, struct isds_message
**message
,
4439 xmlXPathContextPtr xpath_ctx
) {
4440 isds_error err
= IE_SUCCESS
;
4441 xmlNodePtr message_node
;
4443 if (!context
) return IE_INVALID_CONTEXT
;
4444 if (!message
) return IE_INVAL
;
4445 isds_message_free(message
);
4446 if (!xpath_ctx
) return IE_INVAL
;
4449 *message
= calloc(1, sizeof(**message
));
4455 /* Save message XPATH context node */
4456 message_node
= xpath_ctx
->node
;
4460 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4461 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4462 if (err
) { err
= IE_ERROR
; goto leave
; }
4463 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4464 if (err
) goto leave
;
4466 if (include_documents
) {
4467 struct isds_list
*item
;
4469 /* Extract dmFiles */
4470 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4472 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4473 err
= IE_ISDS
; goto leave
;
4475 if (err
) { err
= IE_ERROR
; goto leave
; }
4476 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4477 if (err
) goto leave
;
4479 /* Store xmlDoc of this message if needed */
4480 /* Only if we got a XML document in all the documents. */
4481 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4482 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4483 (*message
)->xml
= xpath_ctx
->doc
;
4490 /* Restore context to message */
4491 xpath_ctx
->node
= message_node
;
4493 /* Extract dmHash */
4494 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4496 if (err
) goto leave
;
4498 /* Extract dmQTimestamp, */
4499 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4501 if (err
) goto leave
;
4503 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4504 * dmAcceptanceTime. */
4505 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4506 if (err
) goto leave
;
4508 /* Get message type */
4509 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4510 if (err
) goto leave
;
4513 if (err
) isds_message_free(message
);
4518 /* Extract message event into reallocated isds_event structure
4519 * @context is ISDS context
4520 * @event is automatically reallocated message event structure
4521 * @xpath_ctx is XPath context with current node as isds:dmEvent
4522 * In case of error @event will be freed. */
4523 static isds_error
extract_event(struct isds_ctx
*context
,
4524 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4525 isds_error err
= IE_SUCCESS
;
4526 xmlXPathObjectPtr result
= NULL
;
4527 xmlNodePtr event_node
;
4528 char *string
= NULL
;
4530 if (!context
) return IE_INVALID_CONTEXT
;
4531 if (!event
) return IE_INVAL
;
4532 isds_event_free(event
);
4533 if (!xpath_ctx
) return IE_INVAL
;
4534 event_node
= xpath_ctx
->node
;
4536 *event
= calloc(1, sizeof(**event
));
4542 /* Extract event data.
4543 * All elements are optional according XSD. That's funny. */
4544 EXTRACT_STRING("sisds:dmEventTime", string
);
4546 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4548 char *string_locale
= _isds_utf82locale(string
);
4549 if (err
== IE_DATE
) err
= IE_ISDS
;
4550 isds_printf_message(context
,
4551 _("Could not convert dmEventTime as ISO time: %s"),
4553 free(string_locale
);
4559 /* dmEventDescr element has prefix and the rest */
4560 EXTRACT_STRING("sisds:dmEventDescr", string
);
4562 err
= eventstring2event((xmlChar
*) string
, *event
);
4563 if (err
) goto leave
;
4568 if (err
) isds_event_free(event
);
4570 xmlXPathFreeObject(result
);
4571 xpath_ctx
->node
= event_node
;
4576 /* Convert element of XSD tEventsArray type from XML tree into
4577 * isds_list of isds_event's structure. The list is automatically reallocated.
4578 * @context is ISDS context
4579 * @events is automatically reallocated list of event structures
4580 * @xpath_ctx is XPath context with current node as tEventsArray
4581 * In case of error @events will be freed. */
4582 static isds_error
extract_events(struct isds_ctx
*context
,
4583 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4584 isds_error err
= IE_SUCCESS
;
4585 xmlXPathObjectPtr result
= NULL
;
4586 xmlNodePtr events_node
;
4587 struct isds_list
*event
, *prev_event
= NULL
;
4589 if (!context
) return IE_INVALID_CONTEXT
;
4590 if (!events
) return IE_INVAL
;
4591 if (!xpath_ctx
) return IE_INVAL
;
4592 events_node
= xpath_ctx
->node
;
4595 isds_list_free(events
);
4598 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4605 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4606 isds_printf_message(context
,
4607 _("Delivery info does not contain any event"));
4613 /* Iterate over events */
4614 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4616 /* Allocate and append list item */
4617 event
= calloc(1, sizeof(*event
));
4622 event
->destructor
= (void (*)(void **))isds_event_free
;
4623 if (i
== 0) *events
= event
;
4624 else prev_event
->next
= event
;
4628 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4629 err
= extract_event(context
,
4630 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4631 if (err
) goto leave
;
4636 if (err
) isds_list_free(events
);
4637 xmlXPathFreeObject(result
);
4638 xpath_ctx
->node
= events_node
;
4644 /* Insert Base64 encoded data as element with text child.
4645 * @context is session context
4646 * @parent is XML node to append @element with @data as child
4647 * @ns is XML namespace of @element, use NULL to inherit from @parent
4648 * @element is UTF-8 encoded name of new element
4649 * @data is bit stream to encode into @element
4650 * @length is size of @data in bytes
4651 * @return standard error code and fill long error message if needed */
4652 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4653 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4654 const void *data
, size_t length
) {
4655 isds_error err
= IE_SUCCESS
;
4658 if (!context
) return IE_INVALID_CONTEXT
;
4659 if (!data
&& length
> 0) return IE_INVAL
;
4660 if (!parent
|| !element
) return IE_INVAL
;
4662 xmlChar
*base64data
= NULL
;
4663 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4665 isds_printf_message(context
,
4666 ngettext("Not enough memory to encode %zd byte into Base64",
4667 "Not enough memory to encode %zd bytes into Base64",
4673 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4681 /* Convert isds_document structure into XML tree and append to dmFiles node.
4682 * @context is session context
4683 * @document is ISDS document
4684 * @dm_files is XML element the resulting tree will be appended to as a child.
4685 * @return error code, in case of error context' message is filled. */
4686 static isds_error
insert_document(struct isds_ctx
*context
,
4687 struct isds_document
*document
, xmlNodePtr dm_files
) {
4688 isds_error err
= IE_SUCCESS
;
4689 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4690 xmlAttrPtr attribute_node
;
4692 if (!context
) return IE_INVALID_CONTEXT
;
4693 if (!document
|| !dm_files
) return IE_INVAL
;
4695 /* Allocate new dmFile */
4696 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4698 isds_printf_message(context
, _("Could not allocate main dmFile"));
4702 /* Append the new dmFile.
4703 * XXX: Main document must go first */
4704 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4705 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4707 file
= xmlAddChild(dm_files
, new_file
);
4710 xmlFreeNode(new_file
); new_file
= NULL
;
4711 isds_printf_message(context
, _("Could not add dmFile child to "
4712 "%s element"), dm_files
->name
);
4717 /* @dmMimeType is required */
4718 if (!document
->dmMimeType
) {
4719 isds_log_message(context
,
4720 _("Document is missing mandatory MIME type definition"));
4724 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4726 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4728 isds_printf_message(context
,
4729 _("Document has unknown dmFileMetaType: %ld"),
4730 document
->dmFileMetaType
);
4734 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4736 if (document
->dmFileGuid
) {
4737 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4739 if (document
->dmUpFileGuid
) {
4740 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4743 /* @dmFileDescr is required */
4744 if (!document
->dmFileDescr
) {
4745 isds_log_message(context
,
4746 _("Document is missing mandatory description (title)"));
4750 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4752 if (document
->dmFormat
) {
4753 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4757 /* Insert content (body) of the document. */
4758 if (document
->is_xml
) {
4759 /* XML document requested */
4761 /* Allocate new dmXMLContent */
4762 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4764 isds_printf_message(context
,
4765 _("Could not allocate dmXMLContent element"));
4770 node
= xmlAddChild(file
, xmlcontent
);
4772 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4773 isds_printf_message(context
,
4774 _("Could not add dmXMLContent child to %s element"),
4780 /* Copy non-empty node list */
4781 if (document
->xml_node_list
) {
4782 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4783 document
->xml_node_list
);
4785 isds_printf_message(context
,
4786 _("Not enough memory to copy XML document"));
4791 if (!xmlAddChildList(node
, content
)) {
4792 xmlFreeNodeList(content
);
4793 isds_printf_message(context
,
4794 _("Error while adding XML document into dmXMLContent"));
4798 /* XXX: We cannot free the content here because it's part of node's
4799 * document since now. It will be freed with it automatically. */
4802 /* Binary document requested */
4803 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4804 document
->data
, document
->data_length
);
4805 if (err
) goto leave
;
4813 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4814 * The copy must be preallocated, the date are just appended into structure.
4815 * @context is ISDS context
4816 * @copy is message copy structure
4817 * @xpath_ctx is XPath context with current node as tMStatus */
4818 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4819 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4820 isds_error err
= IE_SUCCESS
;
4821 xmlXPathObjectPtr result
= NULL
;
4822 char *code
= NULL
, *message
= NULL
;
4824 if (!context
) return IE_INVALID_CONTEXT
;
4825 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4827 /* Free old values */
4828 zfree(copy
->dmStatus
);
4831 /* Get error specific to this copy */
4832 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4834 isds_log_message(context
,
4835 _("Missing isds:dmStatusCode under "
4836 "XSD:tMStatus type element"));
4841 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4842 /* This copy failed */
4843 copy
->error
= IE_ISDS
;
4844 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4846 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4847 if (!copy
->dmStatus
) {
4848 copy
->dmStatus
= code
;
4852 copy
->dmStatus
= code
;
4856 /* This copy succeeded. In this case only, message ID is valid */
4857 copy
->error
= IE_SUCCESS
;
4859 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4861 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4862 "but did not returned assigned message ID\n"));
4870 xmlXPathFreeObject(result
);
4875 /* Insert struct isds_approval data (box approval) into XML tree
4876 * @context is session context
4877 * @approval is libisds structure with approval description. NULL is
4879 * @parent is XML element to append @approval to */
4880 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4881 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4883 isds_error err
= IE_SUCCESS
;
4886 if (!context
) return IE_INVALID_CONTEXT
;
4887 if (!parent
) return IE_INVAL
;
4889 if (!approval
) return IE_SUCCESS
;
4891 /* Build XSD:gExtApproval */
4892 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4893 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4900 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4902 * @context is session context
4903 * @service_name is name of SERVICE_DB_ACCESS
4904 * @response is reallocated server SOAP body response as XML document
4905 * @raw_response is reallocated bit stream with response body. Use
4906 * NULL if you don't care
4907 * @raw_response_length is size of @raw_response in bytes
4908 * @code is reallocated ISDS status code
4909 * @status_message is reallocated ISDS status message
4910 * @return error coded from lower layer, context message will be set up
4912 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4913 const xmlChar
*service_name
,
4914 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4915 xmlChar
**code
, xmlChar
**status_message
) {
4917 isds_error err
= IE_SUCCESS
;
4918 char *service_name_locale
= NULL
;
4919 xmlNodePtr request
= NULL
, node
;
4920 xmlNsPtr isds_ns
= NULL
;
4922 if (!context
) return IE_INVALID_CONTEXT
;
4923 if (!service_name
) return IE_INVAL
;
4924 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4925 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4927 /* Free output argument */
4928 xmlFreeDoc(*response
); *response
= NULL
;
4929 if (raw_response
) zfree(*raw_response
);
4931 zfree(*status_message
);
4934 /* Check if connection is established
4935 * TODO: This check should be done downstairs. */
4936 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4938 service_name_locale
= _isds_utf82locale((char*)service_name
);
4939 if (!service_name_locale
) {
4945 request
= xmlNewNode(NULL
, service_name
);
4947 isds_printf_message(context
,
4948 _("Could not build %s request"), service_name_locale
);
4952 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4954 isds_log_message(context
, _("Could not create ISDS name space"));
4958 xmlSetNs(request
, isds_ns
);
4961 /* Add XSD:tDummyInput child */
4962 INSERT_STRING(request
, "dbDummy", NULL
);
4965 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4966 service_name_locale
);
4969 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4970 raw_response
, raw_response_length
);
4971 xmlFreeNode(request
); request
= NULL
;
4974 isds_log(ILF_ISDS
, ILL_DEBUG
,
4975 _("Processing ISDS response on %s request failed\n"),
4976 service_name_locale
);
4980 /* Check for response status */
4981 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4982 code
, status_message
, NULL
);
4984 isds_log(ILF_ISDS
, ILL_DEBUG
,
4985 _("ISDS response on %s request is missing status\n"),
4986 service_name_locale
);
4990 /* Request processed, but nothing found */
4991 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4992 char *code_locale
= _isds_utf82locale((char*) *code
);
4993 char *status_message_locale
=
4994 _isds_utf82locale((char*) *status_message
);
4995 isds_log(ILF_ISDS
, ILL_DEBUG
,
4996 _("Server refused %s request (code=%s, message=%s)\n"),
4997 service_name_locale
, code_locale
, status_message_locale
);
4998 isds_log_message(context
, status_message_locale
);
5000 free(status_message_locale
);
5006 free(service_name_locale
);
5007 xmlFreeNode(request
);
5013 /* Get data about logged in user and his box.
5014 * @context is session context
5015 * @db_owner_info is reallocated box owner description. It will be freed on
5017 * @return error code from lower layer, context message will be set up
5019 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
5020 struct isds_DbOwnerInfo
**db_owner_info
) {
5021 isds_error err
= IE_SUCCESS
;
5023 xmlDocPtr response
= NULL
;
5024 xmlChar
*code
= NULL
, *message
= NULL
;
5025 xmlXPathContextPtr xpath_ctx
= NULL
;
5026 xmlXPathObjectPtr result
= NULL
;
5027 char *string
= NULL
;
5030 if (!context
) return IE_INVALID_CONTEXT
;
5031 zfree(context
->long_message
);
5032 if (!db_owner_info
) return IE_INVAL
;
5033 isds_DbOwnerInfo_free(db_owner_info
);
5036 /* Check if connection is established */
5037 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5040 /* Do request and check for success */
5041 err
= build_send_check_dbdummy_request(context
,
5042 BAD_CAST
"GetOwnerInfoFromLogin",
5043 &response
, NULL
, NULL
, &code
, &message
);
5044 if (err
) goto leave
;
5048 /* Prepare structure */
5049 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
5050 if (!*db_owner_info
) {
5054 xpath_ctx
= xmlXPathNewContext(response
);
5059 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5064 /* Set context node */
5065 result
= xmlXPathEvalExpression(BAD_CAST
5066 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
5071 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5072 isds_log_message(context
, _("Missing dbOwnerInfo element"));
5076 if (result
->nodesetval
->nodeNr
> 1) {
5077 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
5081 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5082 xmlXPathFreeObject(result
); result
= NULL
;
5085 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
5090 isds_DbOwnerInfo_free(db_owner_info
);
5094 xmlXPathFreeObject(result
);
5095 xmlXPathFreeContext(xpath_ctx
);
5099 xmlFreeDoc(response
);
5102 isds_log(ILF_ISDS
, ILL_DEBUG
,
5103 _("GetOwnerInfoFromLogin request processed by server "
5104 "successfully.\n"));
5105 #else /* not HAVE_LIBCURL */
5113 /* Get data about logged in user. */
5114 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
5115 struct isds_DbUserInfo
**db_user_info
) {
5116 isds_error err
= IE_SUCCESS
;
5118 xmlDocPtr response
= NULL
;
5119 xmlChar
*code
= NULL
, *message
= NULL
;
5120 xmlXPathContextPtr xpath_ctx
= NULL
;
5121 xmlXPathObjectPtr result
= NULL
;
5124 if (!context
) return IE_INVALID_CONTEXT
;
5125 zfree(context
->long_message
);
5126 if (!db_user_info
) return IE_INVAL
;
5127 isds_DbUserInfo_free(db_user_info
);
5130 /* Check if connection is established */
5131 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5134 /* Do request and check for success */
5135 err
= build_send_check_dbdummy_request(context
,
5136 BAD_CAST
"GetUserInfoFromLogin",
5137 &response
, NULL
, NULL
, &code
, &message
);
5138 if (err
) goto leave
;
5142 /* Prepare structure */
5143 *db_user_info
= calloc(1, sizeof(**db_user_info
));
5144 if (!*db_user_info
) {
5148 xpath_ctx
= xmlXPathNewContext(response
);
5153 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5158 /* Set context node */
5159 result
= xmlXPathEvalExpression(BAD_CAST
5160 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5165 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5166 isds_log_message(context
, _("Missing dbUserInfo element"));
5170 if (result
->nodesetval
->nodeNr
> 1) {
5171 isds_log_message(context
, _("Multiple dbUserInfo element"));
5175 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5176 xmlXPathFreeObject(result
); result
= NULL
;
5179 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5183 isds_DbUserInfo_free(db_user_info
);
5186 xmlXPathFreeObject(result
);
5187 xmlXPathFreeContext(xpath_ctx
);
5191 xmlFreeDoc(response
);
5194 isds_log(ILF_ISDS
, ILL_DEBUG
,
5195 _("GetUserInfoFromLogin request processed by server "
5196 "successfully.\n"));
5197 #else /* not HAVE_LIBCURL */
5205 /* Get expiration time of current password
5206 * @context is session context
5207 * @expiration is automatically reallocated time when password expires. If
5208 * password expiration is disabled, NULL will be returned. In case of error
5209 * it will be nulled too. */
5210 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5211 struct timeval
**expiration
) {
5212 isds_error err
= IE_SUCCESS
;
5214 xmlDocPtr response
= NULL
;
5215 xmlChar
*code
= NULL
, *message
= NULL
;
5216 xmlXPathContextPtr xpath_ctx
= NULL
;
5217 xmlXPathObjectPtr result
= NULL
;
5218 char *string
= NULL
;
5221 if (!context
) return IE_INVALID_CONTEXT
;
5222 zfree(context
->long_message
);
5223 if (!expiration
) return IE_INVAL
;
5227 /* Check if connection is established */
5228 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5231 /* Do request and check for success */
5232 err
= build_send_check_dbdummy_request(context
,
5233 BAD_CAST
"GetPasswordInfo",
5234 &response
, NULL
, NULL
, &code
, &message
);
5235 if (err
) goto leave
;
5239 xpath_ctx
= xmlXPathNewContext(response
);
5244 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5249 /* Set context node */
5250 result
= xmlXPathEvalExpression(BAD_CAST
5251 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5256 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5257 isds_log_message(context
,
5258 _("Missing GetPasswordInfoResponse element"));
5262 if (result
->nodesetval
->nodeNr
> 1) {
5263 isds_log_message(context
,
5264 _("Multiple GetPasswordInfoResponse element"));
5268 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5269 xmlXPathFreeObject(result
); result
= NULL
;
5271 /* Extract expiration date */
5272 EXTRACT_STRING("isds:pswExpDate", string
);
5274 /* And convert it if any returned. Otherwise expiration is disabled. */
5275 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5277 char *string_locale
= _isds_utf82locale(string
);
5278 if (err
== IE_DATE
) err
= IE_ISDS
;
5279 isds_printf_message(context
,
5280 _("Could not convert pswExpDate as ISO time: %s"),
5282 free(string_locale
);
5295 xmlXPathFreeObject(result
);
5296 xmlXPathFreeContext(xpath_ctx
);
5300 xmlFreeDoc(response
);
5303 isds_log(ILF_ISDS
, ILL_DEBUG
,
5304 _("GetPasswordInfo request processed by server "
5305 "successfully.\n"));
5306 #else /* not HAVE_LIBCURL */
5315 /* Request delivering new TOTP code from ISDS through side channel before
5316 * changing password.
5317 * @context is session context
5318 * @password is current password.
5319 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5320 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5321 * function for more details.
5322 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5323 * NULL, if you don't care.
5324 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5326 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5327 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5328 isds_error err
= IE_SUCCESS
;
5329 char *saved_url
= NULL
; /* No copy */
5330 #if HAVE_CURL_REAUTHORIZATION_BUG
5331 CURL
*saved_curl
= NULL
; /* No copy */
5333 xmlNsPtr isds_ns
= NULL
;
5334 xmlNodePtr request
= NULL
;
5335 xmlDocPtr response
= NULL
;
5336 xmlChar
*code
= NULL
, *message
= NULL
;
5337 const xmlChar
*codes
[] = {
5342 const char *meanings
[] = {
5343 N_("Unexpected error"),
5344 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5345 N_("One-time code could not been sent. Try later again.")
5347 const isds_otp_resolution resolutions
[] = {
5348 OTP_RESOLUTION_UNKNOWN
,
5349 OTP_RESOLUTION_TO_FAST
,
5350 OTP_RESOLUTION_TOTP_NOT_SENT
5353 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5354 zfree(context
->long_message
);
5355 if (NULL
== password
) {
5356 isds_log_message(context
,
5357 _("Second argument (password) of isds_change_password() "
5362 /* Check if connection is established
5363 * TODO: This check should be done downstairs. */
5364 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5366 if (!context
->otp
) {
5367 isds_log_message(context
, _("This function requires OTP-authenticated "
5369 return IE_INVALID_CONTEXT
;
5372 isds_log_message(context
, _("If one-time password authentication "
5373 "method is in use, requesting new OTP code requires "
5374 "one-time credentials argument either"));
5377 if (otp
->method
!= OTP_TIME
) {
5378 isds_log_message(context
, _("Requesting new time-based OTP code from "
5379 "server requires one-time password authentication "
5383 if (otp
->otp_code
!= NULL
) {
5384 isds_log_message(context
, _("Requesting new time-based OTP code from "
5385 "server requires undefined OTP code member in "
5386 "one-time credentials argument"));
5392 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5394 isds_log_message(context
, _("Could not build SendSMSCode request"));
5397 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5399 isds_log_message(context
, _("Could not create ISDS name space"));
5400 xmlFreeNode(request
);
5403 xmlSetNs(request
, isds_ns
);
5405 /* Change URL temporarily for sending this request only */
5407 char *new_url
= NULL
;
5408 if ((err
= _isds_build_url_from_context(context
,
5409 "%1$.*2$sasws/changePassword", &new_url
))) {
5412 saved_url
= context
->url
;
5413 context
->url
= new_url
;
5416 /* Store credentials for sending this request only */
5417 context
->otp_credentials
= otp
;
5418 _isds_discard_credentials(context
, 0);
5419 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5421 _isds_discard_credentials(context
, 0);
5424 #if HAVE_CURL_REAUTHORIZATION_BUG
5425 saved_curl
= context
->curl
;
5426 context
->curl
= curl_easy_init();
5427 if (NULL
== context
->curl
) {
5431 if (context
->timeout
) {
5432 err
= isds_set_timeout(context
, context
->timeout
);
5433 if (err
) goto leave
;
5437 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5440 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5442 /* Remove temporal credentials */
5443 _isds_discard_credentials(context
, 0);
5444 /* Detach pointer to OTP credentials from context */
5445 context
->otp_credentials
= NULL
;
5446 /* Keep context->otp true to keep signaling this is OTP session */
5448 /* Destroy request */
5449 xmlFreeNode(request
); request
= NULL
;
5452 isds_log(ILF_ISDS
, ILL_DEBUG
,
5453 _("Processing ISDS response on SendSMSCode request failed\n"));
5457 /* Check for response status */
5458 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5459 &code
, &message
, (xmlChar
**)refnumber
);
5461 isds_log(ILF_ISDS
, ILL_DEBUG
,
5462 _("ISDS response on SendSMSCode request is missing "
5467 /* Check for error */
5468 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5469 char *code_locale
= _isds_utf82locale((char*)code
);
5470 char *message_locale
= _isds_utf82locale((char*)message
);
5472 isds_log(ILF_ISDS
, ILL_DEBUG
,
5473 _("Server refused to send new code on SendSMSCode "
5474 "request (code=%s, message=%s)\n"),
5475 code_locale
, message_locale
);
5477 /* Check for known error codes */
5478 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5479 if (!xmlStrcmp(code
, codes
[i
])) break;
5481 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5482 isds_log_message(context
, _(meanings
[i
]));
5483 /* Mimic otp->resolution according to the code, specification does
5484 * prescribe OTP header to be available. */
5485 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5486 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5487 otp
->resolution
= resolutions
[i
];
5489 isds_log_message(context
, message_locale
);
5492 free(message_locale
);
5498 /* Otherwise new code sent successfully */
5499 /* Mimic otp->resolution according to the code, specification does
5500 * prescribe OTP header to be available. */
5501 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5502 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5505 if (NULL
!= saved_url
) {
5506 /* Revert URL to original one */
5507 zfree(context
->url
);
5508 context
->url
= saved_url
;
5510 #if HAVE_CURL_REAUTHORIZATION_BUG
5511 if (NULL
!= saved_curl
) {
5512 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5513 context
->curl
= saved_curl
;
5519 xmlFreeDoc(response
);
5520 xmlFreeNode(request
);
5523 isds_log(ILF_ISDS
, ILL_DEBUG
,
5524 _("New OTP code has been sent successfully on SendSMSCode "
5530 /* Convert response status code to isds_error code and set long message
5531 * @context is context to save long message to
5532 * @map is mapping from codes to errors and messages. Pass NULL for generic
5534 * @code is status code to translate
5535 * @message is non-localized status message to put into long message in case
5536 * of uknown error. It can be NULL if server did not provide any.
5537 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5538 * invalid invocation. */
5539 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5540 const struct code_map_isds_error
*map
,
5541 const xmlChar
*code
, const xmlChar
*message
) {
5543 isds_log_message(context
,
5544 _("NULL status code passed to statuscode2isds_error()"));
5549 /* Check for known error codes */
5550 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5551 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5552 isds_log_message(context
, _(map
->meanings
[i
]));
5553 return map
->errors
[i
];
5559 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5560 char *message_locale
= _isds_utf82locale((char*)message
);
5561 if (NULL
== message_locale
)
5562 isds_log_message(context
, _("ISDS server returned unknown error"));
5564 isds_log_message(context
, message_locale
);
5565 free(message_locale
);
5574 /* Change user password in ISDS.
5575 * User must supply old password, new password will takes effect after some
5576 * time, current session can continue. Password must fulfill some constraints.
5577 * @context is session context
5578 * @old_password is current password.
5579 * @new_password is requested new password
5580 * @otp auxiliary data required if one-time password authentication is in use,
5581 * defines OTP code (if known) and returns fine grade resolution of OTP
5582 * procedure. Pass NULL, if one-time password authentication is not needed.
5583 * Please note the @otp argument must match OTP method used at log-in time. See
5584 * isds_login() function for more details.
5585 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5586 * NULL, if you don't care.
5587 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5588 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5589 * awaiting OTP code that has been delivered by side channel to the user. */
5590 isds_error
isds_change_password(struct isds_ctx
*context
,
5591 const char *old_password
, const char *new_password
,
5592 struct isds_otp
*otp
, char **refnumber
) {
5593 isds_error err
= IE_SUCCESS
;
5595 char *saved_url
= NULL
; /* No copy */
5596 #if HAVE_CURL_REAUTHORIZATION_BUG
5597 CURL
*saved_curl
= NULL
; /* No copy */
5599 xmlNsPtr isds_ns
= NULL
;
5600 xmlNodePtr request
= NULL
, node
;
5601 xmlDocPtr response
= NULL
;
5602 xmlChar
*code
= NULL
, *message
= NULL
;
5603 const xmlChar
*codes
[] = {
5616 const char *meanings
[] = {
5617 N_("Password length must be between 8 and 32 characters"),
5618 N_("Password cannot be reused"), /* Server does not distinguish 1067
5619 and 1091 on ChangePasswordOTP */
5620 N_("Password contains forbidden character"),
5621 N_("Password must contain at least one upper-case letter, "
5622 "one lower-case, and one digit"),
5623 N_("Password cannot contain sequence of three identical characters"),
5624 N_("Password cannot contain user identifier"),
5625 N_("Password is too simmple"),
5626 N_("Old password is not valid"),
5627 N_("Password cannot be reused"),
5628 N_("Unexpected error"),
5629 N_("LDAP update error")
5633 if (!context
) return IE_INVALID_CONTEXT
;
5634 zfree(context
->long_message
);
5635 if (NULL
!= refnumber
)
5637 if (NULL
== old_password
) {
5638 isds_log_message(context
,
5639 _("Second argument (old password) of isds_change_password() "
5643 if (NULL
== otp
&& NULL
== new_password
) {
5644 isds_log_message(context
,
5645 _("Third argument (new password) of isds_change_password() "
5651 /* Check if connection is established
5652 * TODO: This check should be done downstairs. */
5653 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5655 if (context
->otp
&& NULL
== otp
) {
5656 isds_log_message(context
, _("If one-time password authentication "
5657 "method is in use, changing password requires one-time "
5658 "credentials either"));
5662 /* Build ChangeISDSPassword request */
5663 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5664 BAD_CAST
"ChangePasswordOTP");
5666 isds_log_message(context
, (NULL
== otp
) ?
5667 _("Could not build ChangeISDSPassword request") :
5668 _("Could not build ChangePasswordOTP request"));
5671 isds_ns
= xmlNewNs(request
,
5672 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5675 isds_log_message(context
, _("Could not create ISDS name space"));
5676 xmlFreeNode(request
);
5679 xmlSetNs(request
, isds_ns
);
5681 INSERT_STRING(request
, "dbOldPassword", old_password
);
5682 INSERT_STRING(request
, "dbNewPassword", new_password
);
5685 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5686 switch (otp
->method
) {
5688 isds_log(ILF_SEC
, ILL_INFO
,
5689 _("Selected authentication method: "
5690 "HMAC-based one-time password\n"));
5691 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5694 isds_log(ILF_SEC
, ILL_INFO
,
5695 _("Selected authentication method: "
5696 "Time-based one-time password\n"));
5697 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5698 if (otp
->otp_code
== NULL
) {
5699 isds_log(ILF_SEC
, ILL_INFO
,
5700 _("OTP code has not been provided by "
5701 "application, requesting server for "
5703 err
= _isds_request_totp_code(context
, old_password
, otp
,
5705 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5709 isds_log(ILF_SEC
, ILL_INFO
,
5710 _("OTP code has been provided by "
5711 "application, not requesting server "
5716 isds_log_message(context
,
5717 _("Unknown one-time password authentication "
5718 "method requested by application"));
5723 /* Change URL temporarily for sending this request only */
5725 char *new_url
= NULL
;
5726 if ((err
= _isds_build_url_from_context(context
,
5727 "%1$.*2$sasws/changePassword", &new_url
))) {
5730 saved_url
= context
->url
;
5731 context
->url
= new_url
;
5734 /* Store credentials for sending this request only */
5735 context
->otp_credentials
= otp
;
5736 _isds_discard_credentials(context
, 0);
5737 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5738 old_password
, NULL
))) {
5739 _isds_discard_credentials(context
, 0);
5742 #if HAVE_CURL_REAUTHORIZATION_BUG
5743 saved_curl
= context
->curl
;
5744 context
->curl
= curl_easy_init();
5745 if (NULL
== context
->curl
) {
5749 if (context
->timeout
) {
5750 err
= isds_set_timeout(context
, context
->timeout
);
5751 if (err
) goto leave
;
5756 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5757 _("Sending ChangeISDSPassword request to ISDS\n") :
5758 _("Sending ChangePasswordOTP request to ISDS\n"));
5761 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5762 request
, &response
, NULL
, NULL
);
5765 /* Remove temporal credentials */
5766 _isds_discard_credentials(context
, 0);
5767 /* Detach pointer to OTP credentials from context */
5768 context
->otp_credentials
= NULL
;
5769 /* Keep context->otp true to keep signaling this is OTP session */
5772 /* Destroy request */
5773 xmlFreeNode(request
); request
= NULL
;
5776 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5777 _("Processing ISDS response on ChangeISDSPassword "
5778 "request failed\n") :
5779 _("Processing ISDS response on ChangePasswordOTP "
5780 "request failed\n"));
5784 /* Check for response status */
5785 err
= isds_response_status(context
,
5786 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5787 &code
, &message
, (xmlChar
**)refnumber
);
5789 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5790 _("ISDS response on ChangeISDSPassword request is missing "
5792 _("ISDS response on ChangePasswordOTP request is missing "
5797 /* Check for known error codes */
5798 for (size_t i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5799 if (!xmlStrcmp(code
, codes
[i
])) {
5800 char *code_locale
= _isds_utf82locale((char*)code
);
5801 char *message_locale
= _isds_utf82locale((char*)message
);
5802 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5803 _("Server refused to change password on ChangeISDSPassword "
5804 "request (code=%s, message=%s)\n") :
5805 _("Server refused to change password on ChangePasswordOTP "
5806 "request (code=%s, message=%s)\n"),
5807 code_locale
, message_locale
);
5809 free(message_locale
);
5810 isds_log_message(context
, _(meanings
[i
]));
5817 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5818 char *code_locale
= _isds_utf82locale((char*)code
);
5819 char *message_locale
= _isds_utf82locale((char*)message
);
5820 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5821 _("Server refused to change password on ChangeISDSPassword "
5822 "request (code=%s, message=%s)\n") :
5823 _("Server refused to change password on ChangePasswordOTP "
5824 "request (code=%s, message=%s)\n"),
5825 code_locale
, message_locale
);
5826 isds_log_message(context
, message_locale
);
5828 free(message_locale
);
5833 /* Otherwise password changed successfully */
5836 if (NULL
!= saved_url
) {
5837 /* Revert URL to original one */
5838 zfree(context
->url
);
5839 context
->url
= saved_url
;
5841 #if HAVE_CURL_REAUTHORIZATION_BUG
5842 if (NULL
!= saved_curl
) {
5843 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5844 context
->curl
= saved_curl
;
5850 xmlFreeDoc(response
);
5851 xmlFreeNode(request
);
5854 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5855 _("Password changed successfully on ChangeISDSPassword "
5857 _("Password changed successfully on ChangePasswordOTP "
5859 #else /* not HAVE_LIBCURL */
5868 /* Generic middle part with request sending and response check.
5869 * It sends prepared request and checks for error code.
5870 * @context is ISDS session context.
5871 * @service is ISDS service handler
5872 * @service_name is name in scope of given @service
5873 * @request is XML tree with request. Will be freed to save memory.
5874 * @response is XML document outputting ISDS response.
5875 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5876 * @map is mapping from status code to library error. Pass NULL if no special
5877 * handling is requested.
5878 * NULL, if you don't care. */
5879 static isds_error
send_destroy_request_check_response(
5880 struct isds_ctx
*context
,
5881 const isds_service service
, const xmlChar
*service_name
,
5882 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5883 const struct code_map_isds_error
*map
) {
5884 isds_error err
= IE_SUCCESS
;
5885 char *service_name_locale
= NULL
;
5886 xmlChar
*code
= NULL
, *message
= NULL
;
5889 if (!context
) return IE_INVALID_CONTEXT
;
5890 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5894 /* Check if connection is established
5895 * TODO: This check should be done downstairs. */
5896 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5898 service_name_locale
= _isds_utf82locale((char*) service_name
);
5899 if (!service_name_locale
) {
5904 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5905 service_name_locale
);
5908 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5909 xmlFreeNode(*request
); *request
= NULL
;
5912 isds_log(ILF_ISDS
, ILL_DEBUG
,
5913 _("Processing ISDS response on %s request failed\n"),
5914 service_name_locale
);
5918 /* Check for response status */
5919 err
= isds_response_status(context
, service
, *response
,
5920 &code
, &message
, refnumber
);
5922 isds_log(ILF_ISDS
, ILL_DEBUG
,
5923 _("ISDS response on %s request is missing status\n"),
5924 service_name_locale
);
5928 err
= statuscode2isds_error(context
, map
, code
, message
);
5930 /* Request processed, but server failed */
5931 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5932 char *code_locale
= _isds_utf82locale((char*) code
);
5933 char *message_locale
= _isds_utf82locale((char*) message
);
5934 isds_log(ILF_ISDS
, ILL_DEBUG
,
5935 _("Server refused %s request (code=%s, message=%s)\n"),
5936 service_name_locale
, code_locale
, message_locale
);
5938 free(message_locale
);
5946 if (err
&& *response
) {
5947 xmlFreeDoc(*response
);
5951 xmlFreeNode(*request
);
5954 free(service_name_locale
);
5960 /* Generic bottom half with request sending.
5961 * It sends prepared request, checks for error code, destroys response and
5962 * request and log success or failure.
5963 * @context is ISDS session context.
5964 * @service is ISDS service handler
5965 * @service_name is name in scope of given @service
5966 * @request is XML tree with request. Will be freed to save memory.
5967 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5968 * NULL, if you don't care. */
5969 static isds_error
send_request_check_drop_response(
5970 struct isds_ctx
*context
,
5971 const isds_service service
, const xmlChar
*service_name
,
5972 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5973 isds_error err
= IE_SUCCESS
;
5974 xmlDocPtr response
= NULL
;
5977 if (!context
) return IE_INVALID_CONTEXT
;
5978 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5981 /* Send request and check response*/
5982 err
= send_destroy_request_check_response(context
,
5983 service
, service_name
, request
, &response
, refnumber
, NULL
);
5985 xmlFreeDoc(response
);
5988 xmlFreeNode(*request
);
5993 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5994 isds_log(ILF_ISDS
, ILL_DEBUG
,
5995 _("%s request processed by server successfully.\n"),
5996 service_name_locale
);
5997 free(service_name_locale
);
6004 /* Insert isds_credentials_delivery structure into XML request if not NULL
6005 * @context is session context
6006 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
6007 * credentials delivery. The email field is passed.
6008 * @parent is XML element where to insert */
6009 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
6010 const struct isds_credentials_delivery
*credentials_delivery
,
6011 xmlNodePtr parent
) {
6012 isds_error err
= IE_SUCCESS
;
6015 if (!context
) return IE_INVALID_CONTEXT
;
6016 if (!parent
) return IE_INVAL
;
6018 if (credentials_delivery
) {
6019 /* Following elements are valid only for services:
6020 * NewAccessData, AddDataBoxUser, CreateDataBox */
6021 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
6022 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
6030 /* Extract credentials delivery from ISDS response.
6031 * @context is session context
6032 * @credentials_delivery is pointer to valid structure to fill in returned
6033 * user's password (and new log-in name). If NULL, do not extract the data.
6034 * @response is pointer to XML document with ISDS response
6035 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
6036 * @return IE_SUCCESS even if new user name has not been found because it's not
6037 * clear whether it's returned always. */
6038 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
6039 struct isds_credentials_delivery
*credentials_delivery
,
6040 xmlDocPtr response
, const char *request_name
) {
6041 isds_error err
= IE_SUCCESS
;
6042 xmlXPathContextPtr xpath_ctx
= NULL
;
6043 xmlXPathObjectPtr result
= NULL
;
6044 char *xpath_query
= NULL
;
6046 if (!context
) return IE_INVALID_CONTEXT
;
6047 if (credentials_delivery
) {
6048 zfree(credentials_delivery
->token
);
6049 zfree(credentials_delivery
->new_user_name
);
6051 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
6054 /* Extract optional token */
6055 if (credentials_delivery
) {
6056 xpath_ctx
= xmlXPathNewContext(response
);
6061 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6066 /* Verify root element */
6067 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
6072 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
6077 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6078 char *request_name_locale
= _isds_utf82locale(request_name
);
6079 isds_log(ILF_ISDS
, ILL_WARNING
,
6080 _("Wrong element in ISDS response for %s request "
6081 "while extracting credentials delivery details\n"),
6082 request_name_locale
);
6083 free(request_name_locale
);
6087 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6090 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
6092 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
6094 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
6095 if (!credentials_delivery
->token
) {
6096 char *request_name_locale
= _isds_utf82locale(request_name
);
6097 isds_log(ILF_ISDS
, ILL_ERR
,
6098 _("ISDS did not return token on %s request "
6099 "even if requested\n"), request_name_locale
);
6100 free(request_name_locale
);
6107 xmlXPathFreeObject(result
);
6108 xmlXPathFreeContext(xpath_ctx
);
6114 /* Build XSD:tCreateDBInput request type for box creating.
6115 * @context is session context
6116 * @request outputs built XML tree
6117 * @service_name is request name of SERVICE_DB_MANIPULATION service
6118 * @box is box description to create including single primary user (in case of
6119 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6121 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6122 * box, or contact address of PFO box owner)
6123 * @former_names is optional former name of box owner. Pass NULL if otherwise.
6124 * @upper_box_id is optional ID of supper box if currently created box is
6126 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
6128 * @credentials_delivery is valid pointer if ISDS should return token that box
6129 * owner can use to obtain his new credentials in on-line way. Then valid email
6130 * member value should be supplied.
6131 * @approval is optional external approval of box manipulation */
6132 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
6133 xmlNodePtr
*request
, const xmlChar
*service_name
,
6134 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6135 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
6136 const xmlChar
*ceo_label
,
6137 const struct isds_credentials_delivery
*credentials_delivery
,
6138 const struct isds_approval
*approval
) {
6139 isds_error err
= IE_SUCCESS
;
6140 xmlNsPtr isds_ns
= NULL
;
6141 xmlNodePtr node
, dbPrimaryUsers
;
6142 xmlChar
*string
= NULL
;
6143 const struct isds_list
*item
;
6146 if (!context
) return IE_INVALID_CONTEXT
;
6147 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
6151 /* Build CreateDataBox-similar request */
6152 *request
= xmlNewNode(NULL
, service_name
);
6154 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
6155 isds_printf_message(context
, _("Could build %s request"),
6156 service_name_locale
);
6157 free(service_name_locale
);
6160 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
6161 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
6163 isds_log_message(context
, _("Could not create ISDS1 name space"));
6164 xmlFreeNode(*request
);
6168 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6170 isds_log_message(context
, _("Could not create ISDS name space"));
6171 xmlFreeNode(*request
);
6175 xmlSetNs(*request
, isds_ns
);
6177 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6178 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6179 if (err
) goto leave
;
6182 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6183 * verbose documentation allows none dbUserInfo */
6184 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6185 for (item
= users
; item
; item
= item
->next
) {
6187 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6188 err
= insert_DbUserInfo(context
,
6189 (struct isds_DbUserInfo
*) item
->data
, 1, node
);
6190 if (err
) goto leave
;
6194 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6195 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6196 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6198 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6199 if (err
) goto leave
;
6201 err
= insert_GExtApproval(context
, approval
, *request
);
6202 if (err
) goto leave
;
6206 xmlFreeNode(*request
);
6212 #endif /* HAVE_LIBCURL */
6216 * @context is session context
6217 * @box is box description to create including single primary user (in case of
6218 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
6219 * ignored. It outputs box ID assigned by ISDS in dbID element.
6220 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6221 * box, or contact address of PFO box owner)
6222 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6223 * @upper_box_id is optional ID of supper box if currently created box is
6225 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6226 * @credentials_delivery is NULL if new password should be delivered off-line
6227 * to box owner. It is valid pointer if owner should obtain new password on-line
6228 * on dedicated web server. Then input @credentials_delivery.email value is
6229 * his e-mail address he must provide to dedicated web server together
6230 * with output reallocated @credentials_delivery.token member. Output
6231 * member @credentials_delivery.new_user_name is unused up on this call.
6232 * @approval is optional external approval of box manipulation
6233 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6234 * NULL, if you don't care.*/
6235 isds_error
isds_add_box(struct isds_ctx
*context
,
6236 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6237 const char *former_names
, const char *upper_box_id
,
6238 const char *ceo_label
,
6239 struct isds_credentials_delivery
*credentials_delivery
,
6240 const struct isds_approval
*approval
, char **refnumber
) {
6241 isds_error err
= IE_SUCCESS
;
6243 xmlNodePtr request
= NULL
;
6244 xmlDocPtr response
= NULL
;
6245 xmlXPathContextPtr xpath_ctx
= NULL
;
6246 xmlXPathObjectPtr result
= NULL
;
6250 if (!context
) return IE_INVALID_CONTEXT
;
6251 zfree(context
->long_message
);
6252 if (credentials_delivery
) {
6253 zfree(credentials_delivery
->token
);
6254 zfree(credentials_delivery
->new_user_name
);
6256 if (!box
) return IE_INVAL
;
6259 /* Scratch box ID */
6262 /* Build CreateDataBox request */
6263 err
= build_CreateDBInput_request(context
,
6264 &request
, BAD_CAST
"CreateDataBox",
6265 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6266 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6267 if (err
) goto leave
;
6269 /* Send it to server and process response */
6270 err
= send_destroy_request_check_response(context
,
6271 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6272 &response
, (xmlChar
**) refnumber
, NULL
);
6274 /* Extract box ID */
6275 xpath_ctx
= xmlXPathNewContext(response
);
6280 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6284 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6286 /* Extract optional token */
6287 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6291 xmlXPathFreeObject(result
);
6292 xmlXPathFreeContext(xpath_ctx
);
6293 xmlFreeDoc(response
);
6294 xmlFreeNode(request
);
6297 isds_log(ILF_ISDS
, ILL_DEBUG
,
6298 _("CreateDataBox request processed by server successfully.\n"));
6300 #else /* not HAVE_LIBCURL */
6308 /* Notify ISDS about new PFO entity.
6309 * This function has no real effect.
6310 * @context is session context
6311 * @box is PFO description including single primary user. aifoIsds,
6312 * address->adCode, address->adDistrict members are ignored.
6313 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6314 * @former_names is optional undocumented string. Pass NULL if you don't care.
6315 * @upper_box_id is optional ID of supper box if currently created box is
6317 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6318 * @approval is optional external approval of box manipulation
6319 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6320 * NULL, if you don't care.*/
6321 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6322 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6323 const char *former_names
, const char *upper_box_id
,
6324 const char *ceo_label
, const struct isds_approval
*approval
,
6326 isds_error err
= IE_SUCCESS
;
6328 xmlNodePtr request
= NULL
;
6331 if (!context
) return IE_INVALID_CONTEXT
;
6332 zfree(context
->long_message
);
6333 if (!box
) return IE_INVAL
;
6336 /* Build CreateDataBoxPFOInfo request */
6337 err
= build_CreateDBInput_request(context
,
6338 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6339 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6340 (xmlChar
*) ceo_label
, NULL
, approval
);
6341 if (err
) goto leave
;
6343 /* Send it to server and process response */
6344 err
= send_request_check_drop_response(context
,
6345 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6346 (xmlChar
**) refnumber
);
6347 /* XXX: XML Schema names output dbID element but textual documentation
6348 * states no box identifier is returned. */
6350 xmlFreeNode(request
);
6351 #else /* not HAVE_LIBCURL */
6358 /* Common implementation for removing given box.
6359 * @context is session context
6360 * @service_name is UTF-8 encoded name fo ISDS service
6361 * @box is box description to delete. aifoIsds, address->adCode,
6362 * address->adDistrict members are ignored.
6363 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6364 * carry sane value. If NULL, do not inject this information into request.
6365 * @approval is optional external approval of box manipulation
6366 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6367 * NULL, if you don't care.*/
6368 static isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6369 const xmlChar
*service_name
,
6370 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6371 const struct isds_approval
*approval
, char **refnumber
) {
6372 isds_error err
= IE_SUCCESS
;
6374 xmlNsPtr isds_ns
= NULL
;
6375 xmlNodePtr request
= NULL
;
6377 xmlChar
*string
= NULL
;
6381 if (!context
) return IE_INVALID_CONTEXT
;
6382 zfree(context
->long_message
);
6383 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6387 /* Build DeleteDataBox(Promptly) request */
6388 request
= xmlNewNode(NULL
, service_name
);
6390 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6391 isds_printf_message(context
,
6392 _("Could build %s request"), service_name_locale
);
6393 free(service_name_locale
);
6396 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6398 isds_log_message(context
, _("Could not create ISDS name space"));
6399 xmlFreeNode(request
);
6402 xmlSetNs(request
, isds_ns
);
6404 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6405 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6406 if (err
) goto leave
;
6409 err
= tm2datestring(since
, &string
);
6411 isds_log_message(context
,
6412 _("Could not convert `since' argument to ISO date string"));
6415 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6419 err
= insert_GExtApproval(context
, approval
, request
);
6420 if (err
) goto leave
;
6423 /* Send it to server and process response */
6424 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6425 service_name
, &request
, (xmlChar
**) refnumber
);
6428 xmlFreeNode(request
);
6430 #else /* not HAVE_LIBCURL */
6437 /* Remove given box permanently.
6438 * @context is session context
6439 * @box is box description to delete. aifoIsds, address->adCode,
6440 * address->adDistrict members are ignored.
6441 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6443 * @approval is optional external approval of box manipulation
6444 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6445 * NULL, if you don't care.*/
6446 isds_error
isds_delete_box(struct isds_ctx
*context
,
6447 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6448 const struct isds_approval
*approval
, char **refnumber
) {
6449 if (!context
) return IE_INVALID_CONTEXT
;
6450 zfree(context
->long_message
);
6451 if (!box
|| !since
) return IE_INVAL
;
6453 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6454 box
, since
, approval
, refnumber
);
6458 /* Undocumented function.
6459 * @context is session context
6460 * @box is box description to delete. aifoIsds, address->adCode,
6461 * address->adDistrict members are ignored.
6462 * @approval is optional external approval of box manipulation
6463 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6464 * NULL, if you don't care.*/
6465 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6466 const struct isds_DbOwnerInfo
*box
,
6467 const struct isds_approval
*approval
, char **refnumber
) {
6468 if (!context
) return IE_INVALID_CONTEXT
;
6469 zfree(context
->long_message
);
6470 if (!box
) return IE_INVAL
;
6472 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6473 box
, NULL
, approval
, refnumber
);
6477 /* Update data about given box.
6478 * @context is session context
6479 * @old_box current box description. aifoIsds, address->adCode,
6480 * address->adDistrict members are ignored.
6481 * @new_box are updated data about @old_box. aifoIsds, address->adCode,
6482 * address->adDistrict members are ignored.
6483 * @approval is optional external approval of box manipulation
6484 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6485 * NULL, if you don't care.*/
6486 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6487 const struct isds_DbOwnerInfo
*old_box
,
6488 const struct isds_DbOwnerInfo
*new_box
,
6489 const struct isds_approval
*approval
, char **refnumber
) {
6490 isds_error err
= IE_SUCCESS
;
6492 xmlNsPtr isds_ns
= NULL
;
6493 xmlNodePtr request
= NULL
;
6498 if (!context
) return IE_INVALID_CONTEXT
;
6499 zfree(context
->long_message
);
6500 if (!old_box
|| !new_box
) return IE_INVAL
;
6504 /* Build UpdateDataBoxDescr request */
6505 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6507 isds_log_message(context
,
6508 _("Could build UpdateDataBoxDescr request"));
6511 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6513 isds_log_message(context
, _("Could not create ISDS name space"));
6514 xmlFreeNode(request
);
6517 xmlSetNs(request
, isds_ns
);
6519 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6520 err
= insert_DbOwnerInfo(context
, old_box
, 0, node
);
6521 if (err
) goto leave
;
6523 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6524 err
= insert_DbOwnerInfo(context
, new_box
, 0, node
);
6525 if (err
) goto leave
;
6527 err
= insert_GExtApproval(context
, approval
, request
);
6528 if (err
) goto leave
;
6531 /* Send it to server and process response */
6532 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6533 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6536 xmlFreeNode(request
);
6537 #else /* not HAVE_LIBCURL */
6546 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6548 * @context is session context
6549 * @service is SOAP service
6550 * @service_name is name of request in @service
6551 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6552 * @box_id is box ID of interest
6553 * @approval is optional external approval of box manipulation
6554 * @response is server SOAP body response as XML document
6555 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6556 * NULL, if you don't care.
6557 * @return error coded from lower layer, context message will be set up
6559 static isds_error
build_send_dbid_request_check_response(
6560 struct isds_ctx
*context
, const isds_service service
,
6561 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6562 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6563 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6565 isds_error err
= IE_SUCCESS
;
6566 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6567 xmlNodePtr request
= NULL
, node
;
6568 xmlNsPtr isds_ns
= NULL
;
6570 if (!context
) return IE_INVALID_CONTEXT
;
6571 if (!service_name
|| !box_id
) return IE_INVAL
;
6572 if (!response
) return IE_INVAL
;
6574 /* Free output argument */
6575 xmlFreeDoc(*response
); *response
= NULL
;
6577 /* Prepare strings */
6578 service_name_locale
= _isds_utf82locale((char*)service_name
);
6579 if (!service_name_locale
) {
6583 box_id_locale
= _isds_utf82locale((char*)box_id
);
6584 if (!box_id_locale
) {
6590 request
= xmlNewNode(NULL
, service_name
);
6592 isds_printf_message(context
,
6593 _("Could not build %s request for %s box"), service_name_locale
,
6598 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6600 isds_log_message(context
, _("Could not create ISDS name space"));
6604 xmlSetNs(request
, isds_ns
);
6606 /* Add XSD:tIdDbInput children */
6607 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6608 INSERT_STRING(request
, box_id_element
, box_id
);
6609 err
= insert_GExtApproval(context
, approval
, request
);
6610 if (err
) goto leave
;
6612 /* Send request and check response*/
6613 err
= send_destroy_request_check_response(context
,
6614 service
, service_name
, &request
, response
, refnumber
, NULL
);
6617 free(service_name_locale
);
6618 free(box_id_locale
);
6619 xmlFreeNode(request
);
6622 #endif /* HAVE_LIBCURL */
6625 /* Get data about all users assigned to given box.
6626 * @context is session context
6628 * @users is automatically reallocated list of struct isds_DbUserInfo */
6629 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6630 struct isds_list
**users
) {
6631 isds_error err
= IE_SUCCESS
;
6633 xmlDocPtr response
= NULL
;
6634 xmlXPathContextPtr xpath_ctx
= NULL
;
6635 xmlXPathObjectPtr result
= NULL
;
6637 struct isds_list
*item
, *prev_item
= NULL
;
6640 if (!context
) return IE_INVALID_CONTEXT
;
6641 zfree(context
->long_message
);
6642 if (!users
|| !box_id
) return IE_INVAL
;
6643 isds_list_free(users
);
6647 /* Do request and check for success */
6648 err
= build_send_dbid_request_check_response(context
,
6649 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6650 BAD_CAST box_id
, NULL
, &response
, NULL
);
6651 if (err
) goto leave
;
6655 /* Prepare structure */
6656 xpath_ctx
= xmlXPathNewContext(response
);
6661 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6666 /* Set context node */
6667 result
= xmlXPathEvalExpression(BAD_CAST
6668 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6674 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6675 /* Iterate over all users */
6676 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6678 /* Prepare structure */
6679 item
= calloc(1, sizeof(*item
));
6684 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6685 if (i
== 0) *users
= item
;
6686 else prev_item
->next
= item
;
6690 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6691 err
= extract_DbUserInfo(context
,
6692 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6693 if (err
) goto leave
;
6699 isds_list_free(users
);
6702 xmlXPathFreeObject(result
);
6703 xmlXPathFreeContext(xpath_ctx
);
6704 xmlFreeDoc(response
);
6707 isds_log(ILF_ISDS
, ILL_DEBUG
,
6708 _("GetDataBoxUsers request processed by server "
6709 "successfully.\n"));
6710 #else /* not HAVE_LIBCURL */
6718 /* Update data about user assigned to given box.
6719 * @context is session context
6720 * @box is box identification. aifoIsds, address->adCode,
6721 * address->adDistrict members are ignored.
6722 * @old_user identifies user to update, aifo_ticket member is ignored
6723 * @new_user are updated data about @old_user, aifo_ticket member is ignored
6724 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6725 * NULL, if you don't care.*/
6726 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6727 const struct isds_DbOwnerInfo
*box
,
6728 const struct isds_DbUserInfo
*old_user
,
6729 const struct isds_DbUserInfo
*new_user
,
6731 isds_error err
= IE_SUCCESS
;
6733 xmlNsPtr isds_ns
= NULL
;
6734 xmlNodePtr request
= NULL
;
6739 if (!context
) return IE_INVALID_CONTEXT
;
6740 zfree(context
->long_message
);
6741 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6745 /* Build UpdateDataBoxUser request */
6746 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6748 isds_log_message(context
,
6749 _("Could build UpdateDataBoxUser request"));
6752 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6754 isds_log_message(context
, _("Could not create ISDS name space"));
6755 xmlFreeNode(request
);
6758 xmlSetNs(request
, isds_ns
);
6760 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6761 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6762 if (err
) goto leave
;
6764 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6765 err
= insert_DbUserInfo(context
, old_user
, 0, node
);
6766 if (err
) goto leave
;
6768 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6769 err
= insert_DbUserInfo(context
, new_user
, 0, node
);
6770 if (err
) goto leave
;
6772 /* Send it to server and process response */
6773 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6774 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6777 xmlFreeNode(request
);
6778 #else /* not HAVE_LIBCURL */
6786 /* Undocumented function.
6787 * @context is session context
6788 * @box_id is UTF-8 encoded box identifier
6789 * @token is UTF-8 encoded temporary password
6790 * @user_id outputs UTF-8 encoded reallocated user identifier
6791 * @password outpus UTF-8 encoded reallocated user password
6792 * Output arguments will be nulled in case of error */
6793 isds_error
isds_activate(struct isds_ctx
*context
,
6794 const char *box_id
, const char *token
,
6795 char **user_id
, char **password
) {
6796 isds_error err
= IE_SUCCESS
;
6798 xmlNsPtr isds_ns
= NULL
;
6799 xmlNodePtr request
= NULL
, node
;
6800 xmlDocPtr response
= NULL
;
6801 xmlXPathContextPtr xpath_ctx
= NULL
;
6802 xmlXPathObjectPtr result
= NULL
;
6806 if (!context
) return IE_INVALID_CONTEXT
;
6807 zfree(context
->long_message
);
6809 if (user_id
) zfree(*user_id
);
6810 if (password
) zfree(*password
);
6812 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6816 /* Build Activate request */
6817 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6819 isds_log_message(context
, _("Could build Activate request"));
6822 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6824 isds_log_message(context
, _("Could not create ISDS name space"));
6825 xmlFreeNode(request
);
6828 xmlSetNs(request
, isds_ns
);
6830 INSERT_STRING(request
, "dbAccessDataId", token
);
6831 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6832 INSERT_STRING(request
, "dbID", box_id
);
6835 /* Send request and check response*/
6836 err
= send_destroy_request_check_response(context
,
6837 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6838 &response
, NULL
, NULL
);
6839 if (err
) goto leave
;
6843 xpath_ctx
= xmlXPathNewContext(response
);
6848 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6852 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6858 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6859 isds_log_message(context
, _("Missing ActivateResponse element"));
6863 if (result
->nodesetval
->nodeNr
> 1) {
6864 isds_log_message(context
, _("Multiple ActivateResponse element"));
6868 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6869 xmlXPathFreeObject(result
); result
= NULL
;
6871 EXTRACT_STRING("isds:userId", *user_id
);
6873 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6874 "but did not return `userId' element.\n"));
6876 EXTRACT_STRING("isds:password", *password
);
6878 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6879 "but did not return `password' element.\n"));
6882 xmlXPathFreeObject(result
);
6883 xmlXPathFreeContext(xpath_ctx
);
6884 xmlFreeDoc(response
);
6885 xmlFreeNode(request
);
6888 isds_log(ILF_ISDS
, ILL_DEBUG
,
6889 _("Activate request processed by server successfully.\n"));
6890 #else /* not HAVE_LIBCURL */
6898 /* Reset credentials of user assigned to given box.
6899 * @context is session context
6900 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
6901 * members are ignored.
6902 * @user identifies user to reset password, aifo_ticket member is ignored
6903 * @fee_paid is true if fee has been paid, false otherwise
6904 * @approval is optional external approval of box manipulation
6905 * @credentials_delivery is NULL if new password should be delivered off-line
6906 * to the user. It is valid pointer if user should obtain new password on-line
6907 * on dedicated web server. Then input @credentials_delivery.email value is
6908 * user's e-mail address user must provide to dedicated web server together
6909 * with @credentials_delivery.token. The output reallocated token user needs
6910 * to use to authorize on the web server to view his new password. Output
6911 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6912 * ISDS changed up on this call. (No reason why server could change the name
6914 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6915 * NULL, if you don't care.*/
6916 isds_error
isds_reset_password(struct isds_ctx
*context
,
6917 const struct isds_DbOwnerInfo
*box
,
6918 const struct isds_DbUserInfo
*user
,
6919 const _Bool fee_paid
, const struct isds_approval
*approval
,
6920 struct isds_credentials_delivery
*credentials_delivery
,
6922 isds_error err
= IE_SUCCESS
;
6924 xmlNsPtr isds_ns
= NULL
;
6925 xmlNodePtr request
= NULL
, node
;
6926 xmlDocPtr response
= NULL
;
6930 if (!context
) return IE_INVALID_CONTEXT
;
6931 zfree(context
->long_message
);
6933 if (credentials_delivery
) {
6934 zfree(credentials_delivery
->token
);
6935 zfree(credentials_delivery
->new_user_name
);
6937 if (!box
|| !user
) return IE_INVAL
;
6941 /* Build NewAccessData request */
6942 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6944 isds_log_message(context
,
6945 _("Could build NewAccessData request"));
6948 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6950 isds_log_message(context
, _("Could not create ISDS name space"));
6951 xmlFreeNode(request
);
6954 xmlSetNs(request
, isds_ns
);
6956 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6957 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
6958 if (err
) goto leave
;
6960 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6961 err
= insert_DbUserInfo(context
, user
, 0, node
);
6962 if (err
) goto leave
;
6964 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6966 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6967 if (err
) goto leave
;
6969 err
= insert_GExtApproval(context
, approval
, request
);
6970 if (err
) goto leave
;
6972 /* Send request and check response*/
6973 err
= send_destroy_request_check_response(context
,
6974 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6975 &response
, (xmlChar
**) refnumber
, NULL
);
6976 if (err
) goto leave
;
6979 /* Extract optional token */
6980 err
= extract_credentials_delivery(context
, credentials_delivery
,
6981 response
, "NewAccessData");
6984 xmlFreeDoc(response
);
6985 xmlFreeNode(request
);
6988 isds_log(ILF_ISDS
, ILL_DEBUG
,
6989 _("NewAccessData request processed by server "
6990 "successfully.\n"));
6991 #else /* not HAVE_LIBCURL */
6999 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
7000 * code, destroy response and log success.
7001 * @context is ISDS session context.
7002 * @service_name is name of SERVICE_DB_MANIPULATION service
7003 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7004 * members are ignored.
7005 * @user identifies user to remove
7006 * @honor_aifo_ticket is true for inserting @user's aifo_ticket member
7007 * @credentials_delivery is NULL if new user's password should be delivered
7008 * off-line to the user. It is valid pointer if user should obtain new
7009 * password on-line on dedicated web server. Then input
7010 * @credentials_delivery.email value is user's e-mail address user must
7011 * provide to dedicated web server together with @credentials_delivery.token.
7012 * The output reallocated token user needs to use to authorize on the web
7013 * server to view his new password. Output reallocated
7014 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7015 * assingned or changed up on this call.
7016 * @approval is optional external approval of box manipulation
7017 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7018 * NULL, if you don't care. */
7019 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
7020 struct isds_ctx
*context
, const xmlChar
*service_name
,
7021 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7022 _Bool honor_aifo_ticket
,
7023 struct isds_credentials_delivery
*credentials_delivery
,
7024 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7025 isds_error err
= IE_SUCCESS
;
7027 xmlNsPtr isds_ns
= NULL
;
7028 xmlNodePtr request
= NULL
, node
;
7029 xmlDocPtr response
= NULL
;
7033 if (!context
) return IE_INVALID_CONTEXT
;
7034 zfree(context
->long_message
);
7035 if (credentials_delivery
) {
7036 zfree(credentials_delivery
->token
);
7037 zfree(credentials_delivery
->new_user_name
);
7039 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
7044 /* Build NewAccessData or similar request */
7045 request
= xmlNewNode(NULL
, service_name
);
7047 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7048 isds_printf_message(context
, _("Could not build %s request"),
7049 service_name_locale
);
7050 free(service_name_locale
);
7053 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7055 isds_log_message(context
, _("Could not create ISDS name space"));
7056 xmlFreeNode(request
);
7059 xmlSetNs(request
, isds_ns
);
7061 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7062 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
7063 if (err
) goto leave
;
7065 INSERT_ELEMENT(node
, request
, "dbUserInfo");
7066 err
= insert_DbUserInfo(context
, user
, honor_aifo_ticket
, node
);
7067 if (err
) goto leave
;
7069 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
7070 if (err
) goto leave
;
7072 err
= insert_GExtApproval(context
, approval
, request
);
7073 if (err
) goto leave
;
7076 /* Send request and check response*/
7077 err
= send_destroy_request_check_response(context
,
7078 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
7081 xmlFreeNode(request
);
7084 /* Pick up credentials_delivery if requested */
7085 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
7086 (char *)service_name
);
7089 xmlFreeDoc(response
);
7090 if (request
) xmlFreeNode(request
);
7093 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7094 isds_log(ILF_ISDS
, ILL_DEBUG
,
7095 _("%s request processed by server successfully.\n"),
7096 service_name_locale
);
7097 free(service_name_locale
);
7099 #else /* not HAVE_LIBCURL */
7107 /* Assign new user to given box.
7108 * @context is session context
7109 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7110 * members are ignored.
7111 * @user defines new user to add
7112 * @credentials_delivery is NULL if new user's password should be delivered
7113 * off-line to the user. It is valid pointer if user should obtain new
7114 * password on-line on dedicated web server. Then input
7115 * @credentials_delivery.email value is user's e-mail address user must
7116 * provide to dedicated web server together with @credentials_delivery.token.
7117 * The output reallocated token user needs to use to authorize on the web
7118 * server to view his new password. Output reallocated
7119 * @credentials_delivery.new_user_name is user's log-in name that ISDS
7120 * assingned up on this call.
7121 * @approval is optional external approval of box manipulation
7122 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7123 * NULL, if you don't care.*/
7124 isds_error
isds_add_user(struct isds_ctx
*context
,
7125 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7126 struct isds_credentials_delivery
*credentials_delivery
,
7127 const struct isds_approval
*approval
, char **refnumber
) {
7128 return build_send_manipulationboxuser_request_check_drop_response(context
,
7129 BAD_CAST
"AddDataBoxUser", box
, user
, 1, credentials_delivery
,
7130 approval
, (xmlChar
**) refnumber
);
7134 /* Remove user assigned to given box.
7135 * @context is session context
7136 * @box is box identification. aifoIsds, address->adCode, address->adDistrict
7137 * members are ignored.
7138 * @user identifies user to remove, aifo_ticket member is ignored
7139 * @approval is optional external approval of box manipulation
7140 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7141 * NULL, if you don't care.*/
7142 isds_error
isds_delete_user(struct isds_ctx
*context
,
7143 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
7144 const struct isds_approval
*approval
, char **refnumber
) {
7145 return build_send_manipulationboxuser_request_check_drop_response(context
,
7146 BAD_CAST
"DeleteDataBoxUser", box
, user
, 0, NULL
, approval
,
7147 (xmlChar
**) refnumber
);
7151 /* Get list of boxes in ZIP archive.
7152 * @context is session context
7153 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
7154 * System recognizes following values currently: ALL (all boxes), UPG
7155 * (effectively OVM boxes), POA (active boxes allowing receiving commercial
7156 * messages), OVM (OVM gross type boxes), OPN (boxes allowing receiving
7157 * commercial messages). This argument is a string because specification
7158 * states new values can appear in the future. Not all list types are
7159 * available to all users.
7160 * @buffer is automatically reallocated memory to store the list of boxes. The
7161 * list is zipped CSV file.
7162 * @buffer_length is size of @buffer data in bytes.
7163 * In case of error @buffer will be freed and @buffer_length will be
7165 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
7166 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
7167 isds_error err
= IE_SUCCESS
;
7169 xmlNsPtr isds_ns
= NULL
;
7170 xmlNodePtr request
= NULL
, node
;
7171 xmlDocPtr response
= NULL
;
7172 xmlXPathContextPtr xpath_ctx
= NULL
;
7173 xmlXPathObjectPtr result
= NULL
;
7174 char *string
= NULL
;
7178 if (!context
) return IE_INVALID_CONTEXT
;
7179 zfree(context
->long_message
);
7180 if (buffer
) zfree(*buffer
);
7181 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7185 /* Check if connection is established
7186 * TODO: This check should be done downstairs. */
7187 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7190 /* Build AuthenticateMessage request */
7191 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7193 isds_log_message(context
,
7194 _("Could not build GetDataBoxList request"));
7197 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7199 isds_log_message(context
, _("Could not create ISDS name space"));
7200 xmlFreeNode(request
);
7203 xmlSetNs(request
, isds_ns
);
7204 INSERT_STRING(request
, "dblType", list_identifier
);
7206 /* Send request to server and process response */
7207 err
= send_destroy_request_check_response(context
,
7208 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7209 &response
, NULL
, NULL
);
7210 if (err
) goto leave
;
7213 /* Extract Base-64 encoded ZIP file */
7214 xpath_ctx
= xmlXPathNewContext(response
);
7219 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7223 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7225 /* Decode non-empty archive */
7226 if (string
&& string
[0] != '\0') {
7227 *buffer_length
= _isds_b64decode(string
, buffer
);
7228 if (*buffer_length
== (size_t) -1) {
7229 isds_printf_message(context
,
7230 _("Error while Base64-decoding box list archive"));
7239 xmlXPathFreeObject(result
);
7240 xmlXPathFreeContext(xpath_ctx
);
7241 xmlFreeDoc(response
);
7242 xmlFreeNode(request
);
7245 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7246 "processed by server successfully.\n"));
7248 #else /* not HAVE_LIBCURL */
7256 /* Build ISDS request of XSD tDbOwnerInfo or tDbPersonalOwnerInfoRequest type,
7257 * send it, check for error code, extract list of results, destroy response
7259 * @context is ISDS session context.
7260 * @service_name is name of SERVICE_DB_SEARCH service
7261 * @pfo_service is false if tDbOwnerInfo request should be built from
7262 * @criteria and corresponding result extracted. It is true if
7263 * tDbPersonalOwnerInfoRequest request should be built. The request and
7264 * response differ subset of significant isds_DbOwnerInfo structure members.
7265 * @criteria is filter. You should fill in at least some members.
7266 * If @pfo_service is false, aifoIsds, address->adCode, address->adDistrict
7267 * members will be ignored. If @pfo_service is true, dbType, ic,
7268 * personName->pnLastNameAtBirth, firmName, email, telNumber, identifier,
7269 * registryCode, dbState, dbEffectiveOVM, dbOpenAdressing members will be
7271 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7272 * possibly empty. Input NULL or valid old structure. The same memebers as
7273 * in described for @criteria argument will be NULL according to @pfo_service
7276 * IE_SUCCESS if search succeeded, @boxes contains useful data
7277 * IE_NOEXIST if no such box exists, @boxes will be NULL
7278 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7279 * contains still valid data
7280 * other code if something bad happens. @boxes will be NULL. */
7281 static isds_error
build_send_findbox_request_check_parse_drop_response(
7282 struct isds_ctx
*context
, const xmlChar
*service_name
,
7283 _Bool pfo_service
, const struct isds_DbOwnerInfo
*criteria
,
7284 struct isds_list
**boxes
) {
7285 isds_error err
= IE_SUCCESS
;
7287 char *service_name_locale
= NULL
;
7288 _Bool truncated
= 0;
7289 xmlNsPtr isds_ns
= NULL
;
7290 xmlNodePtr request
= NULL
;
7291 xmlDocPtr response
= NULL
;
7292 xmlChar
*code
= NULL
, *message
= NULL
;
7293 xmlNodePtr db_owner_info
;
7294 xmlXPathContextPtr xpath_ctx
= NULL
;
7295 xmlXPathObjectPtr result
= NULL
;
7296 xmlChar
*string
= NULL
;
7300 if (!context
) return IE_INVALID_CONTEXT
;
7301 zfree(context
->long_message
);
7302 if (!boxes
) return IE_INVAL
;
7303 isds_list_free(boxes
);
7310 /* Check if connection is established
7311 * TODO: This check should be done downstairs. */
7312 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7313 service_name_locale
= _isds_utf82locale((char *) service_name
);
7316 request
= xmlNewNode(NULL
, service_name
);
7318 isds_printf_message(context
, _("Could not build %s request"),
7319 service_name_locale
);
7320 free(service_name_locale
);
7323 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7325 isds_log_message(context
, _("Could not create ISDS name space"));
7326 free(service_name_locale
);
7327 xmlFreeNode(request
);
7330 xmlSetNs(request
, isds_ns
);
7331 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7332 if (!db_owner_info
) {
7333 isds_printf_message(context
,
7334 _("Could not add dbOwnerInfo child to %s element"),
7335 service_name_locale
);
7336 free(service_name_locale
);
7337 xmlFreeNode(request
);
7341 err
= insert_DbOwnerInfo(context
, criteria
, pfo_service
, db_owner_info
);
7342 if (err
) goto leave
;
7346 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
7347 service_name_locale
);
7348 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7350 /* Destroy request */
7351 xmlFreeNode(request
); request
= NULL
;
7354 isds_log(ILF_ISDS
, ILL_DEBUG
,
7355 _("Processing ISDS response on %s request failed\n"),
7356 service_name_locale
);
7360 /* Check for response status */
7361 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7362 &code
, &message
, NULL
);
7364 isds_log(ILF_ISDS
, ILL_DEBUG
,
7365 _("ISDS response on %s request is missing status\n"),
7366 service_name_locale
);
7370 /* Request processed, but nothing found */
7371 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7372 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7373 char *code_locale
= _isds_utf82locale((char*)code
);
7374 char *message_locale
= _isds_utf82locale((char*)message
);
7375 isds_log(ILF_ISDS
, ILL_DEBUG
,
7376 _("Server did not find any box on %s request "
7377 "(code=%s, message=%s)\n"), service_name_locale
,
7378 code_locale
, message_locale
);
7379 isds_log_message(context
, message_locale
);
7381 free(message_locale
);
7386 /* Warning, not an error */
7387 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7388 char *code_locale
= _isds_utf82locale((char*)code
);
7389 char *message_locale
= _isds_utf82locale((char*)message
);
7390 isds_log(ILF_ISDS
, ILL_DEBUG
,
7391 _("Server truncated response on %s request "
7392 "(code=%s, message=%s)\n"), service_name_locale
,
7393 code_locale
, message_locale
);
7394 isds_log_message(context
, message_locale
);
7396 free(message_locale
);
7401 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7402 char *code_locale
= _isds_utf82locale((char*)code
);
7403 char *message_locale
= _isds_utf82locale((char*)message
);
7404 isds_log(ILF_ISDS
, ILL_DEBUG
,
7405 _("Server refused %s request (code=%s, message=%s)\n"),
7406 service_name_locale
, code_locale
, message_locale
);
7407 isds_log_message(context
, message_locale
);
7409 free(message_locale
);
7414 xpath_ctx
= xmlXPathNewContext(response
);
7419 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7424 /* Extract boxes if they present */
7425 if (-1 == isds_asprintf((char **)&string
,
7426 "/isds:%sResponse/isds:dbResults/isds:dbOwnerInfo",
7431 result
= xmlXPathEvalExpression(string
, xpath_ctx
);
7437 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7438 struct isds_list
*item
, *prev_item
= NULL
;
7439 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7440 item
= calloc(1, sizeof(*item
));
7446 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7447 if (i
== 0) *boxes
= item
;
7448 else prev_item
->next
= item
;
7451 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7452 err
= extract_DbOwnerInfo(context
,
7453 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7454 if (err
) goto leave
;
7460 isds_list_free(boxes
);
7462 if (truncated
) err
= IE_2BIG
;
7466 xmlFreeNode(request
);
7467 xmlXPathFreeObject(result
);
7468 xmlXPathFreeContext(xpath_ctx
);
7472 xmlFreeDoc(response
);
7475 isds_log(ILF_ISDS
, ILL_DEBUG
,
7476 _("%s request processed by server successfully.\n"),
7477 service_name_locale
);
7478 free(service_name_locale
);
7479 #else /* not HAVE_LIBCURL */
7487 /* Find boxes suiting given criteria.
7488 * @criteria is filter. You should fill in at least some members. aifoIsds,
7489 * address->adCode, address->adDistrict members are ignored.
7490 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7491 * possibly empty. Input NULL or valid old structure.
7493 * IE_SUCCESS if search succeeded, @boxes contains useful data
7494 * IE_NOEXIST if no such box exists, @boxes will be NULL
7495 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7496 * contains still valid data
7497 * other code if something bad happens. @boxes will be NULL. */
7498 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7499 const struct isds_DbOwnerInfo
*criteria
,
7500 struct isds_list
**boxes
) {
7501 return build_send_findbox_request_check_parse_drop_response(context
,
7502 BAD_CAST
"FindDataBox", 0, criteria
, boxes
);
7507 /* Convert a string with match markers into a plain string with list of
7508 * pointers to the matches
7509 * @string is an UTF-8 encoded non-constant string with match markers
7510 * "|$*HL_START*$|" for start and "|$*HL_END*$|" for end of a match.
7511 * The markers will be removed from the string.
7512 * @starts is a reallocated list of static pointers into the @string pointing
7513 * to places where match start markers occured.
7514 * @ends is a reallocated list of static pointers into the @string pointing
7515 * to places where match end markers occured.
7516 * @return IE_SUCCESS in case of no failure. */
7517 static isds_error
interpret_matches(xmlChar
*string
,
7518 struct isds_list
**starts
, struct isds_list
**ends
) {
7519 isds_error err
= IE_SUCCESS
;
7520 xmlChar
*pointer
, *destination
, *source
;
7521 struct isds_list
*item
, *prev_start
= NULL
, *prev_end
= NULL
;
7523 isds_list_free(starts
);
7524 isds_list_free(ends
);
7525 if (NULL
== starts
|| NULL
== ends
) return IE_INVAL
;
7526 if (NULL
== string
) return IE_SUCCESS
;
7528 for (pointer
= string
; *pointer
!= '\0';) {
7529 if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_START*$|", 14)) {
7530 /* Remove the start marker */
7531 for (source
= pointer
+ 14, destination
= pointer
;
7532 *source
!= '\0'; source
++, destination
++) {
7533 *destination
= *source
;
7535 *destination
= '\0';
7536 /* Append the pointer into the list */
7537 item
= calloc(1, sizeof(*item
));
7542 item
->destructor
= (void (*)(void **))NULL
;
7543 item
->data
= pointer
;
7544 if (NULL
== prev_start
) *starts
= item
;
7545 else prev_start
->next
= item
;
7547 } else if (!xmlStrncmp(pointer
, BAD_CAST
"|$*HL_END*$|", 12)) {
7548 /* Remove the end marker */
7549 for (source
= pointer
+ 12, destination
= pointer
;
7550 *source
!= '\0'; source
++, destination
++) {
7551 *destination
= *source
;
7553 *destination
= '\0';
7554 /* Append the pointer into the list */
7555 item
= calloc(1, sizeof(*item
));
7560 item
->destructor
= (void (*)(void **))NULL
;
7561 item
->data
= pointer
;
7562 if (NULL
== prev_end
) *ends
= item
;
7563 else prev_end
->next
= item
;
7572 isds_list_free(starts
);
7573 isds_list_free(ends
);
7579 /* Convert isds:dbResult XML tree into structure
7580 * @context is ISDS context.
7581 * @fulltext_result is automatically reallocated found box structure.
7582 * @xpath_ctx is XPath context with current node as isds:dbResult element.
7583 * @collect_matches is true to interpret match markers.
7584 * In case of error @result will be freed. */
7585 static isds_error
extract_dbResult(struct isds_ctx
*context
,
7586 struct isds_fulltext_result
**fulltext_result
,
7587 xmlXPathContextPtr xpath_ctx
, _Bool collect_matches
) {
7588 isds_error err
= IE_SUCCESS
;
7589 xmlXPathObjectPtr result
= NULL
;
7590 char *string
= NULL
;
7592 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7593 if (NULL
== fulltext_result
) return IE_INVAL
;
7594 isds_fulltext_result_free(fulltext_result
);
7595 if (!xpath_ctx
) return IE_INVAL
;
7598 *fulltext_result
= calloc(1, sizeof(**fulltext_result
));
7599 if (NULL
== *fulltext_result
) {
7605 EXTRACT_STRING("isds:dbID", (*fulltext_result
)->dbID
);
7607 EXTRACT_STRING("isds:dbType", string
);
7608 if (NULL
== string
) {
7610 isds_log_message(context
, _("Empty isds:dbType element"));
7613 err
= string2isds_DbType((xmlChar
*)string
, &(*fulltext_result
)->dbType
);
7615 if (err
== IE_ENUM
) {
7617 char *string_locale
= _isds_utf82locale(string
);
7618 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
7620 free(string_locale
);
7626 EXTRACT_STRING("isds:dbName", (*fulltext_result
)->name
);
7627 EXTRACT_STRING("isds:dbAddress", (*fulltext_result
)->address
);
7629 err
= extract_BiDate(context
, &(*fulltext_result
)->biDate
, xpath_ctx
);
7630 if (err
) goto leave
;
7632 EXTRACT_STRING("isds:dbICO", (*fulltext_result
)->ic
);
7633 EXTRACT_BOOLEANNOPTR("isds:dbEffectiveOVM",
7634 (*fulltext_result
)->dbEffectiveOVM
);
7636 EXTRACT_STRING("isds:dbSendOptions", string
);
7637 if (NULL
== string
) {
7639 isds_log_message(context
, _("Empty isds:dbSendOptions element"));
7642 if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DZ")) {
7643 (*fulltext_result
)->active
= 1;
7644 (*fulltext_result
)->public_sending
= 1;
7645 (*fulltext_result
)->commercial_sending
= 0;
7646 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"ALL")) {
7647 (*fulltext_result
)->active
= 1;
7648 (*fulltext_result
)->public_sending
= 1;
7649 (*fulltext_result
)->commercial_sending
= 1;
7650 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"PDZ")) {
7651 (*fulltext_result
)->active
= 1;
7652 (*fulltext_result
)->public_sending
= 0;
7653 (*fulltext_result
)->commercial_sending
= 1;
7654 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"NONE")) {
7655 (*fulltext_result
)->active
= 1;
7656 (*fulltext_result
)->public_sending
= 0;
7657 (*fulltext_result
)->commercial_sending
= 0;
7658 } else if (!xmlStrcmp(BAD_CAST string
, BAD_CAST
"DISABLED")) {
7659 (*fulltext_result
)->active
= 0;
7660 (*fulltext_result
)->public_sending
= 0;
7661 (*fulltext_result
)->commercial_sending
= 0;
7664 char *string_locale
= _isds_utf82locale(string
);
7665 isds_printf_message(context
, _("Unknown isds:dbSendOptions value: %s"),
7667 free(string_locale
);
7672 /* Interpret match marks */
7673 if (collect_matches
) {
7674 err
= interpret_matches(BAD_CAST (*fulltext_result
)->name
,
7675 &((*fulltext_result
)->name_match_start
),
7676 &((*fulltext_result
)->name_match_end
));
7677 if (err
) goto leave
;
7678 err
= interpret_matches(BAD_CAST (*fulltext_result
)->address
,
7679 &((*fulltext_result
)->address_match_start
),
7680 &((*fulltext_result
)->address_match_end
));
7681 if (err
) goto leave
;
7685 if (err
) isds_fulltext_result_free(fulltext_result
);
7687 xmlXPathFreeObject(result
);
7690 #endif /* HAVE_LIBCURL */
7693 /* Find boxes matching a given full-text criteria.
7694 * @context is a session context
7695 * @query is a non-empty string which consists of words to search
7696 * @target selects box attributes to search for @query words. Pass NULL if you
7698 * @box_type restricts searching to given box type. Value DBTYPE_SYSTEM means
7699 * to search in all box types. Value DBTYPE_OVM_MAIN means to search in
7700 * non-subsudiary OVM box types. Pass NULL to let server to use default value
7701 * which is DBTYPE_SYSTEM.
7702 * @page_size defines count of boxes to constitute a response page. It counts
7703 * from zero. Pass NULL to let server to use a default value (50 now).
7704 * @page_number defines ordinar number of the response page to return. It
7705 * counts from zero. Pass NULL to let server to use a default value (0 now).
7706 * @track_matches points to true for marking @query words found in the box
7707 * attributes. It points to false for not marking. Pass NULL to let the server
7708 * to use default value (false now).
7709 * @total_matching_boxes outputs reallocated number of all boxes matching the
7710 * query. Will be pointer to NULL if server did not provide the value.
7711 * Pass NULL if you don't care.
7712 * @current_page_beginning outputs reallocated ordinar number of the first box
7713 * in this @boxes page. It counts from zero. It will be pointer to NULL if the
7714 * server did not provide the value. Pass NULL if you don't care.
7715 * @current_page_size outputs reallocated count of boxes in the this @boxes
7716 * page. It will be pointer to NULL if the server did not provide the value.
7717 * Pass NULL if you don't care.
7718 * @last_page outputs pointer to reallocated boolean. True if this @boxes page
7719 * is the last one, false if more boxes match, NULL if the server did not
7720 * provude the value. Pass NULL if you don't care.
7721 * @boxes outputs reallocated list of isds_fulltext_result structures,
7724 * IE_SUCCESS if search succeeded
7725 * IE_2BIG if @page_size is too large
7726 * other code if something bad happens; output arguments will be NULL. */
7727 isds_error
isds_find_box_by_fulltext(struct isds_ctx
*context
,
7729 const isds_fulltext_target
*target
,
7730 const isds_DbType
*box_type
,
7731 const unsigned long int *page_size
,
7732 const unsigned long int *page_number
,
7733 const _Bool
*track_matches
,
7734 unsigned long int **total_matching_boxes
,
7735 unsigned long int **current_page_beginning
,
7736 unsigned long int **current_page_size
,
7738 struct isds_list
**boxes
) {
7739 isds_error err
= IE_SUCCESS
;
7741 xmlNsPtr isds_ns
= NULL
;
7742 xmlNodePtr request
= NULL
;
7743 xmlDocPtr response
= NULL
;
7745 xmlXPathContextPtr xpath_ctx
= NULL
;
7746 xmlXPathObjectPtr result
= NULL
;
7747 const xmlChar
*static_string
= NULL
;
7748 xmlChar
*string
= NULL
;
7750 const xmlChar
*codes
[] = {
7760 const char *meanings
[] = {
7761 N_("You are not allowed to perform the search"),
7762 N_("The query string is empty"),
7763 N_("Searched box ID is malformed"),
7764 N_("Searched organization ID is malformed"),
7765 N_("Invalid input"),
7766 N_("Requested page size is too large"),
7767 N_("Search engine internal error")
7769 const isds_error errors
[] = {
7778 struct code_map_isds_error map
= {
7780 .meanings
= meanings
,
7786 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7787 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7788 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7789 if (NULL
!= last_page
) zfree(*last_page
);
7790 isds_list_free(boxes
);
7792 if (NULL
== context
) return IE_INVALID_CONTEXT
;
7793 zfree(context
->long_message
);
7795 if (NULL
== boxes
) return IE_INVAL
;
7797 if (NULL
== query
|| !xmlStrcmp(BAD_CAST query
, BAD_CAST
"")) {
7798 isds_log_message(context
, _("Query string must be non-empty"));
7803 /* Check if connection is established
7804 * TODO: This check should be done downstairs. */
7805 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7807 /* Build FindDataBox request */
7808 request
= xmlNewNode(NULL
, BAD_CAST
"ISDSSearch2");
7809 if (NULL
== request
) {
7810 isds_log_message(context
,
7811 _("Could not build ISDSSearch2 request"));
7814 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7815 if(NULL
== isds_ns
) {
7816 isds_log_message(context
, _("Could not create ISDS name space"));
7817 xmlFreeNode(request
);
7820 xmlSetNs(request
, isds_ns
);
7822 INSERT_STRING(request
, "searchText", query
);
7824 if (NULL
!= target
) {
7825 static_string
= isds_fulltext_target2string(*(target
));
7826 if (NULL
== static_string
) {
7827 isds_printf_message(context
, _("Invalid target value: %d"),
7833 INSERT_STRING(request
, "searchType", static_string
);
7834 static_string
= NULL
;
7836 if (NULL
!= box_type
) {
7837 /* XXX: Handle DBTYPE_SYSTEM value as "ALL" */
7838 if (DBTYPE_SYSTEM
== *box_type
) {
7839 static_string
= BAD_CAST
"ALL";
7840 } else if (DBTYPE_OVM_MAIN
== *box_type
) {
7841 static_string
= BAD_CAST
"OVM_MAIN";
7843 static_string
= isds_DbType2string(*(box_type
));
7844 if (NULL
== static_string
) {
7845 isds_printf_message(context
, _("Invalid box type value: %d"),
7852 INSERT_STRING(request
, "searchScope", static_string
);
7853 static_string
= NULL
;
7855 INSERT_ULONGINT(request
, "page", page_number
, string
);
7856 INSERT_ULONGINT(request
, "pageSize", page_size
, string
);
7857 INSERT_BOOLEAN(request
, "highlighting", track_matches
);
7859 /* Send request and check response */
7860 err
= send_destroy_request_check_response(context
,
7861 SERVICE_DB_SEARCH
, BAD_CAST
"ISDSSearch2",
7862 &request
, &response
, NULL
, &map
);
7863 if (err
) goto leave
;
7865 /* Parse response */
7866 xpath_ctx
= xmlXPathNewContext(response
);
7867 if (NULL
== xpath_ctx
) {
7871 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7875 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ISDSSearch2Response",
7881 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7882 isds_log_message(context
, _("Missing ISDSSearch2 element"));
7886 if (result
->nodesetval
->nodeNr
> 1) {
7887 isds_log_message(context
, _("Multiple ISDSSearch2 element"));
7891 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7892 xmlXPathFreeObject(result
); result
= NULL
;
7895 /* Extract counters */
7896 if (NULL
!= total_matching_boxes
) {
7897 EXTRACT_ULONGINT("isds:totalCount", *total_matching_boxes
, 0);
7899 if (NULL
!= current_page_size
) {
7900 EXTRACT_ULONGINT("isds:currentCount", *current_page_size
, 0);
7902 if (NULL
!= current_page_beginning
) {
7903 EXTRACT_ULONGINT("isds:position", *current_page_beginning
, 0);
7905 if (NULL
!= last_page
) {
7906 EXTRACT_BOOLEAN("isds:lastPage", *last_page
);
7908 xmlXPathFreeObject(result
); result
= NULL
;
7910 /* Extract boxes if they present */
7911 result
= xmlXPathEvalExpression(BAD_CAST
7912 "isds:dbResults/isds:dbResult", xpath_ctx
);
7913 if (NULL
== result
) {
7917 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7918 struct isds_list
*item
, *prev_item
= NULL
;
7919 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7920 item
= calloc(1, sizeof(*item
));
7926 item
->destructor
= (void (*)(void **))isds_fulltext_result_free
;
7927 if (i
== 0) *boxes
= item
;
7928 else prev_item
->next
= item
;
7931 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7932 err
= extract_dbResult(context
,
7933 (struct isds_fulltext_result
**) &(item
->data
), xpath_ctx
,
7934 (NULL
== track_matches
) ? 0 : *track_matches
);
7935 if (err
) goto leave
;
7941 if (NULL
!= total_matching_boxes
) zfree(*total_matching_boxes
);
7942 if (NULL
!= current_page_beginning
) zfree(*current_page_beginning
);
7943 if (NULL
!= current_page_size
) zfree(*current_page_size
);
7944 if (NULL
!= last_page
) zfree(*last_page
);
7945 isds_list_free(boxes
);
7949 xmlFreeNode(request
);
7950 xmlXPathFreeObject(result
);
7951 xmlXPathFreeContext(xpath_ctx
);
7952 xmlFreeDoc(response
);
7955 isds_log(ILF_ISDS
, ILL_DEBUG
,
7956 _("ISDSSearch2 request processed by server successfully.\n"));
7957 #else /* not HAVE_LIBCURL */
7965 /* Get status of a box.
7966 * @context is ISDS session context.
7967 * @box_id is UTF-8 encoded box identifier as zero terminated string
7968 * @box_status is return value of box status.
7970 * IE_SUCCESS if box has been found and its status retrieved
7971 * IE_NOEXIST if box is not known to ISDS server
7972 * or other appropriate error.
7973 * You can use isds_DbState to enumerate box status. However out of enum
7974 * range value can be returned too. This is feature because ISDS
7975 * specification leaves the set of values open.
7976 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7977 * the box has been deleted, but ISDS still lists its former existence. */
7978 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7979 long int *box_status
) {
7980 isds_error err
= IE_SUCCESS
;
7982 xmlNsPtr isds_ns
= NULL
;
7983 xmlNodePtr request
= NULL
, db_id
;
7984 xmlDocPtr response
= NULL
;
7985 xmlXPathContextPtr xpath_ctx
= NULL
;
7986 xmlXPathObjectPtr result
= NULL
;
7987 xmlChar
*string
= NULL
;
7989 const xmlChar
*codes
[] = {
7995 const char *meanings
[] = {
7996 "The box does not exist",
7997 "Box ID is malformed",
8000 const isds_error errors
[] = {
8005 struct code_map_isds_error map
= {
8007 .meanings
= meanings
,
8012 if (!context
) return IE_INVALID_CONTEXT
;
8013 zfree(context
->long_message
);
8014 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
8017 /* Check if connection is established
8018 * TODO: This check should be done downstairs. */
8019 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8022 /* Build CheckDataBox request */
8023 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
8025 isds_log_message(context
,
8026 _("Could build CheckDataBox request"));
8029 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8031 isds_log_message(context
, _("Could not create ISDS name space"));
8032 xmlFreeNode(request
);
8035 xmlSetNs(request
, isds_ns
);
8036 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
8038 isds_log_message(context
, _("Could not add dbID child to "
8039 "CheckDataBox element"));
8040 xmlFreeNode(request
);
8045 /* Send request and check response*/
8046 err
= send_destroy_request_check_response(context
,
8047 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
8048 &request
, &response
, NULL
, &map
);
8049 if (err
) goto leave
;
8053 xpath_ctx
= xmlXPathNewContext(response
);
8058 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8062 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
8068 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8069 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
8073 if (result
->nodesetval
->nodeNr
> 1) {
8074 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
8078 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8079 xmlXPathFreeObject(result
); result
= NULL
;
8081 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
8086 xmlXPathFreeObject(result
);
8087 xmlXPathFreeContext(xpath_ctx
);
8089 xmlFreeDoc(response
);
8092 isds_log(ILF_ISDS
, ILL_DEBUG
,
8093 _("CheckDataBox request processed by server successfully.\n"));
8094 #else /* not HAVE_LIBCURL */
8102 /* Get list of permissions to send commercial messages.
8103 * @context is ISDS session context.
8104 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
8105 * @permissions is a reallocated list of permissions (struct
8106 * isds_commercial_permission*) to send commercial messages from @box_id. The
8107 * order of permissions is significant as the server applies the permissions
8108 * and associated pre-paid credits in the order. Empty list means no
8111 * IE_SUCCESS if the list has been obtained correctly,
8112 * or other appropriate error. */
8113 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
8114 const char *box_id
, struct isds_list
**permissions
) {
8115 isds_error err
= IE_SUCCESS
;
8117 xmlDocPtr response
= NULL
;
8118 xmlXPathContextPtr xpath_ctx
= NULL
;
8119 xmlXPathObjectPtr result
= NULL
;
8122 if (!context
) return IE_INVALID_CONTEXT
;
8123 zfree(context
->long_message
);
8124 if (NULL
== permissions
) return IE_INVAL
;
8125 isds_list_free(permissions
);
8126 if (NULL
== box_id
) return IE_INVAL
;
8129 /* Check if connection is established */
8130 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8132 /* Do request and check for success */
8133 err
= build_send_dbid_request_check_response(context
,
8134 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
8135 BAD_CAST box_id
, NULL
, &response
, NULL
);
8137 isds_log(ILF_ISDS
, ILL_DEBUG
,
8138 _("PDZInfo request processed by server successfully.\n"));
8142 /* Prepare structure */
8143 xpath_ctx
= xmlXPathNewContext(response
);
8148 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8153 /* Set context node */
8154 result
= xmlXPathEvalExpression(BAD_CAST
8155 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
8161 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8162 struct isds_list
*prev_item
= NULL
;
8164 /* Iterate over all permission records */
8165 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8166 struct isds_list
*item
;
8168 /* Prepare structure */
8169 item
= calloc(1, sizeof(*item
));
8174 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
8175 if (i
== 0) *permissions
= item
;
8176 else prev_item
->next
= item
;
8180 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8181 err
= extract_DbPDZRecord(context
,
8182 (struct isds_commercial_permission
**) (&item
->data
),
8184 if (err
) goto leave
;
8190 isds_list_free(permissions
);
8193 xmlXPathFreeObject(result
);
8194 xmlXPathFreeContext(xpath_ctx
);
8195 xmlFreeDoc(response
);
8197 #else /* not HAVE_LIBCURL */
8205 /* Get details about credit for sending pre-paid commercial messages.
8206 * @context is ISDS session context.
8207 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
8208 * @from_date is first day of credit history to return in @history. Only
8209 * tm_year, tm_mon and tm_mday carry sane value.
8210 * @to_date is last day of credit history to return in @history. Only
8211 * tm_year, tm_mon and tm_mday carry sane value.
8212 * @credit outputs current credit value into pre-allocated memory. Pass NULL
8213 * if you don't care. This and all other credit values are integers in
8214 * hundredths of Czech Crowns.
8215 * @email outputs notification e-mail address where notifications about credit
8216 * are sent. This is automatically reallocated string. Pass NULL if you don't
8217 * care. It can return NULL if no address is defined.
8218 * @history outputs auto-reallocated list of pointers to struct
8219 * isds_credit_event. Events in closed interval @from_time to @to_time are
8220 * returned. Pass NULL @to_time and @from_time if you don't care. The events
8221 * are sorted by time.
8223 * IE_SUCCESS if the credit details have been obtained correctly,
8224 * or other appropriate error. Please note that server allows to retrieve
8225 * only limited history of events. */
8226 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
8228 const struct tm
*from_date
, const struct tm
*to_date
,
8229 long int *credit
, char **email
, struct isds_list
**history
) {
8230 isds_error err
= IE_SUCCESS
;
8232 char *box_id_locale
= NULL
;
8233 xmlNodePtr request
= NULL
, node
;
8234 xmlNsPtr isds_ns
= NULL
;
8235 xmlChar
*string
= NULL
;
8237 xmlDocPtr response
= NULL
;
8238 xmlXPathContextPtr xpath_ctx
= NULL
;
8239 xmlXPathObjectPtr result
= NULL
;
8241 const xmlChar
*codes
[] = {
8249 const char *meanings
[] = {
8250 "Insufficient priviledges for the box",
8251 "The box does not exist",
8252 "Date is too long (history is not available after 15 months)",
8253 "Interval is too long (limit is 3 months)",
8256 const isds_error errors
[] = {
8263 struct code_map_isds_error map
= {
8265 .meanings
= meanings
,
8270 if (!context
) return IE_INVALID_CONTEXT
;
8271 zfree(context
->long_message
);
8273 /* Free output argument */
8274 if (NULL
!= credit
) *credit
= 0;
8275 if (NULL
!= email
) zfree(*email
);
8276 isds_list_free(history
);
8278 if (NULL
== box_id
) return IE_INVAL
;
8281 /* Check if connection is established */
8282 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
8284 box_id_locale
= _isds_utf82locale((char*)box_id
);
8285 if (NULL
== box_id_locale
) {
8291 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
8292 if (NULL
== request
) {
8293 isds_printf_message(context
,
8294 _("Could not build DataBoxCreditInfo request for %s box"),
8299 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8301 isds_log_message(context
, _("Could not create ISDS name space"));
8305 xmlSetNs(request
, isds_ns
);
8307 /* Add mandatory XSD:tIdDbInput child */
8308 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
8309 /* Add mandatory dates elements with optional values */
8311 err
= tm2datestring(from_date
, &string
);
8313 isds_log_message(context
,
8314 _("Could not convert `from_date' argument to ISO date "
8318 INSERT_STRING(request
, "ciFromDate", string
);
8321 INSERT_STRING(request
, "ciFromDate", NULL
);
8324 err
= tm2datestring(to_date
, &string
);
8326 isds_log_message(context
,
8327 _("Could not convert `to_date' argument to ISO date "
8331 INSERT_STRING(request
, "ciTodate", string
);
8334 INSERT_STRING(request
, "ciTodate", NULL
);
8337 /* Send request and check response*/
8338 err
= send_destroy_request_check_response(context
,
8339 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
8340 &request
, &response
, NULL
, &map
);
8341 if (err
) goto leave
;
8345 /* Set context to the root */
8346 xpath_ctx
= xmlXPathNewContext(response
);
8351 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8355 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
8361 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8362 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
8366 if (result
->nodesetval
->nodeNr
> 1) {
8367 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
8371 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8372 xmlXPathFreeObject(result
); result
= NULL
;
8374 /* Extract common data */
8375 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
8376 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
8378 /* Extract records */
8379 if (NULL
== history
) goto leave
;
8380 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
8386 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8387 struct isds_list
*prev_item
= NULL
;
8389 /* Iterate over all records */
8390 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
8391 struct isds_list
*item
;
8393 /* Prepare structure */
8394 item
= calloc(1, sizeof(*item
));
8399 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
8400 if (i
== 0) *history
= item
;
8401 else prev_item
->next
= item
;
8405 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8406 err
= extract_CiRecord(context
,
8407 (struct isds_credit_event
**) (&item
->data
),
8409 if (err
) goto leave
;
8415 isds_log(ILF_ISDS
, ILL_DEBUG
,
8416 _("DataBoxCreditInfo request processed by server successfully.\n"));
8419 isds_list_free(history
);
8420 if (NULL
!= email
) zfree(*email
)
8423 free(box_id_locale
);
8424 xmlXPathFreeObject(result
);
8425 xmlXPathFreeContext(xpath_ctx
);
8426 xmlFreeDoc(response
);
8428 #else /* not HAVE_LIBCURL */
8436 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
8437 * code, destroy response and log success.
8438 * @context is ISDS session context.
8439 * @service_name is name of SERVICE_DB_MANIPULATION service
8440 * @box_id is UTF-8 encoded box identifier as zero terminated string
8441 * @approval is optional external approval of box manipulation
8442 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8443 * NULL, if you don't care. */
8444 static isds_error
build_send_manipulationdbid_request_check_drop_response(
8445 struct isds_ctx
*context
, const xmlChar
*service_name
,
8446 const xmlChar
*box_id
, const struct isds_approval
*approval
,
8447 xmlChar
**refnumber
) {
8448 isds_error err
= IE_SUCCESS
;
8450 xmlDocPtr response
= NULL
;
8453 if (!context
) return IE_INVALID_CONTEXT
;
8454 zfree(context
->long_message
);
8455 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
8458 /* Check if connection is established */
8459 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8461 /* Do request and check for success */
8462 err
= build_send_dbid_request_check_response(context
,
8463 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
8464 &response
, refnumber
);
8465 xmlFreeDoc(response
);
8468 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
8469 isds_log(ILF_ISDS
, ILL_DEBUG
,
8470 _("%s request processed by server successfully.\n"),
8471 service_name_locale
);
8472 free(service_name_locale
);
8474 #else /* not HAVE_LIBCURL */
8482 /* Switch box into state where box can receive commercial messages (off by
8484 * @context is ISDS session context.
8485 * @box_id is UTF-8 encoded box identifier as zero terminated string
8486 * @allow is true for enable, false for disable commercial messages income
8487 * @approval is optional external approval of box manipulation
8488 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8489 * NULL, if you don't care. */
8490 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
8491 const char *box_id
, const _Bool allow
,
8492 const struct isds_approval
*approval
, char **refnumber
) {
8493 return build_send_manipulationdbid_request_check_drop_response(context
,
8494 (allow
) ? BAD_CAST
"SetOpenAddressing" :
8495 BAD_CAST
"ClearOpenAddressing",
8496 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8500 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
8501 * message acceptance). This is just a box permission. Sender must apply
8502 * such role by sending each message.
8503 * @context is ISDS session context.
8504 * @box_id is UTF-8 encoded box identifier as zero terminated string
8505 * @allow is true for enable, false for disable OVM role permission
8506 * @approval is optional external approval of box manipulation
8507 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8508 * NULL, if you don't care. */
8509 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
8510 const char *box_id
, const _Bool allow
,
8511 const struct isds_approval
*approval
, char **refnumber
) {
8512 return build_send_manipulationdbid_request_check_drop_response(context
,
8513 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
8514 BAD_CAST
"ClearEffectiveOVM",
8515 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
8519 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
8520 * code, destroy response and log success.
8521 * @context is ISDS session context.
8522 * @service_name is name of SERVICE_DB_MANIPULATION service
8523 * @owner is structure describing box. aifoIsds, address->adCode,
8524 * address->adDistrict members are ignored.
8525 * @approval is optional external approval of box manipulation
8526 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8527 * NULL, if you don't care. */
8528 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
8529 struct isds_ctx
*context
, const xmlChar
*service_name
,
8530 const struct isds_DbOwnerInfo
*owner
,
8531 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
8532 isds_error err
= IE_SUCCESS
;
8534 char *service_name_locale
= NULL
;
8535 xmlNodePtr request
= NULL
, db_owner_info
;
8536 xmlNsPtr isds_ns
= NULL
;
8540 if (!context
) return IE_INVALID_CONTEXT
;
8541 zfree(context
->long_message
);
8542 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
8545 service_name_locale
= _isds_utf82locale((char*)service_name
);
8546 if (!service_name_locale
) {
8552 request
= xmlNewNode(NULL
, service_name
);
8554 isds_printf_message(context
,
8555 _("Could not build %s request"), service_name_locale
);
8559 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8561 isds_log_message(context
, _("Could not create ISDS name space"));
8565 xmlSetNs(request
, isds_ns
);
8568 /* Add XSD:tOwnerInfoInput child*/
8569 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
8570 err
= insert_DbOwnerInfo(context
, owner
, 0, db_owner_info
);
8571 if (err
) goto leave
;
8573 /* Add XSD:gExtApproval*/
8574 err
= insert_GExtApproval(context
, approval
, request
);
8575 if (err
) goto leave
;
8577 /* Send it to server and process response */
8578 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8579 service_name
, &request
, refnumber
);
8582 xmlFreeNode(request
);
8583 free(service_name_locale
);
8584 #else /* not HAVE_LIBCURL */
8592 /* Switch box accessibility state on request of box owner.
8593 * Despite the name, owner must do the request off-line. This function is
8594 * designed for such off-line meeting points (e.g. Czech POINT).
8595 * @context is ISDS session context.
8596 * @box identifies box to switch accessibility state. aifoIsds,
8597 * address->adCode, address->adDistrict members are ignored.
8598 * @allow is true for making accessible, false to disallow access.
8599 * @approval is optional external approval of box manipulation
8600 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8601 * NULL, if you don't care. */
8602 isds_error
isds_switch_box_accessibility_on_owner_request(
8603 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8604 const _Bool allow
, const struct isds_approval
*approval
,
8606 return build_send_manipulationdbowner_request_check_drop_response(context
,
8607 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
8608 BAD_CAST
"DisableOwnDataBox",
8609 box
, approval
, (xmlChar
**) refnumber
);
8613 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
8615 * @context is ISDS session context.
8616 * @box identifies box to switch accessibility state. aifoIsds,
8617 * address->adCode, address->adDistrict members are ignored.
8618 * @since is date since accessibility has been denied. This can be past too.
8619 * Only tm_year, tm_mon and tm_mday carry sane value.
8620 * @approval is optional external approval of box manipulation
8621 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8622 * NULL, if you don't care. */
8623 isds_error
isds_disable_box_accessibility_externaly(
8624 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
8625 const struct tm
*since
, const struct isds_approval
*approval
,
8627 isds_error err
= IE_SUCCESS
;
8629 char *service_name_locale
= NULL
;
8630 xmlNodePtr request
= NULL
, node
;
8631 xmlNsPtr isds_ns
= NULL
;
8632 xmlChar
*string
= NULL
;
8636 if (!context
) return IE_INVALID_CONTEXT
;
8637 zfree(context
->long_message
);
8638 if (!box
|| !since
) return IE_INVAL
;
8642 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
8644 isds_printf_message(context
,
8645 _("Could not build %s request"), "DisableDataBoxExternally");
8649 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8651 isds_log_message(context
, _("Could not create ISDS name space"));
8655 xmlSetNs(request
, isds_ns
);
8658 /* Add @box identification */
8659 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
8660 err
= insert_DbOwnerInfo(context
, box
, 0, node
);
8661 if (err
) goto leave
;
8663 /* Add @since date */
8664 err
= tm2datestring(since
, &string
);
8666 isds_log_message(context
,
8667 _("Could not convert `since' argument to ISO date string"));
8670 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
8674 err
= insert_GExtApproval(context
, approval
, request
);
8675 if (err
) goto leave
;
8677 /* Send it to server and process response */
8678 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
8679 BAD_CAST
"DisableDataBoxExternally", &request
,
8680 (xmlChar
**) refnumber
);
8684 xmlFreeNode(request
);
8685 free(service_name_locale
);
8686 #else /* not HAVE_LIBCURL */
8695 /* Insert struct isds_message data (envelope (recipient data optional) and
8696 * documents into XML tree
8697 * @context is session context
8698 * @outgoing_message is libisds structure with message data
8699 * @create_message is XML CreateMessage or CreateMultipleMessage element
8700 * @process_recipient true for recipient data serialization, false for no
8702 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8703 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8704 const _Bool process_recipient
) {
8706 isds_error err
= IE_SUCCESS
;
8707 xmlNodePtr envelope
, dm_files
, node
;
8708 xmlChar
*string
= NULL
;
8710 if (!context
) return IE_INVALID_CONTEXT
;
8711 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8714 /* Build envelope */
8715 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8717 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8718 "%s element"), create_message
->name
);
8722 if (!outgoing_message
->envelope
) {
8723 isds_log_message(context
, _("Outgoing message is missing envelope"));
8728 /* Insert optional message type */
8729 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8731 if (err
) goto leave
;
8733 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8734 outgoing_message
->envelope
->dmSenderOrgUnit
);
8735 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8736 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8738 if (process_recipient
) {
8739 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8740 isds_log_message(context
,
8741 _("Outgoing message is missing recipient box identifier"));
8745 INSERT_STRING(envelope
, "dbIDRecipient",
8746 outgoing_message
->envelope
->dbIDRecipient
);
8748 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8749 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8750 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8751 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8752 INSERT_STRING(envelope
, "dmToHands",
8753 outgoing_message
->envelope
->dmToHands
);
8756 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8758 INSERT_STRING(envelope
, "dmAnnotation",
8759 outgoing_message
->envelope
->dmAnnotation
);
8761 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8762 0, 50, "dmRecipientRefNumber");
8763 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8764 outgoing_message
->envelope
->dmRecipientRefNumber
);
8766 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8767 0, 50, "dmSenderRefNumber");
8768 INSERT_STRING(envelope
, "dmSenderRefNumber",
8769 outgoing_message
->envelope
->dmSenderRefNumber
);
8771 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8772 0, 50, "dmRecipientIdent");
8773 INSERT_STRING(envelope
, "dmRecipientIdent",
8774 outgoing_message
->envelope
->dmRecipientIdent
);
8776 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8777 0, 50, "dmSenderIdent");
8778 INSERT_STRING(envelope
, "dmSenderIdent",
8779 outgoing_message
->envelope
->dmSenderIdent
);
8781 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8782 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8783 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8784 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8785 INSERT_STRING(envelope
, "dmLegalTitleSect",
8786 outgoing_message
->envelope
->dmLegalTitleSect
);
8787 INSERT_STRING(envelope
, "dmLegalTitlePar",
8788 outgoing_message
->envelope
->dmLegalTitlePar
);
8789 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8790 outgoing_message
->envelope
->dmLegalTitlePoint
);
8792 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8793 outgoing_message
->envelope
->dmPersonalDelivery
);
8794 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8795 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8797 /* ???: Should we require value for dbEffectiveOVM sender?
8798 * ISDS has default as true */
8799 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8800 INSERT_BOOLEAN(envelope
, "dmOVM",
8801 outgoing_message
->envelope
->dmPublishOwnID
);
8804 /* Append dmFiles */
8805 if (!outgoing_message
->documents
) {
8806 isds_log_message(context
,
8807 _("Outgoing message is missing list of documents"));
8811 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8813 isds_printf_message(context
, _("Could not add dmFiles child to "
8814 "%s element"), create_message
->name
);
8819 /* Check for document hierarchy */
8820 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8821 if (err
) goto leave
;
8823 /* Process each document */
8824 for (struct isds_list
*item
=
8825 (struct isds_list
*) outgoing_message
->documents
;
8826 item
; item
= item
->next
) {
8828 isds_log_message(context
,
8829 _("List of documents contains empty item"));
8833 /* FIXME: Check for dmFileMetaType and for document references.
8834 * Only first document can be of MAIN type */
8835 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8838 if (err
) goto leave
;
8845 #endif /* HAVE_LIBCURL */
8848 /* Send a message via ISDS to a recipient
8849 * @context is session context
8850 * @outgoing_message is message to send; Some members are mandatory (like
8851 * dbIDRecipient), some are optional and some are irrelevant (especially data
8852 * about sender). Included pointer to isds_list documents must contain at
8853 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8854 * members will be filled with valid data from ISDS. Exact list of write
8855 * members is subject to change. Currently dmID is changed.
8856 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8857 isds_error
isds_send_message(struct isds_ctx
*context
,
8858 struct isds_message
*outgoing_message
) {
8860 isds_error err
= IE_SUCCESS
;
8862 xmlNsPtr isds_ns
= NULL
;
8863 xmlNodePtr request
= NULL
;
8864 xmlDocPtr response
= NULL
;
8865 xmlChar
*code
= NULL
, *message
= NULL
;
8866 xmlXPathContextPtr xpath_ctx
= NULL
;
8867 xmlXPathObjectPtr result
= NULL
;
8868 /*_Bool message_is_complete = 0;*/
8871 if (!context
) return IE_INVALID_CONTEXT
;
8872 zfree(context
->long_message
);
8873 if (!outgoing_message
) return IE_INVAL
;
8876 /* Check if connection is established
8877 * TODO: This check should be done downstairs. */
8878 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8881 /* Build CreateMessage request */
8882 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8884 isds_log_message(context
,
8885 _("Could not build CreateMessage request"));
8888 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8890 isds_log_message(context
, _("Could not create ISDS name space"));
8891 xmlFreeNode(request
);
8894 xmlSetNs(request
, isds_ns
);
8896 /* Append envelope and files */
8897 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8898 if (err
) goto leave
;
8901 /* Signal we can serialize message since now */
8902 /*message_is_complete = 1;*/
8905 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8908 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8910 /* Don't' destroy request, we want to provide it to application later */
8913 isds_log(ILF_ISDS
, ILL_DEBUG
,
8914 _("Processing ISDS response on CreateMessage "
8915 "request failed\n"));
8919 /* Check for response status */
8920 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8921 &code
, &message
, NULL
);
8923 isds_log(ILF_ISDS
, ILL_DEBUG
,
8924 _("ISDS response on CreateMessage request "
8925 "is missing status\n"));
8929 /* Request processed, but refused by server or server failed */
8930 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8931 char *box_id_locale
=
8932 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8933 char *code_locale
= _isds_utf82locale((char*)code
);
8934 char *message_locale
= _isds_utf82locale((char*)message
);
8935 isds_log(ILF_ISDS
, ILL_DEBUG
,
8936 _("Server did not accept message for %s on CreateMessage "
8937 "request (code=%s, message=%s)\n"),
8938 box_id_locale
, code_locale
, message_locale
);
8939 isds_log_message(context
, message_locale
);
8940 free(box_id_locale
);
8942 free(message_locale
);
8949 xpath_ctx
= xmlXPathNewContext(response
);
8954 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8958 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8964 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8965 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8969 if (result
->nodesetval
->nodeNr
> 1) {
8970 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8974 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8975 xmlXPathFreeObject(result
); result
= NULL
;
8977 if (outgoing_message
->envelope
->dmID
) {
8978 free(outgoing_message
->envelope
->dmID
);
8979 outgoing_message
->envelope
->dmID
= NULL
;
8981 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8982 if (!outgoing_message
->envelope
->dmID
) {
8983 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8984 "but did not return assigned message ID\n"));
8988 /* TODO: Serialize message into structure member raw */
8989 /* XXX: Each web service transport message in different format.
8990 * Therefore it's not possible to save them directly.
8991 * To save them, one must figure out common format.
8992 * We can leave it on application, or we can implement the ESS format. */
8993 /*if (message_is_complete) {
8994 if (outgoing_message->envelope->dmID) {
8996 /* Add assigned message ID as first child*/
8997 /*xmlNodePtr dmid_text = xmlNewText(
8998 (xmlChar *) outgoing_message->envelope->dmID);
8999 if (!dmid_text) goto serialization_failed;
9001 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
9003 if (!dmid_element) {
9004 xmlFreeNode(dmid_text);
9005 goto serialization_failed;
9008 xmlNodePtr dmid_element_with_text =
9009 xmlAddChild(dmid_element, dmid_text);
9010 if (!dmid_element_with_text) {
9011 xmlFreeNode(dmid_element);
9012 xmlFreeNode(dmid_text);
9013 goto serialization_failed;
9016 node = xmlAddPrevSibling(envelope->childern,
9017 dmid_element_with_text);
9019 xmlFreeNodeList(dmid_element_with_text);
9020 goto serialization_failed;
9024 /* Serialize message with ID into raw */
9025 /*buffer = serialize_element(envelope)*/
9028 serialization_failed:
9033 xmlXPathFreeObject(result
);
9034 xmlXPathFreeContext(xpath_ctx
);
9038 xmlFreeDoc(response
);
9039 xmlFreeNode(request
);
9042 isds_log(ILF_ISDS
, ILL_DEBUG
,
9043 _("CreateMessage request processed by server "
9044 "successfully.\n"));
9045 #else /* not HAVE_LIBCURL */
9053 /* Send a message via ISDS to a multiple recipients
9054 * @context is session context
9055 * @outgoing_message is message to send; Some members are mandatory,
9056 * some are optional and some are irrelevant (especially data
9057 * about sender). Data about recipient will be substituted by ISDS from
9058 * @copies. Included pointer to isds_list documents must
9059 * contain at least one document of FILEMETATYPE_MAIN.
9060 * @copies is list of isds_message_copy structures addressing all desired
9061 * recipients. This is read-write structure, some members will be filled with
9062 * valid data from ISDS (message IDs, error codes, error descriptions).
9064 * ISDS_SUCCESS if all messages have been sent
9065 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
9066 * succeeded messages can be identified by copies->data->error),
9067 * or other error code if something other goes wrong. */
9068 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
9069 const struct isds_message
*outgoing_message
,
9070 struct isds_list
*copies
) {
9072 isds_error err
= IE_SUCCESS
;
9074 isds_error append_err
;
9075 xmlNsPtr isds_ns
= NULL
;
9076 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
9077 struct isds_list
*item
;
9078 struct isds_message_copy
*copy
;
9079 xmlDocPtr response
= NULL
;
9080 xmlChar
*code
= NULL
, *message
= NULL
;
9081 xmlXPathContextPtr xpath_ctx
= NULL
;
9082 xmlXPathObjectPtr result
= NULL
;
9083 xmlChar
*string
= NULL
;
9087 if (!context
) return IE_INVALID_CONTEXT
;
9088 zfree(context
->long_message
);
9089 if (!outgoing_message
|| !copies
) return IE_INVAL
;
9092 /* Check if connection is established
9093 * TODO: This check should be done downstairs. */
9094 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9097 /* Build CreateMultipleMessage request */
9098 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
9100 isds_log_message(context
,
9101 _("Could not build CreateMultipleMessage request"));
9104 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9106 isds_log_message(context
, _("Could not create ISDS name space"));
9107 xmlFreeNode(request
);
9110 xmlSetNs(request
, isds_ns
);
9113 /* Build recipients */
9114 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
9116 isds_log_message(context
, _("Could not add dmRecipients child to "
9117 "CreateMultipleMessage element"));
9118 xmlFreeNode(request
);
9122 /* Insert each recipient */
9123 for (item
= copies
; item
; item
= item
->next
) {
9124 copy
= (struct isds_message_copy
*) item
->data
;
9126 isds_log_message(context
,
9127 _("`copies' list item contains empty data"));
9132 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
9134 isds_log_message(context
, _("Could not add dmRecipient child to "
9135 "dmRecipients element"));
9140 if (!copy
->dbIDRecipient
) {
9141 isds_log_message(context
,
9142 _("Message copy is missing recipient box identifier"));
9146 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
9147 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
9148 copy
->dmRecipientOrgUnit
);
9149 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
9150 copy
->dmRecipientOrgUnitNum
, string
);
9151 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
9154 /* Append envelope and files */
9155 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
9156 if (err
) goto leave
;
9159 isds_log(ILF_ISDS
, ILL_DEBUG
,
9160 _("Sending CreateMultipleMessage request to ISDS\n"));
9163 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
9165 isds_log(ILF_ISDS
, ILL_DEBUG
,
9166 _("Processing ISDS response on CreateMultipleMessage "
9167 "request failed\n"));
9171 /* Check for response status */
9172 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
9173 &code
, &message
, NULL
);
9175 isds_log(ILF_ISDS
, ILL_DEBUG
,
9176 _("ISDS response on CreateMultipleMessage request "
9177 "is missing status\n"));
9181 /* Request processed, but some copies failed */
9182 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
9183 char *box_id_locale
=
9184 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9185 char *code_locale
= _isds_utf82locale((char*)code
);
9186 char *message_locale
= _isds_utf82locale((char*)message
);
9187 isds_log(ILF_ISDS
, ILL_DEBUG
,
9188 _("Server did accept message for multiple recipients "
9189 "on CreateMultipleMessage request but delivery to "
9190 "some of them failed (code=%s, message=%s)\n"),
9191 box_id_locale
, code_locale
, message_locale
);
9192 isds_log_message(context
, message_locale
);
9193 free(box_id_locale
);
9195 free(message_locale
);
9196 err
= IE_PARTIAL_SUCCESS
;
9199 /* Request refused by server as whole */
9200 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9201 char *box_id_locale
=
9202 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
9203 char *code_locale
= _isds_utf82locale((char*)code
);
9204 char *message_locale
= _isds_utf82locale((char*)message
);
9205 isds_log(ILF_ISDS
, ILL_DEBUG
,
9206 _("Server did not accept message for multiple recipients "
9207 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
9208 box_id_locale
, code_locale
, message_locale
);
9209 isds_log_message(context
, message_locale
);
9210 free(box_id_locale
);
9212 free(message_locale
);
9219 xpath_ctx
= xmlXPathNewContext(response
);
9224 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9228 result
= xmlXPathEvalExpression(
9229 BAD_CAST
"/isds:CreateMultipleMessageResponse"
9230 "/isds:dmMultipleStatus/isds:dmSingleStatus",
9236 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9237 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
9242 /* Extract message ID and delivery status for each copy */
9243 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
9244 item
= item
->next
, i
++) {
9245 copy
= (struct isds_message_copy
*) item
->data
;
9246 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
9248 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
9254 if (item
|| i
< result
->nodesetval
->nodeNr
) {
9255 isds_printf_message(context
, _("ISDS returned unexpected number of "
9256 "message copy delivery states: %d"),
9257 result
->nodesetval
->nodeNr
);
9266 xmlXPathFreeObject(result
);
9267 xmlXPathFreeContext(xpath_ctx
);
9271 xmlFreeDoc(response
);
9272 xmlFreeNode(request
);
9275 isds_log(ILF_ISDS
, ILL_DEBUG
,
9276 _("CreateMultipleMessageResponse request processed by server "
9277 "successfully.\n"));
9278 #else /* not HAVE_LIBCURL */
9286 /* Get list of messages. This is common core for getting sent or received
9288 * Any criterion argument can be NULL, if you don't care about it.
9289 * @context is session context. Must not be NULL.
9290 * @outgoing_direction is true if you want list of outgoing messages,
9291 * it's false if you want incoming messages.
9292 * @from_time is minimal time and date of message sending inclusive.
9293 * @to_time is maximal time and date of message sending inclusive
9294 * @organization_unit_number is number of sender/recipient respectively.
9295 * @status_filter is bit field of isds_message_status values. Use special
9296 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9297 * all values, you can use bit-wise arithmetic if you want.)
9298 * @offset is index of first message we are interested in. First message is 1.
9299 * Set to 0 (or 1) if you don't care.
9300 * @number is maximal length of list you want to get as input value, outputs
9301 * number of messages matching these criteria. Can be NULL if you don't care
9302 * (applies to output value either).
9303 * @messages is automatically reallocated list of isds_message's. Be ware that
9304 * it returns only brief overview (envelope and some other fields) about each
9305 * message, not the complete message. FIXME: Specify exact fields.
9306 * The list is sorted by delivery time in ascending order.
9307 * Use NULL if you don't care about don't need the data (useful if you want to
9308 * know only the @number). If you provide &NULL, list will be allocated on
9309 * heap, if you provide pointer to non-NULL, list will be freed automatically
9310 * at first. Also in case of error the list will be NULLed.
9311 * @return IE_SUCCESS or appropriate error code. */
9312 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
9313 _Bool outgoing_direction
,
9314 const struct timeval
*from_time
, const struct timeval
*to_time
,
9315 const long int *organization_unit_number
,
9316 const unsigned int status_filter
,
9317 const unsigned long int offset
, unsigned long int *number
,
9318 struct isds_list
**messages
) {
9320 isds_error err
= IE_SUCCESS
;
9322 xmlNsPtr isds_ns
= NULL
;
9323 xmlNodePtr request
= NULL
, node
;
9324 xmlDocPtr response
= NULL
;
9325 xmlChar
*code
= NULL
, *message
= NULL
;
9326 xmlXPathContextPtr xpath_ctx
= NULL
;
9327 xmlXPathObjectPtr result
= NULL
;
9328 xmlChar
*string
= NULL
;
9332 if (!context
) return IE_INVALID_CONTEXT
;
9333 zfree(context
->long_message
);
9335 /* Free former message list if any */
9336 if (messages
) isds_list_free(messages
);
9339 /* Check if connection is established
9340 * TODO: This check should be done downstairs. */
9341 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9343 /* Build GetListOf*Messages request */
9344 request
= xmlNewNode(NULL
,
9345 (outgoing_direction
) ?
9346 BAD_CAST
"GetListOfSentMessages" :
9347 BAD_CAST
"GetListOfReceivedMessages"
9350 isds_log_message(context
,
9351 (outgoing_direction
) ?
9352 _("Could not build GetListOfSentMessages request") :
9353 _("Could not build GetListOfReceivedMessages request")
9357 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9359 isds_log_message(context
, _("Could not create ISDS name space"));
9360 xmlFreeNode(request
);
9363 xmlSetNs(request
, isds_ns
);
9367 err
= timeval2timestring(from_time
, &string
);
9368 if (err
) goto leave
;
9370 INSERT_STRING(request
, "dmFromTime", string
);
9371 free(string
); string
= NULL
;
9374 err
= timeval2timestring(to_time
, &string
);
9375 if (err
) goto leave
;
9377 INSERT_STRING(request
, "dmToTime", string
);
9378 free(string
); string
= NULL
;
9380 if (outgoing_direction
) {
9381 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
9382 organization_unit_number
, string
);
9384 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
9385 organization_unit_number
, string
);
9388 if (status_filter
> MESSAGESTATE_ANY
) {
9389 isds_printf_message(context
,
9390 _("Invalid message state filter value: %ld"), status_filter
);
9394 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
9397 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
9399 INSERT_STRING(request
, "dmOffset", "1");
9402 /* number 0 means no limit */
9403 if (number
&& *number
== 0) {
9404 INSERT_STRING(request
, "dmLimit", NULL
);
9406 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
9410 isds_log(ILF_ISDS
, ILL_DEBUG
,
9411 (outgoing_direction
) ?
9412 _("Sending GetListOfSentMessages request to ISDS\n") :
9413 _("Sending GetListOfReceivedMessages request to ISDS\n")
9417 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
9418 xmlFreeNode(request
); request
= NULL
;
9421 isds_log(ILF_ISDS
, ILL_DEBUG
,
9422 (outgoing_direction
) ?
9423 _("Processing ISDS response on GetListOfSentMessages "
9424 "request failed\n") :
9425 _("Processing ISDS response on GetListOfReceivedMessages "
9431 /* Check for response status */
9432 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
9433 &code
, &message
, NULL
);
9435 isds_log(ILF_ISDS
, ILL_DEBUG
,
9436 (outgoing_direction
) ?
9437 _("ISDS response on GetListOfSentMessages request "
9438 "is missing status\n") :
9439 _("ISDS response on GetListOfReceivedMessages request "
9440 "is missing status\n")
9445 /* Request processed, but nothing found */
9446 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
9447 char *code_locale
= _isds_utf82locale((char*)code
);
9448 char *message_locale
= _isds_utf82locale((char*)message
);
9449 isds_log(ILF_ISDS
, ILL_DEBUG
,
9450 (outgoing_direction
) ?
9451 _("Server refused GetListOfSentMessages request "
9452 "(code=%s, message=%s)\n") :
9453 _("Server refused GetListOfReceivedMessages request "
9454 "(code=%s, message=%s)\n"),
9455 code_locale
, message_locale
);
9456 isds_log_message(context
, message_locale
);
9458 free(message_locale
);
9465 xpath_ctx
= xmlXPathNewContext(response
);
9470 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9474 result
= xmlXPathEvalExpression(
9475 (outgoing_direction
) ?
9476 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
9477 "isds:dmRecords/isds:dmRecord" :
9478 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
9479 "isds:dmRecords/isds:dmRecord",
9486 /* Fill output arguments in */
9487 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9488 struct isds_envelope
*envelope
;
9489 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9491 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9492 /* Create new message */
9493 item
= calloc(1, sizeof(*item
));
9498 item
->destructor
= (void(*)(void**)) &isds_message_free
;
9499 item
->data
= calloc(1, sizeof(struct isds_message
));
9501 isds_list_free(&item
);
9506 /* Extract envelope data */
9507 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9509 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
9511 isds_list_free(&item
);
9515 /* Attach extracted envelope */
9516 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
9518 /* Append new message into the list */
9520 *messages
= last_item
= item
;
9522 last_item
->next
= item
;
9527 if (number
) *number
= count
;
9531 isds_list_free(messages
);
9535 xmlXPathFreeObject(result
);
9536 xmlXPathFreeContext(xpath_ctx
);
9540 xmlFreeDoc(response
);
9541 xmlFreeNode(request
);
9544 isds_log(ILF_ISDS
, ILL_DEBUG
,
9545 (outgoing_direction
) ?
9546 _("GetListOfSentMessages request processed by server "
9547 "successfully.\n") :
9548 _("GetListOfReceivedMessages request processed by server "
9551 #else /* not HAVE_LIBCURL */
9558 /* Get list of outgoing (already sent) messages.
9559 * Any criterion argument can be NULL, if you don't care about it.
9560 * @context is session context. Must not be NULL.
9561 * @from_time is minimal time and date of message sending inclusive.
9562 * @to_time is maximal time and date of message sending inclusive
9563 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
9564 * @status_filter is bit field of isds_message_status values. Use special
9565 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9566 * all values, you can use bit-wise arithmetic if you want.)
9567 * @offset is index of first message we are interested in. First message is 1.
9568 * Set to 0 (or 1) if you don't care.
9569 * @number is maximal length of list you want to get as input value, outputs
9570 * number of messages matching these criteria. Can be NULL if you don't care
9571 * (applies to output value either).
9572 * @messages is automatically reallocated list of isds_message's. Be ware that
9573 * it returns only brief overview (envelope and some other fields) about each
9574 * message, not the complete message. FIXME: Specify exact fields.
9575 * The list is sorted by delivery time in ascending order.
9576 * Use NULL if you don't care about the meta data (useful if you want to know
9577 * only the @number). If you provide &NULL, list will be allocated on heap,
9578 * if you provide pointer to non-NULL, list will be freed automatically at
9579 * first. Also in case of error the list will be NULLed.
9580 * @return IE_SUCCESS or appropriate error code. */
9581 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
9582 const struct timeval
*from_time
, const struct timeval
*to_time
,
9583 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
9584 const unsigned long int offset
, unsigned long int *number
,
9585 struct isds_list
**messages
) {
9587 return isds_get_list_of_messages(
9589 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
9595 /* Get list of incoming (addressed to you) messages.
9596 * Any criterion argument can be NULL, if you don't care about it.
9597 * @context is session context. Must not be NULL.
9598 * @from_time is minimal time and date of message sending inclusive.
9599 * @to_time is maximal time and date of message sending inclusive
9600 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
9601 * @status_filter is bit field of isds_message_status values. Use special
9602 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
9603 * all values, you can use bit-wise arithmetic if you want.)
9604 * @offset is index of first message we are interested in. First message is 1.
9605 * Set to 0 (or 1) if you don't care.
9606 * @number is maximal length of list you want to get as input value, outputs
9607 * number of messages matching these criteria. Can be NULL if you don't care
9608 * (applies to output value either).
9609 * @messages is automatically reallocated list of isds_message's. Be ware that
9610 * it returns only brief overview (envelope and some other fields) about each
9611 * message, not the complete message. FIXME: Specify exact fields.
9612 * Use NULL if you don't care about the meta data (useful if you want to know
9613 * only the @number). If you provide &NULL, list will be allocated on heap,
9614 * if you provide pointer to non-NULL, list will be freed automatically at
9615 * first. Also in case of error the list will be NULLed.
9616 * @return IE_SUCCESS or appropriate error code. */
9617 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
9618 const struct timeval
*from_time
, const struct timeval
*to_time
,
9619 const long int *dmRecipientOrgUnitNum
,
9620 const unsigned int status_filter
,
9621 const unsigned long int offset
, unsigned long int *number
,
9622 struct isds_list
**messages
) {
9624 return isds_get_list_of_messages(
9626 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
9632 /* Get list of sent message state changes.
9633 * Any criterion argument can be NULL, if you don't care about it.
9634 * @context is session context. Must not be NULL.
9635 * @from_time is minimal time and date of status changes inclusive
9636 * @to_time is maximal time and date of status changes inclusive
9637 * @changed_states is automatically reallocated list of
9638 * isds_message_status_change's. If you provide &NULL, list will be allocated
9639 * on heap, if you provide pointer to non-NULL, list will be freed
9640 * automatically at first. Also in case of error the list will be NULLed.
9641 * XXX: The list item ordering is not specified.
9642 * XXX: Server provides only `recent' changes.
9643 * @return IE_SUCCESS or appropriate error code. */
9644 isds_error
isds_get_list_of_sent_message_state_changes(
9645 struct isds_ctx
*context
,
9646 const struct timeval
*from_time
, const struct timeval
*to_time
,
9647 struct isds_list
**changed_states
) {
9649 isds_error err
= IE_SUCCESS
;
9651 xmlNsPtr isds_ns
= NULL
;
9652 xmlNodePtr request
= NULL
, node
;
9653 xmlDocPtr response
= NULL
;
9654 xmlXPathContextPtr xpath_ctx
= NULL
;
9655 xmlXPathObjectPtr result
= NULL
;
9656 xmlChar
*string
= NULL
;
9660 if (!context
) return IE_INVALID_CONTEXT
;
9661 zfree(context
->long_message
);
9663 /* Free former message list if any */
9664 isds_list_free(changed_states
);
9667 /* Check if connection is established
9668 * TODO: This check should be done downstairs. */
9669 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9671 /* Build GetMessageStateChanges request */
9672 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
9674 isds_log_message(context
,
9675 _("Could not build GetMessageStateChanges request"));
9678 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9680 isds_log_message(context
, _("Could not create ISDS name space"));
9681 xmlFreeNode(request
);
9684 xmlSetNs(request
, isds_ns
);
9688 err
= timeval2timestring(from_time
, &string
);
9689 if (err
) goto leave
;
9691 INSERT_STRING(request
, "dmFromTime", string
);
9695 err
= timeval2timestring(to_time
, &string
);
9696 if (err
) goto leave
;
9698 INSERT_STRING(request
, "dmToTime", string
);
9703 err
= send_destroy_request_check_response(context
,
9704 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9705 &response
, NULL
, NULL
);
9706 if (err
) goto leave
;
9710 xpath_ctx
= xmlXPathNewContext(response
);
9715 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9719 result
= xmlXPathEvalExpression(
9720 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9721 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9727 /* Fill output arguments in */
9728 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9729 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9731 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9732 /* Create new status change */
9733 item
= calloc(1, sizeof(*item
));
9739 (void(*)(void**)) &isds_message_status_change_free
;
9741 /* Extract message status change */
9742 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9743 err
= extract_StateChangesRecord(context
,
9744 (struct isds_message_status_change
**) &item
->data
,
9747 isds_list_free(&item
);
9751 /* Append new message status change into the list */
9752 if (!*changed_states
) {
9753 *changed_states
= last_item
= item
;
9755 last_item
->next
= item
;
9763 isds_list_free(changed_states
);
9767 xmlXPathFreeObject(result
);
9768 xmlXPathFreeContext(xpath_ctx
);
9769 xmlFreeDoc(response
);
9770 xmlFreeNode(request
);
9773 isds_log(ILF_ISDS
, ILL_DEBUG
,
9774 _("GetMessageStateChanges request processed by server "
9775 "successfully.\n"));
9776 #else /* not HAVE_LIBCURL */
9784 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9786 * @context is session context
9787 * @service is ISDS WS service handler
9788 * @service_name is name of SERVICE_DM_OPERATIONS
9789 * @message_id is message ID to send as service argument to ISDS
9790 * @response is reallocated server SOAP body response as XML document
9791 * @raw_response is reallocated bit stream with response body. Use
9792 * NULL if you don't care
9793 * @raw_response_length is size of @raw_response in bytes
9794 * @code is reallocated ISDS status code
9795 * @status_message is reallocated ISDS status message
9796 * @return error coded from lower layer, context message will be set up
9798 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9799 const isds_service service
, const xmlChar
*service_name
,
9800 const char *message_id
,
9801 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9802 xmlChar
**code
, xmlChar
**status_message
) {
9804 isds_error err
= IE_SUCCESS
;
9805 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9806 xmlNodePtr request
= NULL
, node
;
9807 xmlNsPtr isds_ns
= NULL
;
9809 if (!context
) return IE_INVALID_CONTEXT
;
9810 if (!service_name
|| !message_id
) return IE_INVAL
;
9811 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9812 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9814 /* Free output argument */
9815 xmlFreeDoc(*response
); *response
= NULL
;
9816 if (raw_response
) zfree(*raw_response
);
9818 zfree(*status_message
);
9821 /* Check if connection is established
9822 * TODO: This check should be done downstairs. */
9823 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9825 service_name_locale
= _isds_utf82locale((char*)service_name
);
9826 message_id_locale
= _isds_utf82locale(message_id
);
9827 if (!service_name_locale
|| !message_id_locale
) {
9833 request
= xmlNewNode(NULL
, service_name
);
9835 isds_printf_message(context
,
9836 _("Could not build %s request for %s message ID"),
9837 service_name_locale
, message_id_locale
);
9841 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9843 isds_log_message(context
, _("Could not create ISDS name space"));
9847 xmlSetNs(request
, isds_ns
);
9850 /* Add requested ID */
9851 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9852 if (err
) goto leave
;
9853 INSERT_STRING(request
, "dmID", message_id
);
9856 isds_log(ILF_ISDS
, ILL_DEBUG
,
9857 _("Sending %s request for %s message ID to ISDS\n"),
9858 service_name_locale
, message_id_locale
);
9861 err
= _isds(context
, service
, request
, response
,
9862 raw_response
, raw_response_length
);
9863 xmlFreeNode(request
); request
= NULL
;
9866 isds_log(ILF_ISDS
, ILL_DEBUG
,
9867 _("Processing ISDS response on %s request failed\n"),
9868 service_name_locale
);
9872 /* Check for response status */
9873 err
= isds_response_status(context
, service
, *response
,
9874 code
, status_message
, NULL
);
9876 isds_log(ILF_ISDS
, ILL_DEBUG
,
9877 _("ISDS response on %s request is missing status\n"),
9878 service_name_locale
);
9882 /* Request processed, but nothing found */
9883 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9884 char *code_locale
= _isds_utf82locale((char*) *code
);
9885 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9886 isds_log(ILF_ISDS
, ILL_DEBUG
,
9887 _("Server refused %s request for %s message ID "
9888 "(code=%s, message=%s)\n"),
9889 service_name_locale
, message_id_locale
,
9890 code_locale
, status_message_locale
);
9891 isds_log_message(context
, status_message_locale
);
9893 free(status_message_locale
);
9899 free(message_id_locale
);
9900 free(service_name_locale
);
9901 xmlFreeNode(request
);
9906 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9907 * signed data and free ISDS response.
9908 * @context is session context
9909 * @message_id is UTF-8 encoded message ID for logging purpose
9910 * @response is parsed XML document. It will be freed and NULLed in the middle
9911 * of function run to save memory. This is not guaranteed in case of error.
9912 * @request_name is name of ISDS request used to construct response root
9913 * element name and for logging purpose.
9914 * @raw is reallocated output buffer with DER encoded CMS data
9915 * @raw_length is size of @raw buffer in bytes
9916 * @returns standard error codes, in case of error, @raw will be freed and
9917 * NULLed, @response sometimes. */
9918 static isds_error
find_extract_signed_data_free_response(
9919 struct isds_ctx
*context
, const xmlChar
*message_id
,
9920 xmlDocPtr
*response
, const xmlChar
*request_name
,
9921 void **raw
, size_t *raw_length
) {
9923 isds_error err
= IE_SUCCESS
;
9924 char *xpath_expression
= NULL
;
9925 xmlXPathContextPtr xpath_ctx
= NULL
;
9926 xmlXPathObjectPtr result
= NULL
;
9927 char *encoded_structure
= NULL
;
9929 if (!context
) return IE_INVALID_CONTEXT
;
9930 if (!raw
) return IE_INVAL
;
9932 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9935 /* Build XPath expression */
9936 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9937 "Response/isds:dmSignature");
9938 if (!xpath_expression
) return IE_NOMEM
;
9941 xpath_ctx
= xmlXPathNewContext(*response
);
9946 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9950 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9955 /* Empty response */
9956 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9957 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9958 isds_printf_message(context
,
9959 _("Server did not return any signed data for message ID `%s' "
9961 message_id_locale
, request_name
);
9962 free(message_id_locale
);
9966 /* More responses */
9967 if (result
->nodesetval
->nodeNr
> 1) {
9968 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9969 isds_printf_message(context
,
9970 _("Server did return more signed data for message ID `%s' "
9972 message_id_locale
, request_name
);
9973 free(message_id_locale
);
9978 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9980 /* Extract PKCS#7 structure */
9981 EXTRACT_STRING(".", encoded_structure
);
9982 if (!encoded_structure
) {
9983 isds_log_message(context
, _("dmSignature element is empty"));
9986 /* Here we have delivery info as standalone CMS in encoded_structure.
9987 * We don't need any other data, free them: */
9988 xmlXPathFreeObject(result
); result
= NULL
;
9989 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9990 xmlFreeDoc(*response
); *response
= NULL
;
9993 /* Decode PKCS#7 to DER format */
9994 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9995 if (*raw_length
== (size_t) -1) {
9996 isds_log_message(context
,
9997 _("Error while Base64-decoding PKCS#7 structure"));
10008 free(encoded_structure
);
10009 xmlXPathFreeObject(result
);
10010 xmlXPathFreeContext(xpath_ctx
);
10011 free(xpath_expression
);
10015 #endif /* HAVE_LIBCURL */
10018 /* Download incoming message envelope identified by ID.
10019 * @context is session context
10020 * @message_id is message identifier (you can get them from
10021 * isds_get_list_of_received_messages())
10022 * @message is automatically reallocated message retrieved from ISDS.
10023 * It will miss documents per se. Use isds_get_received_message(), if you are
10024 * interested in documents (content) too.
10025 * Returned hash and timestamp require documents to be verifiable. */
10026 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
10027 const char *message_id
, struct isds_message
**message
) {
10029 isds_error err
= IE_SUCCESS
;
10031 xmlDocPtr response
= NULL
;
10032 xmlChar
*code
= NULL
, *status_message
= NULL
;
10033 xmlXPathContextPtr xpath_ctx
= NULL
;
10034 xmlXPathObjectPtr result
= NULL
;
10037 if (!context
) return IE_INVALID_CONTEXT
;
10038 zfree(context
->long_message
);
10040 /* Free former message if any */
10041 if (!message
) return IE_INVAL
;
10042 isds_message_free(message
);
10045 /* Do request and check for success */
10046 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10047 BAD_CAST
"MessageEnvelopeDownload", message_id
,
10048 &response
, NULL
, NULL
, &code
, &status_message
);
10049 if (err
) goto leave
;
10052 xpath_ctx
= xmlXPathNewContext(response
);
10057 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10061 result
= xmlXPathEvalExpression(
10062 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
10063 "isds:dmReturnedMessageEnvelope",
10069 /* Empty response */
10070 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10071 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10072 isds_printf_message(context
,
10073 _("Server did not return any envelope for ID `%s' "
10074 "on MessageEnvelopeDownload request"), message_id_locale
);
10075 free(message_id_locale
);
10079 /* More envelops */
10080 if (result
->nodesetval
->nodeNr
> 1) {
10081 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10082 isds_printf_message(context
,
10083 _("Server did return more envelopes for ID `%s' "
10084 "on MessageEnvelopeDownload request"), message_id_locale
);
10085 free(message_id_locale
);
10090 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10092 /* Extract the envelope (= message without documents, hence 0) */
10093 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10094 if (err
) goto leave
;
10096 /* Save XML blob */
10097 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
10098 &(*message
)->raw_length
);
10102 isds_message_free(message
);
10105 xmlXPathFreeObject(result
);
10106 xmlXPathFreeContext(xpath_ctx
);
10109 free(status_message
);
10110 if (!*message
|| !(*message
)->xml
) {
10111 xmlFreeDoc(response
);
10115 isds_log(ILF_ISDS
, ILL_DEBUG
,
10116 _("MessageEnvelopeDownload request processed by server "
10119 #else /* not HAVE_LIBCURL */
10126 /* Load delivery info of any format from buffer.
10127 * @context is session context
10128 * @raw_type advertises format of @buffer content. Only delivery info types
10130 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
10131 * retrieve such data from message->raw after calling
10132 * isds_get_signed_delivery_info().
10133 * @length is length of buffer in bytes.
10134 * @message is automatically reallocated message parsed from @buffer.
10135 * @strategy selects how buffer will be attached into raw isds_message member.
10137 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
10138 const isds_raw_type raw_type
,
10139 const void *buffer
, const size_t length
,
10140 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10142 isds_error err
= IE_SUCCESS
;
10143 message_ns_type message_ns
;
10144 xmlDocPtr message_doc
= NULL
;
10145 xmlXPathContextPtr xpath_ctx
= NULL
;
10146 xmlXPathObjectPtr result
= NULL
;
10147 void *xml_stream
= NULL
;
10148 size_t xml_stream_length
= 0;
10150 if (!context
) return IE_INVALID_CONTEXT
;
10151 zfree(context
->long_message
);
10152 if (!message
) return IE_INVAL
;
10153 isds_message_free(message
);
10154 if (!buffer
) return IE_INVAL
;
10157 /* Select buffer format and extract XML from CMS*/
10158 switch (raw_type
) {
10159 case RAWTYPE_DELIVERYINFO
:
10160 message_ns
= MESSAGE_NS_UNSIGNED
;
10161 xml_stream
= (void *) buffer
;
10162 xml_stream_length
= length
;
10165 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
10166 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10167 xml_stream
= (void *) buffer
;
10168 xml_stream_length
= length
;
10171 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
10172 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
10173 err
= _isds_extract_cms_data(context
, buffer
, length
,
10174 &xml_stream
, &xml_stream_length
);
10175 if (err
) goto leave
;
10179 isds_log_message(context
, _("Bad raw delivery representation type"));
10184 isds_log(ILF_ISDS
, ILL_DEBUG
,
10185 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
10186 xml_stream_length
, xml_stream
);
10188 /* Convert delivery info XML stream into XPath context */
10189 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10190 if (!message_doc
) {
10194 xpath_ctx
= xmlXPathNewContext(message_doc
);
10199 /* XXX: Name spaces mangled for signed delivery info:
10200 * http://isds.czechpoint.cz/v20/delivery:
10202 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
10204 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10205 * <p:dmID>170272</p:dmID>
10208 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10210 * </q:dmEvents>...</q:dmEvents>
10212 * </q:GetDeliveryInfoResponse>
10214 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10218 result
= xmlXPathEvalExpression(
10219 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
10225 /* Empty delivery info */
10226 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10227 isds_printf_message(context
,
10228 _("XML document is not sisds:dmDelivery document"));
10232 /* More delivery info's */
10233 if (result
->nodesetval
->nodeNr
> 1) {
10234 isds_printf_message(context
,
10235 _("XML document has more sisds:dmDelivery elements"));
10239 /* One delivery info */
10240 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10242 /* Extract the envelope (= message without documents, hence 0).
10243 * XXX: extract_TReturnedMessage() can obtain attachments size,
10244 * but delivery info carries none. It's coded as option elements,
10245 * so it should work. */
10246 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
10247 if (err
) goto leave
;
10249 /* Extract events */
10250 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
10251 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
10252 if (err
) { err
= IE_ERROR
; goto leave
; }
10253 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
10254 if (err
) goto leave
;
10256 /* Append raw CMS structure into message */
10257 (*message
)->raw_type
= raw_type
;
10258 switch (strategy
) {
10259 case BUFFER_DONT_STORE
:
10262 (*message
)->raw
= malloc(length
);
10263 if (!(*message
)->raw
) {
10267 memcpy((*message
)->raw
, buffer
, length
);
10268 (*message
)->raw_length
= length
;
10271 (*message
)->raw
= (void *) buffer
;
10272 (*message
)->raw_length
= length
;
10281 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10282 isds_message_free(message
);
10285 xmlXPathFreeObject(result
);
10286 xmlXPathFreeContext(xpath_ctx
);
10287 if (!*message
|| !(*message
)->xml
) {
10288 xmlFreeDoc(message_doc
);
10290 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10293 isds_log(ILF_ISDS
, ILL_DEBUG
,
10294 _("Delivery info loaded successfully.\n"));
10299 /* Download signed delivery info-sheet of given message identified by ID.
10300 * @context is session context
10301 * @message_id is message identifier (you can get them from
10302 * isds_get_list_of_{sent,received}_messages())
10303 * @message is automatically reallocated message retrieved from ISDS.
10304 * It will miss documents per se. Use isds_get_signed_received_message(),
10305 * if you are interested in documents (content). OTOH, only this function
10306 * can get list events message has gone through. */
10307 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
10308 const char *message_id
, struct isds_message
**message
) {
10310 isds_error err
= IE_SUCCESS
;
10312 xmlDocPtr response
= NULL
;
10313 xmlChar
*code
= NULL
, *status_message
= NULL
;
10315 size_t raw_length
= 0;
10318 if (!context
) return IE_INVALID_CONTEXT
;
10319 zfree(context
->long_message
);
10321 /* Free former message if any */
10322 if (!message
) return IE_INVAL
;
10323 isds_message_free(message
);
10326 /* Do request and check for success */
10327 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10328 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
10329 &response
, NULL
, NULL
, &code
, &status_message
);
10330 if (err
) goto leave
;
10332 /* Find signed delivery info, extract it into raw and maybe free
10334 err
= find_extract_signed_data_free_response(context
,
10335 (xmlChar
*)message_id
, &response
,
10336 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
10337 if (err
) goto leave
;
10339 /* Parse delivery info */
10340 err
= isds_load_delivery_info(context
,
10341 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
10342 message
, BUFFER_MOVE
);
10343 if (err
) goto leave
;
10349 isds_message_free(message
);
10354 free(status_message
);
10355 xmlFreeDoc(response
);
10358 isds_log(ILF_ISDS
, ILL_DEBUG
,
10359 _("GetSignedDeliveryInfo request processed by server "
10362 #else /* not HAVE_LIBCURL */
10369 /* Download delivery info-sheet of given message identified by ID.
10370 * @context is session context
10371 * @message_id is message identifier (you can get them from
10372 * isds_get_list_of_{sent,received}_messages())
10373 * @message is automatically reallocated message retrieved from ISDS.
10374 * It will miss documents per se. Use isds_get_received_message(), if you are
10375 * interested in documents (content). OTOH, only this function can get list
10376 * of events message has gone through. */
10377 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
10378 const char *message_id
, struct isds_message
**message
) {
10380 isds_error err
= IE_SUCCESS
;
10382 xmlDocPtr response
= NULL
;
10383 xmlChar
*code
= NULL
, *status_message
= NULL
;
10384 xmlNodePtr delivery_node
= NULL
;
10386 size_t raw_length
= 0;
10389 if (!context
) return IE_INVALID_CONTEXT
;
10390 zfree(context
->long_message
);
10392 /* Free former message if any */
10393 if (!message
) return IE_INVAL
;
10394 isds_message_free(message
);
10397 /* Do request and check for success */
10398 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10399 BAD_CAST
"GetDeliveryInfo", message_id
,
10400 &response
, NULL
, NULL
, &code
, &status_message
);
10401 if (err
) goto leave
;
10404 /* Serialize delivery info */
10405 delivery_node
= xmlDocGetRootElement(response
);
10406 if (!delivery_node
) {
10407 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10408 isds_printf_message(context
,
10409 _("Server did not return any delivery info for ID `%s' "
10410 "on GetDeliveryInfo request"), message_id_locale
);
10411 free(message_id_locale
);
10415 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
10416 if (err
) goto leave
;
10418 /* Parse delivery info */
10419 /* TODO: Here we parse the response second time. We could single delivery
10420 * parser from isds_load_delivery_info() to make things faster. */
10421 err
= isds_load_delivery_info(context
,
10422 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
10423 message
, BUFFER_MOVE
);
10424 if (err
) goto leave
;
10431 isds_message_free(message
);
10436 free(status_message
);
10437 xmlFreeDoc(response
);
10440 isds_log(ILF_ISDS
, ILL_DEBUG
,
10441 _("GetDeliveryInfo request processed by server "
10444 #else /* not HAVE_LIBCURL */
10451 /* Download incoming message identified by ID.
10452 * @context is session context
10453 * @message_id is message identifier (you can get them from
10454 * isds_get_list_of_received_messages())
10455 * @message is automatically reallocated message retrieved from ISDS */
10456 isds_error
isds_get_received_message(struct isds_ctx
*context
,
10457 const char *message_id
, struct isds_message
**message
) {
10459 isds_error err
= IE_SUCCESS
;
10461 xmlDocPtr response
= NULL
;
10462 void *xml_stream
= NULL
;
10463 size_t xml_stream_length
;
10464 xmlChar
*code
= NULL
, *status_message
= NULL
;
10465 xmlXPathContextPtr xpath_ctx
= NULL
;
10466 xmlXPathObjectPtr result
= NULL
;
10467 char *phys_path
= NULL
;
10468 size_t phys_start
, phys_end
;
10471 if (!context
) return IE_INVALID_CONTEXT
;
10472 zfree(context
->long_message
);
10474 /* Free former message if any */
10475 if (NULL
== message
) return IE_INVAL
;
10476 if (message
) isds_message_free(message
);
10479 /* Do request and check for success */
10480 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10481 BAD_CAST
"MessageDownload", message_id
,
10482 &response
, &xml_stream
, &xml_stream_length
,
10483 &code
, &status_message
);
10484 if (err
) goto leave
;
10487 xpath_ctx
= xmlXPathNewContext(response
);
10492 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10496 result
= xmlXPathEvalExpression(
10497 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
10503 /* Empty response */
10504 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10505 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10506 isds_printf_message(context
,
10507 _("Server did not return any message for ID `%s' "
10508 "on MessageDownload request"), message_id_locale
);
10509 free(message_id_locale
);
10513 /* More messages */
10514 if (result
->nodesetval
->nodeNr
> 1) {
10515 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10516 isds_printf_message(context
,
10517 _("Server did return more messages for ID `%s' "
10518 "on MessageDownload request"), message_id_locale
);
10519 free(message_id_locale
);
10524 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10526 /* Extract the message */
10527 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10528 if (err
) goto leave
;
10530 /* Locate raw XML blob */
10531 phys_path
= strdup(
10532 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
10533 PHYSXML_ELEMENT_SEPARATOR
10534 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
10535 PHYSXML_ELEMENT_SEPARATOR
10536 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10542 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10543 phys_path
, &phys_start
, &phys_end
);
10546 isds_log_message(context
,
10547 _("Substring with isds:MessageDownloadResponse element "
10548 "could not be located in raw SOAP message"));
10551 /* Save XML blob */
10552 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
10553 &(*message)->raw_length);*/
10554 /* TODO: Store name space declarations from ancestors */
10555 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
10556 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10557 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
10558 (*message
)->raw
= malloc((*message
)->raw_length
);
10559 if (!(*message
)->raw
) {
10563 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
10568 isds_message_free(message
);
10573 xmlXPathFreeObject(result
);
10574 xmlXPathFreeContext(xpath_ctx
);
10577 free(status_message
);
10579 if (!*message
|| !(*message
)->xml
) {
10580 xmlFreeDoc(response
);
10584 isds_log(ILF_ISDS
, ILL_DEBUG
,
10585 _("MessageDownload request processed by server "
10588 #else /* not HAVE_LIBCURL */
10595 /* Load message of any type from buffer.
10596 * @context is session context
10597 * @raw_type defines content type of @buffer. Only message types are allowed.
10598 * @buffer is message raw representation. Format (CMS, plain signed,
10599 * message direction) is defined in @raw_type. You can retrieve such data
10600 * from message->raw after calling isds_get_[signed]{received,sent}_message().
10601 * @length is length of buffer in bytes.
10602 * @message is automatically reallocated message parsed from @buffer.
10603 * @strategy selects how buffer will be attached into raw isds_message member.
10605 isds_error
isds_load_message(struct isds_ctx
*context
,
10606 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
10607 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
10609 isds_error err
= IE_SUCCESS
;
10610 void *xml_stream
= NULL
;
10611 size_t xml_stream_length
= 0;
10612 message_ns_type message_ns
;
10613 xmlDocPtr message_doc
= NULL
;
10614 xmlXPathContextPtr xpath_ctx
= NULL
;
10615 xmlXPathObjectPtr result
= NULL
;
10617 if (!context
) return IE_INVALID_CONTEXT
;
10618 zfree(context
->long_message
);
10619 if (!message
) return IE_INVAL
;
10620 isds_message_free(message
);
10621 if (!buffer
) return IE_INVAL
;
10624 /* Select buffer format and extract XML from CMS*/
10625 switch (raw_type
) {
10626 case RAWTYPE_INCOMING_MESSAGE
:
10627 message_ns
= MESSAGE_NS_UNSIGNED
;
10628 xml_stream
= (void *) buffer
;
10629 xml_stream_length
= length
;
10632 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10633 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10634 xml_stream
= (void *) buffer
;
10635 xml_stream_length
= length
;
10638 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10639 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
10640 err
= _isds_extract_cms_data(context
, buffer
, length
,
10641 &xml_stream
, &xml_stream_length
);
10642 if (err
) goto leave
;
10645 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10646 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10647 xml_stream
= (void *) buffer
;
10648 xml_stream_length
= length
;
10651 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10652 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
10653 err
= _isds_extract_cms_data(context
, buffer
, length
,
10654 &xml_stream
, &xml_stream_length
);
10655 if (err
) goto leave
;
10659 isds_log_message(context
, _("Bad raw message representation type"));
10664 isds_log(ILF_ISDS
, ILL_DEBUG
,
10665 _("Loading message:\n%.*s\nEnd of message\n"),
10666 xml_stream_length
, xml_stream
);
10668 /* Convert messages XML stream into XPath context */
10669 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
10670 if (!message_doc
) {
10674 xpath_ctx
= xmlXPathNewContext(message_doc
);
10679 /* XXX: Standard name space for unsigned incoming direction:
10680 * http://isds.czechpoint.cz/v20/
10682 * XXX: Name spaces mangled for signed outgoing direction:
10683 * http://isds.czechpoint.cz/v20/SentMessage:
10685 * <q:MessageDownloadResponse
10686 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
10687 * <q:dmReturnedMessage>
10688 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10689 * <p:dmID>151916</p:dmID>
10692 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10694 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10695 * </q:dmReturnedMessage>
10696 * </q:MessageDownloadResponse>
10698 * XXX: Name spaces mangled for signed incoming direction:
10699 * http://isds.czechpoint.cz/v20/message:
10701 * <q:MessageDownloadResponse
10702 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10703 * <q:dmReturnedMessage>
10704 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10705 * <p:dmID>151916</p:dmID>
10708 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10710 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10711 * </q:dmReturnedMessage>
10712 * </q:MessageDownloadResponse>
10714 * Stupidity of ISDS developers is unlimited */
10715 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10719 result
= xmlXPathEvalExpression(
10720 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10726 /* Empty message */
10727 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10728 isds_printf_message(context
,
10729 _("XML document does not contain "
10730 "sisds:dmReturnedMessage element"));
10734 /* More messages */
10735 if (result
->nodesetval
->nodeNr
> 1) {
10736 isds_printf_message(context
,
10737 _("XML document has more sisds:dmReturnedMessage elements"));
10742 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10744 /* Extract the message */
10745 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10746 if (err
) goto leave
;
10748 /* Append raw buffer into message */
10749 (*message
)->raw_type
= raw_type
;
10750 switch (strategy
) {
10751 case BUFFER_DONT_STORE
:
10754 (*message
)->raw
= malloc(length
);
10755 if (!(*message
)->raw
) {
10759 memcpy((*message
)->raw
, buffer
, length
);
10760 (*message
)->raw_length
= length
;
10763 (*message
)->raw
= (void *) buffer
;
10764 (*message
)->raw_length
= length
;
10774 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10775 isds_message_free(message
);
10778 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10779 xmlXPathFreeObject(result
);
10780 xmlXPathFreeContext(xpath_ctx
);
10781 if (!*message
|| !(*message
)->xml
) {
10782 xmlFreeDoc(message_doc
);
10786 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10791 /* Determine type of raw message or delivery info according some heuristics.
10792 * It does not validate the raw blob.
10793 * @context is session context
10794 * @raw_type returns content type of @buffer. Valid only if exit code of this
10795 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10796 * reallocated memory.
10797 * @buffer is message raw representation.
10798 * @length is length of buffer in bytes. */
10799 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10800 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10802 void *xml_stream
= NULL
;
10803 size_t xml_stream_length
= 0;
10804 xmlDocPtr document
= NULL
;
10805 xmlNodePtr root
= NULL
;
10807 if (!context
) return IE_INVALID_CONTEXT
;
10808 zfree(context
->long_message
);
10809 if (length
== 0 || !buffer
) return IE_INVAL
;
10810 if (!raw_type
) return IE_INVAL
;
10813 err
= _isds_extract_cms_data(context
, buffer
, length
,
10814 &xml_stream
, &xml_stream_length
);
10816 xml_stream
= (void *) buffer
;
10817 xml_stream_length
= (size_t) length
;
10822 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10824 isds_printf_message(context
,
10825 _("Could not parse data as XML document"));
10830 /* Get root element */
10831 root
= xmlDocGetRootElement(document
);
10833 isds_printf_message(context
,
10834 _("XML document is missing root element"));
10839 if (!root
->ns
|| !root
->ns
->href
) {
10840 isds_printf_message(context
,
10841 _("Root element does not belong to any name space"));
10846 /* Test name space */
10847 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10848 if (xml_stream
== buffer
)
10849 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10851 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10852 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10853 if (xml_stream
== buffer
)
10854 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10856 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10857 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10858 if (xml_stream
== buffer
)
10859 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10861 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10862 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10863 if (xml_stream
!= buffer
) {
10864 isds_printf_message(context
,
10865 _("Document in ISDS name space is encapsulated into CMS" ));
10867 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10868 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10869 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10870 *raw_type
= RAWTYPE_DELIVERYINFO
;
10872 isds_printf_message(context
,
10873 _("Unknown root element in ISDS name space"));
10877 isds_printf_message(context
,
10878 _("Unknown name space"));
10883 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10884 xmlFreeDoc(document
);
10889 /* Download signed incoming/outgoing message identified by ID.
10890 * @context is session context
10891 * @output is true for outgoing message, false for incoming message
10892 * @message_id is message identifier (you can get them from
10893 * isds_get_list_of_{sent,received}_messages())
10894 * @message is automatically reallocated message retrieved from ISDS. The raw
10895 * member will be filled with PKCS#7 structure in DER format. */
10896 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10897 const _Bool outgoing
, const char *message_id
,
10898 struct isds_message
**message
) {
10900 isds_error err
= IE_SUCCESS
;
10902 xmlDocPtr response
= NULL
;
10903 xmlChar
*code
= NULL
, *status_message
= NULL
;
10904 xmlXPathContextPtr xpath_ctx
= NULL
;
10905 xmlXPathObjectPtr result
= NULL
;
10906 char *encoded_structure
= NULL
;
10908 size_t raw_length
= 0;
10911 if (!context
) return IE_INVALID_CONTEXT
;
10912 zfree(context
->long_message
);
10913 if (!message
) return IE_INVAL
;
10914 isds_message_free(message
);
10917 /* Do request and check for success */
10918 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10919 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10920 BAD_CAST
"SignedMessageDownload",
10921 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10922 if (err
) goto leave
;
10924 /* Find signed message, extract it into raw and maybe free
10926 err
= find_extract_signed_data_free_response(context
,
10927 (xmlChar
*)message_id
, &response
,
10928 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10929 BAD_CAST
"SignedMessageDownload",
10930 &raw
, &raw_length
);
10931 if (err
) goto leave
;
10933 /* Parse message */
10934 err
= isds_load_message(context
,
10935 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10936 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10937 raw
, raw_length
, message
, BUFFER_MOVE
);
10938 if (err
) goto leave
;
10944 isds_message_free(message
);
10947 free(encoded_structure
);
10948 xmlXPathFreeObject(result
);
10949 xmlXPathFreeContext(xpath_ctx
);
10953 free(status_message
);
10954 xmlFreeDoc(response
);
10957 isds_log(ILF_ISDS
, ILL_DEBUG
,
10959 _("SignedSentMessageDownload request processed by server "
10960 "successfully.\n") :
10961 _("SignedMessageDownload request processed by server "
10964 #else /* not HAVE_LIBCURL */
10971 /* Download signed incoming message identified by ID.
10972 * @context is session context
10973 * @message_id is message identifier (you can get them from
10974 * isds_get_list_of_received_messages())
10975 * @message is automatically reallocated message retrieved from ISDS. The raw
10976 * member will be filled with PKCS#7 structure in DER format. */
10977 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10978 const char *message_id
, struct isds_message
**message
) {
10979 return isds_get_signed_message(context
, 0, message_id
, message
);
10983 /* Download signed outgoing message identified by ID.
10984 * @context is session context
10985 * @message_id is message identifier (you can get them from
10986 * isds_get_list_of_sent_messages())
10987 * @message is automatically reallocated message retrieved from ISDS. The raw
10988 * member will be filled with PKCS#7 structure in DER format. */
10989 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10990 const char *message_id
, struct isds_message
**message
) {
10991 return isds_get_signed_message(context
, 1, message_id
, message
);
10995 /* Get type and name of user who sent a message identified by ID.
10996 * @context is session context
10997 * @message_id is message identifier
10998 * @sender_type is pointer to automatically allocated type of sender detected
10999 * from @raw_sender_type string. If @raw_sender_type is unknown to this
11000 * library or to the server, NULL will be returned. Pass NULL if you don't
11002 * @raw_sender_type is automatically reallocated UTF-8 string describing
11003 * sender type or NULL if not known to server. Pass NULL if you don't care.
11004 * @sender_name is automatically reallocated UTF-8 name of user who sent the
11005 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
11006 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
11007 const char *message_id
, isds_sender_type
**sender_type
,
11008 char **raw_sender_type
, char **sender_name
) {
11009 isds_error err
= IE_SUCCESS
;
11011 xmlDocPtr response
= NULL
;
11012 xmlChar
*code
= NULL
, *status_message
= NULL
;
11013 xmlXPathContextPtr xpath_ctx
= NULL
;
11014 xmlXPathObjectPtr result
= NULL
;
11015 char *type_string
= NULL
;
11018 if (!context
) return IE_INVALID_CONTEXT
;
11019 zfree(context
->long_message
);
11020 if (sender_type
) zfree(*sender_type
);
11021 if (raw_sender_type
) zfree(*raw_sender_type
);
11022 if (sender_name
) zfree(*sender_name
);
11023 if (!message_id
) return IE_INVAL
;
11026 /* Do request and check for success */
11027 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11028 BAD_CAST
"GetMessageAuthor",
11029 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
11030 if (err
) goto leave
;
11033 xpath_ctx
= xmlXPathNewContext(response
);
11038 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11042 result
= xmlXPathEvalExpression(
11043 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
11048 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11049 isds_log_message(context
,
11050 _("Missing GetMessageAuthorResponse element"));
11054 if (result
->nodesetval
->nodeNr
> 1) {
11055 isds_log_message(context
,
11056 _("Multiple GetMessageAuthorResponse element"));
11060 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11061 xmlXPathFreeObject(result
); result
= NULL
;
11063 /* Fill output arguments in */
11064 EXTRACT_STRING("isds:userType", type_string
);
11065 if (NULL
!= type_string
) {
11066 if (NULL
!= sender_type
) {
11067 *sender_type
= calloc(1, sizeof(**sender_type
));
11068 if (NULL
== *sender_type
) {
11073 err
= string2isds_sender_type((xmlChar
*)type_string
,
11076 zfree(*sender_type
);
11077 if (err
== IE_ENUM
) {
11079 char *type_string_locale
= _isds_utf82locale(type_string
);
11080 isds_log(ILF_ISDS
, ILL_WARNING
,
11081 _("Unknown isds:userType value: %s"),
11082 type_string_locale
);
11083 free(type_string_locale
);
11088 if (NULL
!= sender_name
)
11089 EXTRACT_STRING("isds:authorName", *sender_name
);
11093 if (NULL
!= sender_type
) zfree(*sender_type
);
11094 zfree(type_string
);
11095 if (NULL
!= sender_name
) zfree(*sender_name
);
11097 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
11099 xmlXPathFreeObject(result
);
11100 xmlXPathFreeContext(xpath_ctx
);
11103 free(status_message
);
11104 xmlFreeDoc(response
);
11107 isds_log(ILF_ISDS
, ILL_DEBUG
,
11108 _("GetMessageAuthor request processed by server "
11109 "successfully.\n"));
11110 #else /* not HAVE_LIBCURL */
11117 /* Retrieve hash of message identified by ID stored in ISDS.
11118 * @context is session context
11119 * @message_id is message identifier
11120 * @hash is automatically reallocated message hash downloaded from ISDS.
11121 * Message must exist in system and must not be deleted. */
11122 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
11123 const char *message_id
, struct isds_hash
**hash
) {
11125 isds_error err
= IE_SUCCESS
;
11127 xmlDocPtr response
= NULL
;
11128 xmlChar
*code
= NULL
, *status_message
= NULL
;
11129 xmlXPathContextPtr xpath_ctx
= NULL
;
11130 xmlXPathObjectPtr result
= NULL
;
11133 if (!context
) return IE_INVALID_CONTEXT
;
11134 zfree(context
->long_message
);
11136 isds_hash_free(hash
);
11139 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11140 BAD_CAST
"VerifyMessage", message_id
,
11141 &response
, NULL
, NULL
, &code
, &status_message
);
11142 if (err
) goto leave
;
11146 xpath_ctx
= xmlXPathNewContext(response
);
11151 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11155 result
= xmlXPathEvalExpression(
11156 BAD_CAST
"/isds:VerifyMessageResponse",
11162 /* Empty response */
11163 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11164 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11165 isds_printf_message(context
,
11166 _("Server did not return any response for ID `%s' "
11167 "on VerifyMessage request"), message_id_locale
);
11168 free(message_id_locale
);
11172 /* More responses */
11173 if (result
->nodesetval
->nodeNr
> 1) {
11174 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
11175 isds_printf_message(context
,
11176 _("Server did return more responses for ID `%s' "
11177 "on VerifyMessage request"), message_id_locale
);
11178 free(message_id_locale
);
11183 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11185 /* Extract the hash */
11186 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
11190 isds_hash_free(hash
);
11193 xmlXPathFreeObject(result
);
11194 xmlXPathFreeContext(xpath_ctx
);
11197 free(status_message
);
11198 xmlFreeDoc(response
);
11201 isds_log(ILF_ISDS
, ILL_DEBUG
,
11202 _("VerifyMessage request processed by server "
11205 #else /* not HAVE_LIBCURL */
11212 /* Erase message specified by @message_id from long term storage. Other
11213 * message cannot be erased on user request.
11214 * @context is session context
11215 * @message_id is message identifier.
11216 * @incoming is true for incoming message, false for outgoing message.
11218 * IE_SUCCESS if message has ben removed
11219 * IE_INVAL if message does not exist in long term storage or message
11220 * belongs to different box
11221 * TODO: IE_NOEPRM if user has no permission to erase a message */
11222 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
11223 const char *message_id
, _Bool incoming
) {
11224 isds_error err
= IE_SUCCESS
;
11226 xmlNodePtr request
= NULL
, node
;
11227 xmlNsPtr isds_ns
= NULL
;
11228 xmlDocPtr response
= NULL
;
11229 xmlChar
*code
= NULL
, *status_message
= NULL
;
11232 if (!context
) return IE_INVALID_CONTEXT
;
11233 zfree(context
->long_message
);
11234 if (NULL
== message_id
) return IE_INVAL
;
11236 /* Check if connection is established
11237 * TODO: This check should be done downstairs. */
11238 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11241 /* Build request */
11242 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
11244 isds_log_message(context
,
11245 _("Could build EraseMessage request"));
11248 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11250 isds_log_message(context
, _("Could not create ISDS name space"));
11251 xmlFreeNode(request
);
11254 xmlSetNs(request
, isds_ns
);
11256 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
11257 if (err
) goto leave
;
11258 INSERT_STRING(request
, "dmID", message_id
);
11260 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
11264 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
11265 "message ID %s to ISDS\n"), message_id
);
11266 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
11267 xmlFreeNode(request
); request
= NULL
;
11270 isds_log(ILF_ISDS
, ILL_DEBUG
,
11271 _("Processing ISDS response on EraseMessage request "
11276 /* Check for response status */
11277 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
11278 &code
, &status_message
, NULL
);
11280 isds_log(ILF_ISDS
, ILL_DEBUG
,
11281 _("ISDS response on EraseMessage request is missing "
11286 /* Check server status code */
11287 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
11288 isds_log_message(context
, _("Message to erase belongs to other box"));
11290 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
11291 isds_log_message(context
, _("Message to erase is not saved in "
11292 "long term storage or the direction does not match"));
11294 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
11295 char *code_locale
= _isds_utf82locale((char*) code
);
11296 char *message_locale
= _isds_utf82locale((char*) status_message
);
11297 isds_log(ILF_ISDS
, ILL_DEBUG
,
11298 _("Server refused EraseMessage request "
11299 "(code=%s, message=%s)\n"),
11300 code_locale
, message_locale
);
11301 isds_log_message(context
, message_locale
);
11303 free(message_locale
);
11310 free(status_message
);
11311 xmlFreeDoc(response
);
11312 xmlFreeNode(request
);
11315 isds_log(ILF_ISDS
, ILL_DEBUG
,
11316 _("EraseMessage request processed by server "
11319 #else /* not HAVE_LIBCURL */
11326 /* Mark message as read. This is a transactional commit function to acknowledge
11327 * to ISDS the message has been downloaded and processed by client properly.
11328 * @context is session context
11329 * @message_id is message identifier. */
11330 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
11331 const char *message_id
) {
11333 isds_error err
= IE_SUCCESS
;
11335 xmlDocPtr response
= NULL
;
11336 xmlChar
*code
= NULL
, *status_message
= NULL
;
11339 if (!context
) return IE_INVALID_CONTEXT
;
11340 zfree(context
->long_message
);
11343 /* Do request and check for success */
11344 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11345 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
11346 &response
, NULL
, NULL
, &code
, &status_message
);
11349 free(status_message
);
11350 xmlFreeDoc(response
);
11353 isds_log(ILF_ISDS
, ILL_DEBUG
,
11354 _("MarkMessageAsDownloaded request processed by server "
11357 #else /* not HAVE_LIBCURL */
11364 /* Mark message as received by recipient. This is applicable only to
11365 * commercial message. Use envelope->dmType message member to distinguish
11366 * commercial message from government message. Government message is
11367 * received automatically (by law), commercial message on recipient request.
11368 * @context is session context
11369 * @message_id is message identifier. */
11370 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
11371 const char *message_id
) {
11373 isds_error err
= IE_SUCCESS
;
11375 xmlDocPtr response
= NULL
;
11376 xmlChar
*code
= NULL
, *status_message
= NULL
;
11379 if (!context
) return IE_INVALID_CONTEXT
;
11380 zfree(context
->long_message
);
11383 /* Do request and check for success */
11384 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
11385 BAD_CAST
"ConfirmDelivery", message_id
,
11386 &response
, NULL
, NULL
, &code
, &status_message
);
11389 free(status_message
);
11390 xmlFreeDoc(response
);
11393 isds_log(ILF_ISDS
, ILL_DEBUG
,
11394 _("ConfirmDelivery request processed by server "
11397 #else /* not HAVE_LIBCURL */
11404 /* Send document for authorized conversion into Czech POINT system.
11405 * This is public anonymous service, no log-in necessary. Special context is
11406 * used to reuse keep-a-live HTTPS connection.
11407 * @context is Czech POINT session context. DO NOT use context connected to
11408 * ISDS server. Use new context or context used by this function previously.
11409 * @document is document to convert. Only data, data_length, dmFileDescr and
11410 * is_xml members are significant. Be ware that not all document formats can be
11411 * converted (signed PDF 1.3 and higher only (2010-02 state)).
11412 * @id is reallocated identifier assigned by Czech POINT system to
11413 * your document on submit. Use is to tell it to Czech POINT officer.
11414 * @date is reallocated document submit date (submitted documents
11415 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
11417 isds_error
czp_convert_document(struct isds_ctx
*context
,
11418 const struct isds_document
*document
,
11419 char **id
, struct tm
**date
) {
11420 isds_error err
= IE_SUCCESS
;
11422 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
11423 xmlNodePtr request
= NULL
, node
;
11424 xmlDocPtr response
= NULL
;
11426 xmlXPathContextPtr xpath_ctx
= NULL
;
11427 xmlXPathObjectPtr result
= NULL
;
11428 long int status
= -1;
11429 long int *status_ptr
= &status
;
11430 char *string
= NULL
;
11434 if (!context
) return IE_INVALID_CONTEXT
;
11435 zfree(context
->long_message
);
11436 if (!document
|| !id
|| !date
) return IE_INVAL
;
11438 if (document
->is_xml
) {
11439 isds_log_message(context
,
11440 _("XML documents cannot be submitted to conversion"));
11444 /* Free output arguments */
11449 /* Store configuration */
11450 context
->type
= CTX_TYPE_CZP
;
11451 free(context
->url
);
11452 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
11453 if (!(context
->url
))
11456 /* Prepare CURL handle if not yet connected */
11457 if (!context
->curl
) {
11458 context
->curl
= curl_easy_init();
11459 if (!(context
->curl
))
11463 /* Build conversion request */
11464 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
11466 isds_log_message(context
,
11467 _("Could not build Czech POINT conversion request"));
11470 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
11472 isds_log_message(context
,
11473 _("Could not create Czech POINT deposit name space"));
11474 xmlFreeNode(request
);
11477 xmlSetNs(request
, deposit_ns
);
11479 /* Insert children. They are in empty namespace! */
11480 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
11482 isds_log_message(context
, _("Could not create empty name space"));
11486 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
11487 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
11488 document
->dmFileDescr
);
11490 /* Document encoded in Base64 */
11491 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
11492 document
->data
, document
->data_length
);
11493 if (err
) goto leave
;
11495 isds_log(ILF_ISDS
, ILL_DEBUG
,
11496 _("Submitting document for conversion into Czech POINT deposit"));
11498 /* Send conversion request */
11499 err
= _czp_czpdeposit(context
, request
, &response
);
11500 xmlFreeNode(request
); request
= NULL
;
11503 czp_do_close_connection(context
);
11508 /* Extract response */
11509 xpath_ctx
= xmlXPathNewContext(response
);
11514 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11518 result
= xmlXPathEvalExpression(
11519 BAD_CAST
"/deposit:saveDocumentResponse/return",
11525 /* Empty response */
11526 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11527 isds_printf_message(context
,
11528 _("Missing `return' element in Czech POINT deposit response"));
11532 /* More responses */
11533 if (result
->nodesetval
->nodeNr
> 1) {
11534 isds_printf_message(context
,
11535 _("Multiple `return' element in Czech POINT deposit response"));
11540 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11543 EXTRACT_LONGINT("status", status_ptr
, 1);
11545 EXTRACT_STRING("statusMsg", string
);
11546 char *string_locale
= _isds_utf82locale(string
);
11547 isds_printf_message(context
,
11548 _("Czech POINT deposit refused document for conversion "
11549 "(code=%ld, message=%s)"),
11550 status
, string_locale
);
11551 free(string_locale
);
11556 /* Get document ID */
11557 EXTRACT_STRING("documentID", *id
);
11559 /* Get submit date */
11560 EXTRACT_STRING("dateInserted", string
);
11562 *date
= calloc(1, sizeof(**date
));
11567 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
11569 if (err
== IE_NOTSUP
) {
11571 char *string_locale
= _isds_utf82locale(string
);
11572 isds_printf_message(context
,
11573 _("Invalid dateInserted value: %s"), string_locale
);
11574 free(string_locale
);
11582 xmlXPathFreeObject(result
);
11583 xmlXPathFreeContext(xpath_ctx
);
11585 xmlFreeDoc(response
);
11586 xmlFreeNode(request
);
11589 char *id_locale
= _isds_utf82locale((char *) *id
);
11590 isds_log(ILF_ISDS
, ILL_DEBUG
,
11591 _("Document %s has been submitted for conversion "
11592 "to server successfully\n"), id_locale
);
11595 #else /* not HAVE_LIBCURL */
11602 /* Close possibly opened connection to Czech POINT document deposit.
11603 * @context is Czech POINT session context. */
11604 isds_error
czp_close_connection(struct isds_ctx
*context
) {
11605 if (!context
) return IE_INVALID_CONTEXT
;
11606 zfree(context
->long_message
);
11608 return czp_do_close_connection(context
);
11615 /* Send request for new box creation in testing ISDS instance.
11616 * It's not possible to request for a production box currently, as it
11617 * communicates via e-mail.
11618 * XXX: This function does not work either. Server complains about invalid
11620 * XXX: Remove context->type hacks in isds.c and validator.c when removing
11622 * @context is special session context for box creation request. DO NOT use
11623 * standard context as it could reveal your password. Use fresh new context or
11624 * context previously used by this function.
11625 * @box is box description to create including single primary user (in case of
11626 * FO box type). aifoIsds, address->adCode, address->adDistrict members are
11627 * ignored. It outputs box ID assigned by ISDS in dbID element.
11628 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
11629 * box, or contact address of PFO box owner). The email member is mandatory as
11630 * it will be used to deliver credentials.
11631 * @former_names is former name of box owner. Pass NULL if you don't care.
11632 * @approval is optional external approval of box manipulation
11633 * @refnumber is reallocated serial number of request assigned by ISDS. Use
11634 * NULL, if you don't care.*/
11635 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
11636 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
11637 const char *former_names
, const struct isds_approval
*approval
,
11638 char **refnumber
) {
11639 isds_error err
= IE_SUCCESS
;
11641 xmlNodePtr request
= NULL
;
11642 xmlDocPtr response
= NULL
;
11643 xmlXPathContextPtr xpath_ctx
= NULL
;
11644 xmlXPathObjectPtr result
= NULL
;
11648 if (!context
) return IE_INVALID_CONTEXT
;
11649 zfree(context
->long_message
);
11650 if (!box
) return IE_INVAL
;
11653 if (!box
->email
|| box
->email
[0] == '\0') {
11654 isds_log_message(context
, _("E-mail field is mandatory"));
11658 /* Scratch box ID */
11661 /* Store configuration */
11662 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
11663 free(context
->url
);
11664 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
11665 if (!(context
->url
))
11668 /* Prepare CURL handle if not yet connected */
11669 if (!context
->curl
) {
11670 context
->curl
= curl_easy_init();
11671 if (!(context
->curl
))
11675 /* Build CreateDataBox request */
11676 err
= build_CreateDBInput_request(context
,
11677 &request
, BAD_CAST
"CreateDataBox",
11678 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
11679 if (err
) goto leave
;
11681 /* Send it to server and process response */
11682 err
= send_destroy_request_check_response(context
,
11683 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
11684 &response
, (xmlChar
**) refnumber
, NULL
);
11685 if (err
) goto leave
;
11687 /* Extract box ID */
11688 xpath_ctx
= xmlXPathNewContext(response
);
11693 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11697 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11700 xmlXPathFreeObject(result
);
11701 xmlXPathFreeContext(xpath_ctx
);
11702 xmlFreeDoc(response
);
11703 xmlFreeNode(request
);
11706 isds_log(ILF_ISDS
, ILL_DEBUG
,
11707 _("CreateDataBox request processed by server successfully.\n"));
11709 #else /* not HAVE_LIBCURL */
11717 /* Submit CMS signed message to ISDS to verify its originality. This is
11718 * stronger form of isds_verify_message_hash() because ISDS does more checks
11719 * than simple one (potentialy old weak) hash comparison.
11720 * @context is session context
11721 * @message is memory with raw CMS signed message bit stream
11722 * @length is @message size in bytes
11724 * IE_SUCCESS if message originates in ISDS
11725 * IE_NOTEQUAL if message is unknown to ISDS
11726 * other code for other errors */
11727 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11728 const void *message
, size_t length
) {
11729 isds_error err
= IE_SUCCESS
;
11731 xmlNsPtr isds_ns
= NULL
;
11732 xmlNodePtr request
= NULL
;
11733 xmlDocPtr response
= NULL
;
11734 xmlXPathContextPtr xpath_ctx
= NULL
;
11735 xmlXPathObjectPtr result
= NULL
;
11736 _Bool
*authentic
= NULL
;
11739 if (!context
) return IE_INVALID_CONTEXT
;
11740 zfree(context
->long_message
);
11741 if (!message
|| length
== 0) return IE_INVAL
;
11744 /* Check if connection is established
11745 * TODO: This check should be done downstairs. */
11746 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11749 /* Build AuthenticateMessage request */
11750 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11752 isds_log_message(context
,
11753 _("Could not build AuthenticateMessage request"));
11756 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11758 isds_log_message(context
, _("Could not create ISDS name space"));
11759 xmlFreeNode(request
);
11762 xmlSetNs(request
, isds_ns
);
11764 /* Insert Base64 encoded message */
11765 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11767 if (err
) goto leave
;
11769 /* Send request to server and process response */
11770 err
= send_destroy_request_check_response(context
,
11771 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11772 &response
, NULL
, NULL
);
11773 if (err
) goto leave
;
11776 /* ISDS has decided */
11777 xpath_ctx
= xmlXPathNewContext(response
);
11782 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11787 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11790 isds_log_message(context
,
11791 _("Server did not return any response on "
11792 "AuthenticateMessage request"));
11797 isds_log(ILF_ISDS
, ILL_DEBUG
,
11798 _("ISDS authenticated the message successfully\n"));
11800 isds_log_message(context
, _("ISDS does not know the message"));
11807 xmlXPathFreeObject(result
);
11808 xmlXPathFreeContext(xpath_ctx
);
11810 xmlFreeDoc(response
);
11811 xmlFreeNode(request
);
11812 #else /* not HAVE_LIBCURL */
11820 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11821 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11823 * @context is session context
11824 * @input_data is memory with raw CMS signed message or delivery info bit
11825 * stream to re-sign
11826 * @input_length is @input_data size in bytes
11827 * @output_data is pointer to auto-allocated memory where to store re-signed
11828 * input data blob. Caller must free it.
11829 * @output_data is pointer where to store @output_data size in bytes
11830 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11831 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11833 * IE_SUCCESS if CMS blob has been re-signed successfully
11834 * other code for other errors */
11835 isds_error
isds_resign_message(struct isds_ctx
*context
,
11836 const void *input_data
, size_t input_length
,
11837 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11838 isds_error err
= IE_SUCCESS
;
11840 xmlNsPtr isds_ns
= NULL
;
11841 xmlNodePtr request
= NULL
;
11842 xmlDocPtr response
= NULL
;
11843 xmlXPathContextPtr xpath_ctx
= NULL
;
11844 xmlXPathObjectPtr result
= NULL
;
11845 char *string
= NULL
;
11846 const xmlChar
*codes
[] = {
11853 const char *meanings
[] = {
11855 "Message is not original",
11856 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11857 "Time stamp could not been generated in time"
11859 const isds_error errors
[] = {
11865 struct code_map_isds_error map
= {
11867 .meanings
= meanings
,
11872 if (NULL
!= output_data
) *output_data
= NULL
;
11873 if (NULL
!= output_length
) *output_length
= 0;
11874 if (NULL
!= valid_to
) *valid_to
= NULL
;
11876 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11877 zfree(context
->long_message
);
11878 if (NULL
== input_data
|| 0 == input_length
) {
11879 isds_log_message(context
, _("Empty CMS blob on input"));
11882 if (NULL
== output_data
|| NULL
== output_length
) {
11883 isds_log_message(context
,
11884 _("NULL pointer provided for output CMS blob"));
11889 /* Check if connection is established
11890 * TODO: This check should be done downstairs. */
11891 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11894 /* Build Re-signISDSDocument request */
11895 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11897 isds_log_message(context
,
11898 _("Could not build Re-signISDSDocument request"));
11901 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11903 isds_log_message(context
, _("Could not create ISDS name space"));
11904 xmlFreeNode(request
);
11907 xmlSetNs(request
, isds_ns
);
11909 /* Insert Base64 encoded CMS blob */
11910 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11911 input_data
, input_length
);
11912 if (err
) goto leave
;
11914 /* Send request to server and process response */
11915 err
= send_destroy_request_check_response(context
,
11916 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11917 &response
, NULL
, &map
);
11918 if (err
) goto leave
;
11921 /* Extract re-signed data */
11922 xpath_ctx
= xmlXPathNewContext(response
);
11927 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11931 result
= xmlXPathEvalExpression(
11932 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11937 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11938 isds_log_message(context
,
11939 _("Missing Re-signISDSDocumentResponse element"));
11943 if (result
->nodesetval
->nodeNr
> 1) {
11944 isds_log_message(context
,
11945 _("Multiple Re-signISDSDocumentResponse element"));
11949 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11950 xmlXPathFreeObject(result
); result
= NULL
;
11952 EXTRACT_STRING("isds:dmResultDoc", string
);
11953 /* Decode non-empty data */
11954 if (NULL
!= string
&& string
[0] != '\0') {
11955 *output_length
= _isds_b64decode(string
, output_data
);
11956 if (*output_length
== (size_t) -1) {
11957 isds_log_message(context
,
11958 _("Error while Base64-decoding re-signed data"));
11963 isds_log_message(context
, _("Server did not send re-signed data"));
11969 if (NULL
!= valid_to
) {
11970 /* Get time stamp expiration date */
11971 EXTRACT_STRING("isds:dmValidTo", string
);
11972 if (NULL
!= string
) {
11973 *valid_to
= calloc(1, sizeof(**valid_to
));
11978 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11980 if (err
== IE_NOTSUP
) {
11982 char *string_locale
= _isds_utf82locale(string
);
11983 isds_printf_message(context
,
11984 _("Invalid dmValidTo value: %s"), string_locale
);
11985 free(string_locale
);
11995 xmlXPathFreeObject(result
);
11996 xmlXPathFreeContext(xpath_ctx
);
11998 xmlFreeDoc(response
);
11999 xmlFreeNode(request
);
12000 #else /* not HAVE_LIBCURL */
12007 #undef INSERT_ELEMENT
12008 #undef CHECK_FOR_STRING_LENGTH
12009 #undef INSERT_STRING_ATTRIBUTE
12010 #undef INSERT_ULONGINTNOPTR
12011 #undef INSERT_ULONGINT
12012 #undef INSERT_LONGINT
12013 #undef INSERT_BOOLEAN
12014 #undef INSERT_SCALAR_BOOLEAN
12015 #undef INSERT_STRING
12016 #undef INSERT_STRING_WITH_NS
12017 #undef EXTRACT_STRING_ATTRIBUTE
12018 #undef EXTRACT_ULONGINT
12019 #undef EXTRACT_LONGINT
12020 #undef EXTRACT_BOOLEAN
12021 #undef EXTRACT_STRING
12024 /* Compute hash of message from raw representation and store it into envelope.
12025 * Original hash structure will be destroyed in envelope.
12026 * @context is session context
12027 * @message is message carrying raw XML message blob
12028 * @algorithm is desired hash algorithm to use */
12029 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
12030 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
12031 isds_error err
= IE_SUCCESS
;
12033 void *xml_stream
= NULL
;
12034 size_t xml_stream_length
;
12035 size_t phys_start
, phys_end
;
12036 char *phys_path
= NULL
;
12037 struct isds_hash
*new_hash
= NULL
;
12040 if (!context
) return IE_INVALID_CONTEXT
;
12041 zfree(context
->long_message
);
12042 if (!message
) return IE_INVAL
;
12044 if (!message
->raw
) {
12045 isds_log_message(context
,
12046 _("Message does not carry raw representation"));
12050 switch (message
->raw_type
) {
12051 case RAWTYPE_INCOMING_MESSAGE
:
12053 xml_stream
= message
->raw
;
12054 xml_stream_length
= message
->raw_length
;
12057 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
12058 nsuri
= SISDS_INCOMING_NS
;
12059 xml_stream
= message
->raw
;
12060 xml_stream_length
= message
->raw_length
;
12063 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
12064 nsuri
= SISDS_INCOMING_NS
;
12065 err
= _isds_extract_cms_data(context
,
12066 message
->raw
, message
->raw_length
,
12067 &xml_stream
, &xml_stream_length
);
12068 if (err
) goto leave
;
12071 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
12072 nsuri
= SISDS_OUTGOING_NS
;
12073 xml_stream
= message
->raw
;
12074 xml_stream_length
= message
->raw_length
;
12077 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
12078 nsuri
= SISDS_OUTGOING_NS
;
12079 err
= _isds_extract_cms_data(context
,
12080 message
->raw
, message
->raw_length
,
12081 &xml_stream
, &xml_stream_length
);
12082 if (err
) goto leave
;
12086 isds_log_message(context
, _("Bad raw representation type"));
12092 /* XXX: Hash is computed from original string representing isds:dmDm
12093 * subtree. That means no encoding, white space, xmlns attributes changes.
12094 * In other words, input for hash can be invalid XML stream. */
12095 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
12096 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
12097 PHYSXML_ELEMENT_SEPARATOR
,
12098 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
12099 PHYSXML_ELEMENT_SEPARATOR
12100 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
12104 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
12105 phys_path
, &phys_start
, &phys_end
);
12108 isds_log_message(context
,
12109 _("Substring with isds:dmDM element could not be located "
12110 "in raw message"));
12116 new_hash
= calloc(1, sizeof(*new_hash
));
12121 new_hash
->algorithm
= algorithm
;
12122 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
12125 isds_log_message(context
, _("Could not compute message hash"));
12129 /* Save computed hash */
12130 if (!message
->envelope
) {
12131 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
12132 if (!message
->envelope
) {
12137 isds_hash_free(&message
->envelope
->hash
);
12138 message
->envelope
->hash
= new_hash
;
12142 isds_hash_free(&new_hash
);
12146 if (xml_stream
!= message
->raw
) free(xml_stream
);
12151 /* Compare two hashes.
12152 * @h1 is first hash
12153 * @h2 is another hash
12155 * IE_SUCCESS if hashes equal
12156 * IE_NOTUNIQ if hashes are comparable, but they don't equal
12157 * IE_ENUM if not comparable, but both structures defined
12158 * IE_INVAL if some of the structures are undefined (NULL)
12159 * IE_ERROR if internal error occurs */
12160 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
12161 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
12162 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
12163 if (h1
->length
!= h2
->length
) return IE_ERROR
;
12164 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
12165 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
12167 for (size_t i
= 0; i
< h1
->length
; i
++) {
12168 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
12169 return IE_NOTEQUAL
;
12175 /* Check message has gone through ISDS by comparing message hash stored in
12176 * ISDS and locally computed hash. You must provide message with valid raw
12177 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
12178 * This is convenient wrapper for isds_download_message_hash(),
12179 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
12180 * @context is session context
12181 * @message is message with valid raw and envelope member; envelope->hash
12182 * member will be changed during function run. Use envelope on heap only.
12184 * IE_SUCCESS if message originates in ISDS
12185 * IE_NOTEQUAL if message is unknown to ISDS
12186 * other code for other errors */
12187 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
12188 struct isds_message
*message
) {
12189 isds_error err
= IE_SUCCESS
;
12190 struct isds_hash
*downloaded_hash
= NULL
;
12192 if (!context
) return IE_INVALID_CONTEXT
;
12193 zfree(context
->long_message
);
12194 if (!message
) return IE_INVAL
;
12196 if (!message
->envelope
) {
12197 isds_log_message(context
,
12198 _("Given message structure is missing envelope"));
12201 if (!message
->raw
) {
12202 isds_log_message(context
,
12203 _("Given message structure is missing raw representation"));
12207 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
12209 if (err
) goto leave
;
12211 err
= isds_compute_message_hash(context
, message
,
12212 downloaded_hash
->algorithm
);
12213 if (err
) goto leave
;
12215 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
12218 isds_hash_free(&downloaded_hash
);
12223 /* Search for document by document ID in list of documents. IDs are compared
12225 * @documents is list of isds_documents
12226 * @id is document identifier
12227 * @return first matching document or NULL. */
12228 const struct isds_document
*isds_find_document_by_id(
12229 const struct isds_list
*documents
, const char *id
) {
12230 const struct isds_list
*item
;
12231 const struct isds_document
*document
;
12233 for (item
= documents
; item
; item
= item
->next
) {
12234 document
= (struct isds_document
*) item
->data
;
12235 if (!document
) continue;
12237 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
12245 /* Normalize @mime_type to be proper MIME type.
12246 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
12247 * guess regular MIME type (e.g. "application/pdf").
12248 * @mime_type is UTF-8 encoded MIME type to fix
12249 * @return original @mime_type if no better interpretation exists, or
12250 * constant static UTF-8 encoded string with proper MIME type. */
12251 const char *isds_normalize_mime_type(const char *mime_type
) {
12252 if (!mime_type
) return NULL
;
12254 for (size_t offset
= 0;
12255 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
12257 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
12258 extension_map_mime
[offset
]))
12259 return (const char *) extension_map_mime
[offset
+ 1];
12266 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
12267 struct isds_message **message);
12268 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
12269 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
12270 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
12271 struct isds_address **address);
12273 int isds_message_free(struct isds_message **message);
12274 int isds_address_free(struct isds_address **address);
12278 /* Makes known all relevant namespaces to given XPath context
12279 * @xpath_ctx is XPath context
12280 * @message_ns selects proper message name space. Unsigned and signed
12281 * messages and delivery info's differ in prefix and URI. */
12282 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
12283 const message_ns_type message_ns
) {
12284 const xmlChar
*message_namespace
= NULL
;
12286 if (!xpath_ctx
) return IE_ERROR
;
12288 switch(message_ns
) {
12290 message_namespace
= BAD_CAST ISDS1_NS
; break;
12291 case MESSAGE_NS_UNSIGNED
:
12292 message_namespace
= BAD_CAST ISDS_NS
; break;
12293 case MESSAGE_NS_SIGNED_INCOMING
:
12294 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
12295 case MESSAGE_NS_SIGNED_OUTGOING
:
12296 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
12297 case MESSAGE_NS_SIGNED_DELIVERY
:
12298 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
12303 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
12305 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
12307 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
12309 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
12311 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
12313 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))