8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
13 #include "validator.h"
19 * Allocated in isds_init() and deallocated in isds_cleanup(). */
20 unsigned int log_facilities
;
21 isds_log_level log_level
;
22 isds_log_callback log_callback
;
23 void *log_callback_data
;
24 const char *version_gpgme
= N_("n/a");
25 const char *version_gcrypt
= N_("n/a");
26 const char *version_openssl
= N_("n/a");
27 const char *version_expat
= N_("n/a");
30 /* Base URL of production ISDS instance */
31 const char isds_locator
[] = "https://ws1.mojedatovaschranka.cz/";
32 const char isds_cert_locator
[] = "https://ws1c.mojedatovaschranka.cz/";
33 const char isds_otp_locator
[] = "https://www.mojedatovaschranka.cz/";
35 /* Base URL of production ISDS instance */
36 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
37 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
38 const char isds_otp_testing_locator
[] = "https://www.czebox.cz/";
40 /* Extension to MIME type map */
41 static const xmlChar
*extension_map_mime
[] = {
42 BAD_CAST
"cer", BAD_CAST
"application/x-x509-ca-cert",
43 BAD_CAST
"crt", BAD_CAST
"application/x-x509-ca-cert",
44 BAD_CAST
"der", BAD_CAST
"application/x-x509-ca-cert",
45 BAD_CAST
"doc", BAD_CAST
"application/msword",
46 BAD_CAST
"docx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
47 "wordprocessingml.document",
48 BAD_CAST
"dbf", BAD_CAST
"application/octet-stream",
49 BAD_CAST
"prj", BAD_CAST
"application/octet-stream",
50 BAD_CAST
"qix", BAD_CAST
"application/octet-stream",
51 BAD_CAST
"sbn", BAD_CAST
"application/octet-stream",
52 BAD_CAST
"sbx", BAD_CAST
"application/octet-stream",
53 BAD_CAST
"shp", BAD_CAST
"application/octet-stream",
54 BAD_CAST
"shx", BAD_CAST
"application/octet-stream",
55 BAD_CAST
"dgn", BAD_CAST
"application/octet-stream",
56 BAD_CAST
"dwg", BAD_CAST
"image/vnd.dwg",
57 BAD_CAST
"edi", BAD_CAST
"application/edifact",
58 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.form+xml",
59 BAD_CAST
"gfs", BAD_CAST
"application/xml",
60 BAD_CAST
"gml", BAD_CAST
"application/xml",
61 BAD_CAST
"gif", BAD_CAST
"image/gif",
62 BAD_CAST
"htm", BAD_CAST
"text/html",
63 BAD_CAST
"html", BAD_CAST
"text/html",
64 BAD_CAST
"isdoc", BAD_CAST
"text/isdoc",
65 BAD_CAST
"isdocx", BAD_CAST
"text/isdocx",
66 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
67 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
68 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
69 BAD_CAST
"mpeg", BAD_CAST
"video/mpeg",
70 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
71 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg",
72 BAD_CAST
"mpg", BAD_CAST
"video/mpeg",
73 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
74 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
75 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
76 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
77 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
78 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
79 BAD_CAST
"p7b", BAD_CAST
"application/pkcs7-certificates",
80 BAD_CAST
"p7c", BAD_CAST
"application/pkcs7-mime",
81 BAD_CAST
"p7m", BAD_CAST
"application/pkcs7-mime",
82 BAD_CAST
"p7f", BAD_CAST
"application/pkcs7-signature",
83 BAD_CAST
"p7s", BAD_CAST
"application/pkcs7-signature",
84 BAD_CAST
"pk7", BAD_CAST
"application/pkcs7-mime",
85 BAD_CAST
"png", BAD_CAST
"image/png",
86 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
87 BAD_CAST
"pptx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
88 "presentationml.presentation",
89 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
90 BAD_CAST
"tif", BAD_CAST
"image/tiff",
91 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
92 BAD_CAST
"tsr", BAD_CAST
"application/timestamp-reply",
93 BAD_CAST
"tst", BAD_CAST
"application/timestamp-reply",
94 BAD_CAST
"txt", BAD_CAST
"text/plain",
95 BAD_CAST
"wav", BAD_CAST
"audio/wav",
96 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
97 BAD_CAST
"xlsx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
98 "spreadsheetml.sheet",
99 BAD_CAST
"xml", BAD_CAST
"application/xml",
100 BAD_CAST
"xsd", BAD_CAST
"application/xml",
101 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.form-xml-zip"
104 /* Structure type to hold conversion table from status code to isds_error and
106 struct code_map_isds_error
{
107 const xmlChar
**codes
; /* NULL terminated array of status codes */
108 const char **meanings
; /* Mapping to non-localized long messages */
109 const isds_error
*errors
; /* Mapping to isds_error code */
112 /* Deallocate structure isds_pki_credentials and NULL it.
113 * Pass-phrase is discarded.
114 * @pki credentials to to free */
115 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
116 if(!pki
|| !*pki
) return;
118 free((*pki
)->engine
);
119 free((*pki
)->certificate
);
122 if ((*pki
)->passphrase
) {
123 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
124 free((*pki
)->passphrase
);
131 /* Free isds_list with all member data.
132 * @list list to free, on return will be NULL */
133 void isds_list_free(struct isds_list
**list
) {
134 struct isds_list
*item
, *next_item
;
136 if (!list
|| !*list
) return;
138 for(item
= *list
; item
; item
= next_item
) {
139 if (item
->destructor
) (item
->destructor
)(&(item
->data
));
140 next_item
= item
->next
;
148 /* Deallocate structure isds_hash and NULL it.
149 * @hash hash to to free */
150 void isds_hash_free(struct isds_hash
**hash
) {
151 if(!hash
|| !*hash
) return;
152 free((*hash
)->value
);
157 /* Deallocate structure isds_PersonName recursively and NULL it */
158 void isds_PersonName_free(struct isds_PersonName
**person_name
) {
159 if (!person_name
|| !*person_name
) return;
161 free((*person_name
)->pnFirstName
);
162 free((*person_name
)->pnMiddleName
);
163 free((*person_name
)->pnLastName
);
164 free((*person_name
)->pnLastNameAtBirth
);
171 /* Deallocate structure isds_BirthInfo recursively and NULL it */
172 void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
173 if (!birth_info
|| !*birth_info
) return;
175 free((*birth_info
)->biDate
);
176 free((*birth_info
)->biCity
);
177 free((*birth_info
)->biCounty
);
178 free((*birth_info
)->biState
);
185 /* Deallocate structure isds_Address recursively and NULL it */
186 void isds_Address_free(struct isds_Address
**address
) {
187 if (!address
|| !*address
) return;
189 free((*address
)->adCity
);
190 free((*address
)->adStreet
);
191 free((*address
)->adNumberInStreet
);
192 free((*address
)->adNumberInMunicipality
);
193 free((*address
)->adZipCode
);
194 free((*address
)->adState
);
201 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
202 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
203 if (!db_owner_info
|| !*db_owner_info
) return;
205 free((*db_owner_info
)->dbID
);
206 free((*db_owner_info
)->dbType
);
207 free((*db_owner_info
)->ic
);
208 isds_PersonName_free(&((*db_owner_info
)->personName
));
209 free((*db_owner_info
)->firmName
);
210 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
211 isds_Address_free(&((*db_owner_info
)->address
));
212 free((*db_owner_info
)->nationality
);
213 free((*db_owner_info
)->email
);
214 free((*db_owner_info
)->telNumber
);
215 free((*db_owner_info
)->identifier
);
216 free((*db_owner_info
)->registryCode
);
217 free((*db_owner_info
)->dbState
);
218 free((*db_owner_info
)->dbEffectiveOVM
);
219 free((*db_owner_info
)->dbOpenAddressing
);
221 free(*db_owner_info
);
222 *db_owner_info
= NULL
;
225 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
226 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
227 if (!db_user_info
|| !*db_user_info
) return;
229 free((*db_user_info
)->userID
);
230 free((*db_user_info
)->userType
);
231 free((*db_user_info
)->userPrivils
);
232 isds_PersonName_free(&((*db_user_info
)->personName
));
233 isds_Address_free(&((*db_user_info
)->address
));
234 free((*db_user_info
)->biDate
);
235 free((*db_user_info
)->ic
);
236 free((*db_user_info
)->firmName
);
237 free((*db_user_info
)->caStreet
);
238 free((*db_user_info
)->caCity
);
239 free((*db_user_info
)->caZipCode
);
240 free((*db_user_info
)->caState
);
242 zfree(*db_user_info
);
246 /* Deallocate struct isds_event recursively and NULL it */
247 void isds_event_free(struct isds_event
**event
) {
248 if (!event
|| !*event
) return;
250 free((*event
)->time
);
251 free((*event
)->type
);
252 free((*event
)->description
);
257 /* Deallocate struct isds_envelope recursively and NULL it */
258 void isds_envelope_free(struct isds_envelope
**envelope
) {
259 if (!envelope
|| !*envelope
) return;
261 free((*envelope
)->dmID
);
262 free((*envelope
)->dbIDSender
);
263 free((*envelope
)->dmSender
);
264 free((*envelope
)->dmSenderAddress
);
265 free((*envelope
)->dmSenderType
);
266 free((*envelope
)->dmRecipient
);
267 free((*envelope
)->dmRecipientAddress
);
268 free((*envelope
)->dmAmbiguousRecipient
);
269 free((*envelope
)->dmType
);
271 free((*envelope
)->dmOrdinal
);
272 free((*envelope
)->dmMessageStatus
);
273 free((*envelope
)->dmDeliveryTime
);
274 free((*envelope
)->dmAcceptanceTime
);
275 isds_hash_free(&(*envelope
)->hash
);
276 free((*envelope
)->timestamp
);
277 isds_list_free(&(*envelope
)->events
);
279 free((*envelope
)->dmSenderOrgUnit
);
280 free((*envelope
)->dmSenderOrgUnitNum
);
281 free((*envelope
)->dbIDRecipient
);
282 free((*envelope
)->dmRecipientOrgUnit
);
283 free((*envelope
)->dmRecipientOrgUnitNum
);
284 free((*envelope
)->dmToHands
);
285 free((*envelope
)->dmAnnotation
);
286 free((*envelope
)->dmRecipientRefNumber
);
287 free((*envelope
)->dmSenderRefNumber
);
288 free((*envelope
)->dmRecipientIdent
);
289 free((*envelope
)->dmSenderIdent
);
291 free((*envelope
)->dmLegalTitleLaw
);
292 free((*envelope
)->dmLegalTitleYear
);
293 free((*envelope
)->dmLegalTitleSect
);
294 free((*envelope
)->dmLegalTitlePar
);
295 free((*envelope
)->dmLegalTitlePoint
);
297 free((*envelope
)->dmPersonalDelivery
);
298 free((*envelope
)->dmAllowSubstDelivery
);
300 free((*envelope
)->dmOVM
);
301 free((*envelope
)->dmPublishOwnID
);
308 /* Deallocate struct isds_message recursively and NULL it */
309 void isds_message_free(struct isds_message
**message
) {
310 if (!message
|| !*message
) return;
312 free((*message
)->raw
);
313 isds_envelope_free(&((*message
)->envelope
));
314 isds_list_free(&((*message
)->documents
));
315 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
322 /* Deallocate struct isds_document recursively and NULL it */
323 void isds_document_free(struct isds_document
**document
) {
324 if (!document
|| !*document
) return;
326 if (!(*document
)->is_xml
) {
327 free((*document
)->data
);
329 free((*document
)->dmMimeType
);
330 free((*document
)->dmFileGuid
);
331 free((*document
)->dmUpFileGuid
);
332 free((*document
)->dmFileDescr
);
333 free((*document
)->dmFormat
);
340 /* Deallocate struct isds_message_copy recursively and NULL it */
341 void isds_message_copy_free(struct isds_message_copy
**copy
) {
342 if (!copy
|| !*copy
) return;
344 free((*copy
)->dbIDRecipient
);
345 free((*copy
)->dmRecipientOrgUnit
);
346 free((*copy
)->dmRecipientOrgUnitNum
);
347 free((*copy
)->dmToHands
);
349 free((*copy
)->dmStatus
);
356 /* Deallocate struct isds_message_status_change recursively and NULL it */
357 void isds_message_status_change_free(
358 struct isds_message_status_change
**message_status_change
) {
359 if (!message_status_change
|| !*message_status_change
) return;
361 free((*message_status_change
)->dmID
);
362 free((*message_status_change
)->time
);
363 free((*message_status_change
)->dmMessageStatus
);
365 zfree(*message_status_change
);
369 /* Deallocate struct isds_approval recursively and NULL it */
370 void isds_approval_free(struct isds_approval
**approval
) {
371 if (!approval
|| !*approval
) return;
373 free((*approval
)->refference
);
379 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
380 * The email string is deallocated too. */
381 void isds_credentials_delivery_free(
382 struct isds_credentials_delivery
**credentials_delivery
) {
383 if (!credentials_delivery
|| !*credentials_delivery
) return;
385 free((*credentials_delivery
)->email
);
386 free((*credentials_delivery
)->token
);
387 free((*credentials_delivery
)->new_user_name
);
389 zfree(*credentials_delivery
);
393 /* Deallocate struct isds_commercial_permission recursively and NULL it */
394 void isds_commercial_permission_free(
395 struct isds_commercial_permission
**permission
) {
396 if (NULL
== permission
|| NULL
== *permission
) return;
398 free((*permission
)->recipient
);
399 free((*permission
)->payer
);
400 free((*permission
)->expiration
);
401 free((*permission
)->count
);
402 free((*permission
)->reply_identifier
);
408 /* Deallocate struct isds_credit_event recursively and NULL it */
409 void isds_credit_event_free(struct isds_credit_event
**event
) {
410 if (NULL
== event
|| NULL
== *event
) return;
412 free((*event
)->time
);
413 switch ((*event
)->type
) {
414 case ISDS_CREDIT_CHARGED
:
415 free((*event
)->details
.charged
.transaction
);
417 case ISDS_CREDIT_DISCHARGED
:
418 free((*event
)->details
.discharged
.transaction
);
420 case ISDS_CREDIT_MESSAGE_SENT
:
421 free((*event
)->details
.message_sent
.recipient
);
422 free((*event
)->details
.message_sent
.message_id
);
424 case ISDS_CREDIT_STORAGE_SET
:
425 free((*event
)->details
.storage_set
.new_valid_from
);
426 free((*event
)->details
.storage_set
.new_valid_to
);
427 free((*event
)->details
.storage_set
.old_capacity
);
428 free((*event
)->details
.storage_set
.old_valid_from
);
429 free((*event
)->details
.storage_set
.old_valid_to
);
430 free((*event
)->details
.storage_set
.initiator
);
432 case ISDS_CREDIT_EXPIRED
:
440 /* *DUP_OR_ERROR macros needs error label */
441 #define STRDUP_OR_ERROR(new, template) { \
445 (new) = strdup(template); \
446 if (!new) goto error; \
450 #define FLATDUP_OR_ERROR(new, template) { \
454 (new) = malloc(sizeof(*(new))); \
455 if (!new) goto error; \
456 memcpy((new), (template), sizeof(*(template))); \
460 /* Copy structure isds_pki_credentials recursively. */
461 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
462 const struct isds_pki_credentials
*template) {
463 struct isds_pki_credentials
*new = NULL
;
465 if(!template) return NULL
;
467 new = calloc(1, sizeof(*new));
468 if (!new) return NULL
;
470 STRDUP_OR_ERROR(new->engine
, template->engine
);
471 new->certificate_format
= template->certificate_format
;
472 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
473 new->key_format
= template->key_format
;
474 STRDUP_OR_ERROR(new->key
, template->key
);
475 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
480 isds_pki_credentials_free(&new);
485 /* Copy structure isds_PersonName recursively */
486 struct isds_PersonName
*isds_PersonName_duplicate(
487 const struct isds_PersonName
*src
) {
488 struct isds_PersonName
*new = NULL
;
490 if (!src
) return NULL
;
492 new = calloc(1, sizeof(*new));
493 if (!new) return NULL
;
495 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
496 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
497 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
498 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
503 isds_PersonName_free(&new);
508 /* Copy structure isds_BirthInfo recursively */
509 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
510 const struct isds_BirthInfo
*template) {
511 struct isds_BirthInfo
*new = NULL
;
513 if (!template) return NULL
;
515 new = calloc(1, sizeof(*new));
516 if (!new) return NULL
;
518 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
519 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
520 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
521 STRDUP_OR_ERROR(new->biState
, template->biState
);
526 isds_BirthInfo_free(&new);
531 /* Copy structure isds_Address recursively */
532 struct isds_Address
*isds_Address_duplicate(
533 const struct isds_Address
*src
) {
534 struct isds_Address
*new = NULL
;
536 if (!src
) return NULL
;
538 new = calloc(1, sizeof(*new));
539 if (!new) return NULL
;
541 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
542 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
543 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
544 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
545 src
->adNumberInMunicipality
);
546 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
547 STRDUP_OR_ERROR(new->adState
, src
->adState
);
552 isds_Address_free(&new);
557 /* Copy structure isds_DbOwnerInfo recursively */
558 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
559 const struct isds_DbOwnerInfo
*src
) {
560 struct isds_DbOwnerInfo
*new = NULL
;
561 if (!src
) return NULL
;
563 new = calloc(1, sizeof(*new));
564 if (!new) return NULL
;
566 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
567 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
568 STRDUP_OR_ERROR(new->ic
, src
->ic
);
570 if (src
->personName
) {
571 if (!(new->personName
=
572 isds_PersonName_duplicate(src
->personName
)))
576 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
578 if (src
->birthInfo
) {
579 if (!(new->birthInfo
=
580 isds_BirthInfo_duplicate(src
->birthInfo
)))
585 if (!(new->address
= isds_Address_duplicate(src
->address
)))
589 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
590 STRDUP_OR_ERROR(new->email
, src
->email
);
591 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
592 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
593 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
594 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
595 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
596 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
601 isds_DbOwnerInfo_free(&new);
606 /* Copy structure isds_DbUserInfo recursively */
607 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
608 const struct isds_DbUserInfo
*src
) {
609 struct isds_DbUserInfo
*new = NULL
;
610 if (!src
) return NULL
;
612 new = calloc(1, sizeof(*new));
613 if (!new) return NULL
;
615 STRDUP_OR_ERROR(new->userID
, src
->userID
);
616 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
617 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
619 if (src
->personName
) {
620 if (!(new->personName
=
621 isds_PersonName_duplicate(src
->personName
)))
626 if (!(new->address
= isds_Address_duplicate(src
->address
)))
630 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
631 STRDUP_OR_ERROR(new->ic
, src
->ic
);
632 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
633 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
634 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
635 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
636 STRDUP_OR_ERROR(new->caState
, src
->caState
);
641 isds_DbUserInfo_free(&new);
645 #undef FLATDUP_OR_ERROR
646 #undef STRDUP_OR_ERROR
649 /* Logs libxml2 errors. Should be registered to libxml2 library.
650 * @ctx is unused currently
651 * @msg is printf-like formated message from libxml2 (UTF-8?)
652 * @... are variadic arguments for @msg */
653 static void log_xml(void *ctx
, const char *msg
, ...) {
660 isds_vasprintf(&text
, msg
, ap
);
664 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
669 /* Initialize ISDS library.
670 * Global function, must be called before other functions.
671 * If it fails you can not use ISDS library and must call isds_cleanup() to
672 * free partially initialized global variables. */
673 isds_error
isds_init(void) {
674 /* NULL global variables */
675 log_facilities
= ILF_ALL
;
676 log_level
= ILL_WARNING
;
678 log_callback_data
= NULL
;
681 /* Initialize gettext */
682 bindtextdomain(PACKAGE
, LOCALEDIR
);
686 /* Initialize CURL */
687 if (curl_global_init(CURL_GLOBAL_ALL
)) {
688 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
691 #endif /* HAVE_LIBCURL */
693 /* Initialise cryptographic back-ends. */
694 if (IE_SUCCESS
!= _isds_init_crypto()) {
695 isds_log(ILF_ISDS
, ILL_CRIT
,
696 _("initialisation of cryptographic back-end failed\n"));
700 /* This can _exit() current program. Find not so assertive check. */
702 xmlSetGenericErrorFunc(NULL
, log_xml
);
705 if (_isds_init_expat(&version_expat
)) {
706 isds_log(ILF_ISDS
, ILL_CRIT
,
707 _("expat library initialization failed\n"));
711 /* Allocate global variables */
718 /* Deinitialize ISDS library.
719 * Global function, must be called as last library function. */
720 isds_error
isds_cleanup(void) {
726 curl_global_cleanup();
733 /* Return version string of this library. Version of dependencies can be
734 * embedded. Do no try to parse it. You must free it. */
735 char *isds_version(void) {
738 isds_asprintf(&buffer
,
740 # ifndef USE_OPENSSL_BACKEND
741 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
743 _("%s (%s, %s, %s, libxml2 %s)"),
746 # ifndef USE_OPENSSL_BACKEND
747 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
749 _("%s (%s, %s, libxml2 %s)"),
756 #ifndef USE_OPENSSL_BACKEND
757 version_gpgme
, version_gcrypt
,
761 version_expat
, xmlParserVersion
);
766 /* Return text description of ISDS error */
767 const char *isds_strerror(const isds_error error
) {
770 return(_("Success")); break;
772 return(_("Unspecified error")); break;
774 return(_("Not supported")); break;
776 return(_("Invalid value")); break;
777 case IE_INVALID_CONTEXT
:
778 return(_("Invalid context")); break;
779 case IE_NOT_LOGGED_IN
:
780 return(_("Not logged in")); break;
781 case IE_CONNECTION_CLOSED
:
782 return(_("Connection closed")); break;
784 return(_("Timed out")); break;
786 return(_("Not exist")); break;
788 return(_("Out of memory")); break;
790 return(_("Network problem")); break;
792 return(_("HTTP problem")); break;
794 return(_("SOAP problem")); break;
796 return(_("XML problem")); break;
798 return(_("ISDS server problem")); break;
800 return(_("Invalid enum value")); break;
802 return(_("Invalid date value")); break;
804 return(_("Too big")); break;
806 return(_("Too small")); break;
808 return(_("Value not unique")); break;
810 return(_("Values not equal")); break;
811 case IE_PARTIAL_SUCCESS
:
812 return(_("Some suboperations failed")); break;
814 return(_("Operation aborted")); break;
816 return(_("Security problem")); break;
818 return(_("Unknown error"));
823 /* Create ISDS context.
824 * Each context can be used for different sessions to (possibly) different
825 * ISDS server with different credentials. */
826 struct isds_ctx
*isds_ctx_create(void) {
827 struct isds_ctx
*context
;
828 context
= malloc(sizeof(*context
));
829 if (context
) memset(context
, 0, sizeof(*context
));
834 /* Close possibly opened connection to Czech POINT document deposit without
835 * resetting long_message buffer.
836 * XXX: Do not use czp_close_connection() if you do not want to destroy log
838 * @context is Czech POINT session context. */
839 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
840 if (!context
) return IE_INVALID_CONTEXT
;
841 _isds_close_connection(context
);
846 /* Discard credentials.
847 * @context is ISDS context
848 * @discard_saved_username is true for removing saved username, false for
850 * Only that. It does not cause log out, connection close or similar. */
851 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
852 _Bool discard_saved_username
) {
853 if(!context
) return IE_INVALID_CONTEXT
;
855 if (context
->username
) {
856 memset(context
->username
, 0, strlen(context
->username
));
857 zfree(context
->username
);
859 if (context
->password
) {
860 memset(context
->password
, 0, strlen(context
->password
));
861 zfree(context
->password
);
863 isds_pki_credentials_free(&context
->pki_credentials
);
864 if (discard_saved_username
&& context
->saved_username
) {
865 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
866 zfree(context
->saved_username
);
871 #endif /* HAVE_LIBCURL */
874 /* Destroy ISDS context and free memory.
875 * @context will be NULLed on success. */
876 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
877 if (!context
|| !*context
) {
878 return IE_INVALID_CONTEXT
;
882 /* Discard credentials and close connection */
883 switch ((*context
)->type
) {
884 case CTX_TYPE_NONE
: break;
885 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
887 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
888 czp_do_close_connection(*context
); break;
892 _isds_discard_credentials(*context
, 1);
894 /* Free other structures */
895 free((*context
)->tls_verify_server
);
896 free((*context
)->tls_ca_file
);
897 free((*context
)->tls_ca_dir
);
898 free((*context
)->tls_crl_file
);
899 #endif /* HAVE_LIBCURL */
900 free((*context
)->long_message
);
908 /* Return long message text produced by library function, e.g. detailed error
909 * message. Returned pointer is only valid until new library function is
910 * called for the same context. Could be NULL, especially if NULL context is
911 * supplied. Return string is locale encoded. */
912 char *isds_long_message(const struct isds_ctx
*context
) {
913 if (!context
) return NULL
;
914 return context
->long_message
;
918 /* Stores message into context' long_message buffer.
919 * Application can pick the message up using isds_long_message().
920 * NULL @message truncates the buffer but does not deallocate it.
921 * @message is coded in locale encoding */
922 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
923 const char *message
) {
927 if (!context
) return IE_INVALID_CONTEXT
;
929 /* FIXME: Check for integer overflow */
930 length
= 1 + ((message
) ? strlen(message
) : 0);
931 buffer
= realloc(context
->long_message
, length
);
932 if (!buffer
) return IE_NOMEM
;
935 strcpy(buffer
, message
);
939 context
->long_message
= buffer
;
944 /* Appends message into context' long_message buffer.
945 * Application can pick the message up using isds_long_message().
946 * NULL message has void effect. */
947 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
948 const char *message
) {
950 size_t old_length
, length
;
952 if (!context
) return IE_INVALID_CONTEXT
;
953 if (!message
) return IE_SUCCESS
;
954 if (!context
->long_message
)
955 return isds_log_message(context
, message
);
957 old_length
= strlen(context
->long_message
);
958 /* FIXME: Check for integer overflow */
959 length
= 1 + old_length
+ strlen(message
);
960 buffer
= realloc(context
->long_message
, length
);
961 if (!buffer
) return IE_NOMEM
;
963 strcpy(buffer
+ old_length
, message
);
965 context
->long_message
= buffer
;
970 /* Stores formatted message into context' long_message buffer.
971 * Application can pick the message up using isds_long_message(). */
972 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
973 const char *format
, ...) {
977 if (!context
) return IE_INVALID_CONTEXT
;
978 va_start(ap
, format
);
979 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
982 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
987 * @facilities is bit mask of isds_log_facility values,
988 * @level is verbosity level. */
989 void isds_set_logging(const unsigned int facilities
,
990 const isds_log_level level
) {
991 log_facilities
= facilities
;
996 /* Register callback function libisds calls when new global log message is
997 * produced by library. Library logs to stderr by default.
998 * @callback is function provided by application libisds will call. See type
999 * definition for @callback argument explanation. Pass NULL to revert logging to
1000 * default behaviour.
1001 * @data is application specific data @callback gets as last argument */
1002 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1003 log_callback
= callback
;
1004 log_callback_data
= data
;
1008 /* Log @message in class @facility with log @level into global log. @message
1009 * is printf(3) formatting string, variadic arguments may be necessary.
1010 * For debugging purposes. */
1011 _hidden isds_error
isds_log(const isds_log_facility facility
,
1012 const isds_log_level level
, const char *message
, ...) {
1014 char *buffer
= NULL
;
1017 if (level
> log_level
) return IE_SUCCESS
;
1018 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1019 if (!message
) return IE_INVAL
;
1022 /* Pass message to application supplied callback function */
1023 va_start(ap
, message
);
1024 length
= isds_vasprintf(&buffer
, message
, ap
);
1031 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1035 /* Default: Log it to stderr */
1036 va_start(ap
, message
);
1037 vfprintf(stderr
, message
, ap
);
1039 /* Line buffered printf is default.
1047 /* Set timeout in milliseconds for each network job like connecting to server
1048 * or sending message. Use 0 to disable timeout limits. */
1049 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1050 const unsigned int timeout
) {
1051 if (!context
) return IE_INVALID_CONTEXT
;
1052 zfree(context
->long_message
);
1055 context
->timeout
= timeout
;
1057 if (context
->curl
) {
1060 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1062 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1063 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1066 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1067 context
->timeout
/ 1000);
1068 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1069 if (curl_err
) return IE_ERROR
;
1073 #else /* not HAVE_LIBCURL */
1079 /* Register callback function libisds calls periodically during HTTP data
1081 * @context is session context
1082 * @callback is function provided by application libisds will call. See type
1083 * definition for @callback argument explanation.
1084 * @data is application specific data @callback gets as last argument */
1085 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1086 isds_progress_callback callback
, void *data
) {
1087 if (!context
) return IE_INVALID_CONTEXT
;
1088 zfree(context
->long_message
);
1091 context
->progress_callback
= callback
;
1092 context
->progress_callback_data
= data
;
1095 #else /* not HAVE_LIBCURL */
1101 /* Change context settings.
1102 * @context is context which setting will be applied to
1103 * @option is name of option. It determines the type of last argument. See
1104 * isds_option definition for more info.
1105 * @... is value of new setting. Type is determined by @option
1107 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1109 isds_error err
= IE_SUCCESS
;
1112 char *pointer
, *string
;
1115 if (!context
) return IE_INVALID_CONTEXT
;
1116 zfree(context
->long_message
);
1118 va_start(ap
, option
);
1120 #define REPLACE_VA_BOOLEAN(destination) { \
1121 if (!(destination)) { \
1122 (destination) = malloc(sizeof(*(destination))); \
1123 if (!(destination)) { \
1124 err = IE_NOMEM; goto leave; \
1127 *(destination) = (_Bool) !!va_arg(ap, int); \
1130 #define REPLACE_VA_STRING(destination) { \
1131 string = va_arg(ap, char *); \
1133 pointer = realloc((destination), 1 + strlen(string)); \
1134 if (!pointer) { err = IE_NOMEM; goto leave; } \
1135 strcpy(pointer, string); \
1136 (destination) = pointer; \
1138 free(destination); \
1139 (destination) = NULL; \
1144 case IOPT_TLS_VERIFY_SERVER
:
1146 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1148 err
= IE_NOTSUP
; goto leave
;
1151 case IOPT_TLS_CA_FILE
:
1153 REPLACE_VA_STRING(context
->tls_ca_file
);
1155 err
= IE_NOTSUP
; goto leave
;
1158 case IOPT_TLS_CA_DIRECTORY
:
1160 REPLACE_VA_STRING(context
->tls_ca_dir
);
1162 err
= IE_NOTSUP
; goto leave
;
1165 case IOPT_TLS_CRL_FILE
:
1167 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1168 REPLACE_VA_STRING(context
->tls_crl_file
);
1170 isds_log_message(context
,
1171 _("Curl library does not support CRL definition"));
1173 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1175 err
= IE_NOTSUP
; goto leave
;
1176 #endif /* not HAVE_LIBCURL */
1178 case IOPT_NORMALIZE_MIME_TYPE
:
1179 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1183 err
= IE_ENUM
; goto leave
;
1186 #undef REPLACE_VA_STRING
1187 #undef REPLACE_VA_BOOLEAN
1196 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1197 * Destination for NULL argument will not be touched.
1198 * Destination pointers must be freed before calling this function.
1199 * If @username is @context->saved_username, the saved_username will not be
1200 * replaced. The saved_username is clobbered only if context has set otp
1202 * Return IE_SUCCESS on success. */
1203 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1204 const char *username
, const char *password
,
1205 const struct isds_pki_credentials
*pki_credentials
) {
1206 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1208 /* FIXME: mlock password
1209 * (I have a library) */
1212 context
->username
= strdup(username
);
1213 if (context
->otp
&& context
->saved_username
!= username
)
1214 context
->saved_username
= strdup(username
);
1217 if (NULL
== context
->otp_credentials
)
1218 context
->password
= strdup(password
);
1220 context
->password
= _isds_astrcat(password
,
1221 context
->otp_credentials
->otp_code
);
1223 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1225 if ((NULL
!= username
&& NULL
== context
->username
) ||
1226 (NULL
!= password
&& NULL
== context
->password
) ||
1227 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1228 (context
->otp
&& NULL
!= context
->username
&&
1229 NULL
== context
->saved_username
)) {
1238 /* Connect and log into ISDS server.
1239 * All required arguments will be copied, you do not have to keep them after
1241 * ISDS supports six different authentication methods. Exact method is
1242 * selected on @username, @password, @pki_credentials, and @otp arguments:
1243 * - If @pki_credentials == NULL, @username and @password must be supplied
1245 * - If @otp == NULL, simple authentication by username and password will
1247 * - If @otp != NULL, authentication by username and password and OTP
1249 * - If @pki_credentials != NULL, then
1250 * - If @username == NULL, only certificate will be used
1251 * - If @username != NULL, then
1252 * - If @password == NULL, then certificate will be used and
1253 * @username shifts meaning to box ID. This is used for hosted
1255 * - Otherwise all three arguments will be used.
1256 * Please note, that different cases require different certificate type
1257 * (system qualified one or commercial non qualified one). This library
1258 * does not check such political issues. Please see ISDS Specification
1260 * @url is base address of ISDS web service. Pass extern isds_locator
1261 * variable to use production ISDS instance without client certificate
1262 * authentication (or extern isds_cert_locator with client certificate
1263 * authentication or extern isds_otp_locators with OTP authentication).
1264 * Passing NULL has the same effect, autoselection between isds_locator,
1265 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1266 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1267 * isds_otp_testing_locator) variable to select testing instance.
1268 * @username is user name of ISDS user or box ID
1269 * @password is user's secret password
1270 * @pki_credentials defines public key cryptographic material to use in client
1272 * @otp selects one-time password authentication method to use, defines OTP
1273 * code (if known) and returns fine grade resolution of OTP procedure.
1275 * IE_SUCCESS if authentication succeeds
1276 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1277 * requested, fine grade reason will be set into @otp->resolution. Error
1278 * message from server can be obtained by isds_long_message() call.
1279 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1280 * server has sent OTP code through side channel. Application is expected to
1281 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1282 * this call to complete second phase of TOTP authentication;
1283 * or other appropriate error. */
1284 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1285 const char *username
, const char *password
,
1286 const struct isds_pki_credentials
*pki_credentials
,
1287 struct isds_otp
*otp
) {
1289 isds_error err
= IE_NOT_LOGGED_IN
;
1290 isds_error soap_err
;
1291 xmlNsPtr isds_ns
= NULL
;
1292 xmlNodePtr request
= NULL
;
1293 xmlNodePtr response
= NULL
;
1294 #endif /* HAVE_LIBCURL */
1296 if (!context
) return IE_INVALID_CONTEXT
;
1297 zfree(context
->long_message
);
1300 /* Close connection if already logged in */
1301 if (context
->curl
) {
1302 _isds_close_connection(context
);
1305 /* Store configuration */
1306 context
->type
= CTX_TYPE_ISDS
;
1307 zfree(context
->url
);
1309 /* Mangle base URI according to requested authentication method */
1310 if (NULL
== pki_credentials
) {
1311 isds_log(ILF_SEC
, ILL_INFO
,
1312 _("Selected authentication method: no certificate, "
1313 "username and password\n"));
1314 if (!username
|| !password
) {
1315 isds_log_message(context
,
1316 _("Both username and password must be supplied"));
1319 context
->otp_credentials
= otp
;
1320 context
->otp
= (NULL
!= context
->otp_credentials
);
1322 if (!context
->otp
) {
1323 /* Default locator is official system (without certificate or
1325 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1327 const char *authenticator_uri
= NULL
;
1328 if (!url
) url
= isds_otp_locator
;
1329 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1330 switch (context
->otp_credentials
->method
) {
1332 isds_log(ILF_SEC
, ILL_INFO
,
1333 _("Selected authentication method: "
1334 "HMAC-based one-time password\n"));
1336 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1339 isds_log(ILF_SEC
, ILL_INFO
,
1340 _("Selected authentication method: "
1341 "Time-based one-time password\n"));
1342 if (context
->otp_credentials
->otp_code
== NULL
) {
1343 isds_log(ILF_SEC
, ILL_INFO
,
1344 _("OTP code has not been provided by "
1345 "application, requesting server for "
1348 "%1$sas/processLogin?type=totp&sendSms=true&"
1351 isds_log(ILF_SEC
, ILL_INFO
,
1352 _("OTP code has been provided by "
1353 "application, not requesting server "
1356 "%1$sas/processLogin?type=totp&"
1361 isds_log_message(context
,
1362 _("Unknown one-time password authentication "
1363 "method requested by application"));
1366 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1370 /* Default locator is official system (with client certificate) */
1372 context
->otp_credentials
= NULL
;
1373 if (!url
) url
= isds_cert_locator
;
1376 isds_log(ILF_SEC
, ILL_INFO
,
1377 _("Selected authentication method: system certificate, "
1378 "no username and no password\n"));
1380 context
->url
= _isds_astrcat(url
, "cert/");
1383 isds_log(ILF_SEC
, ILL_INFO
,
1384 _("Selected authentication method: system certificate, "
1385 "box ID and no password\n"));
1386 context
->url
= _isds_astrcat(url
, "hspis/");
1388 isds_log(ILF_SEC
, ILL_INFO
,
1389 _("Selected authentication method: commercial "
1390 "certificate, username and password\n"));
1391 context
->url
= _isds_astrcat(url
, "certds/");
1395 if (!(context
->url
))
1398 /* Prepare CURL handle */
1399 context
->curl
= curl_easy_init();
1400 if (!(context
->curl
))
1403 /* Build log-in request */
1404 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1406 isds_log_message(context
, _("Could not build ISDS log-in request"));
1409 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1411 isds_log_message(context
, _("Could not create ISDS name space"));
1412 xmlFreeNode(request
);
1415 xmlSetNs(request
, isds_ns
);
1417 /* Store credentials */
1418 _isds_discard_credentials(context
, 1);
1419 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1420 _isds_discard_credentials(context
, 1);
1421 xmlFreeNode(request
);
1425 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1428 /* Send log-in request */
1429 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1432 /* Revert context URL from OTP authentication service URL to OTP web
1433 * service base URL for subsequent calls. Potenial isds_login() retry
1434 * will re-set context URL again. */
1435 zfree(context
->url
);
1436 context
->url
= _isds_astrcat(url
, "apps/");
1437 if (context
->url
== NULL
) {
1438 soap_err
= IE_NOMEM
;
1440 /* Detach pointer to OTP credentials from context */
1441 context
->otp_credentials
= NULL
;
1444 /* Remove credentials */
1445 _isds_discard_credentials(context
, 0);
1447 /* Destroy log-in request */
1448 xmlFreeNode(request
);
1451 xmlFreeNodeList(response
);
1452 _isds_close_connection(context
);
1456 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1457 * authentication succeeded if soap_err == IE_SUCCESS */
1460 xmlFreeNodeList(response
);
1463 isds_log(ILF_ISDS
, ILL_DEBUG
,
1464 _("User %s has been logged into server %s successfully\n"),
1467 #else /* not HAVE_LIBCURL */
1473 /* Log out from ISDS server discards credentials and connection configuration. */
1474 isds_error
isds_logout(struct isds_ctx
*context
) {
1475 if (!context
) return IE_INVALID_CONTEXT
;
1476 zfree(context
->long_message
);
1479 if (context
->curl
) {
1481 isds_error err
= _isds_invalidate_otp_cookie(context
);
1482 if (err
) return err
;
1485 /* Close connection */
1486 _isds_close_connection(context
);
1488 /* Discard credentials for sure. They should not survive isds_login(),
1489 * even successful .*/
1490 _isds_discard_credentials(context
, 1);
1491 zfree(context
->url
);
1493 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1495 _isds_discard_credentials(context
, 1);
1498 #else /* not HAVE_LIBCURL */
1504 /* Verify connection to ISDS is alive and server is responding.
1505 * Send dummy request to ISDS and expect dummy response. */
1506 isds_error
isds_ping(struct isds_ctx
*context
) {
1508 isds_error soap_err
;
1509 xmlNsPtr isds_ns
= NULL
;
1510 xmlNodePtr request
= NULL
;
1511 xmlNodePtr response
= NULL
;
1512 #endif /* HAVE_LIBCURL */
1514 if (!context
) return IE_INVALID_CONTEXT
;
1515 zfree(context
->long_message
);
1518 /* Check if connection is established */
1519 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1522 /* Build dummy request */
1523 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1525 isds_log_message(context
, _("Could build ISDS dummy request"));
1528 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1530 isds_log_message(context
, _("Could not create ISDS name space"));
1531 xmlFreeNode(request
);
1534 xmlSetNs(request
, isds_ns
);
1536 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1538 /* Sent dummy request */
1539 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1541 /* Destroy log-in request */
1542 xmlFreeNode(request
);
1545 isds_log(ILF_ISDS
, ILL_DEBUG
,
1546 _("ISDS server could not be contacted\n"));
1547 xmlFreeNodeList(response
);
1551 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1552 * authentication succeeded if soap_err == IE_SUCCESS */
1553 /* TODO: ISDS documentation does not specify response body.
1554 * However real server sends back DummyOperationResponse */
1557 xmlFreeNodeList(response
);
1559 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1562 #else /* not HAVE_LIBCURL */
1568 /* Send bogus request to ISDS.
1569 * Just for test purposes */
1570 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1573 xmlNsPtr isds_ns
= NULL
;
1574 xmlNodePtr request
= NULL
;
1575 xmlDocPtr response
= NULL
;
1576 xmlChar
*code
= NULL
, *message
= NULL
;
1579 if (!context
) return IE_INVALID_CONTEXT
;
1580 zfree(context
->long_message
);
1583 /* Check if connection is established */
1584 if (!context
->curl
) {
1585 /* Testing printf message */
1586 isds_printf_message(context
, "%s", _("I said connection closed"));
1587 return IE_CONNECTION_CLOSED
;
1591 /* Build dummy request */
1592 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1594 isds_log_message(context
, _("Could build ISDS bogus request"));
1597 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1599 isds_log_message(context
, _("Could not create ISDS name space"));
1600 xmlFreeNode(request
);
1603 xmlSetNs(request
, isds_ns
);
1605 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1607 /* Sent bogus request */
1608 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1610 /* Destroy request */
1611 xmlFreeNode(request
);
1614 isds_log(ILF_ISDS
, ILL_DEBUG
,
1615 _("Processing ISDS response on bogus request failed\n"));
1616 xmlFreeDoc(response
);
1620 /* Check for response status */
1621 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1622 &code
, &message
, NULL
);
1624 isds_log(ILF_ISDS
, ILL_DEBUG
,
1625 _("ISDS response on bogus request is missing status\n"));
1628 xmlFreeDoc(response
);
1631 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1632 char *code_locale
= _isds_utf82locale((char*)code
);
1633 char *message_locale
= _isds_utf82locale((char*)message
);
1634 isds_log(ILF_ISDS
, ILL_DEBUG
,
1635 _("Server refused bogus request (code=%s, message=%s)\n"),
1636 code_locale
, message_locale
);
1637 /* XXX: Literal error messages from ISDS are Czech messages
1638 * (English sometimes) in UTF-8. It's hard to catch them for
1639 * translation. Successfully gettextized would return in locale
1640 * encoding, unsuccessfully translated would pass in UTF-8. */
1641 isds_log_message(context
, message_locale
);
1643 free(message_locale
);
1646 xmlFreeDoc(response
);
1653 xmlFreeDoc(response
);
1655 isds_log(ILF_ISDS
, ILL_DEBUG
,
1656 _("Bogus message accepted by server. This should not happen.\n"));
1659 #else /* not HAVE_LIBCURL */
1666 /* Serialize XML subtree to buffer preserving XML indentation.
1667 * @context is session context
1668 * @subtree is XML element to be serialized (with children)
1669 * @buffer is automatically reallocated buffer where serialize to
1670 * @length is size of serialized stream in bytes
1671 * @return standard error code, free @buffer in case of error */
1672 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1673 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1674 isds_error err
= IE_SUCCESS
;
1675 xmlBufferPtr xml_buffer
= NULL
;
1676 xmlSaveCtxtPtr save_ctx
= NULL
;
1677 xmlDocPtr subtree_doc
= NULL
;
1678 xmlNodePtr subtree_copy
;
1682 if (!context
) return IE_INVALID_CONTEXT
;
1683 if (!buffer
) return IE_INVAL
;
1685 if (!subtree
|| !length
) return IE_INVAL
;
1687 /* Make temporary XML document with @subtree root element */
1688 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1689 * It can result in not well-formed on invalid XML tree (e.g. name space
1690 * prefix definition can miss. */
1693 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1695 isds_log_message(context
, _("Could not build temporary document"));
1700 /* XXX: Copy subtree and attach the copy to document.
1701 * One node can not bee attached into more document at the same time.
1702 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1704 * XXX: Check xmlSaveTree() too. */
1705 subtree_copy
= xmlCopyNodeList(subtree
);
1706 if (!subtree_copy
) {
1707 isds_log_message(context
, _("Could not copy subtree"));
1711 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1713 /* Only this way we get namespace definition as @xmlns:isds,
1714 * otherwise we get namespace prefix without definition */
1715 /* FIXME: Don't overwrite original default namespace */
1716 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1718 isds_log_message(context
, _("Could not create ISDS name space"));
1722 xmlSetNs(subtree_copy
, isds_ns
);
1725 /* Serialize the document into buffer */
1726 xml_buffer
= xmlBufferCreate();
1728 isds_log_message(context
, _("Could not create xmlBuffer"));
1732 /* Last argument 0 means to not format the XML tree */
1733 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1735 isds_log_message(context
, _("Could not create XML serializer"));
1739 /* XXX: According LibXML documentation, this function does not return
1740 * meaningful value yet */
1741 xmlSaveDoc(save_ctx
, subtree_doc
);
1742 if (-1 == xmlSaveFlush(save_ctx
)) {
1743 isds_log_message(context
,
1744 _("Could not serialize XML subtree"));
1748 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1749 * even after xmlSaveFlush(). Thus close it here */
1750 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1753 /* Store and detach buffer from xml_buffer */
1754 *buffer
= xml_buffer
->content
;
1755 *length
= xml_buffer
->use
;
1756 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1759 new_buffer
= realloc(*buffer
, *length
);
1760 if (new_buffer
) *buffer
= new_buffer
;
1768 xmlSaveClose(save_ctx
);
1769 xmlBufferFree(xml_buffer
);
1770 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1773 #endif /* HAVE_LIBCURL */
1777 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1778 * @context is session context
1779 * @document is original document where @nodeset points to
1780 * @nodeset is XPath node set to dump (recursively)
1781 * @buffer is automatically reallocated buffer where serialize to
1782 * @length is size of serialized stream in bytes
1783 * @return standard error code, free @buffer in case of error */
1784 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1785 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1786 void **buffer
, size_t *length
) {
1787 isds_error err
= IE_SUCCESS
;
1788 xmlBufferPtr xml_buffer
= NULL
;
1791 if (!context
) return IE_INVALID_CONTEXT
;
1792 if (!buffer
) return IE_INVAL
;
1794 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1797 /* Empty node set results into NULL buffer */
1798 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1802 /* Resulting the document into buffer */
1803 xml_buffer
= xmlBufferCreate();
1805 isds_log_message(context
, _("Could not create xmlBuffer"));
1810 /* Iterate over all nodes */
1811 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1813 * XXX: xmlNodeDump() appends to xml_buffer. */
1815 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1816 isds_log_message(context
, _("Could not dump XML node"));
1822 /* Store and detach buffer from xml_buffer */
1823 *buffer
= xml_buffer
->content
;
1824 *length
= xml_buffer
->use
;
1825 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1828 new_buffer
= realloc(*buffer
, *length
);
1829 if (new_buffer
) *buffer
= new_buffer
;
1838 xmlBufferFree(xml_buffer
);
1844 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1845 * @context is session context
1846 * @document is original document where @nodeset points to
1847 * @nodeset is XPath node set to dump (recursively)
1848 * @buffer is automatically reallocated buffer where serialize to
1849 * @length is size of serialized stream in bytes
1850 * @return standard error code, free @buffer in case of error */
1851 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1852 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1853 void **buffer
, size_t *length
) {
1854 isds_error err
= IE_SUCCESS
;
1855 xmlBufferPtr xml_buffer
= NULL
;
1856 xmlSaveCtxtPtr save_ctx
= NULL
;
1859 if (!context
) return IE_INVALID_CONTEXT
;
1860 if (!buffer
) return IE_INVAL
;
1862 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1865 /* Empty node set results into NULL buffer */
1866 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1870 /* Resulting the document into buffer */
1871 xml_buffer
= xmlBufferCreate();
1873 isds_log_message(context
, _("Could not create xmlBuffer"));
1877 if (xmlSubstituteEntitiesDefault(1)) {
1878 isds_log_message(context
, _("Could not disable attribute escaping"));
1882 /* Last argument means:
1883 * 0 to not format the XML tree
1884 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1885 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1886 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1888 isds_log_message(context
, _("Could not create XML serializer"));
1892 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1893 isds_log_message(context, _("Could not disable attribute escaping"));
1899 /* Iterate over all nodes */
1900 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1902 * XXX: xmlNodeDump() appends to xml_buffer. */
1904 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1906 /* XXX: According LibXML documentation, this function does not return
1907 * meaningful value yet */
1908 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1909 if (-1 == xmlSaveFlush(save_ctx
)) {
1910 isds_log_message(context
,
1911 _("Could not serialize XML subtree"));
1917 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1918 * even after xmlSaveFlush(). Thus close it here */
1919 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1921 /* Store and detach buffer from xml_buffer */
1922 *buffer
= xml_buffer
->content
;
1923 *length
= xml_buffer
->use
;
1924 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1927 new_buffer
= realloc(*buffer
, *length
);
1928 if (new_buffer
) *buffer
= new_buffer
;
1936 xmlSaveClose(save_ctx
);
1937 xmlBufferFree(xml_buffer
);
1944 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1945 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1946 if (!string
|| !type
) return IE_INVAL
;
1948 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1950 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1952 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1953 *type
= DBTYPE_PFO_ADVOK
;
1954 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1955 *type
= DBTYPE_PFO_DANPOR
;
1956 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1957 *type
= DBTYPE_PFO_INSSPR
;
1958 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1960 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1961 *type
= DBTYPE_PO_ZAK
;
1962 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1963 *type
= DBTYPE_PO_REQ
;
1964 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1966 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1967 *type
= DBTYPE_OVM_NOTAR
;
1968 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1969 *type
= DBTYPE_OVM_EXEKUT
;
1970 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1971 *type
= DBTYPE_OVM_REQ
;
1978 /* Convert ISDS dbType enum @type to UTF-8 string.
1979 * @Return pointer to static string, or NULL if unknown enum value */
1980 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
1982 /* DBTYPE_SYSTEM is invalid value from point of view of public
1983 * SOAP interface. */
1984 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
1985 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
1986 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
1987 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
1988 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
1989 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
1990 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
1991 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
1992 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
1993 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
1994 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
1995 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
1996 default: return NULL
; break;
2001 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2002 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2003 if (!string
|| !type
) return IE_INVAL
;
2005 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2006 *type
= USERTYPE_PRIMARY
;
2007 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2008 *type
= USERTYPE_ENTRUSTED
;
2009 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2010 *type
= USERTYPE_ADMINISTRATOR
;
2011 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2012 *type
= USERTYPE_OFFICIAL
;
2013 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2014 *type
= USERTYPE_OFFICIAL_CERT
;
2015 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2016 *type
= USERTYPE_LIQUIDATOR
;
2023 /* Convert ISDS userType enum @type to UTF-8 string.
2024 * @Return pointer to static string, or NULL if unknown enum value */
2025 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2027 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2028 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2029 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2030 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2031 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2032 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2033 default: return NULL
; break;
2038 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2039 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2040 isds_sender_type
*type
) {
2041 if (!string
|| !type
) return IE_INVAL
;
2043 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2044 *type
= SENDERTYPE_PRIMARY
;
2045 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2046 *type
= SENDERTYPE_ENTRUSTED
;
2047 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2048 *type
= SENDERTYPE_ADMINISTRATOR
;
2049 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2050 *type
= SENDERTYPE_OFFICIAL
;
2051 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2052 *type
= SENDERTYPE_VIRTUAL
;
2053 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2054 *type
= SENDERTYPE_OFFICIAL_CERT
;
2055 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2056 *type
= SENDERTYPE_LIQUIDATOR
;
2063 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2064 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2065 isds_payment_type
*type
) {
2066 if (!string
|| !type
) return IE_INVAL
;
2068 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2069 *type
= PAYMENT_SENDER
;
2070 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2071 *type
= PAYMENT_RESPONSE
;
2072 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2073 *type
= PAYMENT_SPONSOR
;
2074 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2075 *type
= PAYMENT_SPONSOR_LIMITED
;
2076 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2077 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2078 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2079 *type
= PAYMENT_STAMP
;
2086 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2087 * ciEventType is integer but we convert it from string representation
2089 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2090 isds_credit_event_type
*type
) {
2091 if (!string
|| !type
) return IE_INVAL
;
2093 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2094 *type
= ISDS_CREDIT_CHARGED
;
2095 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2096 *type
= ISDS_CREDIT_DISCHARGED
;
2097 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2098 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2099 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2100 *type
= ISDS_CREDIT_STORAGE_SET
;
2101 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2102 *type
= ISDS_CREDIT_EXPIRED
;
2109 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2110 * @Return pointer to static string, or NULL if unknown enum value */
2111 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2113 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2114 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2115 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2116 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2117 default: return NULL
; break;
2120 #endif /* HAVE_LIBCURL */
2123 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2124 * @Return IE_ENUM if @string is not valid enum member */
2125 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2126 isds_FileMetaType
*type
) {
2127 if (!string
|| !type
) return IE_INVAL
;
2129 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2130 *type
= FILEMETATYPE_MAIN
;
2131 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2132 *type
= FILEMETATYPE_ENCLOSURE
;
2133 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2134 *type
= FILEMETATYPE_SIGNATURE
;
2135 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2136 *type
= FILEMETATYPE_META
;
2143 /* Convert UTF-8 @string to ISDS hash @algorithm.
2144 * @Return IE_ENUM if @string is not valid enum member */
2145 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2146 isds_hash_algorithm
*algorithm
) {
2147 if (!string
|| !algorithm
) return IE_INVAL
;
2149 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2150 *algorithm
= HASH_ALGORITHM_MD5
;
2151 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2152 *algorithm
= HASH_ALGORITHM_SHA_1
;
2153 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2154 *algorithm
= HASH_ALGORITHM_SHA_224
;
2155 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2156 *algorithm
= HASH_ALGORITHM_SHA_256
;
2157 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2158 *algorithm
= HASH_ALGORITHM_SHA_384
;
2159 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2160 *algorithm
= HASH_ALGORITHM_SHA_512
;
2168 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2169 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2170 if (!time
|| !string
) return IE_INVAL
;
2172 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2173 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2180 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2181 * respects the @time microseconds too. */
2182 static isds_error
timeval2timestring(const struct timeval
*time
,
2186 if (!time
|| !string
) return IE_INVAL
;
2188 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2189 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2191 /* TODO: small negative year should be formatted as "-0012". This is not
2192 * true for glibc "%04d". We should implement it.
2193 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2194 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2195 if (-1 == isds_asprintf((char **) string
,
2196 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2197 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2198 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2204 #endif /* HAVE_LIBCURL */
2207 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2208 * It respects microseconds too.
2209 * In case of error, @time will be freed. */
2210 static isds_error
timestring2timeval(const xmlChar
*string
,
2211 struct timeval
**time
) {
2213 char *offset
, *delim
, *endptr
;
2215 int offset_hours
, offset_minutes
;
2221 if (!time
) return IE_INVAL
;
2227 memset(&broken
, 0, sizeof(broken
));
2230 *time
= calloc(1, sizeof(**time
));
2231 if (!*time
) return IE_NOMEM
;
2233 memset(*time
, 0, sizeof(**time
));
2237 /* xsd:date is ISO 8601 string, thus ASCII */
2238 /*TODO: negative year */
2242 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2243 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2244 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2250 broken
.tm_year
-= 1900;
2252 offset
= (char*)string
+ i
;
2254 /* Parse date and time without subseconds and offset */
2255 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2262 /* Get subseconds */
2263 if (*offset
== '.' ) {
2266 /* Copy first 6 digits, pad it with zeros.
2267 * XXX: It truncates longer number, no round.
2268 * Current server implementation uses only millisecond resolution. */
2269 /* TODO: isdigit() is locale sensitive */
2271 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
2273 subseconds
[i
] = *offset
;
2275 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
2276 subseconds
[i
] = '0';
2278 subseconds
[6] = '\0';
2280 /* Convert it into integer */
2281 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
2282 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
2283 (*time
)->tv_usec
== LONG_MAX
) {
2288 /* move to the zone offset delimiter or signal NULL*/
2289 delim
= strchr(offset
, '-');
2291 delim
= strchr(offset
, '+');
2293 delim
= strchr(offset
, 'Z');
2297 /* Get zone offset */
2298 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2299 * "" equals to "Z" and it means UTC zone. */
2300 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2301 * colon separator */
2302 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2303 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2307 if (*offset
== '+') {
2308 broken
.tm_hour
-= offset_hours
;
2309 broken
.tm_min
-= offset_minutes
;
2311 broken
.tm_hour
+= offset_hours
;
2312 broken
.tm_min
+= offset_minutes
;
2316 /* Convert to time_t */
2317 (*time
)->tv_sec
= _isds_timegm(&broken
);
2318 if ((*time
)->tv_sec
== (time_t) -1) {
2327 /* Convert unsigned int into isds_message_status.
2328 * @context is session context
2329 * @number is pointer to number value. NULL will be treated as invalid value.
2330 * @status is automatically reallocated status
2331 * @return IE_SUCCESS, or error code and free status */
2332 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2333 const unsigned long int *number
, isds_message_status
**status
) {
2334 if (!context
) return IE_INVALID_CONTEXT
;
2335 if (!status
) return IE_INVAL
;
2337 free(*status
); *status
= NULL
;
2338 if (!number
) return IE_INVAL
;
2340 if (*number
< 1 || *number
> 10) {
2341 isds_printf_message(context
, _("Invalid message status value: %lu"),
2346 *status
= malloc(sizeof(**status
));
2347 if (!*status
) return IE_NOMEM
;
2349 **status
= 1 << *number
;
2354 /* Convert event description string into isds_event members type and
2356 * @string is raw event description starting with event prefix
2357 * @event is structure where to store type and stripped description to
2358 * @return standard error code, unknown prefix is not classified as an error.
2360 static isds_error
eventstring2event(const xmlChar
*string
,
2361 struct isds_event
* event
) {
2362 const xmlChar
*known_prefixes
[] = {
2373 const isds_event_type types
[] = {
2374 EVENT_ENTERED_SYSTEM
,
2375 EVENT_ACCEPTED_BY_RECIPIENT
,
2376 EVENT_ACCEPTED_BY_FICTION
,
2377 EVENT_UNDELIVERABLE
,
2378 EVENT_COMMERCIAL_ACCEPTED
,
2380 EVENT_PRIMARY_LOGIN
,
2381 EVENT_ENTRUSTED_LOGIN
,
2387 if (!string
|| !event
) return IE_INVAL
;
2390 event
->type
= malloc(sizeof(*event
->type
));
2391 if (!(event
->type
)) return IE_NOMEM
;
2393 zfree(event
->description
);
2395 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2397 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2399 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2400 /* Prefix is known */
2401 *event
->type
= types
[index
];
2403 /* Strip prefix from description and spaces */
2404 /* TODO: Recognize all white spaces from UCS blank class and
2405 * operate on UTF-8 chars. */
2406 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2407 event
->description
= strdup((char *) (string
+ length
));
2408 if (!(event
->description
)) return IE_NOMEM
;
2414 /* Unknown event prefix.
2415 * XSD allows any string */
2416 char *string_locale
= _isds_utf82locale((char *) string
);
2417 isds_log(ILF_ISDS
, ILL_WARNING
,
2418 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2419 free(string_locale
);
2421 *event
->type
= EVENT_UKNOWN
;
2422 event
->description
= strdup((char *) string
);
2423 if (!(event
->description
)) return IE_NOMEM
;
2429 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2430 * and leave label */
2431 #define EXTRACT_STRING(element, string) { \
2432 xmlXPathFreeObject(result); \
2433 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2434 if (NULL == (result)) { \
2438 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2439 if (result->nodesetval->nodeNr > 1) { \
2440 isds_printf_message(context, _("Multiple %s element"), element); \
2444 (string) = (char *) \
2445 xmlXPathCastNodeSetToString(result->nodesetval); \
2446 if (NULL == (string)) { \
2453 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2455 char *string = NULL; \
2456 EXTRACT_STRING(element, string); \
2459 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2460 if (!(booleanPtr)) { \
2466 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2467 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2468 *(booleanPtr) = 1; \
2469 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2470 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2471 *(booleanPtr) = 0; \
2473 char *string_locale = _isds_utf82locale((char*)string); \
2474 isds_printf_message(context, \
2475 _("%s value is not valid boolean: %s"), \
2476 element, string_locale); \
2477 free(string_locale); \
2487 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2489 char *string = NULL; \
2490 EXTRACT_STRING(element, string); \
2495 number = strtol((char*)string, &endptr, 10); \
2497 if (*endptr != '\0') { \
2498 char *string_locale = _isds_utf82locale((char *)string); \
2499 isds_printf_message(context, \
2500 _("%s is not valid integer: %s"), \
2501 element, string_locale); \
2502 free(string_locale); \
2508 if (number == LONG_MIN || number == LONG_MAX) { \
2509 char *string_locale = _isds_utf82locale((char *)string); \
2510 isds_printf_message(context, \
2511 _("%s value out of range of long int: %s"), \
2512 element, string_locale); \
2513 free(string_locale); \
2519 free(string); string = NULL; \
2521 if (!(preallocated)) { \
2522 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2523 if (!(longintPtr)) { \
2528 *(longintPtr) = number; \
2532 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2534 char *string = NULL; \
2535 EXTRACT_STRING(element, string); \
2540 number = strtol((char*)string, &endptr, 10); \
2542 if (*endptr != '\0') { \
2543 char *string_locale = _isds_utf82locale((char *)string); \
2544 isds_printf_message(context, \
2545 _("%s is not valid integer: %s"), \
2546 element, string_locale); \
2547 free(string_locale); \
2553 if (number == LONG_MIN || number == LONG_MAX) { \
2554 char *string_locale = _isds_utf82locale((char *)string); \
2555 isds_printf_message(context, \
2556 _("%s value out of range of long int: %s"), \
2557 element, string_locale); \
2558 free(string_locale); \
2564 free(string); string = NULL; \
2566 isds_printf_message(context, \
2567 _("%s value is negative: %ld"), element, number); \
2572 if (!(preallocated)) { \
2573 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2574 if (!(ulongintPtr)) { \
2579 *(ulongintPtr) = number; \
2583 #define EXTRACT_DATE(element, tmPtr) { \
2584 char *string = NULL; \
2585 EXTRACT_STRING(element, string); \
2586 if (NULL != string) { \
2587 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2588 if (NULL == (tmPtr)) { \
2593 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2595 if (err == IE_NOTSUP) { \
2597 char *string_locale = _isds_utf82locale(string); \
2598 char *element_locale = _isds_utf82locale(element); \
2599 isds_printf_message(context, _("Invalid %s value: %s"), \
2600 element_locale, string_locale); \
2601 free(string_locale); \
2602 free(element_locale); \
2611 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2612 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2614 if ((required) && (!string)) { \
2615 char *attribute_locale = _isds_utf82locale(attribute); \
2616 char *element_locale = \
2617 _isds_utf82locale((char *)xpath_ctx->node->name); \
2618 isds_printf_message(context, \
2619 _("Could not extract required %s attribute value from " \
2620 "%s element"), attribute_locale, element_locale); \
2621 free(element_locale); \
2622 free(attribute_locale); \
2629 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2631 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2632 (xmlChar *) (string)); \
2634 isds_printf_message(context, \
2635 _("Could not add %s child to %s element"), \
2636 element, (parent)->name); \
2642 #define INSERT_STRING(parent, element, string) \
2643 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2645 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2647 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2648 else { INSERT_STRING(parent, element, "false"); } \
2651 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2654 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2656 INSERT_STRING(parent, element, NULL); \
2660 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2661 if ((longintPtr)) { \
2662 /* FIXME: locale sensitive */ \
2663 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2667 INSERT_STRING(parent, element, buffer) \
2668 free(buffer); (buffer) = NULL; \
2669 } else { INSERT_STRING(parent, element, NULL) } \
2672 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2673 if ((ulongintPtr)) { \
2674 /* FIXME: locale sensitive */ \
2675 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2679 INSERT_STRING(parent, element, buffer) \
2680 free(buffer); (buffer) = NULL; \
2681 } else { INSERT_STRING(parent, element, NULL) } \
2684 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2686 /* FIXME: locale sensitive */ \
2687 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2691 INSERT_STRING(parent, element, buffer) \
2692 free(buffer); (buffer) = NULL; \
2695 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2697 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2699 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2700 (xmlChar *) (string)); \
2701 if (!attribute_node) { \
2702 isds_printf_message(context, _("Could not add %s " \
2703 "attribute to %s element"), \
2704 (attribute), (parent)->name); \
2710 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2712 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2713 if (length > (maximum)) { \
2714 isds_printf_message(context, \
2715 ngettext("%s has more than %d characters", \
2716 "%s has more than %d characters", (maximum)), \
2717 (name), (maximum)); \
2721 if (length < (minimum)) { \
2722 isds_printf_message(context, \
2723 ngettext("%s has less than %d characters", \
2724 "%s has less than %d characters", (minimum)), \
2725 (name), (minimum)); \
2732 #define INSERT_ELEMENT(child, parent, element) \
2734 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2736 isds_printf_message(context, \
2737 _("Could not add %s child to %s element"), \
2738 (element), (parent)->name); \
2745 /* Find child element by name in given XPath context and switch context onto
2746 * it. The child must be uniq and must exist. Otherwise fails.
2747 * @context is ISDS context
2748 * @child is child element name
2749 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2750 * into it child. In error case, the @xpath_ctx keeps original value. */
2751 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2752 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2753 isds_error err
= IE_SUCCESS
;
2754 xmlXPathObjectPtr result
= NULL
;
2756 if (!context
) return IE_INVALID_CONTEXT
;
2757 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2760 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2767 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2768 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2769 char *child_locale
= _isds_utf82locale((char*) child
);
2770 isds_printf_message(context
,
2771 _("%s element does not contain %s child"),
2772 parent_locale
, child_locale
);
2774 free(parent_locale
);
2780 if (result
->nodesetval
->nodeNr
> 1) {
2781 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2782 char *child_locale
= _isds_utf82locale((char*) child
);
2783 isds_printf_message(context
,
2784 _("%s element contains multiple %s children"),
2785 parent_locale
, child_locale
);
2787 free(parent_locale
);
2792 /* Switch context */
2793 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2796 xmlXPathFreeObject(result
);
2803 /* Find and convert XSD:gPersonName group in current node into structure
2804 * @context is ISDS context
2805 * @personName is automatically reallocated person name structure. If no member
2806 * value is found, will be freed.
2807 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2809 * In case of error @personName will be freed. */
2810 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2811 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2812 isds_error err
= IE_SUCCESS
;
2813 xmlXPathObjectPtr result
= NULL
;
2815 if (!context
) return IE_INVALID_CONTEXT
;
2816 if (!personName
) return IE_INVAL
;
2817 isds_PersonName_free(personName
);
2818 if (!xpath_ctx
) return IE_INVAL
;
2821 *personName
= calloc(1, sizeof(**personName
));
2827 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2828 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2829 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2830 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2832 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2833 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2834 isds_PersonName_free(personName
);
2837 if (err
) isds_PersonName_free(personName
);
2838 xmlXPathFreeObject(result
);
2843 /* Find and convert XSD:gAddress group in current node into structure
2844 * @context is ISDS context
2845 * @address is automatically reallocated address structure. If no member
2846 * value is found, will be freed.
2847 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2849 * In case of error @address will be freed. */
2850 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2851 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2852 isds_error err
= IE_SUCCESS
;
2853 xmlXPathObjectPtr result
= NULL
;
2855 if (!context
) return IE_INVALID_CONTEXT
;
2856 if (!address
) return IE_INVAL
;
2857 isds_Address_free(address
);
2858 if (!xpath_ctx
) return IE_INVAL
;
2861 *address
= calloc(1, sizeof(**address
));
2867 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2868 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2869 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2870 EXTRACT_STRING("isds:adNumberInMunicipality",
2871 (*address
)->adNumberInMunicipality
);
2872 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2873 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2875 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2876 !(*address
)->adNumberInStreet
&&
2877 !(*address
)->adNumberInMunicipality
&&
2878 !(*address
)->adZipCode
&& !(*address
)->adState
)
2879 isds_Address_free(address
);
2882 if (err
) isds_Address_free(address
);
2883 xmlXPathFreeObject(result
);
2888 /* Find and convert isds:biDate element in current node into structure
2889 * @context is ISDS context
2890 * @biDate is automatically reallocated birth date structure. If no member
2891 * value is found, will be freed.
2892 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2894 * In case of error @biDate will be freed. */
2895 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2896 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2897 isds_error err
= IE_SUCCESS
;
2898 xmlXPathObjectPtr result
= NULL
;
2899 char *string
= NULL
;
2901 if (!context
) return IE_INVALID_CONTEXT
;
2902 if (!biDate
) return IE_INVAL
;
2904 if (!xpath_ctx
) return IE_INVAL
;
2906 EXTRACT_STRING("isds:biDate", string
);
2908 *biDate
= calloc(1, sizeof(**biDate
));
2913 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
2915 if (err
== IE_NOTSUP
) {
2917 char *string_locale
= _isds_utf82locale(string
);
2918 isds_printf_message(context
,
2919 _("Invalid isds:biDate value: %s"), string_locale
);
2920 free(string_locale
);
2927 if (err
) zfree(*biDate
);
2929 xmlXPathFreeObject(result
);
2934 /* Convert isds:dBOwnerInfo XML tree into structure
2935 * @context is ISDS context
2936 * @db_owner_info is automatically reallocated box owner info structure
2937 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2938 * In case of error @db_owner_info will be freed. */
2939 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
2940 struct isds_DbOwnerInfo
**db_owner_info
,
2941 xmlXPathContextPtr xpath_ctx
) {
2942 isds_error err
= IE_SUCCESS
;
2943 xmlXPathObjectPtr result
= NULL
;
2944 char *string
= NULL
;
2946 if (!context
) return IE_INVALID_CONTEXT
;
2947 if (!db_owner_info
) return IE_INVAL
;
2948 isds_DbOwnerInfo_free(db_owner_info
);
2949 if (!xpath_ctx
) return IE_INVAL
;
2952 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
2953 if (!*db_owner_info
) {
2958 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
2960 EXTRACT_STRING("isds:dbType", string
);
2962 (*db_owner_info
)->dbType
=
2963 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
2964 if (!(*db_owner_info
)->dbType
) {
2968 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
2970 zfree((*db_owner_info
)->dbType
);
2971 if (err
== IE_ENUM
) {
2973 char *string_locale
= _isds_utf82locale(string
);
2974 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
2976 free(string_locale
);
2983 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
2985 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
2987 if (err
) goto leave
;
2989 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
2991 (*db_owner_info
)->birthInfo
=
2992 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
2993 if (!(*db_owner_info
)->birthInfo
) {
2997 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
2999 if (err
) goto leave
;
3000 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3001 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3002 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3003 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3004 !(*db_owner_info
)->birthInfo
->biCity
&&
3005 !(*db_owner_info
)->birthInfo
->biCounty
&&
3006 !(*db_owner_info
)->birthInfo
->biState
)
3007 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3009 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3010 if (err
) goto leave
;
3012 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3013 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3014 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3015 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3016 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3018 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3020 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3021 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3022 (*db_owner_info
)->dbOpenAddressing
);
3025 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3027 xmlXPathFreeObject(result
);
3032 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3033 * @context is session context
3034 * @owner is libisds structure with box description
3035 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3036 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3037 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3039 isds_error err
= IE_SUCCESS
;
3041 xmlChar
*string
= NULL
;
3043 if (!context
) return IE_INVALID_CONTEXT
;
3044 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3047 /* Build XSD:tDbOwnerInfo */
3048 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3049 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3052 if (owner
->dbType
) {
3053 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3055 isds_printf_message(context
, _("Invalid dbType value: %d"),
3060 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3062 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3063 if (owner
->personName
) {
3064 INSERT_STRING(db_owner_info
, "pnFirstName",
3065 owner
->personName
->pnFirstName
);
3066 INSERT_STRING(db_owner_info
, "pnMiddleName",
3067 owner
->personName
->pnMiddleName
);
3068 INSERT_STRING(db_owner_info
, "pnLastName",
3069 owner
->personName
->pnLastName
);
3070 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3071 owner
->personName
->pnLastNameAtBirth
);
3073 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3074 if (owner
->birthInfo
) {
3075 if (owner
->birthInfo
->biDate
) {
3076 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3077 INSERT_STRING(db_owner_info
, "biDate", string
);
3078 free(string
); string
= NULL
;
3080 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3081 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3082 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3084 if (owner
->address
) {
3085 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3086 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3087 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3088 owner
->address
->adNumberInStreet
);
3089 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3090 owner
->address
->adNumberInMunicipality
);
3091 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3092 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3094 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3095 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3096 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3098 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3099 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3101 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3102 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3104 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3106 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3107 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3108 owner
->dbOpenAddressing
);
3116 /* Convert XSD:tDbUserInfo XML tree into structure
3117 * @context is ISDS context
3118 * @db_user_info is automatically reallocated user info structure
3119 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3120 * In case of error @db_user_info will be freed. */
3121 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3122 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3123 isds_error err
= IE_SUCCESS
;
3124 xmlXPathObjectPtr result
= NULL
;
3125 char *string
= NULL
;
3127 if (!context
) return IE_INVALID_CONTEXT
;
3128 if (!db_user_info
) return IE_INVAL
;
3129 isds_DbUserInfo_free(db_user_info
);
3130 if (!xpath_ctx
) return IE_INVAL
;
3133 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3134 if (!*db_user_info
) {
3139 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3141 EXTRACT_STRING("isds:userType", string
);
3143 (*db_user_info
)->userType
=
3144 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3145 if (!(*db_user_info
)->userType
) {
3149 err
= string2isds_UserType((xmlChar
*)string
,
3150 (*db_user_info
)->userType
);
3152 zfree((*db_user_info
)->userType
);
3153 if (err
== IE_ENUM
) {
3155 char *string_locale
= _isds_utf82locale(string
);
3156 isds_printf_message(context
,
3157 _("Unknown isds:userType value: %s"), string_locale
);
3158 free(string_locale
);
3165 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3167 (*db_user_info
)->personName
=
3168 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3169 if (!(*db_user_info
)->personName
) {
3174 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3176 if (err
) goto leave
;
3178 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3179 if (err
) goto leave
;
3181 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3182 if (err
) goto leave
;
3184 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3185 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3187 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3188 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3189 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3191 /* ???: Default value is "CZ" according specification. Should we provide
3193 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3196 if (err
) isds_DbUserInfo_free(db_user_info
);
3198 xmlXPathFreeObject(result
);
3203 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3204 * @context is session context
3205 * @user is libisds structure with user description
3206 * @db_user_info is XML element of XSD:tDbUserInfo */
3207 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3208 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3210 isds_error err
= IE_SUCCESS
;
3212 xmlChar
*string
= NULL
;
3214 if (!context
) return IE_INVALID_CONTEXT
;
3215 if (!user
|| !db_user_info
) return IE_INVAL
;
3217 /* Build XSD:tDbUserInfo */
3218 if (user
->personName
) {
3219 INSERT_STRING(db_user_info
, "pnFirstName",
3220 user
->personName
->pnFirstName
);
3221 INSERT_STRING(db_user_info
, "pnMiddleName",
3222 user
->personName
->pnMiddleName
);
3223 INSERT_STRING(db_user_info
, "pnLastName",
3224 user
->personName
->pnLastName
);
3225 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3226 user
->personName
->pnLastNameAtBirth
);
3228 if (user
->address
) {
3229 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3230 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3231 INSERT_STRING(db_user_info
, "adNumberInStreet",
3232 user
->address
->adNumberInStreet
);
3233 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3234 user
->address
->adNumberInMunicipality
);
3235 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3236 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3239 if (!tm2datestring(user
->biDate
, &string
))
3240 INSERT_STRING(db_user_info
, "biDate", string
);
3243 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3244 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3247 if (user
->userType
) {
3248 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3250 isds_printf_message(context
, _("Invalid userType value: %d"),
3255 INSERT_STRING(db_user_info
, "userType", type_string
);
3258 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3259 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3260 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3261 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3262 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3263 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3264 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3265 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3266 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3274 /* Convert XSD:tPDZRec XML tree into structure
3275 * @context is ISDS context
3276 * @permission is automatically reallocated commercial permission structure
3277 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3278 * In case of error @permission will be freed. */
3279 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3280 struct isds_commercial_permission
**permission
,
3281 xmlXPathContextPtr xpath_ctx
) {
3282 isds_error err
= IE_SUCCESS
;
3283 xmlXPathObjectPtr result
= NULL
;
3284 char *string
= NULL
;
3286 if (!context
) return IE_INVALID_CONTEXT
;
3287 if (!permission
) return IE_INVAL
;
3288 isds_commercial_permission_free(permission
);
3289 if (!xpath_ctx
) return IE_INVAL
;
3292 *permission
= calloc(1, sizeof(**permission
));
3298 EXTRACT_STRING("isds:PDZType", string
);
3300 err
= string2isds_payment_type((xmlChar
*)string
,
3301 &(*permission
)->type
);
3303 if (err
== IE_ENUM
) {
3305 char *string_locale
= _isds_utf82locale(string
);
3306 isds_printf_message(context
,
3307 _("Unknown isds:PDZType value: %s"), string_locale
);
3308 free(string_locale
);
3315 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3316 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3318 EXTRACT_STRING("isds:PDZExpire", string
);
3320 err
= timestring2timeval((xmlChar
*) string
,
3321 &((*permission
)->expiration
));
3323 char *string_locale
= _isds_utf82locale(string
);
3324 if (err
== IE_DATE
) err
= IE_ISDS
;
3325 isds_printf_message(context
,
3326 _("Could not convert PDZExpire as ISO time: %s"),
3328 free(string_locale
);
3334 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3335 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3338 if (err
) isds_commercial_permission_free(permission
);
3340 xmlXPathFreeObject(result
);
3345 /* Convert XSD:tCiRecord XML tree into structure
3346 * @context is ISDS context
3347 * @event is automatically reallocated commercial credit event structure
3348 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3349 * In case of error @event will be freed. */
3350 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3351 struct isds_credit_event
**event
,
3352 xmlXPathContextPtr xpath_ctx
) {
3353 isds_error err
= IE_SUCCESS
;
3354 xmlXPathObjectPtr result
= NULL
;
3355 char *string
= NULL
;
3356 long int *number_ptr
;
3358 if (!context
) return IE_INVALID_CONTEXT
;
3359 if (!event
) return IE_INVAL
;
3360 isds_credit_event_free(event
);
3361 if (!xpath_ctx
) return IE_INVAL
;
3364 *event
= calloc(1, sizeof(**event
));
3370 EXTRACT_STRING("isds:ciEventTime", string
);
3372 err
= timestring2timeval((xmlChar
*) string
,
3375 char *string_locale
= _isds_utf82locale(string
);
3376 if (err
== IE_DATE
) err
= IE_ISDS
;
3377 isds_printf_message(context
,
3378 _("Could not convert ciEventTime as ISO time: %s"),
3380 free(string_locale
);
3386 EXTRACT_STRING("isds:ciEventType", string
);
3388 err
= string2isds_credit_event_type((xmlChar
*)string
,
3391 if (err
== IE_ENUM
) {
3393 char *string_locale
= _isds_utf82locale(string
);
3394 isds_printf_message(context
,
3395 _("Unknown isds:ciEventType value: %s"), string_locale
);
3396 free(string_locale
);
3403 number_ptr
= &((*event
)->credit_change
);
3404 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3405 number_ptr
= &(*event
)->new_credit
;
3406 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3408 switch((*event
)->type
) {
3409 case ISDS_CREDIT_CHARGED
:
3410 EXTRACT_STRING("isds:ciTransID",
3411 (*event
)->details
.charged
.transaction
);
3413 case ISDS_CREDIT_DISCHARGED
:
3414 EXTRACT_STRING("isds:ciTransID",
3415 (*event
)->details
.discharged
.transaction
);
3417 case ISDS_CREDIT_MESSAGE_SENT
:
3418 EXTRACT_STRING("isds:ciRecipientID",
3419 (*event
)->details
.message_sent
.recipient
);
3420 EXTRACT_STRING("isds:ciPDZID",
3421 (*event
)->details
.message_sent
.message_id
);
3423 case ISDS_CREDIT_STORAGE_SET
:
3424 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3425 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3426 EXTRACT_DATE("isds:ciNewFrom",
3427 (*event
)->details
.storage_set
.new_valid_from
);
3428 EXTRACT_DATE("isds:ciNewTo",
3429 (*event
)->details
.storage_set
.new_valid_to
);
3430 EXTRACT_LONGINT("isds:ciOldCapacity",
3431 (*event
)->details
.storage_set
.old_capacity
, 0);
3432 EXTRACT_DATE("isds:ciOldFrom",
3433 (*event
)->details
.storage_set
.old_valid_from
);
3434 EXTRACT_DATE("isds:ciOldTo",
3435 (*event
)->details
.storage_set
.old_valid_to
);
3436 EXTRACT_STRING("isds:ciDoneBy",
3437 (*event
)->details
.storage_set
.initiator
);
3439 case ISDS_CREDIT_EXPIRED
:
3444 if (err
) isds_credit_event_free(event
);
3446 xmlXPathFreeObject(result
);
3451 #endif /* HAVE_LIBCURL */
3454 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3455 * isds_envelope structure. The envelope is automatically allocated but not
3456 * reallocated. The date are just appended into envelope structure.
3457 * @context is ISDS context
3458 * @envelope is automatically allocated message envelope structure
3459 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3460 * In case of error @envelope will be freed. */
3461 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3462 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3463 isds_error err
= IE_SUCCESS
;
3464 xmlXPathObjectPtr result
= NULL
;
3466 if (!context
) return IE_INVALID_CONTEXT
;
3467 if (!envelope
) return IE_INVAL
;
3468 if (!xpath_ctx
) return IE_INVAL
;
3472 /* Allocate envelope */
3473 *envelope
= calloc(1, sizeof(**envelope
));
3479 /* Else free former data */
3480 zfree((*envelope
)->dmSenderOrgUnit
);
3481 zfree((*envelope
)->dmSenderOrgUnitNum
);
3482 zfree((*envelope
)->dbIDRecipient
);
3483 zfree((*envelope
)->dmRecipientOrgUnit
);
3484 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3485 zfree((*envelope
)->dmToHands
);
3486 zfree((*envelope
)->dmAnnotation
);
3487 zfree((*envelope
)->dmRecipientRefNumber
);
3488 zfree((*envelope
)->dmSenderRefNumber
);
3489 zfree((*envelope
)->dmRecipientIdent
);
3490 zfree((*envelope
)->dmSenderIdent
);
3491 zfree((*envelope
)->dmLegalTitleLaw
);
3492 zfree((*envelope
)->dmLegalTitleYear
);
3493 zfree((*envelope
)->dmLegalTitleSect
);
3494 zfree((*envelope
)->dmLegalTitlePar
);
3495 zfree((*envelope
)->dmLegalTitlePoint
);
3496 zfree((*envelope
)->dmPersonalDelivery
);
3497 zfree((*envelope
)->dmAllowSubstDelivery
);
3500 /* Extract envelope elements added by sender or ISDS
3501 * (XSD: gMessageEnvelopeSub type) */
3502 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3503 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3504 (*envelope
)->dmSenderOrgUnitNum
, 0);
3505 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3506 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3507 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3508 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3509 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3510 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3511 EXTRACT_STRING("isds:dmRecipientRefNumber",
3512 (*envelope
)->dmRecipientRefNumber
);
3513 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3514 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3515 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3517 /* Extract envelope elements regarding law reference */
3518 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3519 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3520 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3521 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3522 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3524 /* Extract envelope other elements */
3525 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3526 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3527 (*envelope
)->dmAllowSubstDelivery
);
3530 if (err
) isds_envelope_free(envelope
);
3531 xmlXPathFreeObject(result
);
3537 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3538 * isds_envelope structure. The envelope is automatically allocated but not
3539 * reallocated. The date are just appended into envelope structure.
3540 * @context is ISDS context
3541 * @envelope is automatically allocated message envelope structure
3542 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3543 * In case of error @envelope will be freed. */
3544 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3545 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3546 isds_error err
= IE_SUCCESS
;
3547 xmlXPathObjectPtr result
= NULL
;
3549 if (!context
) return IE_INVALID_CONTEXT
;
3550 if (!envelope
) return IE_INVAL
;
3551 if (!xpath_ctx
) return IE_INVAL
;
3555 /* Allocate envelope */
3556 *envelope
= calloc(1, sizeof(**envelope
));
3562 /* Else free former data */
3563 zfree((*envelope
)->dmID
);
3564 zfree((*envelope
)->dbIDSender
);
3565 zfree((*envelope
)->dmSender
);
3566 zfree((*envelope
)->dmSenderAddress
);
3567 zfree((*envelope
)->dmSenderType
);
3568 zfree((*envelope
)->dmRecipient
);
3569 zfree((*envelope
)->dmRecipientAddress
);
3570 zfree((*envelope
)->dmAmbiguousRecipient
);
3573 /* Extract envelope elements added by ISDS
3574 * (XSD: gMessageEnvelope type) */
3575 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3576 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3577 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3578 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3579 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3580 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3581 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3582 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3583 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3584 (*envelope
)->dmAmbiguousRecipient
);
3586 /* Extract envelope elements added by sender and ISDS
3587 * (XSD: gMessageEnvelope type) */
3588 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3589 if (err
) goto leave
;
3592 if (err
) isds_envelope_free(envelope
);
3593 xmlXPathFreeObject(result
);
3598 /* Convert other envelope elements from XML tree into isds_envelope structure:
3599 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3600 * The envelope is automatically allocated but not reallocated.
3601 * The data are just appended into envelope structure.
3602 * @context is ISDS context
3603 * @envelope is automatically allocated message envelope structure
3604 * @xpath_ctx is XPath context with current node as parent desired elements
3605 * In case of error @envelope will be freed. */
3606 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3607 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3608 isds_error err
= IE_SUCCESS
;
3609 xmlXPathObjectPtr result
= NULL
;
3610 char *string
= NULL
;
3611 unsigned long int *unumber
= NULL
;
3613 if (!context
) return IE_INVALID_CONTEXT
;
3614 if (!envelope
) return IE_INVAL
;
3615 if (!xpath_ctx
) return IE_INVAL
;
3620 *envelope
= calloc(1, sizeof(**envelope
));
3627 zfree((*envelope
)->dmMessageStatus
);
3628 zfree((*envelope
)->dmAttachmentSize
);
3629 zfree((*envelope
)->dmDeliveryTime
);
3630 zfree((*envelope
)->dmAcceptanceTime
);
3634 /* dmMessageStatus element is mandatory */
3635 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3637 isds_log_message(context
,
3638 _("Missing mandatory sisds:dmMessageStatus integer"));
3642 err
= uint2isds_message_status(context
, unumber
,
3643 &((*envelope
)->dmMessageStatus
));
3645 if (err
== IE_ENUM
) err
= IE_ISDS
;
3648 free(unumber
); unumber
= NULL
;
3650 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3653 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3655 err
= timestring2timeval((xmlChar
*) string
,
3656 &((*envelope
)->dmDeliveryTime
));
3658 char *string_locale
= _isds_utf82locale(string
);
3659 if (err
== IE_DATE
) err
= IE_ISDS
;
3660 isds_printf_message(context
,
3661 _("Could not convert dmDeliveryTime as ISO time: %s"),
3663 free(string_locale
);
3669 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3671 err
= timestring2timeval((xmlChar
*) string
,
3672 &((*envelope
)->dmAcceptanceTime
));
3674 char *string_locale
= _isds_utf82locale(string
);
3675 if (err
== IE_DATE
) err
= IE_ISDS
;
3676 isds_printf_message(context
,
3677 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3679 free(string_locale
);
3686 if (err
) isds_envelope_free(envelope
);
3689 xmlXPathFreeObject(result
);
3694 /* Convert message type attribute of current element into isds_envelope
3696 * TODO: This function can be incorporated into append_status_size_times() as
3697 * they are called always together.
3698 * The envelope is automatically allocated but not reallocated.
3699 * The data are just appended into envelope structure.
3700 * @context is ISDS context
3701 * @envelope is automatically allocated message envelope structure
3702 * @xpath_ctx is XPath context with current node as parent of attribute
3703 * carrying message type
3704 * In case of error @envelope will be freed. */
3705 static isds_error
append_message_type(struct isds_ctx
*context
,
3706 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3707 isds_error err
= IE_SUCCESS
;
3709 if (!context
) return IE_INVALID_CONTEXT
;
3710 if (!envelope
) return IE_INVAL
;
3711 if (!xpath_ctx
) return IE_INVAL
;
3716 *envelope
= calloc(1, sizeof(**envelope
));
3723 zfree((*envelope
)->dmType
);
3727 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3729 if (!(*envelope
)->dmType
) {
3730 /* Use default value */
3731 (*envelope
)->dmType
= strdup("V");
3732 if (!(*envelope
)->dmType
) {
3736 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3737 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3738 isds_printf_message(context
,
3739 _("Message type in dmType attribute is not 1 character long: "
3748 if (err
) isds_envelope_free(envelope
);
3754 /* Convert dmType isds_envelope member into XML attribute and append it to
3756 * @context is ISDS context
3757 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3758 * @dm_envelope is XML element the resulting attribute will be appended to.
3759 * @return error code, in case of error context' message is filled. */
3760 static isds_error
insert_message_type(struct isds_ctx
*context
,
3761 const char *type
, xmlNodePtr dm_envelope
) {
3762 isds_error err
= IE_SUCCESS
;
3763 xmlAttrPtr attribute_node
;
3765 if (!context
) return IE_INVALID_CONTEXT
;
3766 if (!dm_envelope
) return IE_INVAL
;
3768 /* Insert optional message type */
3770 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3771 char *type_locale
= _isds_utf82locale(type
);
3772 isds_printf_message(context
,
3773 _("Message type in envelope is not 1 character long: %s"),
3779 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3785 #endif /* HAVE_LIBCURL */
3788 /* Extract message document into reallocated document structure
3789 * @context is ISDS context
3790 * @document is automatically reallocated message documents structure
3791 * @xpath_ctx is XPath context with current node as isds:dmFile
3792 * In case of error @document will be freed. */
3793 static isds_error
extract_document(struct isds_ctx
*context
,
3794 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3795 isds_error err
= IE_SUCCESS
;
3796 xmlXPathObjectPtr result
= NULL
;
3797 xmlNodePtr file_node
;
3798 char *string
= NULL
;
3800 if (!context
) return IE_INVALID_CONTEXT
;
3801 if (!document
) return IE_INVAL
;
3802 isds_document_free(document
);
3803 if (!xpath_ctx
) return IE_INVAL
;
3804 file_node
= xpath_ctx
->node
;
3806 *document
= calloc(1, sizeof(**document
));
3812 /* Extract document meta data */
3813 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3814 if (context
->normalize_mime_type
) {
3815 const char *normalized_type
=
3816 isds_normalize_mime_type((*document
)->dmMimeType
);
3817 if (NULL
!= normalized_type
&&
3818 normalized_type
!= (*document
)->dmMimeType
) {
3819 char *new_type
= strdup(normalized_type
);
3820 if (NULL
== new_type
) {
3821 isds_printf_message(context
,
3822 _("Not enough memory to normalize document MIME type"));
3826 free((*document
)->dmMimeType
);
3827 (*document
)->dmMimeType
= new_type
;
3831 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3832 err
= string2isds_FileMetaType((xmlChar
*)string
,
3833 &((*document
)->dmFileMetaType
));
3835 char *meta_type_locale
= _isds_utf82locale(string
);
3836 isds_printf_message(context
,
3837 _("Document has invalid dmFileMetaType attribute value: %s"),
3839 free(meta_type_locale
);
3845 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3846 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3847 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3848 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3851 /* Extract document data.
3852 * Base64 encoded blob or XML subtree must be presented. */
3854 /* Check for dmEncodedContent */
3855 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3862 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3863 /* Here we have Base64 blob */
3864 (*document
)->is_xml
= 0;
3866 if (result
->nodesetval
->nodeNr
> 1) {
3867 isds_printf_message(context
,
3868 _("Document has more dmEncodedContent elements"));
3873 xmlXPathFreeObject(result
); result
= NULL
;
3874 EXTRACT_STRING("isds:dmEncodedContent", string
);
3876 /* Decode non-empty document */
3877 if (string
&& string
[0] != '\0') {
3878 (*document
)->data_length
=
3879 _isds_b64decode(string
, &((*document
)->data
));
3880 if ((*document
)->data_length
== (size_t) -1) {
3881 isds_printf_message(context
,
3882 _("Error while Base64-decoding document content"));
3888 /* No Base64 blob, try XML document */
3889 xmlXPathFreeObject(result
); result
= NULL
;
3890 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3897 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3898 /* Here we have XML document */
3899 (*document
)->is_xml
= 1;
3901 if (result
->nodesetval
->nodeNr
> 1) {
3902 isds_printf_message(context
,
3903 _("Document has more dmXMLContent elements"));
3908 /* XXX: We cannot serialize the content simply because:
3909 * - XML document may point out of its scope (e.g. to message
3911 * - isds:dmXMLContent can contain more elements, no element,
3913 * - it's not the XML way
3914 * Thus we provide the only right solution: XML DOM. Let's
3915 * application to cope with this hot potato :) */
3916 (*document
)->xml_node_list
=
3917 result
->nodesetval
->nodeTab
[0]->children
;
3919 /* No base64 blob, nor XML document */
3920 isds_printf_message(context
,
3921 _("Document has no dmEncodedContent, nor dmXMLContent "
3930 if (err
) isds_document_free(document
);
3932 xmlXPathFreeObject(result
);
3933 xpath_ctx
->node
= file_node
;
3939 /* Extract message documents into reallocated list of documents
3940 * @context is ISDS context
3941 * @documents is automatically reallocated message documents list structure
3942 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3943 * In case of error @documents will be freed. */
3944 static isds_error
extract_documents(struct isds_ctx
*context
,
3945 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
3946 isds_error err
= IE_SUCCESS
;
3947 xmlXPathObjectPtr result
= NULL
;
3948 xmlNodePtr files_node
;
3949 struct isds_list
*document
, *prev_document
= NULL
;
3951 if (!context
) return IE_INVALID_CONTEXT
;
3952 if (!documents
) return IE_INVAL
;
3953 isds_list_free(documents
);
3954 if (!xpath_ctx
) return IE_INVAL
;
3955 files_node
= xpath_ctx
->node
;
3957 /* Find documents */
3958 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
3965 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3966 isds_printf_message(context
,
3967 _("Message does not contain any document"));
3973 /* Iterate over documents */
3974 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3976 /* Allocate and append list item */
3977 document
= calloc(1, sizeof(*document
));
3982 document
->destructor
= (void (*)(void **))isds_document_free
;
3983 if (i
== 0) *documents
= document
;
3984 else prev_document
->next
= document
;
3985 prev_document
= document
;
3987 /* Extract document */
3988 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3989 err
= extract_document(context
,
3990 (struct isds_document
**) &(document
->data
), xpath_ctx
);
3991 if (err
) goto leave
;
3996 if (err
) isds_list_free(documents
);
3997 xmlXPathFreeObject(result
);
3998 xpath_ctx
->node
= files_node
;
4004 /* Convert isds:dmRecord XML tree into structure
4005 * @context is ISDS context
4006 * @envelope is automatically reallocated message envelope structure
4007 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4008 * In case of error @envelope will be freed. */
4009 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4010 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4011 isds_error err
= IE_SUCCESS
;
4012 xmlXPathObjectPtr result
= NULL
;
4014 if (!context
) return IE_INVALID_CONTEXT
;
4015 if (!envelope
) return IE_INVAL
;
4016 isds_envelope_free(envelope
);
4017 if (!xpath_ctx
) return IE_INVAL
;
4020 *envelope
= calloc(1, sizeof(**envelope
));
4027 /* Extract tRecord data */
4028 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4030 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4031 * dmAcceptanceTime. */
4032 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4033 if (err
) goto leave
;
4035 /* Extract envelope elements added by sender and ISDS
4036 * (XSD: gMessageEnvelope type) */
4037 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4038 if (err
) goto leave
;
4040 /* Get message type */
4041 err
= append_message_type(context
, envelope
, xpath_ctx
);
4042 if (err
) goto leave
;
4046 if (err
) isds_envelope_free(envelope
);
4047 xmlXPathFreeObject(result
);
4052 /* Convert XSD:tStateChangesRecord type XML tree into structure
4053 * @context is ISDS context
4054 * @changed_status is automatically reallocated message state change structure
4055 * @xpath_ctx is XPath context with current node as element of
4056 * XSD:tStateChangesRecord type
4057 * In case of error @changed_status will be freed. */
4058 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4059 struct isds_message_status_change
**changed_status
,
4060 xmlXPathContextPtr xpath_ctx
) {
4061 isds_error err
= IE_SUCCESS
;
4062 xmlXPathObjectPtr result
= NULL
;
4063 unsigned long int *unumber
= NULL
;
4064 char *string
= NULL
;
4066 if (!context
) return IE_INVALID_CONTEXT
;
4067 if (!changed_status
) return IE_INVAL
;
4068 isds_message_status_change_free(changed_status
);
4069 if (!xpath_ctx
) return IE_INVAL
;
4072 *changed_status
= calloc(1, sizeof(**changed_status
));
4073 if (!*changed_status
) {
4079 /* Extract tGetStateChangesInput data */
4080 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4082 /* dmEventTime is mandatory */
4083 EXTRACT_STRING("isds:dmEventTime", string
);
4085 err
= timestring2timeval((xmlChar
*) string
,
4086 &((*changed_status
)->time
));
4088 char *string_locale
= _isds_utf82locale(string
);
4089 if (err
== IE_DATE
) err
= IE_ISDS
;
4090 isds_printf_message(context
,
4091 _("Could not convert dmEventTime as ISO time: %s"),
4093 free(string_locale
);
4099 /* dmMessageStatus element is mandatory */
4100 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4102 isds_log_message(context
,
4103 _("Missing mandatory isds:dmMessageStatus integer"));
4107 err
= uint2isds_message_status(context
, unumber
,
4108 &((*changed_status
)->dmMessageStatus
));
4110 if (err
== IE_ENUM
) err
= IE_ISDS
;
4119 if (err
) isds_message_status_change_free(changed_status
);
4120 xmlXPathFreeObject(result
);
4123 #endif /* HAVE_LIBCURL */
4126 /* Find and convert isds:dmHash XML tree into structure
4127 * @context is ISDS context
4128 * @envelope is automatically reallocated message hash structure
4129 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4130 * In case of error @hash will be freed. */
4131 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4132 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4133 isds_error err
= IE_SUCCESS
;
4134 xmlNodePtr old_ctx_node
;
4135 xmlXPathObjectPtr result
= NULL
;
4136 char *string
= NULL
;
4138 if (!context
) return IE_INVALID_CONTEXT
;
4139 if (!hash
) return IE_INVAL
;
4140 isds_hash_free(hash
);
4141 if (!xpath_ctx
) return IE_INVAL
;
4143 old_ctx_node
= xpath_ctx
->node
;
4145 *hash
= calloc(1, sizeof(**hash
));
4152 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4153 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4162 /* Get hash algorithm */
4163 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4164 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4166 if (err
== IE_ENUM
) {
4167 char *string_locale
= _isds_utf82locale(string
);
4168 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4170 free(string_locale
);
4176 /* Get hash value */
4177 EXTRACT_STRING(".", string
);
4179 isds_printf_message(context
,
4180 _("sisds:dmHash element is missing hash value"));
4184 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4185 if ((*hash
)->length
== (size_t) -1) {
4186 isds_printf_message(context
,
4187 _("Error while Base64-decoding hash value"));
4193 if (err
) isds_hash_free(hash
);
4195 xmlXPathFreeObject(result
);
4196 xpath_ctx
->node
= old_ctx_node
;
4201 /* Find and append isds:dmQTimestamp XML tree into envelope.
4202 * Because one service is allowed to miss time-stamp content, and we think
4203 * other could too (flaw in specification), this function is deliberated and
4204 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4205 * @context is ISDS context
4206 * @envelope is automatically allocated envelope structure
4207 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4209 * In case of error @envelope will be freed. */
4210 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4211 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4212 isds_error err
= IE_SUCCESS
;
4213 xmlXPathObjectPtr result
= NULL
;
4214 char *string
= NULL
;
4216 if (!context
) return IE_INVALID_CONTEXT
;
4217 if (!envelope
) return IE_INVAL
;
4219 isds_envelope_free(envelope
);
4224 *envelope
= calloc(1, sizeof(**envelope
));
4230 zfree((*envelope
)->timestamp
);
4231 (*envelope
)->timestamp_length
= 0;
4234 /* Get dmQTimestamp */
4235 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4237 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4240 (*envelope
)->timestamp_length
=
4241 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4242 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4243 isds_printf_message(context
,
4244 _("Error while Base64-decoding time stamp value"));
4250 if (err
) isds_envelope_free(envelope
);
4252 xmlXPathFreeObject(result
);
4257 /* Convert XSD tReturnedMessage XML tree into message structure.
4258 * It does not store serialized XML tree into message->raw.
4259 * It does store (pointer to) parsed XML tree into message->xml if needed.
4260 * @context is ISDS context
4261 * @include_documents Use true if documents must be extracted
4262 * (tReturnedMessage XSD type), use false if documents shall be omitted
4263 * (tReturnedMessageEnvelope).
4264 * @message is automatically reallocated message structure
4265 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4267 * In case of error @message will be freed. */
4268 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4269 const _Bool include_documents
, struct isds_message
**message
,
4270 xmlXPathContextPtr xpath_ctx
) {
4271 isds_error err
= IE_SUCCESS
;
4272 xmlNodePtr message_node
;
4274 if (!context
) return IE_INVALID_CONTEXT
;
4275 if (!message
) return IE_INVAL
;
4276 isds_message_free(message
);
4277 if (!xpath_ctx
) return IE_INVAL
;
4280 *message
= calloc(1, sizeof(**message
));
4286 /* Save message XPATH context node */
4287 message_node
= xpath_ctx
->node
;
4291 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4292 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4293 if (err
) { err
= IE_ERROR
; goto leave
; }
4294 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4295 if (err
) goto leave
;
4297 if (include_documents
) {
4298 struct isds_list
*item
;
4300 /* Extract dmFiles */
4301 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4303 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4304 err
= IE_ISDS
; goto leave
;
4306 if (err
) { err
= IE_ERROR
; goto leave
; }
4307 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4308 if (err
) goto leave
;
4310 /* Store xmlDoc of this message if needed */
4311 /* Only if we got a XML document in all the documents. */
4312 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4313 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4314 (*message
)->xml
= xpath_ctx
->doc
;
4321 /* Restore context to message */
4322 xpath_ctx
->node
= message_node
;
4324 /* Extract dmHash */
4325 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4327 if (err
) goto leave
;
4329 /* Extract dmQTimestamp, */
4330 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4332 if (err
) goto leave
;
4334 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4335 * dmAcceptanceTime. */
4336 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4337 if (err
) goto leave
;
4339 /* Get message type */
4340 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4341 if (err
) goto leave
;
4344 if (err
) isds_message_free(message
);
4349 /* Extract message event into reallocated isds_event structure
4350 * @context is ISDS context
4351 * @event is automatically reallocated message event structure
4352 * @xpath_ctx is XPath context with current node as isds:dmEvent
4353 * In case of error @event will be freed. */
4354 static isds_error
extract_event(struct isds_ctx
*context
,
4355 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4356 isds_error err
= IE_SUCCESS
;
4357 xmlXPathObjectPtr result
= NULL
;
4358 xmlNodePtr event_node
;
4359 char *string
= NULL
;
4361 if (!context
) return IE_INVALID_CONTEXT
;
4362 if (!event
) return IE_INVAL
;
4363 isds_event_free(event
);
4364 if (!xpath_ctx
) return IE_INVAL
;
4365 event_node
= xpath_ctx
->node
;
4367 *event
= calloc(1, sizeof(**event
));
4373 /* Extract event data.
4374 * All elements are optional according XSD. That's funny. */
4375 EXTRACT_STRING("sisds:dmEventTime", string
);
4377 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4379 char *string_locale
= _isds_utf82locale(string
);
4380 if (err
== IE_DATE
) err
= IE_ISDS
;
4381 isds_printf_message(context
,
4382 _("Could not convert dmEventTime as ISO time: %s"),
4384 free(string_locale
);
4390 /* dmEventDescr element has prefix and the rest */
4391 EXTRACT_STRING("sisds:dmEventDescr", string
);
4393 err
= eventstring2event((xmlChar
*) string
, *event
);
4394 if (err
) goto leave
;
4399 if (err
) isds_event_free(event
);
4401 xmlXPathFreeObject(result
);
4402 xpath_ctx
->node
= event_node
;
4407 /* Convert element of XSD tEventsArray type from XML tree into
4408 * isds_list of isds_event's structure. The list is automatically reallocated.
4409 * @context is ISDS context
4410 * @events is automatically reallocated list of event structures
4411 * @xpath_ctx is XPath context with current node as tEventsArray
4412 * In case of error @events will be freed. */
4413 static isds_error
extract_events(struct isds_ctx
*context
,
4414 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4415 isds_error err
= IE_SUCCESS
;
4416 xmlXPathObjectPtr result
= NULL
;
4417 xmlNodePtr events_node
;
4418 struct isds_list
*event
, *prev_event
= NULL
;
4420 if (!context
) return IE_INVALID_CONTEXT
;
4421 if (!events
) return IE_INVAL
;
4422 if (!xpath_ctx
) return IE_INVAL
;
4423 events_node
= xpath_ctx
->node
;
4426 isds_list_free(events
);
4429 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4436 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4437 isds_printf_message(context
,
4438 _("Delivery info does not contain any event"));
4444 /* Iterate over events */
4445 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4447 /* Allocate and append list item */
4448 event
= calloc(1, sizeof(*event
));
4453 event
->destructor
= (void (*)(void **))isds_event_free
;
4454 if (i
== 0) *events
= event
;
4455 else prev_event
->next
= event
;
4459 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4460 err
= extract_event(context
,
4461 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4462 if (err
) goto leave
;
4467 if (err
) isds_list_free(events
);
4468 xmlXPathFreeObject(result
);
4469 xpath_ctx
->node
= events_node
;
4475 /* Insert Base64 encoded data as element with text child.
4476 * @context is session context
4477 * @parent is XML node to append @element with @data as child
4478 * @ns is XML namespace of @element, use NULL to inherit from @parent
4479 * @element is UTF-8 encoded name of new element
4480 * @data is bit stream to encode into @element
4481 * @length is size of @data in bytes
4482 * @return standard error code and fill long error message if needed */
4483 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4484 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4485 const void *data
, size_t length
) {
4486 isds_error err
= IE_SUCCESS
;
4489 if (!context
) return IE_INVALID_CONTEXT
;
4490 if (!data
&& length
> 0) return IE_INVAL
;
4491 if (!parent
|| !element
) return IE_INVAL
;
4493 xmlChar
*base64data
= NULL
;
4494 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4496 isds_printf_message(context
,
4497 ngettext("Not enough memory to encode %zd byte into Base64",
4498 "Not enough memory to encode %zd bytes into Base64",
4504 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4512 /* Convert isds_document structure into XML tree and append to dmFiles node.
4513 * @context is session context
4514 * @document is ISDS document
4515 * @dm_files is XML element the resulting tree will be appended to as a child.
4516 * @return error code, in case of error context' message is filled. */
4517 static isds_error
insert_document(struct isds_ctx
*context
,
4518 struct isds_document
*document
, xmlNodePtr dm_files
) {
4519 isds_error err
= IE_SUCCESS
;
4520 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4521 xmlAttrPtr attribute_node
;
4523 if (!context
) return IE_INVALID_CONTEXT
;
4524 if (!document
|| !dm_files
) return IE_INVAL
;
4526 /* Allocate new dmFile */
4527 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4529 isds_printf_message(context
, _("Could not allocate main dmFile"));
4533 /* Append the new dmFile.
4534 * XXX: Main document must go first */
4535 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4536 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4538 file
= xmlAddChild(dm_files
, new_file
);
4541 xmlFreeNode(new_file
); new_file
= NULL
;
4542 isds_printf_message(context
, _("Could not add dmFile child to "
4543 "%s element"), dm_files
->name
);
4548 /* @dmMimeType is required */
4549 if (!document
->dmMimeType
) {
4550 isds_log_message(context
,
4551 _("Document is missing mandatory MIME type definition"));
4555 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4557 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4559 isds_printf_message(context
,
4560 _("Document has unknown dmFileMetaType: %ld"),
4561 document
->dmFileMetaType
);
4565 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4567 if (document
->dmFileGuid
) {
4568 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4570 if (document
->dmUpFileGuid
) {
4571 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4574 /* @dmFileDescr is required */
4575 if (!document
->dmFileDescr
) {
4576 isds_log_message(context
,
4577 _("Document is missing mandatory description (title)"));
4581 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4583 if (document
->dmFormat
) {
4584 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4588 /* Insert content (body) of the document. */
4589 if (document
->is_xml
) {
4590 /* XML document requested */
4592 /* Allocate new dmXMLContent */
4593 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4595 isds_printf_message(context
,
4596 _("Could not allocate dmXMLContent element"));
4601 node
= xmlAddChild(file
, xmlcontent
);
4603 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4604 isds_printf_message(context
,
4605 _("Could not add dmXMLContent child to %s element"),
4611 /* Copy non-empty node list */
4612 if (document
->xml_node_list
) {
4613 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4614 document
->xml_node_list
);
4616 isds_printf_message(context
,
4617 _("Not enough memory to copy XML document"));
4622 if (!xmlAddChildList(node
, content
)) {
4623 xmlFreeNodeList(content
);
4624 isds_printf_message(context
,
4625 _("Error while adding XML document into dmXMLContent"));
4629 /* XXX: We cannot free the content here because it's part of node's
4630 * document since now. It will be freed with it automatically. */
4633 /* Binary document requested */
4634 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4635 document
->data
, document
->data_length
);
4636 if (err
) goto leave
;
4644 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4645 * The copy must be preallocated, the date are just appended into structure.
4646 * @context is ISDS context
4647 * @copy is message copy structure
4648 * @xpath_ctx is XPath context with current node as tMStatus */
4649 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4650 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4651 isds_error err
= IE_SUCCESS
;
4652 xmlXPathObjectPtr result
= NULL
;
4653 char *code
= NULL
, *message
= NULL
;
4655 if (!context
) return IE_INVALID_CONTEXT
;
4656 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4658 /* Free old values */
4659 zfree(copy
->dmStatus
);
4662 /* Get error specific to this copy */
4663 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4665 isds_log_message(context
,
4666 _("Missing isds:dmStatusCode under "
4667 "XSD:tMStatus type element"));
4672 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4673 /* This copy failed */
4674 copy
->error
= IE_ISDS
;
4675 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4677 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4678 if (!copy
->dmStatus
) {
4679 copy
->dmStatus
= code
;
4683 copy
->dmStatus
= code
;
4687 /* This copy succeeded. In this case only, message ID is valid */
4688 copy
->error
= IE_SUCCESS
;
4690 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4692 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4693 "but did not returned assigned message ID\n"));
4701 xmlXPathFreeObject(result
);
4706 /* Insert struct isds_approval data (box approval) into XML tree
4707 * @context is session context
4708 * @approval is libisds structure with approval description. NULL is
4710 * @parent is XML element to append @approval to */
4711 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4712 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4714 isds_error err
= IE_SUCCESS
;
4717 if (!context
) return IE_INVALID_CONTEXT
;
4718 if (!parent
) return IE_INVAL
;
4720 if (!approval
) return IE_SUCCESS
;
4722 /* Build XSD:gExtApproval */
4723 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4724 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4731 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4733 * @context is session context
4734 * @service_name is name of SERVICE_DB_ACCESS
4735 * @response is reallocated server SOAP body response as XML document
4736 * @raw_response is reallocated bit stream with response body. Use
4737 * NULL if you don't care
4738 * @raw_response_length is size of @raw_response in bytes
4739 * @code is reallocated ISDS status code
4740 * @status_message is reallocated ISDS status message
4741 * @return error coded from lower layer, context message will be set up
4743 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4744 const xmlChar
*service_name
,
4745 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4746 xmlChar
**code
, xmlChar
**status_message
) {
4748 isds_error err
= IE_SUCCESS
;
4749 char *service_name_locale
= NULL
;
4750 xmlNodePtr request
= NULL
, node
;
4751 xmlNsPtr isds_ns
= NULL
;
4753 if (!context
) return IE_INVALID_CONTEXT
;
4754 if (!service_name
) return IE_INVAL
;
4755 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4756 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4758 /* Free output argument */
4759 xmlFreeDoc(*response
); *response
= NULL
;
4760 if (raw_response
) zfree(*raw_response
);
4762 zfree(*status_message
);
4765 /* Check if connection is established
4766 * TODO: This check should be done downstairs. */
4767 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4769 service_name_locale
= _isds_utf82locale((char*)service_name
);
4770 if (!service_name_locale
) {
4776 request
= xmlNewNode(NULL
, service_name
);
4778 isds_printf_message(context
,
4779 _("Could not build %s request"), service_name_locale
);
4783 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4785 isds_log_message(context
, _("Could not create ISDS name space"));
4789 xmlSetNs(request
, isds_ns
);
4792 /* Add XSD:tDummyInput child */
4793 INSERT_STRING(request
, "dbDummy", NULL
);
4796 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4797 service_name_locale
);
4800 err
= isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4801 raw_response
, raw_response_length
);
4802 xmlFreeNode(request
); request
= NULL
;
4805 isds_log(ILF_ISDS
, ILL_DEBUG
,
4806 _("Processing ISDS response on %s request failed\n"),
4807 service_name_locale
);
4811 /* Check for response status */
4812 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4813 code
, status_message
, NULL
);
4815 isds_log(ILF_ISDS
, ILL_DEBUG
,
4816 _("ISDS response on %s request is missing status\n"),
4817 service_name_locale
);
4821 /* Request processed, but nothing found */
4822 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4823 char *code_locale
= _isds_utf82locale((char*) *code
);
4824 char *status_message_locale
=
4825 _isds_utf82locale((char*) *status_message
);
4826 isds_log(ILF_ISDS
, ILL_DEBUG
,
4827 _("Server refused %s request (code=%s, message=%s)\n"),
4828 service_name_locale
, code_locale
, status_message_locale
);
4829 isds_log_message(context
, status_message_locale
);
4831 free(status_message_locale
);
4837 free(service_name_locale
);
4838 xmlFreeNode(request
);
4844 /* Get data about logged in user and his box. */
4845 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4846 struct isds_DbOwnerInfo
**db_owner_info
) {
4847 isds_error err
= IE_SUCCESS
;
4849 xmlDocPtr response
= NULL
;
4850 xmlChar
*code
= NULL
, *message
= NULL
;
4851 xmlXPathContextPtr xpath_ctx
= NULL
;
4852 xmlXPathObjectPtr result
= NULL
;
4853 char *string
= NULL
;
4856 if (!context
) return IE_INVALID_CONTEXT
;
4857 zfree(context
->long_message
);
4858 if (!db_owner_info
) return IE_INVAL
;
4859 isds_DbOwnerInfo_free(db_owner_info
);
4862 /* Check if connection is established */
4863 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4866 /* Do request and check for success */
4867 err
= build_send_check_dbdummy_request(context
,
4868 BAD_CAST
"GetOwnerInfoFromLogin",
4869 &response
, NULL
, NULL
, &code
, &message
);
4870 if (err
) goto leave
;
4874 /* Prepare structure */
4875 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4876 if (!*db_owner_info
) {
4880 xpath_ctx
= xmlXPathNewContext(response
);
4885 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4890 /* Set context node */
4891 result
= xmlXPathEvalExpression(BAD_CAST
4892 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4897 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4898 isds_log_message(context
, _("Missing dbOwnerInfo element"));
4902 if (result
->nodesetval
->nodeNr
> 1) {
4903 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
4907 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4908 xmlXPathFreeObject(result
); result
= NULL
;
4911 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
4916 isds_DbOwnerInfo_free(db_owner_info
);
4920 xmlXPathFreeObject(result
);
4921 xmlXPathFreeContext(xpath_ctx
);
4925 xmlFreeDoc(response
);
4928 isds_log(ILF_ISDS
, ILL_DEBUG
,
4929 _("GetOwnerInfoFromLogin request processed by server "
4930 "successfully.\n"));
4931 #else /* not HAVE_LIBCURL */
4939 /* Get data about logged in user. */
4940 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
4941 struct isds_DbUserInfo
**db_user_info
) {
4942 isds_error err
= IE_SUCCESS
;
4944 xmlDocPtr response
= NULL
;
4945 xmlChar
*code
= NULL
, *message
= NULL
;
4946 xmlXPathContextPtr xpath_ctx
= NULL
;
4947 xmlXPathObjectPtr result
= NULL
;
4950 if (!context
) return IE_INVALID_CONTEXT
;
4951 zfree(context
->long_message
);
4952 if (!db_user_info
) return IE_INVAL
;
4953 isds_DbUserInfo_free(db_user_info
);
4956 /* Check if connection is established */
4957 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4960 /* Do request and check for success */
4961 err
= build_send_check_dbdummy_request(context
,
4962 BAD_CAST
"GetUserInfoFromLogin",
4963 &response
, NULL
, NULL
, &code
, &message
);
4964 if (err
) goto leave
;
4968 /* Prepare structure */
4969 *db_user_info
= calloc(1, sizeof(**db_user_info
));
4970 if (!*db_user_info
) {
4974 xpath_ctx
= xmlXPathNewContext(response
);
4979 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4984 /* Set context node */
4985 result
= xmlXPathEvalExpression(BAD_CAST
4986 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
4991 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4992 isds_log_message(context
, _("Missing dbUserInfo element"));
4996 if (result
->nodesetval
->nodeNr
> 1) {
4997 isds_log_message(context
, _("Multiple dbUserInfo element"));
5001 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5002 xmlXPathFreeObject(result
); result
= NULL
;
5005 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5009 isds_DbUserInfo_free(db_user_info
);
5012 xmlXPathFreeObject(result
);
5013 xmlXPathFreeContext(xpath_ctx
);
5017 xmlFreeDoc(response
);
5020 isds_log(ILF_ISDS
, ILL_DEBUG
,
5021 _("GetUserInfoFromLogin request processed by server "
5022 "successfully.\n"));
5023 #else /* not HAVE_LIBCURL */
5031 /* Get expiration time of current password
5032 * @context is session context
5033 * @expiration is automatically reallocated time when password expires. If
5034 * password expiration is disabled, NULL will be returned. In case of error
5035 * it will be nulled too. */
5036 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5037 struct timeval
**expiration
) {
5038 isds_error err
= IE_SUCCESS
;
5040 xmlDocPtr response
= NULL
;
5041 xmlChar
*code
= NULL
, *message
= NULL
;
5042 xmlXPathContextPtr xpath_ctx
= NULL
;
5043 xmlXPathObjectPtr result
= NULL
;
5044 char *string
= NULL
;
5047 if (!context
) return IE_INVALID_CONTEXT
;
5048 zfree(context
->long_message
);
5049 if (!expiration
) return IE_INVAL
;
5053 /* Check if connection is established */
5054 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5057 /* Do request and check for success */
5058 err
= build_send_check_dbdummy_request(context
,
5059 BAD_CAST
"GetPasswordInfo",
5060 &response
, NULL
, NULL
, &code
, &message
);
5061 if (err
) goto leave
;
5065 xpath_ctx
= xmlXPathNewContext(response
);
5070 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5075 /* Set context node */
5076 result
= xmlXPathEvalExpression(BAD_CAST
5077 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5082 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5083 isds_log_message(context
,
5084 _("Missing GetPasswordInfoResponse element"));
5088 if (result
->nodesetval
->nodeNr
> 1) {
5089 isds_log_message(context
,
5090 _("Multiple GetPasswordInfoResponse element"));
5094 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5095 xmlXPathFreeObject(result
); result
= NULL
;
5097 /* Extract expiration date */
5098 EXTRACT_STRING("isds:pswExpDate", string
);
5100 /* And convert it if any returned. Otherwise expiration is disabled. */
5101 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5103 char *string_locale
= _isds_utf82locale(string
);
5104 if (err
== IE_DATE
) err
= IE_ISDS
;
5105 isds_printf_message(context
,
5106 _("Could not convert pswExpDate as ISO time: %s"),
5108 free(string_locale
);
5121 xmlXPathFreeObject(result
);
5122 xmlXPathFreeContext(xpath_ctx
);
5126 xmlFreeDoc(response
);
5129 isds_log(ILF_ISDS
, ILL_DEBUG
,
5130 _("GetPasswordInfo request processed by server "
5131 "successfully.\n"));
5132 #else /* not HAVE_LIBCURL */
5141 /* Request delivering new TOTP code from ISDS through side channel before
5142 * changing password.
5143 * @context is session context
5144 * @password is current password.
5145 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5146 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5147 * function for more details.
5148 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5149 * NULL, if you don't care.
5150 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5152 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5153 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5154 isds_error err
= IE_SUCCESS
;
5155 char *saved_url
= NULL
; /* No copy */
5156 #if HAVE_CURL_REAUTHORIZATION_BUG
5157 CURL
*saved_curl
= NULL
; /* No copy */
5159 xmlNsPtr isds_ns
= NULL
;
5160 xmlNodePtr request
= NULL
;
5161 xmlDocPtr response
= NULL
;
5162 xmlChar
*code
= NULL
, *message
= NULL
;
5163 const xmlChar
*codes
[] = {
5168 const char *meanings
[] = {
5169 N_("Unexpected error"),
5170 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5171 N_("One-time code could not been sent. Try later again.")
5173 const isds_otp_resolution resolutions
[] = {
5174 OTP_RESOLUTION_UNKNOWN
,
5175 OTP_RESOLUTION_TO_FAST
,
5176 OTP_RESOLUTION_TOTP_NOT_SENT
5179 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5180 zfree(context
->long_message
);
5181 if (NULL
== password
) {
5182 isds_log_message(context
,
5183 _("Second argument (password) of isds_change_password() "
5188 /* Check if connection is established
5189 * TODO: This check should be done downstairs. */
5190 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5192 if (!context
->otp
) {
5193 isds_log_message(context
, _("This function requires OTP-authenticated "
5195 return IE_INVALID_CONTEXT
;
5198 isds_log_message(context
, _("If one-time password authentication "
5199 "method is in use, requesting new OTP code requires "
5200 "one-time credentials argument either"));
5203 if (otp
->method
!= OTP_TIME
) {
5204 isds_log_message(context
, _("Requesting new time-based OTP code from "
5205 "server requires one-time password authentication "
5209 if (otp
->otp_code
!= NULL
) {
5210 isds_log_message(context
, _("Requesting new time-based OTP code from "
5211 "server requires undefined OTP code member in "
5212 "one-time credentials argument"));
5218 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5220 isds_log_message(context
, _("Could not build SendSMSCode request"));
5223 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5225 isds_log_message(context
, _("Could not create ISDS name space"));
5226 xmlFreeNode(request
);
5229 xmlSetNs(request
, isds_ns
);
5231 /* Change URL temporarily for sending this request only */
5233 char *new_url
= NULL
;
5234 if ((err
= _isds_build_url_from_context(context
,
5235 "%1$.*2$sasws/changePassword", &new_url
))) {
5238 saved_url
= context
->url
;
5239 context
->url
= new_url
;
5242 /* Store credentials for sending this request only */
5243 context
->otp_credentials
= otp
;
5244 _isds_discard_credentials(context
, 0);
5245 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5247 _isds_discard_credentials(context
, 0);
5250 #if HAVE_CURL_REAUTHORIZATION_BUG
5251 saved_curl
= context
->curl
;
5252 context
->curl
= curl_easy_init();
5253 if (NULL
== context
->curl
) {
5257 if (context
->timeout
) {
5258 err
= isds_set_timeout(context
, context
->timeout
);
5259 if (err
) goto leave
;
5263 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5266 err
= isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5268 /* Remove temporal credentials */
5269 _isds_discard_credentials(context
, 0);
5270 /* Detach pointer to OTP credentials from context */
5271 context
->otp_credentials
= NULL
;
5272 /* Keep context->otp true to keep signaling this is OTP session */
5274 /* Destroy request */
5275 xmlFreeNode(request
); request
= NULL
;
5278 isds_log(ILF_ISDS
, ILL_DEBUG
,
5279 _("Processing ISDS response on SendSMSCode request failed\n"));
5283 /* Check for response status */
5284 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5285 &code
, &message
, (xmlChar
**)refnumber
);
5287 isds_log(ILF_ISDS
, ILL_DEBUG
,
5288 _("ISDS response on SendSMSCode request is missing "
5293 /* Check for error */
5294 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5295 char *code_locale
= _isds_utf82locale((char*)code
);
5296 char *message_locale
= _isds_utf82locale((char*)message
);
5298 isds_log(ILF_ISDS
, ILL_DEBUG
,
5299 _("Server refused to send new code on SendSMSCode "
5300 "request (code=%s, message=%s)\n"),
5301 code_locale
, message_locale
);
5303 /* Check for known error codes */
5304 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5305 if (!xmlStrcmp(code
, codes
[i
])) break;
5307 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5308 isds_log_message(context
, _(meanings
[i
]));
5309 /* Mimic otp->resolution according to the code, specification does
5310 * prescribe OTP header to be available. */
5311 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5312 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5313 otp
->resolution
= resolutions
[i
];
5315 isds_log_message(context
, message_locale
);
5318 free(message_locale
);
5324 /* Otherwise new code sent successfully */
5325 /* Mimic otp->resolution according to the code, specification does
5326 * prescribe OTP header to be available. */
5327 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5328 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5331 if (NULL
!= saved_url
) {
5332 /* Revert URL to original one */
5333 zfree(context
->url
);
5334 context
->url
= saved_url
;
5336 #if HAVE_CURL_REAUTHORIZATION_BUG
5337 if (NULL
!= saved_curl
) {
5338 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5339 context
->curl
= saved_curl
;
5345 xmlFreeDoc(response
);
5346 xmlFreeNode(request
);
5349 isds_log(ILF_ISDS
, ILL_DEBUG
,
5350 _("New OTP code has been sent successfully on SendSMSCode "
5356 /* Convert response status code to isds_error code and set long message
5357 * @context is context to save long message to
5358 * @map is mapping from codes to errors and messages. Pass NULL for generic
5360 * @code is status code to translate
5361 * @message is non-localized status message to put into long message in case
5362 * of uknown error. It can be NULL if server did not provide any.
5363 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5364 * invalid invocation. */
5365 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5366 const struct code_map_isds_error
*map
,
5367 const xmlChar
*code
, const xmlChar
*message
) {
5369 isds_log_message(context
,
5370 _("NULL status code passed to statuscode2isds_error()"));
5375 /* Check for known error codes */
5376 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5377 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5378 isds_log_message(context
, _(map
->meanings
[i
]));
5379 return map
->errors
[i
];
5385 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5386 char *message_locale
= _isds_utf82locale((char*)message
);
5387 if (NULL
== message_locale
)
5388 isds_log_message(context
, _("ISDS server returned unknown error"));
5390 isds_log_message(context
, message_locale
);
5391 free(message_locale
);
5400 /* Change user password in ISDS.
5401 * User must supply old password, new password will takes effect after some
5402 * time, current session can continue. Password must fulfill some constraints.
5403 * @context is session context
5404 * @old_password is current password.
5405 * @new_password is requested new password
5406 * @otp auxiliary data required if one-time password authentication is in use,
5407 * defines OTP code (if known) and returns fine grade resolution of OTP
5408 * procedure. Pass NULL, if one-time password authentication is not needed.
5409 * Please note the @otp argument must match OTP method used at log-in time. See
5410 * isds_login() function for more details.
5411 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5412 * NULL, if you don't care.
5413 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5414 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5415 * awaiting OTP code that has been delivered by side channel to the user. */
5416 isds_error
isds_change_password(struct isds_ctx
*context
,
5417 const char *old_password
, const char *new_password
,
5418 struct isds_otp
*otp
, char **refnumber
) {
5419 isds_error err
= IE_SUCCESS
;
5421 char *saved_url
= NULL
; /* No copy */
5422 #if HAVE_CURL_REAUTHORIZATION_BUG
5423 CURL
*saved_curl
= NULL
; /* No copy */
5425 xmlNsPtr isds_ns
= NULL
;
5426 xmlNodePtr request
= NULL
, node
;
5427 xmlDocPtr response
= NULL
;
5428 xmlChar
*code
= NULL
, *message
= NULL
;
5429 const xmlChar
*codes
[] = {
5442 const char *meanings
[] = {
5443 N_("Password length must be between 8 and 32 characters"),
5444 N_("Password cannot be reused"), /* Server does not distinguish 1067
5445 and 1091 on ChangePasswordOTP */
5446 N_("Password contains forbidden character"),
5447 N_("Password must contain at least one upper-case letter, "
5448 "one lower-case, and one digit"),
5449 N_("Password cannot contain sequence of three identical characters"),
5450 N_("Password cannot contain user identifier"),
5451 N_("Password is too simmple"),
5452 N_("Old password is not valid"),
5453 N_("Password cannot be reused"),
5454 N_("Unexpected error"),
5455 N_("LDAP update error")
5459 if (!context
) return IE_INVALID_CONTEXT
;
5460 zfree(context
->long_message
);
5461 if (NULL
!= refnumber
)
5463 if (NULL
== old_password
) {
5464 isds_log_message(context
,
5465 _("Second argument (old password) of isds_change_password() "
5469 if (NULL
== otp
&& NULL
== new_password
) {
5470 isds_log_message(context
,
5471 _("Third argument (new password) of isds_change_password() "
5477 /* Check if connection is established
5478 * TODO: This check should be done downstairs. */
5479 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5481 if (context
->otp
&& NULL
== otp
) {
5482 isds_log_message(context
, _("If one-time password authentication "
5483 "method is in use, changing password requires one-time "
5484 "credentials either"));
5488 /* Build ChangeISDSPassword request */
5489 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5490 BAD_CAST
"ChangePasswordOTP");
5492 isds_log_message(context
, (NULL
== otp
) ?
5493 _("Could not build ChangeISDSPassword request") :
5494 _("Could not build ChangePasswordOTP request"));
5497 isds_ns
= xmlNewNs(request
,
5498 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5501 isds_log_message(context
, _("Could not create ISDS name space"));
5502 xmlFreeNode(request
);
5505 xmlSetNs(request
, isds_ns
);
5507 INSERT_STRING(request
, "dbOldPassword", old_password
);
5508 INSERT_STRING(request
, "dbNewPassword", new_password
);
5511 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5512 switch (otp
->method
) {
5514 isds_log(ILF_SEC
, ILL_INFO
,
5515 _("Selected authentication method: "
5516 "HMAC-based one-time password\n"));
5517 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5520 isds_log(ILF_SEC
, ILL_INFO
,
5521 _("Selected authentication method: "
5522 "Time-based one-time password\n"));
5523 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5524 if (otp
->otp_code
== NULL
) {
5525 isds_log(ILF_SEC
, ILL_INFO
,
5526 _("OTP code has not been provided by "
5527 "application, requesting server for "
5529 err
= _isds_request_totp_code(context
, old_password
, otp
,
5531 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5535 isds_log(ILF_SEC
, ILL_INFO
,
5536 _("OTP code has been provided by "
5537 "application, not requesting server "
5542 isds_log_message(context
,
5543 _("Unknown one-time password authentication "
5544 "method requested by application"));
5549 /* Change URL temporarily for sending this request only */
5551 char *new_url
= NULL
;
5552 if ((err
= _isds_build_url_from_context(context
,
5553 "%1$.*2$sasws/changePassword", &new_url
))) {
5556 saved_url
= context
->url
;
5557 context
->url
= new_url
;
5560 /* Store credentials for sending this request only */
5561 context
->otp_credentials
= otp
;
5562 _isds_discard_credentials(context
, 0);
5563 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5564 old_password
, NULL
))) {
5565 _isds_discard_credentials(context
, 0);
5568 #if HAVE_CURL_REAUTHORIZATION_BUG
5569 saved_curl
= context
->curl
;
5570 context
->curl
= curl_easy_init();
5571 if (NULL
== context
->curl
) {
5575 if (context
->timeout
) {
5576 err
= isds_set_timeout(context
, context
->timeout
);
5577 if (err
) goto leave
;
5582 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5583 _("Sending ChangeISDSPassword request to ISDS\n") :
5584 _("Sending ChangePasswordOTP request to ISDS\n"));
5587 err
= isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5588 request
, &response
, NULL
, NULL
);
5591 /* Remove temporal credentials */
5592 _isds_discard_credentials(context
, 0);
5593 /* Detach pointer to OTP credentials from context */
5594 context
->otp_credentials
= NULL
;
5595 /* Keep context->otp true to keep signaling this is OTP session */
5598 /* Destroy request */
5599 xmlFreeNode(request
); request
= NULL
;
5602 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5603 _("Processing ISDS response on ChangeISDSPassword "
5604 "request failed\n") :
5605 _("Processing ISDS response on ChangePasswordOTP "
5606 "request failed\n"));
5610 /* Check for response status */
5611 err
= isds_response_status(context
,
5612 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5613 &code
, &message
, (xmlChar
**)refnumber
);
5615 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5616 _("ISDS response on ChangeISDSPassword request is missing "
5618 _("ISDS response on ChangePasswordOTP request is missing "
5623 /* Check for known error codes */
5624 for (int i
=0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5625 if (!xmlStrcmp(code
, codes
[i
])) {
5626 char *code_locale
= _isds_utf82locale((char*)code
);
5627 char *message_locale
= _isds_utf82locale((char*)message
);
5628 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5629 _("Server refused to change password on ChangeISDSPassword "
5630 "request (code=%s, message=%s)\n") :
5631 _("Server refused to change password on ChangePasswordOTP "
5632 "request (code=%s, message=%s)\n"),
5633 code_locale
, message_locale
);
5635 free(message_locale
);
5636 isds_log_message(context
, _(meanings
[i
]));
5643 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5644 char *code_locale
= _isds_utf82locale((char*)code
);
5645 char *message_locale
= _isds_utf82locale((char*)message
);
5646 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5647 _("Server refused to change password on ChangeISDSPassword "
5648 "request (code=%s, message=%s)\n") :
5649 _("Server refused to change password on ChangePasswordOTP "
5650 "request (code=%s, message=%s)\n"),
5651 code_locale
, message_locale
);
5652 isds_log_message(context
, message_locale
);
5654 free(message_locale
);
5659 /* Otherwise password changed successfully */
5662 if (NULL
!= saved_url
) {
5663 /* Revert URL to original one */
5664 zfree(context
->url
);
5665 context
->url
= saved_url
;
5667 #if HAVE_CURL_REAUTHORIZATION_BUG
5668 if (NULL
!= saved_curl
) {
5669 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5670 context
->curl
= saved_curl
;
5676 xmlFreeDoc(response
);
5677 xmlFreeNode(request
);
5680 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5681 _("Password changed successfully on ChangeISDSPassword "
5683 _("Password changed successfully on ChangePasswordOTP "
5685 #else /* not HAVE_LIBCURL */
5694 /* Generic middle part with request sending and response check.
5695 * It sends prepared request and checks for error code.
5696 * @context is ISDS session context.
5697 * @service is ISDS service handler
5698 * @service_name is name in scope of given @service
5699 * @request is XML tree with request. Will be freed to save memory.
5700 * @response is XML document outputting ISDS response.
5701 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5702 * @map is mapping from status code to library error. Pass NULL if no special
5703 * handling is requested.
5704 * NULL, if you don't care. */
5705 static isds_error
send_destroy_request_check_response(
5706 struct isds_ctx
*context
,
5707 const isds_service service
, const xmlChar
*service_name
,
5708 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5709 const struct code_map_isds_error
*map
) {
5710 isds_error err
= IE_SUCCESS
;
5711 char *service_name_locale
= NULL
;
5712 xmlChar
*code
= NULL
, *message
= NULL
;
5715 if (!context
) return IE_INVALID_CONTEXT
;
5716 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5720 /* Check if connection is established
5721 * TODO: This check should be done downstairs. */
5722 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5724 service_name_locale
= _isds_utf82locale((char*) service_name
);
5725 if (!service_name_locale
) {
5730 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5731 service_name_locale
);
5734 err
= isds(context
, service
, *request
, response
, NULL
, NULL
);
5735 xmlFreeNode(*request
); *request
= NULL
;
5738 isds_log(ILF_ISDS
, ILL_DEBUG
,
5739 _("Processing ISDS response on %s request failed\n"),
5740 service_name_locale
);
5744 /* Check for response status */
5745 err
= isds_response_status(context
, service
, *response
,
5746 &code
, &message
, refnumber
);
5748 isds_log(ILF_ISDS
, ILL_DEBUG
,
5749 _("ISDS response on %s request is missing status\n"),
5750 service_name_locale
);
5754 err
= statuscode2isds_error(context
, map
, code
, message
);
5756 /* Request processed, but server failed */
5757 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5758 char *code_locale
= _isds_utf82locale((char*) code
);
5759 char *message_locale
= _isds_utf82locale((char*) message
);
5760 isds_log(ILF_ISDS
, ILL_DEBUG
,
5761 _("Server refused %s request (code=%s, message=%s)\n"),
5762 service_name_locale
, code_locale
, message_locale
);
5764 free(message_locale
);
5772 if (err
&& *response
) {
5773 xmlFreeDoc(*response
);
5777 xmlFreeNode(*request
);
5780 free(service_name_locale
);
5786 /* Generic bottom half with request sending.
5787 * It sends prepared request, checks for error code, destroys response and
5788 * request and log success or failure.
5789 * @context is ISDS session context.
5790 * @service is ISDS service handler
5791 * @service_name is name in scope of given @service
5792 * @request is XML tree with request. Will be freed to save memory.
5793 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5794 * NULL, if you don't care. */
5795 static isds_error
send_request_check_drop_response(
5796 struct isds_ctx
*context
,
5797 const isds_service service
, const xmlChar
*service_name
,
5798 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5799 isds_error err
= IE_SUCCESS
;
5800 xmlDocPtr response
= NULL
;
5803 if (!context
) return IE_INVALID_CONTEXT
;
5804 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5807 /* Send request and check response*/
5808 err
= send_destroy_request_check_response(context
,
5809 service
, service_name
, request
, &response
, refnumber
, NULL
);
5811 xmlFreeDoc(response
);
5814 xmlFreeNode(*request
);
5819 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5820 isds_log(ILF_ISDS
, ILL_DEBUG
,
5821 _("%s request processed by server successfully.\n"),
5822 service_name_locale
);
5823 free(service_name_locale
);
5830 /* Insert isds_credentials_delivery structure into XML request if not NULL
5831 * @context is session context
5832 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5833 * credentials delivery. The email field is passed.
5834 * @parent is XML element where to insert */
5835 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5836 const struct isds_credentials_delivery
*credentials_delivery
,
5837 xmlNodePtr parent
) {
5838 isds_error err
= IE_SUCCESS
;
5841 if (!context
) return IE_INVALID_CONTEXT
;
5842 if (!parent
) return IE_INVAL
;
5844 if (credentials_delivery
) {
5845 /* Following elements are valid only for services:
5846 * NewAccessData, AddDataBoxUser, CreateDataBox */
5847 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5848 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5856 /* Extract credentials delivery from ISDS response.
5857 * @context is session context
5858 * @credentials_delivery is pointer to valid structure to fill in returned
5859 * user's password (and new log-in name). If NULL, do not extract the data.
5860 * @response is pointer to XML document with ISDS response
5861 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5862 * @return IE_SUCCESS even if new user name has not been found because it's not
5863 * clear whether it's returned always. */
5864 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5865 struct isds_credentials_delivery
*credentials_delivery
,
5866 xmlDocPtr response
, const char *request_name
) {
5867 isds_error err
= IE_SUCCESS
;
5868 xmlXPathContextPtr xpath_ctx
= NULL
;
5869 xmlXPathObjectPtr result
= NULL
;
5870 char *xpath_query
= NULL
;
5872 if (!context
) return IE_INVALID_CONTEXT
;
5873 if (credentials_delivery
) {
5874 zfree(credentials_delivery
->token
);
5875 zfree(credentials_delivery
->new_user_name
);
5877 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5880 /* Extract optional token */
5881 if (credentials_delivery
) {
5882 xpath_ctx
= xmlXPathNewContext(response
);
5887 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5892 /* Verify root element */
5893 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5898 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
5903 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5904 char *request_name_locale
= _isds_utf82locale(request_name
);
5905 isds_log(ILF_ISDS
, ILL_WARNING
,
5906 _("Wrong element in ISDS response for %s request "
5907 "while extracting credentials delivery details\n"),
5908 request_name_locale
);
5909 free(request_name_locale
);
5913 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5916 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5918 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
5920 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
5921 if (!credentials_delivery
->token
) {
5922 char *request_name_locale
= _isds_utf82locale(request_name
);
5923 isds_log(ILF_ISDS
, ILL_ERR
,
5924 _("ISDS did not return token on %s request "
5925 "even if requested\n"), request_name_locale
);
5926 free(request_name_locale
);
5933 xmlXPathFreeObject(result
);
5934 xmlXPathFreeContext(xpath_ctx
);
5940 /* Build XSD:tCreateDBInput request type for box creating.
5941 * @context is session context
5942 * @request outputs built XML tree
5943 * @service_name is request name of SERVICE_DB_MANIPULATION service
5944 * @box is box description to create including single primary user (in case of
5946 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5947 * box, or contact address of PFO box owner)
5948 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5949 * @upper_box_id is optional ID of supper box if currently created box is
5951 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5953 * @credentials_delivery is valid pointer if ISDS should return token that box
5954 * owner can use to obtain his new credentials in on-line way. Then valid email
5955 * member value should be supplied.
5956 * @approval is optional external approval of box manipulation */
5957 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
5958 xmlNodePtr
*request
, const xmlChar
*service_name
,
5959 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5960 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
5961 const xmlChar
*ceo_label
,
5962 const struct isds_credentials_delivery
*credentials_delivery
,
5963 const struct isds_approval
*approval
) {
5964 isds_error err
= IE_SUCCESS
;
5965 xmlNsPtr isds_ns
= NULL
;
5966 xmlNodePtr node
, dbPrimaryUsers
;
5967 xmlChar
*string
= NULL
;
5968 const struct isds_list
*item
;
5971 if (!context
) return IE_INVALID_CONTEXT
;
5972 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
5976 /* Build CreateDataBox-similar request */
5977 *request
= xmlNewNode(NULL
, service_name
);
5979 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
5980 isds_printf_message(context
, _("Could build %s request"),
5981 service_name_locale
);
5982 free(service_name_locale
);
5985 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
5986 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
5988 isds_log_message(context
, _("Could not create ISDS1 name space"));
5989 xmlFreeNode(*request
);
5993 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
5995 isds_log_message(context
, _("Could not create ISDS name space"));
5996 xmlFreeNode(*request
);
6000 xmlSetNs(*request
, isds_ns
);
6002 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6003 err
= insert_DbOwnerInfo(context
, box
, node
);
6004 if (err
) goto leave
;
6007 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6008 * verbose documentation allows none dbUserInfo */
6009 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6010 for (item
= users
; item
; item
= item
->next
) {
6012 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6013 err
= insert_DbUserInfo(context
,
6014 (struct isds_DbUserInfo
*) item
->data
, node
);
6015 if (err
) goto leave
;
6019 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6020 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6021 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6023 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6024 if (err
) goto leave
;
6026 err
= insert_GExtApproval(context
, approval
, *request
);
6027 if (err
) goto leave
;
6031 xmlFreeNode(*request
);
6037 #endif /* HAVE_LIBCURL */
6041 * @context is session context
6042 * @box is box description to create including single primary user (in case of
6043 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6044 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6045 * box, or contact address of PFO box owner)
6046 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6047 * @upper_box_id is optional ID of supper box if currently created box is
6049 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6050 * @credentials_delivery is NULL if new password should be delivered off-line
6051 * to box owner. It is valid pointer if owner should obtain new password on-line
6052 * on dedicated web server. Then input @credentials_delivery.email value is
6053 * his e-mail address he must provide to dedicated web server together
6054 * with output reallocated @credentials_delivery.token member. Output
6055 * member @credentials_delivery.new_user_name is unused up on this call.
6056 * @approval is optional external approval of box manipulation
6057 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6058 * NULL, if you don't care.*/
6059 isds_error
isds_add_box(struct isds_ctx
*context
,
6060 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6061 const char *former_names
, const char *upper_box_id
,
6062 const char *ceo_label
,
6063 struct isds_credentials_delivery
*credentials_delivery
,
6064 const struct isds_approval
*approval
, char **refnumber
) {
6065 isds_error err
= IE_SUCCESS
;
6067 xmlNodePtr request
= NULL
;
6068 xmlDocPtr response
= NULL
;
6069 xmlXPathContextPtr xpath_ctx
= NULL
;
6070 xmlXPathObjectPtr result
= NULL
;
6074 if (!context
) return IE_INVALID_CONTEXT
;
6075 zfree(context
->long_message
);
6076 if (credentials_delivery
) {
6077 zfree(credentials_delivery
->token
);
6078 zfree(credentials_delivery
->new_user_name
);
6080 if (!box
) return IE_INVAL
;
6083 /* Scratch box ID */
6086 /* Build CreateDataBox request */
6087 err
= build_CreateDBInput_request(context
,
6088 &request
, BAD_CAST
"CreateDataBox",
6089 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6090 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6091 if (err
) goto leave
;
6093 /* Send it to server and process response */
6094 err
= send_destroy_request_check_response(context
,
6095 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6096 &response
, (xmlChar
**) refnumber
, NULL
);
6098 /* Extract box ID */
6099 xpath_ctx
= xmlXPathNewContext(response
);
6104 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6108 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6110 /* Extract optional token */
6111 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6115 xmlXPathFreeObject(result
);
6116 xmlXPathFreeContext(xpath_ctx
);
6117 xmlFreeDoc(response
);
6118 xmlFreeNode(request
);
6121 isds_log(ILF_ISDS
, ILL_DEBUG
,
6122 _("CreateDataBox request processed by server successfully.\n"));
6124 #else /* not HAVE_LIBCURL */
6132 /* Notify ISDS about new PFO entity.
6133 * This function has no real effect.
6134 * @context is session context
6135 * @box is PFO description including single primary user.
6136 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6137 * @former_names is optional undocumented string. Pass NULL if you don't care.
6138 * @upper_box_id is optional ID of supper box if currently created box is
6140 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6141 * @approval is optional external approval of box manipulation
6142 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6143 * NULL, if you don't care.*/
6144 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6145 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6146 const char *former_names
, const char *upper_box_id
,
6147 const char *ceo_label
, const struct isds_approval
*approval
,
6149 isds_error err
= IE_SUCCESS
;
6151 xmlNodePtr request
= NULL
;
6154 if (!context
) return IE_INVALID_CONTEXT
;
6155 zfree(context
->long_message
);
6156 if (!box
) return IE_INVAL
;
6159 /* Build CreateDataBoxPFOInfo request */
6160 err
= build_CreateDBInput_request(context
,
6161 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6162 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6163 (xmlChar
*) ceo_label
, NULL
, approval
);
6164 if (err
) goto leave
;
6166 /* Send it to server and process response */
6167 err
= send_request_check_drop_response(context
,
6168 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6169 (xmlChar
**) refnumber
);
6170 /* XXX: XML Schema names output dbID element but textual documentation
6171 * states no box identifier is returned. */
6173 xmlFreeNode(request
);
6174 #else /* not HAVE_LIBCURL */
6181 /* Common implementation for removing given box.
6182 * @context is session context
6183 * @service_name is UTF-8 encoded name fo ISDS service
6184 * @box is box description to delete
6185 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6186 * carry sane value. If NULL, do not inject this information into request.
6187 * @approval is optional external approval of box manipulation
6188 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6189 * NULL, if you don't care.*/
6190 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6191 const xmlChar
*service_name
,
6192 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6193 const struct isds_approval
*approval
, char **refnumber
) {
6194 isds_error err
= IE_SUCCESS
;
6196 xmlNsPtr isds_ns
= NULL
;
6197 xmlNodePtr request
= NULL
;
6199 xmlChar
*string
= NULL
;
6203 if (!context
) return IE_INVALID_CONTEXT
;
6204 zfree(context
->long_message
);
6205 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6209 /* Build DeleteDataBox(Promptly) request */
6210 request
= xmlNewNode(NULL
, service_name
);
6212 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6213 isds_printf_message(context
,
6214 _("Could build %s request"), service_name_locale
);
6215 free(service_name_locale
);
6218 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6220 isds_log_message(context
, _("Could not create ISDS name space"));
6221 xmlFreeNode(request
);
6224 xmlSetNs(request
, isds_ns
);
6226 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6227 err
= insert_DbOwnerInfo(context
, box
, node
);
6228 if (err
) goto leave
;
6231 err
= tm2datestring(since
, &string
);
6233 isds_log_message(context
,
6234 _("Could not convert `since' argument to ISO date string"));
6237 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6241 err
= insert_GExtApproval(context
, approval
, request
);
6242 if (err
) goto leave
;
6245 /* Send it to server and process response */
6246 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6247 service_name
, &request
, (xmlChar
**) refnumber
);
6250 xmlFreeNode(request
);
6252 #else /* not HAVE_LIBCURL */
6259 /* Remove given box permanently.
6260 * @context is session context
6261 * @box is box description to delete
6262 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6264 * @approval is optional external approval of box manipulation
6265 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6266 * NULL, if you don't care.*/
6267 isds_error
isds_delete_box(struct isds_ctx
*context
,
6268 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6269 const struct isds_approval
*approval
, char **refnumber
) {
6270 if (!context
) return IE_INVALID_CONTEXT
;
6271 zfree(context
->long_message
);
6272 if (!box
|| !since
) return IE_INVAL
;
6274 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6275 box
, since
, approval
, refnumber
);
6279 /* Undocumented function.
6280 * @context is session context
6281 * @box is box description to delete
6282 * @approval is optional external approval of box manipulation
6283 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6284 * NULL, if you don't care.*/
6285 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6286 const struct isds_DbOwnerInfo
*box
,
6287 const struct isds_approval
*approval
, char **refnumber
) {
6288 if (!context
) return IE_INVALID_CONTEXT
;
6289 zfree(context
->long_message
);
6290 if (!box
) return IE_INVAL
;
6292 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6293 box
, NULL
, approval
, refnumber
);
6297 /* Update data about given box.
6298 * @context is session context
6299 * @old_box current box description
6300 * @new_box are updated data about @old_box
6301 * @approval is optional external approval of box manipulation
6302 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6303 * NULL, if you don't care.*/
6304 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6305 const struct isds_DbOwnerInfo
*old_box
,
6306 const struct isds_DbOwnerInfo
*new_box
,
6307 const struct isds_approval
*approval
, char **refnumber
) {
6308 isds_error err
= IE_SUCCESS
;
6310 xmlNsPtr isds_ns
= NULL
;
6311 xmlNodePtr request
= NULL
;
6316 if (!context
) return IE_INVALID_CONTEXT
;
6317 zfree(context
->long_message
);
6318 if (!old_box
|| !new_box
) return IE_INVAL
;
6322 /* Build UpdateDataBoxDescr request */
6323 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6325 isds_log_message(context
,
6326 _("Could build UpdateDataBoxDescr request"));
6329 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6331 isds_log_message(context
, _("Could not create ISDS name space"));
6332 xmlFreeNode(request
);
6335 xmlSetNs(request
, isds_ns
);
6337 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6338 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6339 if (err
) goto leave
;
6341 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6342 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6343 if (err
) goto leave
;
6345 err
= insert_GExtApproval(context
, approval
, request
);
6346 if (err
) goto leave
;
6349 /* Send it to server and process response */
6350 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6351 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6354 xmlFreeNode(request
);
6355 #else /* not HAVE_LIBCURL */
6364 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6366 * @context is session context
6367 * @service is SOAP service
6368 * @service_name is name of request in @service
6369 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6370 * @box_id is box ID of interest
6371 * @approval is optional external approval of box manipulation
6372 * @response is server SOAP body response as XML document
6373 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6374 * NULL, if you don't care.
6375 * @return error coded from lower layer, context message will be set up
6377 static isds_error
build_send_dbid_request_check_response(
6378 struct isds_ctx
*context
, const isds_service service
,
6379 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6380 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6381 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6383 isds_error err
= IE_SUCCESS
;
6384 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6385 xmlNodePtr request
= NULL
, node
;
6386 xmlNsPtr isds_ns
= NULL
;
6388 if (!context
) return IE_INVALID_CONTEXT
;
6389 if (!service_name
|| !box_id
) return IE_INVAL
;
6390 if (!response
) return IE_INVAL
;
6392 /* Free output argument */
6393 xmlFreeDoc(*response
); *response
= NULL
;
6395 /* Prepare strings */
6396 service_name_locale
= _isds_utf82locale((char*)service_name
);
6397 if (!service_name_locale
) {
6401 box_id_locale
= _isds_utf82locale((char*)box_id
);
6402 if (!box_id_locale
) {
6408 request
= xmlNewNode(NULL
, service_name
);
6410 isds_printf_message(context
,
6411 _("Could not build %s request for %s box"), service_name_locale
,
6416 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6418 isds_log_message(context
, _("Could not create ISDS name space"));
6422 xmlSetNs(request
, isds_ns
);
6424 /* Add XSD:tIdDbInput children */
6425 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6426 INSERT_STRING(request
, box_id_element
, box_id
);
6427 err
= insert_GExtApproval(context
, approval
, request
);
6428 if (err
) goto leave
;
6430 /* Send request and check response*/
6431 err
= send_destroy_request_check_response(context
,
6432 service
, service_name
, &request
, response
, refnumber
, NULL
);
6435 free(service_name_locale
);
6436 free(box_id_locale
);
6437 xmlFreeNode(request
);
6440 #endif /* HAVE_LIBCURL */
6443 /* Get data about all users assigned to given box.
6444 * @context is session context
6446 * @users is automatically reallocated list of struct isds_DbUserInfo */
6447 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6448 struct isds_list
**users
) {
6449 isds_error err
= IE_SUCCESS
;
6451 xmlDocPtr response
= NULL
;
6452 xmlXPathContextPtr xpath_ctx
= NULL
;
6453 xmlXPathObjectPtr result
= NULL
;
6455 struct isds_list
*item
, *prev_item
= NULL
;
6458 if (!context
) return IE_INVALID_CONTEXT
;
6459 zfree(context
->long_message
);
6460 if (!users
|| !box_id
) return IE_INVAL
;
6461 isds_list_free(users
);
6465 /* Do request and check for success */
6466 err
= build_send_dbid_request_check_response(context
,
6467 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6468 BAD_CAST box_id
, NULL
, &response
, NULL
);
6469 if (err
) goto leave
;
6473 /* Prepare structure */
6474 xpath_ctx
= xmlXPathNewContext(response
);
6479 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6484 /* Set context node */
6485 result
= xmlXPathEvalExpression(BAD_CAST
6486 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6492 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6493 /* Iterate over all users */
6494 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6496 /* Prepare structure */
6497 item
= calloc(1, sizeof(*item
));
6502 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6503 if (i
== 0) *users
= item
;
6504 else prev_item
->next
= item
;
6508 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6509 err
= extract_DbUserInfo(context
,
6510 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6511 if (err
) goto leave
;
6517 isds_list_free(users
);
6520 xmlXPathFreeObject(result
);
6521 xmlXPathFreeContext(xpath_ctx
);
6522 xmlFreeDoc(response
);
6525 isds_log(ILF_ISDS
, ILL_DEBUG
,
6526 _("GetDataBoxUsers request processed by server "
6527 "successfully.\n"));
6528 #else /* not HAVE_LIBCURL */
6536 /* Update data about user assigned to given box.
6537 * @context is session context
6538 * @box is box identification
6539 * @old_user identifies user to update
6540 * @new_user are updated data about @old_user
6541 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6542 * NULL, if you don't care.*/
6543 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6544 const struct isds_DbOwnerInfo
*box
,
6545 const struct isds_DbUserInfo
*old_user
,
6546 const struct isds_DbUserInfo
*new_user
,
6548 isds_error err
= IE_SUCCESS
;
6550 xmlNsPtr isds_ns
= NULL
;
6551 xmlNodePtr request
= NULL
;
6556 if (!context
) return IE_INVALID_CONTEXT
;
6557 zfree(context
->long_message
);
6558 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6562 /* Build UpdateDataBoxUser request */
6563 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6565 isds_log_message(context
,
6566 _("Could build UpdateDataBoxUser request"));
6569 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6571 isds_log_message(context
, _("Could not create ISDS name space"));
6572 xmlFreeNode(request
);
6575 xmlSetNs(request
, isds_ns
);
6577 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6578 err
= insert_DbOwnerInfo(context
, box
, node
);
6579 if (err
) goto leave
;
6581 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6582 err
= insert_DbUserInfo(context
, old_user
, node
);
6583 if (err
) goto leave
;
6585 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6586 err
= insert_DbUserInfo(context
, new_user
, node
);
6587 if (err
) goto leave
;
6589 /* Send it to server and process response */
6590 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6591 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6594 xmlFreeNode(request
);
6595 #else /* not HAVE_LIBCURL */
6603 /* Undocumented function.
6604 * @context is session context
6605 * @box_id is UTF-8 encoded box identifier
6606 * @token is UTF-8 encoded temporary password
6607 * @user_id outputs UTF-8 encoded reallocated user identifier
6608 * @password outpus UTF-8 encoded reallocated user password
6609 * Output arguments will be nulled in case of error */
6610 isds_error
isds_activate(struct isds_ctx
*context
,
6611 const char *box_id
, const char *token
,
6612 char **user_id
, char **password
) {
6613 isds_error err
= IE_SUCCESS
;
6615 xmlNsPtr isds_ns
= NULL
;
6616 xmlNodePtr request
= NULL
, node
;
6617 xmlDocPtr response
= NULL
;
6618 xmlXPathContextPtr xpath_ctx
= NULL
;
6619 xmlXPathObjectPtr result
= NULL
;
6623 if (!context
) return IE_INVALID_CONTEXT
;
6624 zfree(context
->long_message
);
6626 if (user_id
) zfree(*user_id
);
6627 if (password
) zfree(*password
);
6629 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6633 /* Build Activate request */
6634 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6636 isds_log_message(context
, _("Could build Activate request"));
6639 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6641 isds_log_message(context
, _("Could not create ISDS name space"));
6642 xmlFreeNode(request
);
6645 xmlSetNs(request
, isds_ns
);
6647 INSERT_STRING(request
, "dbAccessDataId", token
);
6648 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6649 INSERT_STRING(request
, "dbID", box_id
);
6652 /* Send request and check response*/
6653 err
= send_destroy_request_check_response(context
,
6654 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6655 &response
, NULL
, NULL
);
6656 if (err
) goto leave
;
6660 xpath_ctx
= xmlXPathNewContext(response
);
6665 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6669 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6675 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6676 isds_log_message(context
, _("Missing ActivateResponse element"));
6680 if (result
->nodesetval
->nodeNr
> 1) {
6681 isds_log_message(context
, _("Multiple ActivateResponse element"));
6685 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6686 xmlXPathFreeObject(result
); result
= NULL
;
6688 EXTRACT_STRING("isds:userId", *user_id
);
6690 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6691 "but did not return `userId' element.\n"));
6693 EXTRACT_STRING("isds:password", *password
);
6695 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6696 "but did not return `password' element.\n"));
6699 xmlXPathFreeObject(result
);
6700 xmlXPathFreeContext(xpath_ctx
);
6701 xmlFreeDoc(response
);
6702 xmlFreeNode(request
);
6705 isds_log(ILF_ISDS
, ILL_DEBUG
,
6706 _("Activate request processed by server successfully.\n"));
6707 #else /* not HAVE_LIBCURL */
6715 /* Reset credentials of user assigned to given box.
6716 * @context is session context
6717 * @box is box identification
6718 * @user identifies user to reset password
6719 * @fee_paid is true if fee has been paid, false otherwise
6720 * @approval is optional external approval of box manipulation
6721 * @credentials_delivery is NULL if new password should be delivered off-line
6722 * to the user. It is valid pointer if user should obtain new password on-line
6723 * on dedicated web server. Then input @credentials_delivery.email value is
6724 * user's e-mail address user must provide to dedicated web server together
6725 * with @credentials_delivery.token. The output reallocated token user needs
6726 * to use to authorize on the web server to view his new password. Output
6727 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6728 * ISDS changed up on this call. (No reason why server could change the name
6730 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6731 * NULL, if you don't care.*/
6732 isds_error
isds_reset_password(struct isds_ctx
*context
,
6733 const struct isds_DbOwnerInfo
*box
,
6734 const struct isds_DbUserInfo
*user
,
6735 const _Bool fee_paid
, const struct isds_approval
*approval
,
6736 struct isds_credentials_delivery
*credentials_delivery
,
6738 isds_error err
= IE_SUCCESS
;
6740 xmlNsPtr isds_ns
= NULL
;
6741 xmlNodePtr request
= NULL
, node
;
6742 xmlDocPtr response
= NULL
;
6746 if (!context
) return IE_INVALID_CONTEXT
;
6747 zfree(context
->long_message
);
6749 if (credentials_delivery
) {
6750 zfree(credentials_delivery
->token
);
6751 zfree(credentials_delivery
->new_user_name
);
6753 if (!box
|| !user
) return IE_INVAL
;
6757 /* Build NewAccessData request */
6758 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6760 isds_log_message(context
,
6761 _("Could build NewAccessData request"));
6764 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6766 isds_log_message(context
, _("Could not create ISDS name space"));
6767 xmlFreeNode(request
);
6770 xmlSetNs(request
, isds_ns
);
6772 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6773 err
= insert_DbOwnerInfo(context
, box
, node
);
6774 if (err
) goto leave
;
6776 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6777 err
= insert_DbUserInfo(context
, user
, node
);
6778 if (err
) goto leave
;
6780 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6782 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6783 if (err
) goto leave
;
6785 err
= insert_GExtApproval(context
, approval
, request
);
6786 if (err
) goto leave
;
6788 /* Send request and check response*/
6789 err
= send_destroy_request_check_response(context
,
6790 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6791 &response
, (xmlChar
**) refnumber
, NULL
);
6792 if (err
) goto leave
;
6795 /* Extract optional token */
6796 err
= extract_credentials_delivery(context
, credentials_delivery
,
6797 response
, "NewAccessData");
6800 xmlFreeDoc(response
);
6801 xmlFreeNode(request
);
6804 isds_log(ILF_ISDS
, ILL_DEBUG
,
6805 _("NewAccessData request processed by server "
6806 "successfully.\n"));
6807 #else /* not HAVE_LIBCURL */
6815 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6816 * code, destroy response and log success.
6817 * @context is ISDS session context.
6818 * @service_name is name of SERVICE_DB_MANIPULATION service
6819 * @box is box identification
6820 * @user identifies user to remove
6821 * @credentials_delivery is NULL if new user's password should be delivered
6822 * off-line to the user. It is valid pointer if user should obtain new
6823 * password on-line on dedicated web server. Then input
6824 * @credentials_delivery.email value is user's e-mail address user must
6825 * provide to dedicated web server together with @credentials_delivery.token.
6826 * The output reallocated token user needs to use to authorize on the web
6827 * server to view his new password. Output reallocated
6828 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6829 * assingned or changed up on this call.
6830 * @approval is optional external approval of box manipulation
6831 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6832 * NULL, if you don't care. */
6833 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6834 struct isds_ctx
*context
, const xmlChar
*service_name
,
6835 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6836 struct isds_credentials_delivery
*credentials_delivery
,
6837 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6838 isds_error err
= IE_SUCCESS
;
6840 xmlNsPtr isds_ns
= NULL
;
6841 xmlNodePtr request
= NULL
, node
;
6842 xmlDocPtr response
= NULL
;
6846 if (!context
) return IE_INVALID_CONTEXT
;
6847 zfree(context
->long_message
);
6848 if (credentials_delivery
) {
6849 zfree(credentials_delivery
->token
);
6850 zfree(credentials_delivery
->new_user_name
);
6852 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6857 /* Build NewAccessData or similar request */
6858 request
= xmlNewNode(NULL
, service_name
);
6860 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6861 isds_printf_message(context
, _("Could not build %s request"),
6862 service_name_locale
);
6863 free(service_name_locale
);
6866 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6868 isds_log_message(context
, _("Could not create ISDS name space"));
6869 xmlFreeNode(request
);
6872 xmlSetNs(request
, isds_ns
);
6874 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6875 err
= insert_DbOwnerInfo(context
, box
, node
);
6876 if (err
) goto leave
;
6878 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6879 err
= insert_DbUserInfo(context
, user
, node
);
6880 if (err
) goto leave
;
6882 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6883 if (err
) goto leave
;
6885 err
= insert_GExtApproval(context
, approval
, request
);
6886 if (err
) goto leave
;
6889 /* Send request and check response*/
6890 err
= send_destroy_request_check_response(context
,
6891 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6894 xmlFreeNode(request
);
6897 /* Pick up credentials_delivery if requested */
6898 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6899 (char *)service_name
);
6902 xmlFreeDoc(response
);
6903 if (request
) xmlFreeNode(request
);
6906 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6907 isds_log(ILF_ISDS
, ILL_DEBUG
,
6908 _("%s request processed by server successfully.\n"),
6909 service_name_locale
);
6910 free(service_name_locale
);
6912 #else /* not HAVE_LIBCURL */
6920 /* Assign new user to given box.
6921 * @context is session context
6922 * @box is box identification
6923 * @user defines new user to add
6924 * @credentials_delivery is NULL if new user's password should be delivered
6925 * off-line to the user. It is valid pointer if user should obtain new
6926 * password on-line on dedicated web server. Then input
6927 * @credentials_delivery.email value is user's e-mail address user must
6928 * provide to dedicated web server together with @credentials_delivery.token.
6929 * The output reallocated token user needs to use to authorize on the web
6930 * server to view his new password. Output reallocated
6931 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6932 * assingned up on this call.
6933 * @approval is optional external approval of box manipulation
6934 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6935 * NULL, if you don't care.*/
6936 isds_error
isds_add_user(struct isds_ctx
*context
,
6937 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6938 struct isds_credentials_delivery
*credentials_delivery
,
6939 const struct isds_approval
*approval
, char **refnumber
) {
6940 return build_send_manipulationboxuser_request_check_drop_response(context
,
6941 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
6942 approval
, (xmlChar
**) refnumber
);
6946 /* Remove user assigned to given box.
6947 * @context is session context
6948 * @box is box identification
6949 * @user identifies user to remove
6950 * @approval is optional external approval of box manipulation
6951 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6952 * NULL, if you don't care.*/
6953 isds_error
isds_delete_user(struct isds_ctx
*context
,
6954 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6955 const struct isds_approval
*approval
, char **refnumber
) {
6956 return build_send_manipulationboxuser_request_check_drop_response(context
,
6957 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
6958 (xmlChar
**) refnumber
);
6962 /* Get list of boxes in ZIP archive.
6963 * @context is session context
6964 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6965 * System recognizes following values currently: ALL (all boxes), UPG
6966 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6967 * receiving commercial messages). This argument is a string because
6968 * specification states new values can appear in the future. Not all list
6969 * types are available to all users.
6970 * @buffer is automatically reallocated memory to store the list of boxes. The
6971 * list is zipped CSV file.
6972 * @buffer_length is size of @buffer data in bytes.
6973 * In case of error @buffer will be freed and @buffer_length will be
6975 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
6976 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
6977 isds_error err
= IE_SUCCESS
;
6979 xmlNsPtr isds_ns
= NULL
;
6980 xmlNodePtr request
= NULL
, node
;
6981 xmlDocPtr response
= NULL
;
6982 xmlXPathContextPtr xpath_ctx
= NULL
;
6983 xmlXPathObjectPtr result
= NULL
;
6984 char *string
= NULL
;
6988 if (!context
) return IE_INVALID_CONTEXT
;
6989 zfree(context
->long_message
);
6990 if (buffer
) zfree(*buffer
);
6991 if (!buffer
|| !buffer_length
) return IE_INVAL
;
6995 /* Check if connection is established
6996 * TODO: This check should be done downstairs. */
6997 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7000 /* Build AuthenticateMessage request */
7001 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7003 isds_log_message(context
,
7004 _("Could not build GetDataBoxList request"));
7007 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7009 isds_log_message(context
, _("Could not create ISDS name space"));
7010 xmlFreeNode(request
);
7013 xmlSetNs(request
, isds_ns
);
7014 INSERT_STRING(request
, "dblType", list_identifier
);
7016 /* Send request to server and process response */
7017 err
= send_destroy_request_check_response(context
,
7018 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7019 &response
, NULL
, NULL
);
7020 if (err
) goto leave
;
7023 /* Extract Base-64 encoded ZIP file */
7024 xpath_ctx
= xmlXPathNewContext(response
);
7029 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7033 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7035 /* Decode non-empty archive */
7036 if (string
&& string
[0] != '\0') {
7037 *buffer_length
= _isds_b64decode(string
, buffer
);
7038 if (*buffer_length
== (size_t) -1) {
7039 isds_printf_message(context
,
7040 _("Error while Base64-decoding box list archive"));
7049 xmlXPathFreeObject(result
);
7050 xmlXPathFreeContext(xpath_ctx
);
7051 xmlFreeDoc(response
);
7052 xmlFreeNode(request
);
7055 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7056 "processed by server successfully.\n"));
7058 #else /* not HAVE_LIBCURL */
7066 /* Find boxes suiting given criteria.
7067 * @criteria is filter. You should fill in at least some members.
7068 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7069 * possibly empty. Input NULL or valid old structure.
7071 * IE_SUCCESS if search succeeded, @boxes contains useful data
7072 * IE_NOEXIST if no such box exists, @boxes will be NULL
7073 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7074 * contains still valid data
7075 * other code if something bad happens. @boxes will be NULL. */
7076 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7077 const struct isds_DbOwnerInfo
*criteria
,
7078 struct isds_list
**boxes
) {
7079 isds_error err
= IE_SUCCESS
;
7081 _Bool truncated
= 0;
7082 xmlNsPtr isds_ns
= NULL
;
7083 xmlNodePtr request
= NULL
;
7084 xmlDocPtr response
= NULL
;
7085 xmlChar
*code
= NULL
, *message
= NULL
;
7086 xmlNodePtr db_owner_info
;
7087 xmlXPathContextPtr xpath_ctx
= NULL
;
7088 xmlXPathObjectPtr result
= NULL
;
7089 xmlChar
*string
= NULL
;
7093 if (!context
) return IE_INVALID_CONTEXT
;
7094 zfree(context
->long_message
);
7095 if (!boxes
) return IE_INVAL
;
7096 isds_list_free(boxes
);
7103 /* Check if connection is established
7104 * TODO: This check should be done downstairs. */
7105 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7108 /* Build FindDataBox request */
7109 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7111 isds_log_message(context
,
7112 _("Could build FindDataBox request"));
7115 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7117 isds_log_message(context
, _("Could not create ISDS name space"));
7118 xmlFreeNode(request
);
7121 xmlSetNs(request
, isds_ns
);
7122 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7123 if (!db_owner_info
) {
7124 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7125 "FindDataBox element"));
7126 xmlFreeNode(request
);
7130 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7131 if (err
) goto leave
;
7134 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7137 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7139 /* Destroy request */
7140 xmlFreeNode(request
); request
= NULL
;
7143 isds_log(ILF_ISDS
, ILL_DEBUG
,
7144 _("Processing ISDS response on FindDataBox "
7145 "request failed\n"));
7149 /* Check for response status */
7150 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7151 &code
, &message
, NULL
);
7153 isds_log(ILF_ISDS
, ILL_DEBUG
,
7154 _("ISDS response on FindDataBox request is missing status\n"));
7158 /* Request processed, but nothing found */
7159 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7160 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7161 char *code_locale
= _isds_utf82locale((char*)code
);
7162 char *message_locale
= _isds_utf82locale((char*)message
);
7163 isds_log(ILF_ISDS
, ILL_DEBUG
,
7164 _("Server did not found any box on FindDataBox request "
7165 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7166 isds_log_message(context
, message_locale
);
7168 free(message_locale
);
7173 /* Warning, not a error */
7174 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7175 char *code_locale
= _isds_utf82locale((char*)code
);
7176 char *message_locale
= _isds_utf82locale((char*)message
);
7177 isds_log(ILF_ISDS
, ILL_DEBUG
,
7178 _("Server truncated response on FindDataBox request "
7179 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7180 isds_log_message(context
, message_locale
);
7182 free(message_locale
);
7187 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7188 char *code_locale
= _isds_utf82locale((char*)code
);
7189 char *message_locale
= _isds_utf82locale((char*)message
);
7190 isds_log(ILF_ISDS
, ILL_DEBUG
,
7191 _("Server refused FindDataBox request "
7192 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7193 isds_log_message(context
, message_locale
);
7195 free(message_locale
);
7200 xpath_ctx
= xmlXPathNewContext(response
);
7205 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7210 /* Extract boxes if they present */
7211 result
= xmlXPathEvalExpression(BAD_CAST
7212 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7218 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7219 struct isds_list
*item
, *prev_item
= NULL
;
7220 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7221 item
= calloc(1, sizeof(*item
));
7227 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7228 if (i
== 0) *boxes
= item
;
7229 else prev_item
->next
= item
;
7232 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7233 err
= extract_DbOwnerInfo(context
,
7234 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7235 if (err
) goto leave
;
7241 isds_list_free(boxes
);
7243 if (truncated
) err
= IE_2BIG
;
7247 xmlFreeNode(request
);
7248 xmlXPathFreeObject(result
);
7249 xmlXPathFreeContext(xpath_ctx
);
7253 xmlFreeDoc(response
);
7256 isds_log(ILF_ISDS
, ILL_DEBUG
,
7257 _("FindDataBox request processed by server successfully.\n"));
7258 #else /* not HAVE_LIBCURL */
7266 /* Get status of a box.
7267 * @context is ISDS session context.
7268 * @box_id is UTF-8 encoded box identifier as zero terminated string
7269 * @box_status is return value of box status.
7271 * IE_SUCCESS if box has been found and its status retrieved
7272 * IE_NOEXIST if box is not known to ISDS server
7273 * or other appropriate error.
7274 * You can use isds_DbState to enumerate box status. However out of enum
7275 * range value can be returned too. This is feature because ISDS
7276 * specification leaves the set of values open.
7277 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7278 * the box has been deleted, but ISDS still lists its former existence. */
7279 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7280 long int *box_status
) {
7281 isds_error err
= IE_SUCCESS
;
7283 xmlNsPtr isds_ns
= NULL
;
7284 xmlNodePtr request
= NULL
, db_id
;
7285 xmlDocPtr response
= NULL
;
7286 xmlXPathContextPtr xpath_ctx
= NULL
;
7287 xmlXPathObjectPtr result
= NULL
;
7288 xmlChar
*string
= NULL
;
7290 const xmlChar
*codes
[] = {
7296 const char *meanings
[] = {
7297 "The box does not exist",
7298 "Box ID is malformed",
7301 const isds_error errors
[] = {
7306 struct code_map_isds_error map
= {
7308 .meanings
= meanings
,
7313 if (!context
) return IE_INVALID_CONTEXT
;
7314 zfree(context
->long_message
);
7315 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7318 /* Check if connection is established
7319 * TODO: This check should be done downstairs. */
7320 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7323 /* Build CheckDataBox request */
7324 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7326 isds_log_message(context
,
7327 _("Could build CheckDataBox request"));
7330 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7332 isds_log_message(context
, _("Could not create ISDS name space"));
7333 xmlFreeNode(request
);
7336 xmlSetNs(request
, isds_ns
);
7337 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7339 isds_log_message(context
, _("Could not add dbID child to "
7340 "CheckDataBox element"));
7341 xmlFreeNode(request
);
7346 /* Send request and check response*/
7347 err
= send_destroy_request_check_response(context
,
7348 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7349 &request
, &response
, NULL
, &map
);
7350 if (err
) goto leave
;
7354 xpath_ctx
= xmlXPathNewContext(response
);
7359 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7363 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7369 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7370 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7374 if (result
->nodesetval
->nodeNr
> 1) {
7375 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7379 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7380 xmlXPathFreeObject(result
); result
= NULL
;
7382 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7387 xmlXPathFreeObject(result
);
7388 xmlXPathFreeContext(xpath_ctx
);
7390 xmlFreeDoc(response
);
7393 isds_log(ILF_ISDS
, ILL_DEBUG
,
7394 _("CheckDataBox request processed by server successfully.\n"));
7395 #else /* not HAVE_LIBCURL */
7403 /* Get list of permissions to send commercial messages.
7404 * @context is ISDS session context.
7405 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7406 * @permissions is a reallocated list of permissions (struct
7407 * isds_commercial_permission*) to send commercial messages from @box_id. The
7408 * order of permissions is significant as the server applies the permissions
7409 * and associated pre-paid credits in the order. Empty list means no
7412 * IE_SUCCESS if the list has been obtained correctly,
7413 * or other appropriate error. */
7414 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7415 const char *box_id
, struct isds_list
**permissions
) {
7416 isds_error err
= IE_SUCCESS
;
7418 xmlDocPtr response
= NULL
;
7419 xmlXPathContextPtr xpath_ctx
= NULL
;
7420 xmlXPathObjectPtr result
= NULL
;
7423 if (!context
) return IE_INVALID_CONTEXT
;
7424 zfree(context
->long_message
);
7425 if (NULL
== permissions
) return IE_INVAL
;
7426 isds_list_free(permissions
);
7427 if (NULL
== box_id
) return IE_INVAL
;
7430 /* Check if connection is established */
7431 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7433 /* Do request and check for success */
7434 err
= build_send_dbid_request_check_response(context
,
7435 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7436 BAD_CAST box_id
, NULL
, &response
, NULL
);
7438 isds_log(ILF_ISDS
, ILL_DEBUG
,
7439 _("PDZInfo request processed by server successfully.\n"));
7443 /* Prepare structure */
7444 xpath_ctx
= xmlXPathNewContext(response
);
7449 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7454 /* Set context node */
7455 result
= xmlXPathEvalExpression(BAD_CAST
7456 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7462 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7463 struct isds_list
*prev_item
= NULL
;
7465 /* Iterate over all permission records */
7466 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7467 struct isds_list
*item
;
7469 /* Prepare structure */
7470 item
= calloc(1, sizeof(*item
));
7475 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
7476 if (i
== 0) *permissions
= item
;
7477 else prev_item
->next
= item
;
7481 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7482 err
= extract_DbPDZRecord(context
,
7483 (struct isds_commercial_permission
**) (&item
->data
),
7485 if (err
) goto leave
;
7491 isds_list_free(permissions
);
7494 xmlXPathFreeObject(result
);
7495 xmlXPathFreeContext(xpath_ctx
);
7496 xmlFreeDoc(response
);
7498 #else /* not HAVE_LIBCURL */
7506 /* Get details about credit for sending pre-paid commercial messages.
7507 * @context is ISDS session context.
7508 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7509 * @from_date is first day of credit history to return in @history. Only
7510 * tm_year, tm_mon and tm_mday carry sane value.
7511 * @to_date is last day of credit history to return in @history. Only
7512 * tm_year, tm_mon and tm_mday carry sane value.
7513 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7514 * if you don't care. This and all other credit values are integers in
7515 * hundredths of Czech Crowns.
7516 * @email outputs notification e-mail address where notifications about credit
7517 * are sent. This is automatically reallocated string. Pass NULL if you don't
7518 * care. It can return NULL if no address is defined.
7519 * @history outputs auto-reallocated list of pointers to struct
7520 * isds_credit_event. Events in closed interval @from_time to @to_time are
7521 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7522 * are sorted by time.
7524 * IE_SUCCESS if the credit details have been obtained correctly,
7525 * or other appropriate error. Please note that server allows to retrieve
7526 * only limited history of events. */
7527 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
7529 const struct tm
*from_date
, const struct tm
*to_date
,
7530 long int *credit
, char **email
, struct isds_list
**history
) {
7531 isds_error err
= IE_SUCCESS
;
7533 char *box_id_locale
= NULL
;
7534 xmlNodePtr request
= NULL
, node
;
7535 xmlNsPtr isds_ns
= NULL
;
7536 xmlChar
*string
= NULL
;
7538 xmlDocPtr response
= NULL
;
7539 xmlXPathContextPtr xpath_ctx
= NULL
;
7540 xmlXPathObjectPtr result
= NULL
;
7542 const xmlChar
*codes
[] = {
7550 const char *meanings
[] = {
7551 "Insufficient priviledges for the box",
7552 "The box does not exist",
7553 "Date is too long (history is not available after 15 months)",
7554 "Interval is too long (limit is 3 months)",
7557 const isds_error errors
[] = {
7564 struct code_map_isds_error map
= {
7566 .meanings
= meanings
,
7571 if (!context
) return IE_INVALID_CONTEXT
;
7572 zfree(context
->long_message
);
7574 /* Free output argument */
7575 if (NULL
!= credit
) *credit
= 0;
7576 if (NULL
!= email
) zfree(*email
);
7577 isds_list_free(history
);
7579 if (NULL
== box_id
) return IE_INVAL
;
7582 /* Check if connection is established */
7583 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7585 box_id_locale
= _isds_utf82locale((char*)box_id
);
7586 if (NULL
== box_id_locale
) {
7592 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
7593 if (NULL
== request
) {
7594 isds_printf_message(context
,
7595 _("Could not build DataBoxCreditInfo request for %s box"),
7600 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7602 isds_log_message(context
, _("Could not create ISDS name space"));
7606 xmlSetNs(request
, isds_ns
);
7608 /* Add mandatory XSD:tIdDbInput child */
7609 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
7610 /* Add mandatory dates elements with optional values */
7612 err
= tm2datestring(from_date
, &string
);
7614 isds_log_message(context
,
7615 _("Could not convert `from_date' argument to ISO date "
7619 INSERT_STRING(request
, "ciFromDate", string
);
7622 INSERT_STRING(request
, "ciFromDate", NULL
);
7625 err
= tm2datestring(to_date
, &string
);
7627 isds_log_message(context
,
7628 _("Could not convert `to_date' argument to ISO date "
7632 INSERT_STRING(request
, "ciTodate", string
);
7635 INSERT_STRING(request
, "ciTodate", NULL
);
7638 /* Send request and check response*/
7639 err
= send_destroy_request_check_response(context
,
7640 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
7641 &request
, &response
, NULL
, &map
);
7642 if (err
) goto leave
;
7646 /* Set context to the root */
7647 xpath_ctx
= xmlXPathNewContext(response
);
7652 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7656 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
7662 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7663 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
7667 if (result
->nodesetval
->nodeNr
> 1) {
7668 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
7672 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7673 xmlXPathFreeObject(result
); result
= NULL
;
7675 /* Extract common data */
7676 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
7677 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
7679 /* Extract records */
7680 if (NULL
== history
) goto leave
;
7681 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
7687 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7688 struct isds_list
*prev_item
= NULL
;
7690 /* Iterate over all records */
7691 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7692 struct isds_list
*item
;
7694 /* Prepare structure */
7695 item
= calloc(1, sizeof(*item
));
7700 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
7701 if (i
== 0) *history
= item
;
7702 else prev_item
->next
= item
;
7706 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7707 err
= extract_CiRecord(context
,
7708 (struct isds_credit_event
**) (&item
->data
),
7710 if (err
) goto leave
;
7716 isds_log(ILF_ISDS
, ILL_DEBUG
,
7717 _("DataBoxCreditInfo request processed by server successfully.\n"));
7720 isds_list_free(history
);
7721 if (NULL
!= email
) zfree(*email
)
7724 free(box_id_locale
);
7725 xmlXPathFreeObject(result
);
7726 xmlXPathFreeContext(xpath_ctx
);
7727 xmlFreeDoc(response
);
7729 #else /* not HAVE_LIBCURL */
7737 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7738 * code, destroy response and log success.
7739 * @context is ISDS session context.
7740 * @service_name is name of SERVICE_DB_MANIPULATION service
7741 * @box_id is UTF-8 encoded box identifier as zero terminated string
7742 * @approval is optional external approval of box manipulation
7743 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7744 * NULL, if you don't care. */
7745 static isds_error
build_send_manipulationdbid_request_check_drop_response(
7746 struct isds_ctx
*context
, const xmlChar
*service_name
,
7747 const xmlChar
*box_id
, const struct isds_approval
*approval
,
7748 xmlChar
**refnumber
) {
7749 isds_error err
= IE_SUCCESS
;
7751 xmlDocPtr response
= NULL
;
7754 if (!context
) return IE_INVALID_CONTEXT
;
7755 zfree(context
->long_message
);
7756 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
7759 /* Check if connection is established */
7760 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7762 /* Do request and check for success */
7763 err
= build_send_dbid_request_check_response(context
,
7764 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
7765 &response
, refnumber
);
7766 xmlFreeDoc(response
);
7769 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7770 isds_log(ILF_ISDS
, ILL_DEBUG
,
7771 _("%s request processed by server successfully.\n"),
7772 service_name_locale
);
7773 free(service_name_locale
);
7775 #else /* not HAVE_LIBCURL */
7783 /* Switch box into state where box can receive commercial messages (off by
7785 * @context is ISDS session context.
7786 * @box_id is UTF-8 encoded box identifier as zero terminated string
7787 * @allow is true for enable, false for disable commercial messages income
7788 * @approval is optional external approval of box manipulation
7789 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7790 * NULL, if you don't care. */
7791 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
7792 const char *box_id
, const _Bool allow
,
7793 const struct isds_approval
*approval
, char **refnumber
) {
7794 return build_send_manipulationdbid_request_check_drop_response(context
,
7795 (allow
) ? BAD_CAST
"SetOpenAddressing" :
7796 BAD_CAST
"ClearOpenAddressing",
7797 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7801 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7802 * message acceptance). This is just a box permission. Sender must apply
7803 * such role by sending each message.
7804 * @context is ISDS session context.
7805 * @box_id is UTF-8 encoded box identifier as zero terminated string
7806 * @allow is true for enable, false for disable OVM role permission
7807 * @approval is optional external approval of box manipulation
7808 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7809 * NULL, if you don't care. */
7810 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
7811 const char *box_id
, const _Bool allow
,
7812 const struct isds_approval
*approval
, char **refnumber
) {
7813 return build_send_manipulationdbid_request_check_drop_response(context
,
7814 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
7815 BAD_CAST
"ClearEffectiveOVM",
7816 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7820 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7821 * code, destroy response and log success.
7822 * @context is ISDS session context.
7823 * @service_name is name of SERVICE_DB_MANIPULATION service
7824 * @owner is structure describing box
7825 * @approval is optional external approval of box manipulation
7826 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7827 * NULL, if you don't care. */
7828 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
7829 struct isds_ctx
*context
, const xmlChar
*service_name
,
7830 const struct isds_DbOwnerInfo
*owner
,
7831 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7832 isds_error err
= IE_SUCCESS
;
7834 char *service_name_locale
= NULL
;
7835 xmlNodePtr request
= NULL
, db_owner_info
;
7836 xmlNsPtr isds_ns
= NULL
;
7840 if (!context
) return IE_INVALID_CONTEXT
;
7841 zfree(context
->long_message
);
7842 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
7845 service_name_locale
= _isds_utf82locale((char*)service_name
);
7846 if (!service_name_locale
) {
7852 request
= xmlNewNode(NULL
, service_name
);
7854 isds_printf_message(context
,
7855 _("Could not build %s request"), service_name_locale
);
7859 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7861 isds_log_message(context
, _("Could not create ISDS name space"));
7865 xmlSetNs(request
, isds_ns
);
7868 /* Add XSD:tOwnerInfoInput child*/
7869 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
7870 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
7871 if (err
) goto leave
;
7873 /* Add XSD:gExtApproval*/
7874 err
= insert_GExtApproval(context
, approval
, request
);
7875 if (err
) goto leave
;
7877 /* Send it to server and process response */
7878 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7879 service_name
, &request
, refnumber
);
7882 xmlFreeNode(request
);
7883 free(service_name_locale
);
7884 #else /* not HAVE_LIBCURL */
7892 /* Switch box accessibility state on request of box owner.
7893 * Despite the name, owner must do the request off-line. This function is
7894 * designed for such off-line meeting points (e.g. Czech POINT).
7895 * @context is ISDS session context.
7896 * @box identifies box to switch accessibility state.
7897 * @allow is true for making accessible, false to disallow access.
7898 * @approval is optional external approval of box manipulation
7899 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7900 * NULL, if you don't care. */
7901 isds_error
isds_switch_box_accessibility_on_owner_request(
7902 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7903 const _Bool allow
, const struct isds_approval
*approval
,
7905 return build_send_manipulationdbowner_request_check_drop_response(context
,
7906 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
7907 BAD_CAST
"DisableOwnDataBox",
7908 box
, approval
, (xmlChar
**) refnumber
);
7912 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7914 * @context is ISDS session context.
7915 * @box identifies box to switch accessibility state.
7916 * @since is date since accessibility has been denied. This can be past too.
7917 * Only tm_year, tm_mon and tm_mday carry sane value.
7918 * @approval is optional external approval of box manipulation
7919 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7920 * NULL, if you don't care. */
7921 isds_error
isds_disable_box_accessibility_externaly(
7922 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7923 const struct tm
*since
, const struct isds_approval
*approval
,
7925 isds_error err
= IE_SUCCESS
;
7927 char *service_name_locale
= NULL
;
7928 xmlNodePtr request
= NULL
, node
;
7929 xmlNsPtr isds_ns
= NULL
;
7930 xmlChar
*string
= NULL
;
7934 if (!context
) return IE_INVALID_CONTEXT
;
7935 zfree(context
->long_message
);
7936 if (!box
|| !since
) return IE_INVAL
;
7940 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
7942 isds_printf_message(context
,
7943 _("Could not build %s request"), "DisableDataBoxExternally");
7947 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7949 isds_log_message(context
, _("Could not create ISDS name space"));
7953 xmlSetNs(request
, isds_ns
);
7956 /* Add @box identification */
7957 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7958 err
= insert_DbOwnerInfo(context
, box
, node
);
7959 if (err
) goto leave
;
7961 /* Add @since date */
7962 err
= tm2datestring(since
, &string
);
7964 isds_log_message(context
,
7965 _("Could not convert `since' argument to ISO date string"));
7968 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
7972 err
= insert_GExtApproval(context
, approval
, request
);
7973 if (err
) goto leave
;
7975 /* Send it to server and process response */
7976 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7977 BAD_CAST
"DisableDataBoxExternally", &request
,
7978 (xmlChar
**) refnumber
);
7982 xmlFreeNode(request
);
7983 free(service_name_locale
);
7984 #else /* not HAVE_LIBCURL */
7993 /* Insert struct isds_message data (envelope (recipient data optional) and
7994 * documents into XML tree
7995 * @context is session context
7996 * @outgoing_message is libisds structure with message data
7997 * @create_message is XML CreateMessage or CreateMultipleMessage element
7998 * @process_recipient true for recipient data serialization, false for no
8000 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8001 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8002 const _Bool process_recipient
) {
8004 isds_error err
= IE_SUCCESS
;
8005 xmlNodePtr envelope
, dm_files
, node
;
8006 xmlChar
*string
= NULL
;
8008 if (!context
) return IE_INVALID_CONTEXT
;
8009 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8012 /* Build envelope */
8013 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8015 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8016 "%s element"), create_message
->name
);
8020 if (!outgoing_message
->envelope
) {
8021 isds_log_message(context
, _("Outgoing message is missing envelope"));
8026 /* Insert optional message type */
8027 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8029 if (err
) goto leave
;
8031 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8032 outgoing_message
->envelope
->dmSenderOrgUnit
);
8033 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8034 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8036 if (process_recipient
) {
8037 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8038 isds_log_message(context
,
8039 _("Outgoing message is missing recipient box identifier"));
8043 INSERT_STRING(envelope
, "dbIDRecipient",
8044 outgoing_message
->envelope
->dbIDRecipient
);
8046 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8047 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8048 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8049 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8050 INSERT_STRING(envelope
, "dmToHands",
8051 outgoing_message
->envelope
->dmToHands
);
8054 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8056 INSERT_STRING(envelope
, "dmAnnotation",
8057 outgoing_message
->envelope
->dmAnnotation
);
8059 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8060 0, 50, "dmRecipientRefNumber");
8061 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8062 outgoing_message
->envelope
->dmRecipientRefNumber
);
8064 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8065 0, 50, "dmSenderRefNumber");
8066 INSERT_STRING(envelope
, "dmSenderRefNumber",
8067 outgoing_message
->envelope
->dmSenderRefNumber
);
8069 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8070 0, 50, "dmRecipientIdent");
8071 INSERT_STRING(envelope
, "dmRecipientIdent",
8072 outgoing_message
->envelope
->dmRecipientIdent
);
8074 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8075 0, 50, "dmSenderIdent");
8076 INSERT_STRING(envelope
, "dmSenderIdent",
8077 outgoing_message
->envelope
->dmSenderIdent
);
8079 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8080 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8081 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8082 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8083 INSERT_STRING(envelope
, "dmLegalTitleSect",
8084 outgoing_message
->envelope
->dmLegalTitleSect
);
8085 INSERT_STRING(envelope
, "dmLegalTitlePar",
8086 outgoing_message
->envelope
->dmLegalTitlePar
);
8087 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8088 outgoing_message
->envelope
->dmLegalTitlePoint
);
8090 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8091 outgoing_message
->envelope
->dmPersonalDelivery
);
8092 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8093 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8095 /* ???: Should we require value for dbEffectiveOVM sender?
8096 * ISDS has default as true */
8097 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8098 INSERT_BOOLEAN(envelope
, "dmOVM",
8099 outgoing_message
->envelope
->dmPublishOwnID
);
8102 /* Append dmFiles */
8103 if (!outgoing_message
->documents
) {
8104 isds_log_message(context
,
8105 _("Outgoing message is missing list of documents"));
8109 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8111 isds_printf_message(context
, _("Could not add dmFiles child to "
8112 "%s element"), create_message
->name
);
8117 /* Check for document hierarchy */
8118 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8119 if (err
) goto leave
;
8121 /* Process each document */
8122 for (struct isds_list
*item
=
8123 (struct isds_list
*) outgoing_message
->documents
;
8124 item
; item
= item
->next
) {
8126 isds_log_message(context
,
8127 _("List of documents contains empty item"));
8131 /* FIXME: Check for dmFileMetaType and for document references.
8132 * Only first document can be of MAIN type */
8133 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8136 if (err
) goto leave
;
8143 #endif /* HAVE_LIBCURL */
8146 /* Send a message via ISDS to a recipient
8147 * @context is session context
8148 * @outgoing_message is message to send; Some members are mandatory (like
8149 * dbIDRecipient), some are optional and some are irrelevant (especially data
8150 * about sender). Included pointer to isds_list documents must contain at
8151 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8152 * members will be filled with valid data from ISDS. Exact list of write
8153 * members is subject to change. Currently dmID is changed.
8154 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8155 isds_error
isds_send_message(struct isds_ctx
*context
,
8156 struct isds_message
*outgoing_message
) {
8158 isds_error err
= IE_SUCCESS
;
8160 xmlNsPtr isds_ns
= NULL
;
8161 xmlNodePtr request
= NULL
;
8162 xmlDocPtr response
= NULL
;
8163 xmlChar
*code
= NULL
, *message
= NULL
;
8164 xmlXPathContextPtr xpath_ctx
= NULL
;
8165 xmlXPathObjectPtr result
= NULL
;
8166 /*_Bool message_is_complete = 0;*/
8169 if (!context
) return IE_INVALID_CONTEXT
;
8170 zfree(context
->long_message
);
8171 if (!outgoing_message
) return IE_INVAL
;
8174 /* Check if connection is established
8175 * TODO: This check should be done downstairs. */
8176 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8179 /* Build CreateMessage request */
8180 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8182 isds_log_message(context
,
8183 _("Could not build CreateMessage request"));
8186 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8188 isds_log_message(context
, _("Could not create ISDS name space"));
8189 xmlFreeNode(request
);
8192 xmlSetNs(request
, isds_ns
);
8194 /* Append envelope and files */
8195 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8196 if (err
) goto leave
;
8199 /* Signal we can serialize message since now */
8200 /*message_is_complete = 1;*/
8203 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8206 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8208 /* Don't' destroy request, we want to provide it to application later */
8211 isds_log(ILF_ISDS
, ILL_DEBUG
,
8212 _("Processing ISDS response on CreateMessage "
8213 "request failed\n"));
8217 /* Check for response status */
8218 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8219 &code
, &message
, NULL
);
8221 isds_log(ILF_ISDS
, ILL_DEBUG
,
8222 _("ISDS response on CreateMessage request "
8223 "is missing status\n"));
8227 /* Request processed, but refused by server or server failed */
8228 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8229 char *box_id_locale
=
8230 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8231 char *code_locale
= _isds_utf82locale((char*)code
);
8232 char *message_locale
= _isds_utf82locale((char*)message
);
8233 isds_log(ILF_ISDS
, ILL_DEBUG
,
8234 _("Server did not accept message for %s on CreateMessage "
8235 "request (code=%s, message=%s)\n"),
8236 box_id_locale
, code_locale
, message_locale
);
8237 isds_log_message(context
, message_locale
);
8238 free(box_id_locale
);
8240 free(message_locale
);
8247 xpath_ctx
= xmlXPathNewContext(response
);
8252 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8256 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8262 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8263 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8267 if (result
->nodesetval
->nodeNr
> 1) {
8268 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8272 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8273 xmlXPathFreeObject(result
); result
= NULL
;
8275 if (outgoing_message
->envelope
->dmID
) {
8276 free(outgoing_message
->envelope
->dmID
);
8277 outgoing_message
->envelope
->dmID
= NULL
;
8279 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8280 if (!outgoing_message
->envelope
->dmID
) {
8281 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8282 "but did not return assigned message ID\n"));
8286 /* TODO: Serialize message into structure member raw */
8287 /* XXX: Each web service transport message in different format.
8288 * Therefore it's not possible to save them directly.
8289 * To save them, one must figure out common format.
8290 * We can leave it on application, or we can implement the ESS format. */
8291 /*if (message_is_complete) {
8292 if (outgoing_message->envelope->dmID) {
8294 /* Add assigned message ID as first child*/
8295 /*xmlNodePtr dmid_text = xmlNewText(
8296 (xmlChar *) outgoing_message->envelope->dmID);
8297 if (!dmid_text) goto serialization_failed;
8299 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8301 if (!dmid_element) {
8302 xmlFreeNode(dmid_text);
8303 goto serialization_failed;
8306 xmlNodePtr dmid_element_with_text =
8307 xmlAddChild(dmid_element, dmid_text);
8308 if (!dmid_element_with_text) {
8309 xmlFreeNode(dmid_element);
8310 xmlFreeNode(dmid_text);
8311 goto serialization_failed;
8314 node = xmlAddPrevSibling(envelope->childern,
8315 dmid_element_with_text);
8317 xmlFreeNodeList(dmid_element_with_text);
8318 goto serialization_failed;
8322 /* Serialize message with ID into raw */
8323 /*buffer = serialize_element(envelope)*/
8326 serialization_failed:
8331 xmlXPathFreeObject(result
);
8332 xmlXPathFreeContext(xpath_ctx
);
8336 xmlFreeDoc(response
);
8337 xmlFreeNode(request
);
8340 isds_log(ILF_ISDS
, ILL_DEBUG
,
8341 _("CreateMessage request processed by server "
8342 "successfully.\n"));
8343 #else /* not HAVE_LIBCURL */
8351 /* Send a message via ISDS to a multiple recipients
8352 * @context is session context
8353 * @outgoing_message is message to send; Some members are mandatory,
8354 * some are optional and some are irrelevant (especially data
8355 * about sender). Data about recipient will be substituted by ISDS from
8356 * @copies. Included pointer to isds_list documents must
8357 * contain at least one document of FILEMETATYPE_MAIN.
8358 * @copies is list of isds_message_copy structures addressing all desired
8359 * recipients. This is read-write structure, some members will be filled with
8360 * valid data from ISDS (message IDs, error codes, error descriptions).
8362 * ISDS_SUCCESS if all messages have been sent
8363 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8364 * succeeded messages can be identified by copies->data->error),
8365 * or other error code if something other goes wrong. */
8366 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8367 const struct isds_message
*outgoing_message
,
8368 struct isds_list
*copies
) {
8370 isds_error err
= IE_SUCCESS
;
8372 isds_error append_err
;
8373 xmlNsPtr isds_ns
= NULL
;
8374 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8375 struct isds_list
*item
;
8376 struct isds_message_copy
*copy
;
8377 xmlDocPtr response
= NULL
;
8378 xmlChar
*code
= NULL
, *message
= NULL
;
8379 xmlXPathContextPtr xpath_ctx
= NULL
;
8380 xmlXPathObjectPtr result
= NULL
;
8381 xmlChar
*string
= NULL
;
8385 if (!context
) return IE_INVALID_CONTEXT
;
8386 zfree(context
->long_message
);
8387 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8390 /* Check if connection is established
8391 * TODO: This check should be done downstairs. */
8392 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8395 /* Build CreateMultipleMessage request */
8396 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8398 isds_log_message(context
,
8399 _("Could not build CreateMultipleMessage request"));
8402 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8404 isds_log_message(context
, _("Could not create ISDS name space"));
8405 xmlFreeNode(request
);
8408 xmlSetNs(request
, isds_ns
);
8411 /* Build recipients */
8412 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8414 isds_log_message(context
, _("Could not add dmRecipients child to "
8415 "CreateMultipleMessage element"));
8416 xmlFreeNode(request
);
8420 /* Insert each recipient */
8421 for (item
= copies
; item
; item
= item
->next
) {
8422 copy
= (struct isds_message_copy
*) item
->data
;
8424 isds_log_message(context
,
8425 _("`copies' list item contains empty data"));
8430 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8432 isds_log_message(context
, _("Could not add dmRecipient child to "
8433 "dmRecipients element"));
8438 if (!copy
->dbIDRecipient
) {
8439 isds_log_message(context
,
8440 _("Message copy is missing recipient box identifier"));
8444 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
8445 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
8446 copy
->dmRecipientOrgUnit
);
8447 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
8448 copy
->dmRecipientOrgUnitNum
, string
);
8449 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
8452 /* Append envelope and files */
8453 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
8454 if (err
) goto leave
;
8457 isds_log(ILF_ISDS
, ILL_DEBUG
,
8458 _("Sending CreateMultipleMessage request to ISDS\n"));
8461 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8463 isds_log(ILF_ISDS
, ILL_DEBUG
,
8464 _("Processing ISDS response on CreateMultipleMessage "
8465 "request failed\n"));
8469 /* Check for response status */
8470 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8471 &code
, &message
, NULL
);
8473 isds_log(ILF_ISDS
, ILL_DEBUG
,
8474 _("ISDS response on CreateMultipleMessage request "
8475 "is missing status\n"));
8479 /* Request processed, but some copies failed */
8480 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
8481 char *box_id_locale
=
8482 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8483 char *code_locale
= _isds_utf82locale((char*)code
);
8484 char *message_locale
= _isds_utf82locale((char*)message
);
8485 isds_log(ILF_ISDS
, ILL_DEBUG
,
8486 _("Server did accept message for multiple recipients "
8487 "on CreateMultipleMessage request but delivery to "
8488 "some of them failed (code=%s, message=%s)\n"),
8489 box_id_locale
, code_locale
, message_locale
);
8490 isds_log_message(context
, message_locale
);
8491 free(box_id_locale
);
8493 free(message_locale
);
8494 err
= IE_PARTIAL_SUCCESS
;
8497 /* Request refused by server as whole */
8498 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8499 char *box_id_locale
=
8500 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8501 char *code_locale
= _isds_utf82locale((char*)code
);
8502 char *message_locale
= _isds_utf82locale((char*)message
);
8503 isds_log(ILF_ISDS
, ILL_DEBUG
,
8504 _("Server did not accept message for multiple recipients "
8505 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8506 box_id_locale
, code_locale
, message_locale
);
8507 isds_log_message(context
, message_locale
);
8508 free(box_id_locale
);
8510 free(message_locale
);
8517 xpath_ctx
= xmlXPathNewContext(response
);
8522 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8526 result
= xmlXPathEvalExpression(
8527 BAD_CAST
"/isds:CreateMultipleMessageResponse"
8528 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8534 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8535 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
8540 /* Extract message ID and delivery status for each copy */
8541 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
8542 item
= item
->next
, i
++) {
8543 copy
= (struct isds_message_copy
*) item
->data
;
8544 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8546 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
8552 if (item
|| i
< result
->nodesetval
->nodeNr
) {
8553 isds_printf_message(context
, _("ISDS returned unexpected number of "
8554 "message copy delivery states: %d"),
8555 result
->nodesetval
->nodeNr
);
8564 xmlXPathFreeObject(result
);
8565 xmlXPathFreeContext(xpath_ctx
);
8569 xmlFreeDoc(response
);
8570 xmlFreeNode(request
);
8573 isds_log(ILF_ISDS
, ILL_DEBUG
,
8574 _("CreateMultipleMessageResponse request processed by server "
8575 "successfully.\n"));
8576 #else /* not HAVE_LIBCURL */
8584 /* Get list of messages. This is common core for getting sent or received
8586 * Any criterion argument can be NULL, if you don't care about it.
8587 * @context is session context. Must not be NULL.
8588 * @outgoing_direction is true if you want list of outgoing messages,
8589 * it's false if you want incoming messages.
8590 * @from_time is minimal time and date of message sending inclusive.
8591 * @to_time is maximal time and date of message sending inclusive
8592 * @organization_unit_number is number of sender/recipient respectively.
8593 * @status_filter is bit field of isds_message_status values. Use special
8594 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8595 * all values, you can use bit-wise arithmetic if you want.)
8596 * @offset is index of first message we are interested in. First message is 1.
8597 * Set to 0 (or 1) if you don't care.
8598 * @number is maximal length of list you want to get as input value, outputs
8599 * number of messages matching these criteria. Can be NULL if you don't care
8600 * (applies to output value either).
8601 * @messages is automatically reallocated list of isds_message's. Be ware that
8602 * it returns only brief overview (envelope and some other fields) about each
8603 * message, not the complete message. FIXME: Specify exact fields.
8604 * The list is sorted by delivery time in ascending order.
8605 * Use NULL if you don't care about don't need the data (useful if you want to
8606 * know only the @number). If you provide &NULL, list will be allocated on
8607 * heap, if you provide pointer to non-NULL, list will be freed automatically
8608 * at first. Also in case of error the list will be NULLed.
8609 * @return IE_SUCCESS or appropriate error code. */
8610 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
8611 _Bool outgoing_direction
,
8612 const struct timeval
*from_time
, const struct timeval
*to_time
,
8613 const long int *organization_unit_number
,
8614 const unsigned int status_filter
,
8615 const unsigned long int offset
, unsigned long int *number
,
8616 struct isds_list
**messages
) {
8618 isds_error err
= IE_SUCCESS
;
8620 xmlNsPtr isds_ns
= NULL
;
8621 xmlNodePtr request
= NULL
, node
;
8622 xmlDocPtr response
= NULL
;
8623 xmlChar
*code
= NULL
, *message
= NULL
;
8624 xmlXPathContextPtr xpath_ctx
= NULL
;
8625 xmlXPathObjectPtr result
= NULL
;
8626 xmlChar
*string
= NULL
;
8627 long unsigned int count
= 0;
8630 if (!context
) return IE_INVALID_CONTEXT
;
8631 zfree(context
->long_message
);
8633 /* Free former message list if any */
8634 if (messages
) isds_list_free(messages
);
8637 /* Check if connection is established
8638 * TODO: This check should be done downstairs. */
8639 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8641 /* Build GetListOf*Messages request */
8642 request
= xmlNewNode(NULL
,
8643 (outgoing_direction
) ?
8644 BAD_CAST
"GetListOfSentMessages" :
8645 BAD_CAST
"GetListOfReceivedMessages"
8648 isds_log_message(context
,
8649 (outgoing_direction
) ?
8650 _("Could not build GetListOfSentMessages request") :
8651 _("Could not build GetListOfReceivedMessages request")
8655 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8657 isds_log_message(context
, _("Could not create ISDS name space"));
8658 xmlFreeNode(request
);
8661 xmlSetNs(request
, isds_ns
);
8665 err
= timeval2timestring(from_time
, &string
);
8666 if (err
) goto leave
;
8668 INSERT_STRING(request
, "dmFromTime", string
);
8669 free(string
); string
= NULL
;
8672 err
= timeval2timestring(to_time
, &string
);
8673 if (err
) goto leave
;
8675 INSERT_STRING(request
, "dmToTime", string
);
8676 free(string
); string
= NULL
;
8678 if (outgoing_direction
) {
8679 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
8680 organization_unit_number
, string
);
8682 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
8683 organization_unit_number
, string
);
8686 if (status_filter
> MESSAGESTATE_ANY
) {
8687 isds_printf_message(context
,
8688 _("Invalid message state filter value: %ld"), status_filter
);
8692 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
8695 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
8697 INSERT_STRING(request
, "dmOffset", "1");
8700 /* number 0 means no limit */
8701 if (number
&& *number
== 0) {
8702 INSERT_STRING(request
, "dmLimit", NULL
);
8704 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
8708 isds_log(ILF_ISDS
, ILL_DEBUG
,
8709 (outgoing_direction
) ?
8710 _("Sending GetListOfSentMessages request to ISDS\n") :
8711 _("Sending GetListOfReceivedMessages request to ISDS\n")
8715 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
8716 xmlFreeNode(request
); request
= NULL
;
8719 isds_log(ILF_ISDS
, ILL_DEBUG
,
8720 (outgoing_direction
) ?
8721 _("Processing ISDS response on GetListOfSentMessages "
8722 "request failed\n") :
8723 _("Processing ISDS response on GetListOfReceivedMessages "
8729 /* Check for response status */
8730 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
8731 &code
, &message
, NULL
);
8733 isds_log(ILF_ISDS
, ILL_DEBUG
,
8734 (outgoing_direction
) ?
8735 _("ISDS response on GetListOfSentMessages request "
8736 "is missing status\n") :
8737 _("ISDS response on GetListOfReceivedMessages request "
8738 "is missing status\n")
8743 /* Request processed, but nothing found */
8744 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8745 char *code_locale
= _isds_utf82locale((char*)code
);
8746 char *message_locale
= _isds_utf82locale((char*)message
);
8747 isds_log(ILF_ISDS
, ILL_DEBUG
,
8748 (outgoing_direction
) ?
8749 _("Server refused GetListOfSentMessages request "
8750 "(code=%s, message=%s)\n") :
8751 _("Server refused GetListOfReceivedMessages request "
8752 "(code=%s, message=%s)\n"),
8753 code_locale
, message_locale
);
8754 isds_log_message(context
, message_locale
);
8756 free(message_locale
);
8763 xpath_ctx
= xmlXPathNewContext(response
);
8768 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8772 result
= xmlXPathEvalExpression(
8773 (outgoing_direction
) ?
8774 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
8775 "isds:dmRecords/isds:dmRecord" :
8776 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
8777 "isds:dmRecords/isds:dmRecord",
8784 /* Fill output arguments in */
8785 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8786 struct isds_envelope
*envelope
;
8787 struct isds_list
*item
= NULL
, *last_item
= NULL
;
8789 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
8790 /* Create new message */
8791 item
= calloc(1, sizeof(*item
));
8796 item
->destructor
= (void(*)(void**)) &isds_message_free
;
8797 item
->data
= calloc(1, sizeof(struct isds_message
));
8799 isds_list_free(&item
);
8804 /* Extract envelope data */
8805 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
8807 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
8809 isds_list_free(&item
);
8813 /* Attach extracted envelope */
8814 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
8816 /* Append new message into the list */
8818 *messages
= last_item
= item
;
8820 last_item
->next
= item
;
8825 if (number
) *number
= count
;
8829 isds_list_free(messages
);
8833 xmlXPathFreeObject(result
);
8834 xmlXPathFreeContext(xpath_ctx
);
8838 xmlFreeDoc(response
);
8839 xmlFreeNode(request
);
8842 isds_log(ILF_ISDS
, ILL_DEBUG
,
8843 (outgoing_direction
) ?
8844 _("GetListOfSentMessages request processed by server "
8845 "successfully.\n") :
8846 _("GetListOfReceivedMessages request processed by server "
8849 #else /* not HAVE_LIBCURL */
8856 /* Get list of outgoing (already sent) messages.
8857 * Any criterion argument can be NULL, if you don't care about it.
8858 * @context is session context. Must not be NULL.
8859 * @from_time is minimal time and date of message sending inclusive.
8860 * @to_time is maximal time and date of message sending inclusive
8861 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8862 * @status_filter is bit field of isds_message_status values. Use special
8863 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8864 * all values, you can use bit-wise arithmetic if you want.)
8865 * @offset is index of first message we are interested in. First message is 1.
8866 * Set to 0 (or 1) if you don't care.
8867 * @number is maximal length of list you want to get as input value, outputs
8868 * number of messages matching these criteria. Can be NULL if you don't care
8869 * (applies to output value either).
8870 * @messages is automatically reallocated list of isds_message's. Be ware that
8871 * it returns only brief overview (envelope and some other fields) about each
8872 * message, not the complete message. FIXME: Specify exact fields.
8873 * The list is sorted by delivery time in ascending order.
8874 * Use NULL if you don't care about the meta data (useful if you want to know
8875 * only the @number). If you provide &NULL, list will be allocated on heap,
8876 * if you provide pointer to non-NULL, list will be freed automatically at
8877 * first. Also in case of error the list will be NULLed.
8878 * @return IE_SUCCESS or appropriate error code. */
8879 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
8880 const struct timeval
*from_time
, const struct timeval
*to_time
,
8881 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
8882 const unsigned long int offset
, unsigned long int *number
,
8883 struct isds_list
**messages
) {
8885 return isds_get_list_of_messages(
8887 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
8893 /* Get list of incoming (addressed to you) messages.
8894 * Any criterion argument can be NULL, if you don't care about it.
8895 * @context is session context. Must not be NULL.
8896 * @from_time is minimal time and date of message sending inclusive.
8897 * @to_time is maximal time and date of message sending inclusive
8898 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8899 * @status_filter is bit field of isds_message_status values. Use special
8900 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8901 * all values, you can use bit-wise arithmetic if you want.)
8902 * @offset is index of first message we are interested in. First message is 1.
8903 * Set to 0 (or 1) if you don't care.
8904 * @number is maximal length of list you want to get as input value, outputs
8905 * number of messages matching these criteria. Can be NULL if you don't care
8906 * (applies to output value either).
8907 * @messages is automatically reallocated list of isds_message's. Be ware that
8908 * it returns only brief overview (envelope and some other fields) about each
8909 * message, not the complete message. FIXME: Specify exact fields.
8910 * Use NULL if you don't care about the meta data (useful if you want to know
8911 * only the @number). If you provide &NULL, list will be allocated on heap,
8912 * if you provide pointer to non-NULL, list will be freed automatically at
8913 * first. Also in case of error the list will be NULLed.
8914 * @return IE_SUCCESS or appropriate error code. */
8915 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
8916 const struct timeval
*from_time
, const struct timeval
*to_time
,
8917 const long int *dmRecipientOrgUnitNum
,
8918 const unsigned int status_filter
,
8919 const unsigned long int offset
, unsigned long int *number
,
8920 struct isds_list
**messages
) {
8922 return isds_get_list_of_messages(
8924 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
8930 /* Get list of sent message state changes.
8931 * Any criterion argument can be NULL, if you don't care about it.
8932 * @context is session context. Must not be NULL.
8933 * @from_time is minimal time and date of status changes inclusive
8934 * @to_time is maximal time and date of status changes inclusive
8935 * @changed_states is automatically reallocated list of
8936 * isds_message_status_change's. If you provide &NULL, list will be allocated
8937 * on heap, if you provide pointer to non-NULL, list will be freed
8938 * automatically at first. Also in case of error the list will be NULLed.
8939 * XXX: The list item ordering is not specified.
8940 * XXX: Server provides only `recent' changes.
8941 * @return IE_SUCCESS or appropriate error code. */
8942 isds_error
isds_get_list_of_sent_message_state_changes(
8943 struct isds_ctx
*context
,
8944 const struct timeval
*from_time
, const struct timeval
*to_time
,
8945 struct isds_list
**changed_states
) {
8947 isds_error err
= IE_SUCCESS
;
8949 xmlNsPtr isds_ns
= NULL
;
8950 xmlNodePtr request
= NULL
, node
;
8951 xmlDocPtr response
= NULL
;
8952 xmlXPathContextPtr xpath_ctx
= NULL
;
8953 xmlXPathObjectPtr result
= NULL
;
8954 xmlChar
*string
= NULL
;
8955 long unsigned int count
= 0;
8958 if (!context
) return IE_INVALID_CONTEXT
;
8959 zfree(context
->long_message
);
8961 /* Free former message list if any */
8962 isds_list_free(changed_states
);
8965 /* Check if connection is established
8966 * TODO: This check should be done downstairs. */
8967 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8969 /* Build GetMessageStateChanges request */
8970 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
8972 isds_log_message(context
,
8973 _("Could not build GetMessageStateChanges request"));
8976 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8978 isds_log_message(context
, _("Could not create ISDS name space"));
8979 xmlFreeNode(request
);
8982 xmlSetNs(request
, isds_ns
);
8986 err
= timeval2timestring(from_time
, &string
);
8987 if (err
) goto leave
;
8989 INSERT_STRING(request
, "dmFromTime", string
);
8993 err
= timeval2timestring(to_time
, &string
);
8994 if (err
) goto leave
;
8996 INSERT_STRING(request
, "dmToTime", string
);
9001 err
= send_destroy_request_check_response(context
,
9002 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9003 &response
, NULL
, NULL
);
9004 if (err
) goto leave
;
9008 xpath_ctx
= xmlXPathNewContext(response
);
9013 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9017 result
= xmlXPathEvalExpression(
9018 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9019 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9025 /* Fill output arguments in */
9026 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9027 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9029 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9030 /* Create new status change */
9031 item
= calloc(1, sizeof(*item
));
9037 (void(*)(void**)) &isds_message_status_change_free
;
9039 /* Extract message status change */
9040 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9041 err
= extract_StateChangesRecord(context
,
9042 (struct isds_message_status_change
**) &item
->data
,
9045 isds_list_free(&item
);
9049 /* Append new message status change into the list */
9050 if (!*changed_states
) {
9051 *changed_states
= last_item
= item
;
9053 last_item
->next
= item
;
9061 isds_list_free(changed_states
);
9065 xmlXPathFreeObject(result
);
9066 xmlXPathFreeContext(xpath_ctx
);
9067 xmlFreeDoc(response
);
9068 xmlFreeNode(request
);
9071 isds_log(ILF_ISDS
, ILL_DEBUG
,
9072 _("GetMessageStateChanges request processed by server "
9073 "successfully.\n"));
9074 #else /* not HAVE_LIBCURL */
9082 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9084 * @context is session context
9085 * @service is ISDS WS service handler
9086 * @service_name is name of SERVICE_DM_OPERATIONS
9087 * @message_id is message ID to send as service argument to ISDS
9088 * @response is reallocated server SOAP body response as XML document
9089 * @raw_response is reallocated bit stream with response body. Use
9090 * NULL if you don't care
9091 * @raw_response_length is size of @raw_response in bytes
9092 * @code is reallocated ISDS status code
9093 * @status_message is reallocated ISDS status message
9094 * @return error coded from lower layer, context message will be set up
9096 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9097 const isds_service service
, const xmlChar
*service_name
,
9098 const char *message_id
,
9099 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9100 xmlChar
**code
, xmlChar
**status_message
) {
9102 isds_error err
= IE_SUCCESS
;
9103 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9104 xmlNodePtr request
= NULL
, node
;
9105 xmlNsPtr isds_ns
= NULL
;
9107 if (!context
) return IE_INVALID_CONTEXT
;
9108 if (!service_name
|| !message_id
) return IE_INVAL
;
9109 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9110 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9112 /* Free output argument */
9113 xmlFreeDoc(*response
); *response
= NULL
;
9114 if (raw_response
) zfree(*raw_response
);
9116 zfree(*status_message
);
9119 /* Check if connection is established
9120 * TODO: This check should be done downstairs. */
9121 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9123 service_name_locale
= _isds_utf82locale((char*)service_name
);
9124 message_id_locale
= _isds_utf82locale(message_id
);
9125 if (!service_name_locale
|| !message_id_locale
) {
9131 request
= xmlNewNode(NULL
, service_name
);
9133 isds_printf_message(context
,
9134 _("Could not build %s request for %s message ID"),
9135 service_name_locale
, message_id_locale
);
9139 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9141 isds_log_message(context
, _("Could not create ISDS name space"));
9145 xmlSetNs(request
, isds_ns
);
9148 /* Add requested ID */
9149 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9150 if (err
) goto leave
;
9151 INSERT_STRING(request
, "dmID", message_id
);
9154 isds_log(ILF_ISDS
, ILL_DEBUG
,
9155 _("Sending %s request for %s message ID to ISDS\n"),
9156 service_name_locale
, message_id_locale
);
9159 err
= isds(context
, service
, request
, response
,
9160 raw_response
, raw_response_length
);
9161 xmlFreeNode(request
); request
= NULL
;
9164 isds_log(ILF_ISDS
, ILL_DEBUG
,
9165 _("Processing ISDS response on %s request failed\n"),
9166 service_name_locale
);
9170 /* Check for response status */
9171 err
= isds_response_status(context
, service
, *response
,
9172 code
, status_message
, NULL
);
9174 isds_log(ILF_ISDS
, ILL_DEBUG
,
9175 _("ISDS response on %s request is missing status\n"),
9176 service_name_locale
);
9180 /* Request processed, but nothing found */
9181 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9182 char *code_locale
= _isds_utf82locale((char*) *code
);
9183 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9184 isds_log(ILF_ISDS
, ILL_DEBUG
,
9185 _("Server refused %s request for %s message ID "
9186 "(code=%s, message=%s)\n"),
9187 service_name_locale
, message_id_locale
,
9188 code_locale
, status_message_locale
);
9189 isds_log_message(context
, status_message_locale
);
9191 free(status_message_locale
);
9197 free(message_id_locale
);
9198 free(service_name_locale
);
9199 xmlFreeNode(request
);
9204 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9205 * signed data and free ISDS response.
9206 * @context is session context
9207 * @message_id is UTF-8 encoded message ID for logging purpose
9208 * @response is parsed XML document. It will be freed and NULLed in the middle
9209 * of function run to save memory. This is not guaranteed in case of error.
9210 * @request_name is name of ISDS request used to construct response root
9211 * element name and for logging purpose.
9212 * @raw is reallocated output buffer with DER encoded CMS data
9213 * @raw_length is size of @raw buffer in bytes
9214 * @returns standard error codes, in case of error, @raw will be freed and
9215 * NULLed, @response sometimes. */
9216 static isds_error
find_extract_signed_data_free_response(
9217 struct isds_ctx
*context
, const xmlChar
*message_id
,
9218 xmlDocPtr
*response
, const xmlChar
*request_name
,
9219 void **raw
, size_t *raw_length
) {
9221 isds_error err
= IE_SUCCESS
;
9222 char *xpath_expression
= NULL
;
9223 xmlXPathContextPtr xpath_ctx
= NULL
;
9224 xmlXPathObjectPtr result
= NULL
;
9225 char *encoded_structure
= NULL
;
9227 if (!context
) return IE_INVALID_CONTEXT
;
9228 if (!raw
) return IE_INVAL
;
9230 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9233 /* Build XPath expression */
9234 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9235 "Response/isds:dmSignature");
9236 if (!xpath_expression
) return IE_NOMEM
;
9239 xpath_ctx
= xmlXPathNewContext(*response
);
9244 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9248 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9253 /* Empty response */
9254 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9255 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9256 isds_printf_message(context
,
9257 _("Server did not return any signed data for message ID `%s' "
9259 message_id_locale
, request_name
);
9260 free(message_id_locale
);
9264 /* More responses */
9265 if (result
->nodesetval
->nodeNr
> 1) {
9266 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9267 isds_printf_message(context
,
9268 _("Server did return more signed data for message ID `%s' "
9270 message_id_locale
, request_name
);
9271 free(message_id_locale
);
9276 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9278 /* Extract PKCS#7 structure */
9279 EXTRACT_STRING(".", encoded_structure
);
9280 if (!encoded_structure
) {
9281 isds_log_message(context
, _("dmSignature element is empty"));
9284 /* Here we have delivery info as standalone CMS in encoded_structure.
9285 * We don't need any other data, free them: */
9286 xmlXPathFreeObject(result
); result
= NULL
;
9287 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9288 xmlFreeDoc(*response
); *response
= NULL
;
9291 /* Decode PKCS#7 to DER format */
9292 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9293 if (*raw_length
== (size_t) -1) {
9294 isds_log_message(context
,
9295 _("Error while Base64-decoding PKCS#7 structure"));
9306 free(encoded_structure
);
9307 xmlXPathFreeObject(result
);
9308 xmlXPathFreeContext(xpath_ctx
);
9309 free(xpath_expression
);
9313 #endif /* HAVE_LIBCURL */
9316 /* Download incoming message envelope identified by ID.
9317 * @context is session context
9318 * @message_id is message identifier (you can get them from
9319 * isds_get_list_of_received_messages())
9320 * @message is automatically reallocated message retrieved from ISDS.
9321 * It will miss documents per se. Use isds_get_received_message(), if you are
9322 * interested in documents (content) too.
9323 * Returned hash and timestamp require documents to be verifiable. */
9324 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9325 const char *message_id
, struct isds_message
**message
) {
9327 isds_error err
= IE_SUCCESS
;
9329 xmlDocPtr response
= NULL
;
9330 xmlChar
*code
= NULL
, *status_message
= NULL
;
9331 xmlXPathContextPtr xpath_ctx
= NULL
;
9332 xmlXPathObjectPtr result
= NULL
;
9335 if (!context
) return IE_INVALID_CONTEXT
;
9336 zfree(context
->long_message
);
9338 /* Free former message if any */
9339 if (!message
) return IE_INVAL
;
9340 isds_message_free(message
);
9343 /* Do request and check for success */
9344 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9345 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9346 &response
, NULL
, NULL
, &code
, &status_message
);
9347 if (err
) goto leave
;
9350 xpath_ctx
= xmlXPathNewContext(response
);
9355 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9359 result
= xmlXPathEvalExpression(
9360 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9361 "isds:dmReturnedMessageEnvelope",
9367 /* Empty response */
9368 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9369 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9370 isds_printf_message(context
,
9371 _("Server did not return any envelope for ID `%s' "
9372 "on MessageEnvelopeDownload request"), message_id_locale
);
9373 free(message_id_locale
);
9378 if (result
->nodesetval
->nodeNr
> 1) {
9379 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9380 isds_printf_message(context
,
9381 _("Server did return more envelopes for ID `%s' "
9382 "on MessageEnvelopeDownload request"), message_id_locale
);
9383 free(message_id_locale
);
9388 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9390 /* Extract the envelope (= message without documents, hence 0) */
9391 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9392 if (err
) goto leave
;
9395 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9396 &(*message
)->raw_length
);
9400 isds_message_free(message
);
9403 xmlXPathFreeObject(result
);
9404 xmlXPathFreeContext(xpath_ctx
);
9407 free(status_message
);
9408 if (!*message
|| !(*message
)->xml
) {
9409 xmlFreeDoc(response
);
9413 isds_log(ILF_ISDS
, ILL_DEBUG
,
9414 _("MessageEnvelopeDownload request processed by server "
9417 #else /* not HAVE_LIBCURL */
9424 /* Load delivery info of any format from buffer.
9425 * @context is session context
9426 * @raw_type advertises format of @buffer content. Only delivery info types
9428 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9429 * retrieve such data from message->raw after calling
9430 * isds_get_signed_delivery_info().
9431 * @length is length of buffer in bytes.
9432 * @message is automatically reallocated message parsed from @buffer.
9433 * @strategy selects how buffer will be attached into raw isds_message member.
9435 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9436 const isds_raw_type raw_type
,
9437 const void *buffer
, const size_t length
,
9438 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9440 isds_error err
= IE_SUCCESS
;
9441 message_ns_type message_ns
;
9442 xmlDocPtr message_doc
= NULL
;
9443 xmlXPathContextPtr xpath_ctx
= NULL
;
9444 xmlXPathObjectPtr result
= NULL
;
9445 void *xml_stream
= NULL
;
9446 size_t xml_stream_length
= 0;
9448 if (!context
) return IE_INVALID_CONTEXT
;
9449 zfree(context
->long_message
);
9450 if (!message
) return IE_INVAL
;
9451 isds_message_free(message
);
9452 if (!buffer
) return IE_INVAL
;
9455 /* Select buffer format and extract XML from CMS*/
9457 case RAWTYPE_DELIVERYINFO
:
9458 message_ns
= MESSAGE_NS_UNSIGNED
;
9459 xml_stream
= (void *) buffer
;
9460 xml_stream_length
= length
;
9463 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
9464 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9465 xml_stream
= (void *) buffer
;
9466 xml_stream_length
= length
;
9469 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
9470 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9471 err
= _isds_extract_cms_data(context
, buffer
, length
,
9472 &xml_stream
, &xml_stream_length
);
9473 if (err
) goto leave
;
9477 isds_log_message(context
, _("Bad raw delivery representation type"));
9482 isds_log(ILF_ISDS
, ILL_DEBUG
,
9483 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9484 xml_stream_length
, xml_stream
);
9486 /* Convert delivery info XML stream into XPath context */
9487 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9492 xpath_ctx
= xmlXPathNewContext(message_doc
);
9497 /* XXX: Name spaces mangled for signed delivery info:
9498 * http://isds.czechpoint.cz/v20/delivery:
9500 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9502 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9503 * <p:dmID>170272</p:dmID>
9506 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9508 * </q:dmEvents>...</q:dmEvents>
9510 * </q:GetDeliveryInfoResponse>
9512 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
9516 result
= xmlXPathEvalExpression(
9517 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9523 /* Empty delivery info */
9524 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9525 isds_printf_message(context
,
9526 _("XML document is not sisds:dmDelivery document"));
9530 /* More delivery info's */
9531 if (result
->nodesetval
->nodeNr
> 1) {
9532 isds_printf_message(context
,
9533 _("XML document has more sisds:dmDelivery elements"));
9537 /* One delivery info */
9538 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9540 /* Extract the envelope (= message without documents, hence 0).
9541 * XXX: extract_TReturnedMessage() can obtain attachments size,
9542 * but delivery info carries none. It's coded as option elements,
9543 * so it should work. */
9544 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9545 if (err
) goto leave
;
9547 /* Extract events */
9548 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
9549 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
9550 if (err
) { err
= IE_ERROR
; goto leave
; }
9551 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
9552 if (err
) goto leave
;
9554 /* Append raw CMS structure into message */
9555 (*message
)->raw_type
= raw_type
;
9557 case BUFFER_DONT_STORE
:
9560 (*message
)->raw
= malloc(length
);
9561 if (!(*message
)->raw
) {
9565 memcpy((*message
)->raw
, buffer
, length
);
9566 (*message
)->raw_length
= length
;
9569 (*message
)->raw
= (void *) buffer
;
9570 (*message
)->raw_length
= length
;
9579 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
9580 isds_message_free(message
);
9583 xmlXPathFreeObject(result
);
9584 xmlXPathFreeContext(xpath_ctx
);
9585 if (!*message
|| !(*message
)->xml
) {
9586 xmlFreeDoc(message_doc
);
9588 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9591 isds_log(ILF_ISDS
, ILL_DEBUG
,
9592 _("Delivery info loaded successfully.\n"));
9597 /* Download signed delivery info-sheet of given message identified by ID.
9598 * @context is session context
9599 * @message_id is message identifier (you can get them from
9600 * isds_get_list_of_{sent,received}_messages())
9601 * @message is automatically reallocated message retrieved from ISDS.
9602 * It will miss documents per se. Use isds_get_signed_received_message(),
9603 * if you are interested in documents (content). OTOH, only this function
9604 * can get list events message has gone through. */
9605 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
9606 const char *message_id
, struct isds_message
**message
) {
9608 isds_error err
= IE_SUCCESS
;
9610 xmlDocPtr response
= NULL
;
9611 xmlChar
*code
= NULL
, *status_message
= NULL
;
9613 size_t raw_length
= 0;
9616 if (!context
) return IE_INVALID_CONTEXT
;
9617 zfree(context
->long_message
);
9619 /* Free former message if any */
9620 if (!message
) return IE_INVAL
;
9621 isds_message_free(message
);
9624 /* Do request and check for success */
9625 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9626 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
9627 &response
, NULL
, NULL
, &code
, &status_message
);
9628 if (err
) goto leave
;
9630 /* Find signed delivery info, extract it into raw and maybe free
9632 err
= find_extract_signed_data_free_response(context
,
9633 (xmlChar
*)message_id
, &response
,
9634 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
9635 if (err
) goto leave
;
9637 /* Parse delivery info */
9638 err
= isds_load_delivery_info(context
,
9639 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
9640 message
, BUFFER_MOVE
);
9641 if (err
) goto leave
;
9647 isds_message_free(message
);
9652 free(status_message
);
9653 xmlFreeDoc(response
);
9656 isds_log(ILF_ISDS
, ILL_DEBUG
,
9657 _("GetSignedDeliveryInfo request processed by server "
9660 #else /* not HAVE_LIBCURL */
9667 /* Download delivery info-sheet of given message identified by ID.
9668 * @context is session context
9669 * @message_id is message identifier (you can get them from
9670 * isds_get_list_of_{sent,received}_messages())
9671 * @message is automatically reallocated message retrieved from ISDS.
9672 * It will miss documents per se. Use isds_get_received_message(), if you are
9673 * interested in documents (content). OTOH, only this function can get list
9674 * of events message has gone through. */
9675 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
9676 const char *message_id
, struct isds_message
**message
) {
9678 isds_error err
= IE_SUCCESS
;
9680 xmlDocPtr response
= NULL
;
9681 xmlChar
*code
= NULL
, *status_message
= NULL
;
9682 xmlNodePtr delivery_node
= NULL
;
9684 size_t raw_length
= 0;
9687 if (!context
) return IE_INVALID_CONTEXT
;
9688 zfree(context
->long_message
);
9690 /* Free former message if any */
9691 if (!message
) return IE_INVAL
;
9692 isds_message_free(message
);
9695 /* Do request and check for success */
9696 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9697 BAD_CAST
"GetDeliveryInfo", message_id
,
9698 &response
, NULL
, NULL
, &code
, &status_message
);
9699 if (err
) goto leave
;
9702 /* Serialize delivery info */
9703 delivery_node
= xmlDocGetRootElement(response
);
9704 if (!delivery_node
) {
9705 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9706 isds_printf_message(context
,
9707 _("Server did not return any delivery info for ID `%s' "
9708 "on GetDeliveryInfo request"), message_id_locale
);
9709 free(message_id_locale
);
9713 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
9714 if (err
) goto leave
;
9716 /* Parse delivery info */
9717 /* TODO: Here we parse the response second time. We could single delivery
9718 * parser from isds_load_delivery_info() to make things faster. */
9719 err
= isds_load_delivery_info(context
,
9720 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
9721 message
, BUFFER_MOVE
);
9722 if (err
) goto leave
;
9729 isds_message_free(message
);
9734 free(status_message
);
9735 xmlFreeDoc(response
);
9738 isds_log(ILF_ISDS
, ILL_DEBUG
,
9739 _("GetDeliveryInfo request processed by server "
9742 #else /* not HAVE_LIBCURL */
9749 /* Download incoming message identified by ID.
9750 * @context is session context
9751 * @message_id is message identifier (you can get them from
9752 * isds_get_list_of_received_messages())
9753 * @message is automatically reallocated message retrieved from ISDS */
9754 isds_error
isds_get_received_message(struct isds_ctx
*context
,
9755 const char *message_id
, struct isds_message
**message
) {
9757 isds_error err
= IE_SUCCESS
;
9759 xmlDocPtr response
= NULL
;
9760 void *xml_stream
= NULL
;
9761 size_t xml_stream_length
;
9762 xmlChar
*code
= NULL
, *status_message
= NULL
;
9763 xmlXPathContextPtr xpath_ctx
= NULL
;
9764 xmlXPathObjectPtr result
= NULL
;
9765 char *phys_path
= NULL
;
9766 size_t phys_start
, phys_end
;
9769 if (!context
) return IE_INVALID_CONTEXT
;
9770 zfree(context
->long_message
);
9772 /* Free former message if any */
9773 if (NULL
== message
) return IE_INVAL
;
9774 if (message
) isds_message_free(message
);
9777 /* Do request and check for success */
9778 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
9779 BAD_CAST
"MessageDownload", message_id
,
9780 &response
, &xml_stream
, &xml_stream_length
,
9781 &code
, &status_message
);
9782 if (err
) goto leave
;
9785 xpath_ctx
= xmlXPathNewContext(response
);
9790 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9794 result
= xmlXPathEvalExpression(
9795 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9801 /* Empty response */
9802 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9803 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9804 isds_printf_message(context
,
9805 _("Server did not return any message for ID `%s' "
9806 "on MessageDownload request"), message_id_locale
);
9807 free(message_id_locale
);
9812 if (result
->nodesetval
->nodeNr
> 1) {
9813 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9814 isds_printf_message(context
,
9815 _("Server did return more messages for ID `%s' "
9816 "on MessageDownload request"), message_id_locale
);
9817 free(message_id_locale
);
9822 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9824 /* Extract the message */
9825 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
9826 if (err
) goto leave
;
9828 /* Locate raw XML blob */
9830 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
9831 PHYSXML_ELEMENT_SEPARATOR
9832 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
9833 PHYSXML_ELEMENT_SEPARATOR
9834 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
9840 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
9841 phys_path
, &phys_start
, &phys_end
);
9844 isds_log_message(context
,
9845 _("Substring with isds:MessageDownloadResponse element "
9846 "could not be located in raw SOAP message"));
9850 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9851 &(*message)->raw_length);*/
9852 /* TODO: Store name space declarations from ancestors */
9853 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9854 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
9855 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
9856 (*message
)->raw
= malloc((*message
)->raw_length
);
9857 if (!(*message
)->raw
) {
9861 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
9866 isds_message_free(message
);
9871 xmlXPathFreeObject(result
);
9872 xmlXPathFreeContext(xpath_ctx
);
9875 free(status_message
);
9877 if (!*message
|| !(*message
)->xml
) {
9878 xmlFreeDoc(response
);
9882 isds_log(ILF_ISDS
, ILL_DEBUG
,
9883 _("MessageDownload request processed by server "
9886 #else /* not HAVE_LIBCURL */
9893 /* Load message of any type from buffer.
9894 * @context is session context
9895 * @raw_type defines content type of @buffer. Only message types are allowed.
9896 * @buffer is message raw representation. Format (CMS, plain signed,
9897 * message direction) is defined in @raw_type. You can retrieve such data
9898 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9899 * @length is length of buffer in bytes.
9900 * @message is automatically reallocated message parsed from @buffer.
9901 * @strategy selects how buffer will be attached into raw isds_message member.
9903 isds_error
isds_load_message(struct isds_ctx
*context
,
9904 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
9905 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9907 isds_error err
= IE_SUCCESS
;
9908 void *xml_stream
= NULL
;
9909 size_t xml_stream_length
= 0;
9910 message_ns_type message_ns
;
9911 xmlDocPtr message_doc
= NULL
;
9912 xmlXPathContextPtr xpath_ctx
= NULL
;
9913 xmlXPathObjectPtr result
= NULL
;
9915 if (!context
) return IE_INVALID_CONTEXT
;
9916 zfree(context
->long_message
);
9917 if (!message
) return IE_INVAL
;
9918 isds_message_free(message
);
9919 if (!buffer
) return IE_INVAL
;
9922 /* Select buffer format and extract XML from CMS*/
9924 case RAWTYPE_INCOMING_MESSAGE
:
9925 message_ns
= MESSAGE_NS_UNSIGNED
;
9926 xml_stream
= (void *) buffer
;
9927 xml_stream_length
= length
;
9930 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
9931 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9932 xml_stream
= (void *) buffer
;
9933 xml_stream_length
= length
;
9936 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
9937 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9938 err
= _isds_extract_cms_data(context
, buffer
, length
,
9939 &xml_stream
, &xml_stream_length
);
9940 if (err
) goto leave
;
9943 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
9944 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9945 xml_stream
= (void *) buffer
;
9946 xml_stream_length
= length
;
9949 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
9950 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9951 err
= _isds_extract_cms_data(context
, buffer
, length
,
9952 &xml_stream
, &xml_stream_length
);
9953 if (err
) goto leave
;
9957 isds_log_message(context
, _("Bad raw message representation type"));
9962 isds_log(ILF_ISDS
, ILL_DEBUG
,
9963 _("Loading message:\n%.*s\nEnd of message\n"),
9964 xml_stream_length
, xml_stream
);
9966 /* Convert messages XML stream into XPath context */
9967 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9972 xpath_ctx
= xmlXPathNewContext(message_doc
);
9977 /* XXX: Standard name space for unsigned incoming direction:
9978 * http://isds.czechpoint.cz/v20/
9980 * XXX: Name spaces mangled for signed outgoing direction:
9981 * http://isds.czechpoint.cz/v20/SentMessage:
9983 * <q:MessageDownloadResponse
9984 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9985 * <q:dmReturnedMessage>
9986 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9987 * <p:dmID>151916</p:dmID>
9990 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9992 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9993 * </q:dmReturnedMessage>
9994 * </q:MessageDownloadResponse>
9996 * XXX: Name spaces mangled for signed incoming direction:
9997 * http://isds.czechpoint.cz/v20/message:
9999 * <q:MessageDownloadResponse
10000 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10001 * <q:dmReturnedMessage>
10002 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10003 * <p:dmID>151916</p:dmID>
10006 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10008 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10009 * </q:dmReturnedMessage>
10010 * </q:MessageDownloadResponse>
10012 * Stupidity of ISDS developers is unlimited */
10013 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10017 result
= xmlXPathEvalExpression(
10018 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10024 /* Empty message */
10025 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10026 isds_printf_message(context
,
10027 _("XML document does not contain "
10028 "sisds:dmReturnedMessage element"));
10032 /* More messages */
10033 if (result
->nodesetval
->nodeNr
> 1) {
10034 isds_printf_message(context
,
10035 _("XML document has more sisds:dmReturnedMessage elements"));
10040 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10042 /* Extract the message */
10043 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10044 if (err
) goto leave
;
10046 /* Append raw buffer into message */
10047 (*message
)->raw_type
= raw_type
;
10048 switch (strategy
) {
10049 case BUFFER_DONT_STORE
:
10052 (*message
)->raw
= malloc(length
);
10053 if (!(*message
)->raw
) {
10057 memcpy((*message
)->raw
, buffer
, length
);
10058 (*message
)->raw_length
= length
;
10061 (*message
)->raw
= (void *) buffer
;
10062 (*message
)->raw_length
= length
;
10072 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10073 isds_message_free(message
);
10076 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10077 xmlXPathFreeObject(result
);
10078 xmlXPathFreeContext(xpath_ctx
);
10079 if (!*message
|| !(*message
)->xml
) {
10080 xmlFreeDoc(message_doc
);
10084 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10089 /* Determine type of raw message or delivery info according some heuristics.
10090 * It does not validate the raw blob.
10091 * @context is session context
10092 * @raw_type returns content type of @buffer. Valid only if exit code of this
10093 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10094 * reallocated memory.
10095 * @buffer is message raw representation.
10096 * @length is length of buffer in bytes. */
10097 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10098 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10100 void *xml_stream
= NULL
;
10101 size_t xml_stream_length
= 0;
10102 xmlDocPtr document
= NULL
;
10103 xmlNodePtr root
= NULL
;
10105 if (!context
) return IE_INVALID_CONTEXT
;
10106 zfree(context
->long_message
);
10107 if (length
== 0 || !buffer
) return IE_INVAL
;
10108 if (!raw_type
) return IE_INVAL
;
10111 err
= _isds_extract_cms_data(context
, buffer
, length
,
10112 &xml_stream
, &xml_stream_length
);
10114 xml_stream
= (void *) buffer
;
10115 xml_stream_length
= (size_t) length
;
10120 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10122 isds_printf_message(context
,
10123 _("Could not parse data as XML document"));
10128 /* Get root element */
10129 root
= xmlDocGetRootElement(document
);
10131 isds_printf_message(context
,
10132 _("XML document is missing root element"));
10137 if (!root
->ns
|| !root
->ns
->href
) {
10138 isds_printf_message(context
,
10139 _("Root element does not belong to any name space"));
10144 /* Test name space */
10145 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10146 if (xml_stream
== buffer
)
10147 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10149 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10150 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10151 if (xml_stream
== buffer
)
10152 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10154 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10155 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10156 if (xml_stream
== buffer
)
10157 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10159 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10160 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10161 if (xml_stream
!= buffer
) {
10162 isds_printf_message(context
,
10163 _("Document in ISDS name space is encapsulated into CMS" ));
10165 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10166 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10167 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10168 *raw_type
= RAWTYPE_DELIVERYINFO
;
10170 isds_printf_message(context
,
10171 _("Unknown root element in ISDS name space"));
10175 isds_printf_message(context
,
10176 _("Unknown name space"));
10181 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10182 xmlFreeDoc(document
);
10187 /* Download signed incoming/outgoing message identified by ID.
10188 * @context is session context
10189 * @output is true for outgoing message, false for incoming message
10190 * @message_id is message identifier (you can get them from
10191 * isds_get_list_of_{sent,received}_messages())
10192 * @message is automatically reallocated message retrieved from ISDS. The raw
10193 * member will be filled with PKCS#7 structure in DER format. */
10194 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10195 const _Bool outgoing
, const char *message_id
,
10196 struct isds_message
**message
) {
10198 isds_error err
= IE_SUCCESS
;
10200 xmlDocPtr response
= NULL
;
10201 xmlChar
*code
= NULL
, *status_message
= NULL
;
10202 xmlXPathContextPtr xpath_ctx
= NULL
;
10203 xmlXPathObjectPtr result
= NULL
;
10204 char *encoded_structure
= NULL
;
10206 size_t raw_length
= 0;
10209 if (!context
) return IE_INVALID_CONTEXT
;
10210 zfree(context
->long_message
);
10211 if (!message
) return IE_INVAL
;
10212 isds_message_free(message
);
10215 /* Do request and check for success */
10216 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10217 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10218 BAD_CAST
"SignedMessageDownload",
10219 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10220 if (err
) goto leave
;
10222 /* Find signed message, extract it into raw and maybe free
10224 err
= find_extract_signed_data_free_response(context
,
10225 (xmlChar
*)message_id
, &response
,
10226 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10227 BAD_CAST
"SignedMessageDownload",
10228 &raw
, &raw_length
);
10229 if (err
) goto leave
;
10231 /* Parse message */
10232 err
= isds_load_message(context
,
10233 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10234 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10235 raw
, raw_length
, message
, BUFFER_MOVE
);
10236 if (err
) goto leave
;
10242 isds_message_free(message
);
10245 free(encoded_structure
);
10246 xmlXPathFreeObject(result
);
10247 xmlXPathFreeContext(xpath_ctx
);
10251 free(status_message
);
10252 xmlFreeDoc(response
);
10255 isds_log(ILF_ISDS
, ILL_DEBUG
,
10257 _("SignedSentMessageDownload request processed by server "
10258 "successfully.\n") :
10259 _("SignedMessageDownload request processed by server "
10262 #else /* not HAVE_LIBCURL */
10269 /* Download signed incoming message identified by ID.
10270 * @context is session context
10271 * @message_id is message identifier (you can get them from
10272 * isds_get_list_of_received_messages())
10273 * @message is automatically reallocated message retrieved from ISDS. The raw
10274 * member will be filled with PKCS#7 structure in DER format. */
10275 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10276 const char *message_id
, struct isds_message
**message
) {
10277 return isds_get_signed_message(context
, 0, message_id
, message
);
10281 /* Download signed outgoing message identified by ID.
10282 * @context is session context
10283 * @message_id is message identifier (you can get them from
10284 * isds_get_list_of_sent_messages())
10285 * @message is automatically reallocated message retrieved from ISDS. The raw
10286 * member will be filled with PKCS#7 structure in DER format. */
10287 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10288 const char *message_id
, struct isds_message
**message
) {
10289 return isds_get_signed_message(context
, 1, message_id
, message
);
10293 /* Get type and name of user who sent a message identified by ID.
10294 * @context is session context
10295 * @message_id is message identifier
10296 * @sender_type is pointer to automatically allocated type of sender detected
10297 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10298 * library or to the server, NULL will be returned. Pass NULL if you don't
10300 * @raw_sender_type is automatically reallocated UTF-8 string describing
10301 * sender type or NULL if not known to server. Pass NULL if you don't care.
10302 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10303 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10304 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10305 const char *message_id
, isds_sender_type
**sender_type
,
10306 char **raw_sender_type
, char **sender_name
) {
10307 isds_error err
= IE_SUCCESS
;
10309 xmlDocPtr response
= NULL
;
10310 xmlChar
*code
= NULL
, *status_message
= NULL
;
10311 xmlXPathContextPtr xpath_ctx
= NULL
;
10312 xmlXPathObjectPtr result
= NULL
;
10313 char *type_string
= NULL
;
10316 if (!context
) return IE_INVALID_CONTEXT
;
10317 zfree(context
->long_message
);
10318 if (sender_type
) zfree(*sender_type
);
10319 if (raw_sender_type
) zfree(*raw_sender_type
);
10320 if (sender_name
) zfree(*sender_name
);
10321 if (!message_id
) return IE_INVAL
;
10324 /* Do request and check for success */
10325 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10326 BAD_CAST
"GetMessageAuthor",
10327 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10328 if (err
) goto leave
;
10331 xpath_ctx
= xmlXPathNewContext(response
);
10336 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10340 result
= xmlXPathEvalExpression(
10341 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10346 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10347 isds_log_message(context
,
10348 _("Missing GetMessageAuthorResponse element"));
10352 if (result
->nodesetval
->nodeNr
> 1) {
10353 isds_log_message(context
,
10354 _("Multiple GetMessageAuthorResponse element"));
10358 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10359 xmlXPathFreeObject(result
); result
= NULL
;
10361 /* Fill output arguments in */
10362 EXTRACT_STRING("isds:userType", type_string
);
10363 if (NULL
!= type_string
) {
10364 if (NULL
!= sender_type
) {
10365 *sender_type
= calloc(1, sizeof(**sender_type
));
10366 if (NULL
== *sender_type
) {
10371 err
= string2isds_sender_type((xmlChar
*)type_string
,
10374 zfree(*sender_type
);
10375 if (err
== IE_ENUM
) {
10377 char *type_string_locale
= _isds_utf82locale(type_string
);
10378 isds_log(ILF_ISDS
, ILL_WARNING
,
10379 _("Unknown isds:userType value: %s"),
10380 type_string_locale
);
10381 free(type_string_locale
);
10386 if (NULL
!= sender_name
)
10387 EXTRACT_STRING("isds:authorName", *sender_name
);
10391 if (NULL
!= sender_type
) zfree(*sender_type
);
10392 zfree(type_string
);
10393 if (NULL
!= sender_name
) zfree(*sender_name
);
10395 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10397 xmlXPathFreeObject(result
);
10398 xmlXPathFreeContext(xpath_ctx
);
10401 free(status_message
);
10402 xmlFreeDoc(response
);
10405 isds_log(ILF_ISDS
, ILL_DEBUG
,
10406 _("GetMessageAuthor request processed by server "
10407 "successfully.\n"));
10408 #else /* not HAVE_LIBCURL */
10415 /* Retrieve hash of message identified by ID stored in ISDS.
10416 * @context is session context
10417 * @message_id is message identifier
10418 * @hash is automatically reallocated message hash downloaded from ISDS.
10419 * Message must exist in system and must not be deleted. */
10420 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10421 const char *message_id
, struct isds_hash
**hash
) {
10423 isds_error err
= IE_SUCCESS
;
10425 xmlDocPtr response
= NULL
;
10426 xmlChar
*code
= NULL
, *status_message
= NULL
;
10427 xmlXPathContextPtr xpath_ctx
= NULL
;
10428 xmlXPathObjectPtr result
= NULL
;
10431 if (!context
) return IE_INVALID_CONTEXT
;
10432 zfree(context
->long_message
);
10434 isds_hash_free(hash
);
10437 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10438 BAD_CAST
"VerifyMessage", message_id
,
10439 &response
, NULL
, NULL
, &code
, &status_message
);
10440 if (err
) goto leave
;
10444 xpath_ctx
= xmlXPathNewContext(response
);
10449 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10453 result
= xmlXPathEvalExpression(
10454 BAD_CAST
"/isds:VerifyMessageResponse",
10460 /* Empty response */
10461 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10462 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10463 isds_printf_message(context
,
10464 _("Server did not return any response for ID `%s' "
10465 "on VerifyMessage request"), message_id_locale
);
10466 free(message_id_locale
);
10470 /* More responses */
10471 if (result
->nodesetval
->nodeNr
> 1) {
10472 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10473 isds_printf_message(context
,
10474 _("Server did return more responses for ID `%s' "
10475 "on VerifyMessage request"), message_id_locale
);
10476 free(message_id_locale
);
10481 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10483 /* Extract the hash */
10484 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
10488 isds_hash_free(hash
);
10491 xmlXPathFreeObject(result
);
10492 xmlXPathFreeContext(xpath_ctx
);
10495 free(status_message
);
10496 xmlFreeDoc(response
);
10499 isds_log(ILF_ISDS
, ILL_DEBUG
,
10500 _("VerifyMessage request processed by server "
10503 #else /* not HAVE_LIBCURL */
10510 /* Erase message specified by @message_id from long term storage. Other
10511 * message cannot be erased on user request.
10512 * @context is session context
10513 * @message_id is message identifier.
10514 * @incoming is true for incoming message, false for outgoing message.
10516 * IE_SUCCESS if message has ben removed
10517 * IE_INVAL if message does not exist in long term storage or message
10518 * belongs to different box
10519 * TODO: IE_NOEPRM if user has no permission to erase a message */
10520 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
10521 const char *message_id
, _Bool incoming
) {
10522 isds_error err
= IE_SUCCESS
;
10524 xmlNodePtr request
= NULL
, node
;
10525 xmlNsPtr isds_ns
= NULL
;
10526 xmlDocPtr response
= NULL
;
10527 xmlChar
*code
= NULL
, *status_message
= NULL
;
10530 if (!context
) return IE_INVALID_CONTEXT
;
10531 zfree(context
->long_message
);
10532 if (NULL
== message_id
) return IE_INVAL
;
10534 /* Check if connection is established
10535 * TODO: This check should be done downstairs. */
10536 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10539 /* Build request */
10540 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
10542 isds_log_message(context
,
10543 _("Could build EraseMessage request"));
10546 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10548 isds_log_message(context
, _("Could not create ISDS name space"));
10549 xmlFreeNode(request
);
10552 xmlSetNs(request
, isds_ns
);
10554 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
10555 if (err
) goto leave
;
10556 INSERT_STRING(request
, "dmID", message_id
);
10558 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
10562 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
10563 "message ID %s to ISDS\n"), message_id
);
10564 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
10565 xmlFreeNode(request
); request
= NULL
;
10568 isds_log(ILF_ISDS
, ILL_DEBUG
,
10569 _("Processing ISDS response on EraseMessage request "
10574 /* Check for response status */
10575 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
10576 &code
, &status_message
, NULL
);
10578 isds_log(ILF_ISDS
, ILL_DEBUG
,
10579 _("ISDS response on EraseMessage request is missing "
10584 /* Check server status code */
10585 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
10586 isds_log_message(context
, _("Message to erase belongs to other box"));
10588 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
10589 isds_log_message(context
, _("Message to erase is not saved in "
10590 "long term storage or the direction does not match"));
10592 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
10593 char *code_locale
= _isds_utf82locale((char*) code
);
10594 char *message_locale
= _isds_utf82locale((char*) status_message
);
10595 isds_log(ILF_ISDS
, ILL_DEBUG
,
10596 _("Server refused EraseMessage request "
10597 "(code=%s, message=%s)\n"),
10598 code_locale
, message_locale
);
10599 isds_log_message(context
, message_locale
);
10601 free(message_locale
);
10608 free(status_message
);
10609 xmlFreeDoc(response
);
10610 xmlFreeNode(request
);
10613 isds_log(ILF_ISDS
, ILL_DEBUG
,
10614 _("EraseMessage request processed by server "
10617 #else /* not HAVE_LIBCURL */
10624 /* Mark message as read. This is a transactional commit function to acknowledge
10625 * to ISDS the message has been downloaded and processed by client properly.
10626 * @context is session context
10627 * @message_id is message identifier. */
10628 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
10629 const char *message_id
) {
10631 isds_error err
= IE_SUCCESS
;
10633 xmlDocPtr response
= NULL
;
10634 xmlChar
*code
= NULL
, *status_message
= NULL
;
10637 if (!context
) return IE_INVALID_CONTEXT
;
10638 zfree(context
->long_message
);
10641 /* Do request and check for success */
10642 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10643 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
10644 &response
, NULL
, NULL
, &code
, &status_message
);
10647 free(status_message
);
10648 xmlFreeDoc(response
);
10651 isds_log(ILF_ISDS
, ILL_DEBUG
,
10652 _("MarkMessageAsDownloaded request processed by server "
10655 #else /* not HAVE_LIBCURL */
10662 /* Mark message as received by recipient. This is applicable only to
10663 * commercial message. Use envelope->dmType message member to distinguish
10664 * commercial message from government message. Government message is
10665 * received automatically (by law), commercial message on recipient request.
10666 * @context is session context
10667 * @message_id is message identifier. */
10668 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
10669 const char *message_id
) {
10671 isds_error err
= IE_SUCCESS
;
10673 xmlDocPtr response
= NULL
;
10674 xmlChar
*code
= NULL
, *status_message
= NULL
;
10677 if (!context
) return IE_INVALID_CONTEXT
;
10678 zfree(context
->long_message
);
10681 /* Do request and check for success */
10682 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10683 BAD_CAST
"ConfirmDelivery", message_id
,
10684 &response
, NULL
, NULL
, &code
, &status_message
);
10687 free(status_message
);
10688 xmlFreeDoc(response
);
10691 isds_log(ILF_ISDS
, ILL_DEBUG
,
10692 _("ConfirmDelivery request processed by server "
10695 #else /* not HAVE_LIBCURL */
10702 /* Send document for authorized conversion into Czech POINT system.
10703 * This is public anonymous service, no log-in necessary. Special context is
10704 * used to reuse keep-a-live HTTPS connection.
10705 * @context is Czech POINT session context. DO NOT use context connected to
10706 * ISDS server. Use new context or context used by this function previously.
10707 * @document is document to convert. Only data, data_length, dmFileDescr and
10708 * is_xml members are significant. Be ware that not all document formats can be
10709 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10710 * @id is reallocated identifier assigned by Czech POINT system to
10711 * your document on submit. Use is to tell it to Czech POINT officer.
10712 * @date is reallocated document submit date (submitted documents
10713 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10715 isds_error
czp_convert_document(struct isds_ctx
*context
,
10716 const struct isds_document
*document
,
10717 char **id
, struct tm
**date
) {
10718 isds_error err
= IE_SUCCESS
;
10720 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
10721 xmlNodePtr request
= NULL
, node
;
10722 xmlDocPtr response
= NULL
;
10724 xmlXPathContextPtr xpath_ctx
= NULL
;
10725 xmlXPathObjectPtr result
= NULL
;
10726 long int status
= -1;
10727 long int *status_ptr
= &status
;
10728 char *string
= NULL
;
10732 if (!context
) return IE_INVALID_CONTEXT
;
10733 zfree(context
->long_message
);
10734 if (!document
|| !id
|| !date
) return IE_INVAL
;
10736 if (document
->is_xml
) {
10737 isds_log_message(context
,
10738 _("XML documents cannot be submitted to conversion"));
10742 /* Free output arguments */
10747 /* Store configuration */
10748 context
->type
= CTX_TYPE_CZP
;
10749 free(context
->url
);
10750 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
10751 if (!(context
->url
))
10754 /* Prepare CURL handle if not yet connected */
10755 if (!context
->curl
) {
10756 context
->curl
= curl_easy_init();
10757 if (!(context
->curl
))
10761 /* Build conversion request */
10762 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
10764 isds_log_message(context
,
10765 _("Could not build Czech POINT conversion request"));
10768 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
10770 isds_log_message(context
,
10771 _("Could not create Czech POINT deposit name space"));
10772 xmlFreeNode(request
);
10775 xmlSetNs(request
, deposit_ns
);
10777 /* Insert children. They are in empty namespace! */
10778 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
10780 isds_log_message(context
, _("Could not create empty name space"));
10784 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
10785 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
10786 document
->dmFileDescr
);
10788 /* Document encoded in Base64 */
10789 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
10790 document
->data
, document
->data_length
);
10791 if (err
) goto leave
;
10793 isds_log(ILF_ISDS
, ILL_DEBUG
,
10794 _("Submitting document for conversion into Czech POINT deposit"));
10796 /* Send conversion request */
10797 err
= _czp_czpdeposit(context
, request
, &response
);
10798 xmlFreeNode(request
); request
= NULL
;
10801 czp_do_close_connection(context
);
10806 /* Extract response */
10807 xpath_ctx
= xmlXPathNewContext(response
);
10812 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10816 result
= xmlXPathEvalExpression(
10817 BAD_CAST
"/deposit:saveDocumentResponse/return",
10823 /* Empty response */
10824 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10825 isds_printf_message(context
,
10826 _("Missing `return' element in Czech POINT deposit response"));
10830 /* More responses */
10831 if (result
->nodesetval
->nodeNr
> 1) {
10832 isds_printf_message(context
,
10833 _("Multiple `return' element in Czech POINT deposit response"));
10838 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10841 EXTRACT_LONGINT("status", status_ptr
, 1);
10843 EXTRACT_STRING("statusMsg", string
);
10844 char *string_locale
= _isds_utf82locale(string
);
10845 isds_printf_message(context
,
10846 _("Czech POINT deposit refused document for conversion "
10847 "(code=%ld, message=%s)"),
10848 status
, string_locale
);
10849 free(string_locale
);
10854 /* Get document ID */
10855 EXTRACT_STRING("documentID", *id
);
10857 /* Get submit date */
10858 EXTRACT_STRING("dateInserted", string
);
10860 *date
= calloc(1, sizeof(**date
));
10865 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
10867 if (err
== IE_NOTSUP
) {
10869 char *string_locale
= _isds_utf82locale(string
);
10870 isds_printf_message(context
,
10871 _("Invalid dateInserted value: %s"), string_locale
);
10872 free(string_locale
);
10880 xmlXPathFreeObject(result
);
10881 xmlXPathFreeContext(xpath_ctx
);
10883 xmlFreeDoc(response
);
10884 xmlFreeNode(request
);
10887 char *id_locale
= _isds_utf82locale((char *) *id
);
10888 isds_log(ILF_ISDS
, ILL_DEBUG
,
10889 _("Document %s has been submitted for conversion "
10890 "to server successfully\n"), id_locale
);
10893 #else /* not HAVE_LIBCURL */
10900 /* Close possibly opened connection to Czech POINT document deposit.
10901 * @context is Czech POINT session context. */
10902 isds_error
czp_close_connection(struct isds_ctx
*context
) {
10903 if (!context
) return IE_INVALID_CONTEXT
;
10904 zfree(context
->long_message
);
10906 return czp_do_close_connection(context
);
10913 /* Send request for new box creation in testing ISDS instance.
10914 * It's not possible to request for a production box currently, as it
10915 * communicates via e-mail.
10916 * XXX: This function does not work either. Server complains about invalid
10918 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10920 * @context is special session context for box creation request. DO NOT use
10921 * standard context as it could reveal your password. Use fresh new context or
10922 * context previously used by this function.
10923 * @box is box description to create including single primary user (in case of
10924 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10925 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10926 * box, or contact address of PFO box owner). The email member is mandatory as
10927 * it will be used to deliver credentials.
10928 * @former_names is former name of box owner. Pass NULL if you don't care.
10929 * @approval is optional external approval of box manipulation
10930 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10931 * NULL, if you don't care.*/
10932 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
10933 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
10934 const char *former_names
, const struct isds_approval
*approval
,
10935 char **refnumber
) {
10936 isds_error err
= IE_SUCCESS
;
10938 xmlNodePtr request
= NULL
;
10939 xmlDocPtr response
= NULL
;
10940 xmlXPathContextPtr xpath_ctx
= NULL
;
10941 xmlXPathObjectPtr result
= NULL
;
10945 if (!context
) return IE_INVALID_CONTEXT
;
10946 zfree(context
->long_message
);
10947 if (!box
) return IE_INVAL
;
10950 if (!box
->email
|| box
->email
[0] == '\0') {
10951 isds_log_message(context
, _("E-mail field is mandatory"));
10955 /* Scratch box ID */
10958 /* Store configuration */
10959 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
10960 free(context
->url
);
10961 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
10962 if (!(context
->url
))
10965 /* Prepare CURL handle if not yet connected */
10966 if (!context
->curl
) {
10967 context
->curl
= curl_easy_init();
10968 if (!(context
->curl
))
10972 /* Build CreateDataBox request */
10973 err
= build_CreateDBInput_request(context
,
10974 &request
, BAD_CAST
"CreateDataBox",
10975 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
10976 if (err
) goto leave
;
10978 /* Send it to server and process response */
10979 err
= send_destroy_request_check_response(context
,
10980 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
10981 &response
, (xmlChar
**) refnumber
, NULL
);
10982 if (err
) goto leave
;
10984 /* Extract box ID */
10985 xpath_ctx
= xmlXPathNewContext(response
);
10990 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10994 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
10997 xmlXPathFreeObject(result
);
10998 xmlXPathFreeContext(xpath_ctx
);
10999 xmlFreeDoc(response
);
11000 xmlFreeNode(request
);
11003 isds_log(ILF_ISDS
, ILL_DEBUG
,
11004 _("CreateDataBox request processed by server successfully.\n"));
11006 #else /* not HAVE_LIBCURL */
11014 /* Submit CMS signed message to ISDS to verify its originality. This is
11015 * stronger form of isds_verify_message_hash() because ISDS does more checks
11016 * than simple one (potentialy old weak) hash comparison.
11017 * @context is session context
11018 * @message is memory with raw CMS signed message bit stream
11019 * @length is @message size in bytes
11021 * IE_SUCCESS if message originates in ISDS
11022 * IE_NOTEQUAL if message is unknown to ISDS
11023 * other code for other errors */
11024 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11025 const void *message
, size_t length
) {
11026 isds_error err
= IE_SUCCESS
;
11028 xmlNsPtr isds_ns
= NULL
;
11029 xmlNodePtr request
= NULL
;
11030 xmlDocPtr response
= NULL
;
11031 xmlXPathContextPtr xpath_ctx
= NULL
;
11032 xmlXPathObjectPtr result
= NULL
;
11033 _Bool
*authentic
= NULL
;
11036 if (!context
) return IE_INVALID_CONTEXT
;
11037 zfree(context
->long_message
);
11038 if (!message
|| length
== 0) return IE_INVAL
;
11041 /* Check if connection is established
11042 * TODO: This check should be done downstairs. */
11043 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11046 /* Build AuthenticateMessage request */
11047 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11049 isds_log_message(context
,
11050 _("Could not build AuthenticateMessage request"));
11053 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11055 isds_log_message(context
, _("Could not create ISDS name space"));
11056 xmlFreeNode(request
);
11059 xmlSetNs(request
, isds_ns
);
11061 /* Insert Base64 encoded message */
11062 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11064 if (err
) goto leave
;
11066 /* Send request to server and process response */
11067 err
= send_destroy_request_check_response(context
,
11068 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11069 &response
, NULL
, NULL
);
11070 if (err
) goto leave
;
11073 /* ISDS has decided */
11074 xpath_ctx
= xmlXPathNewContext(response
);
11079 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11084 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11087 isds_log_message(context
,
11088 _("Server did not return any response on "
11089 "AuthenticateMessage request"));
11094 isds_log(ILF_ISDS
, ILL_DEBUG
,
11095 _("ISDS authenticated the message successfully\n"));
11097 isds_log_message(context
, _("ISDS does not know the message"));
11104 xmlXPathFreeObject(result
);
11105 xmlXPathFreeContext(xpath_ctx
);
11107 xmlFreeDoc(response
);
11108 xmlFreeNode(request
);
11109 #else /* not HAVE_LIBCURL */
11117 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11118 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11120 * @context is session context
11121 * @input_data is memory with raw CMS signed message or delivery info bit
11122 * stream to re-sign
11123 * @input_length is @input_data size in bytes
11124 * @output_data is pointer to auto-allocated memory where to store re-signed
11125 * input data blob. Caller must free it.
11126 * @output_data is pointer where to store @output_data size in bytes
11127 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11128 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11130 * IE_SUCCESS if CMS blob has been re-signed successfully
11131 * other code for other errors */
11132 isds_error
isds_resign_message(struct isds_ctx
*context
,
11133 const void *input_data
, size_t input_length
,
11134 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11135 isds_error err
= IE_SUCCESS
;
11137 xmlNsPtr isds_ns
= NULL
;
11138 xmlNodePtr request
= NULL
;
11139 xmlDocPtr response
= NULL
;
11140 xmlXPathContextPtr xpath_ctx
= NULL
;
11141 xmlXPathObjectPtr result
= NULL
;
11142 char *string
= NULL
;
11143 const xmlChar
*codes
[] = {
11150 const char *meanings
[] = {
11152 "Message is not original",
11153 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11154 "Time stamp could not been generated in time"
11156 const isds_error errors
[] = {
11162 struct code_map_isds_error map
= {
11164 .meanings
= meanings
,
11169 if (NULL
!= output_data
) *output_data
= NULL
;
11170 if (NULL
!= output_length
) *output_length
= 0;
11171 if (NULL
!= valid_to
) *valid_to
= NULL
;
11173 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11174 zfree(context
->long_message
);
11175 if (NULL
== input_data
|| 0 == input_length
) {
11176 isds_log_message(context
, _("Empty CMS blob on input"));
11179 if (NULL
== output_data
|| NULL
== output_length
) {
11180 isds_log_message(context
,
11181 _("NULL pointer provided for output CMS blob"));
11186 /* Check if connection is established
11187 * TODO: This check should be done downstairs. */
11188 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11191 /* Build Re-signISDSDocument request */
11192 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11194 isds_log_message(context
,
11195 _("Could not build Re-signISDSDocument request"));
11198 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11200 isds_log_message(context
, _("Could not create ISDS name space"));
11201 xmlFreeNode(request
);
11204 xmlSetNs(request
, isds_ns
);
11206 /* Insert Base64 encoded CMS blob */
11207 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11208 input_data
, input_length
);
11209 if (err
) goto leave
;
11211 /* Send request to server and process response */
11212 err
= send_destroy_request_check_response(context
,
11213 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11214 &response
, NULL
, &map
);
11215 if (err
) goto leave
;
11218 /* Extract re-signed data */
11219 xpath_ctx
= xmlXPathNewContext(response
);
11224 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11228 result
= xmlXPathEvalExpression(
11229 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11234 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11235 isds_log_message(context
,
11236 _("Missing Re-signISDSDocumentResponse element"));
11240 if (result
->nodesetval
->nodeNr
> 1) {
11241 isds_log_message(context
,
11242 _("Multiple Re-signISDSDocumentResponse element"));
11246 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11247 xmlXPathFreeObject(result
); result
= NULL
;
11249 EXTRACT_STRING("isds:dmResultDoc", string
);
11250 /* Decode non-empty data */
11251 if (NULL
!= string
&& string
[0] != '\0') {
11252 *output_length
= _isds_b64decode(string
, output_data
);
11253 if (*output_length
== (size_t) -1) {
11254 isds_log_message(context
,
11255 _("Error while Base64-decoding re-signed data"));
11260 isds_log_message(context
, _("Server did not send re-signed data"));
11266 if (NULL
!= valid_to
) {
11267 /* Get time stamp expiration date */
11268 EXTRACT_STRING("isds:dmValidTo", string
);
11269 if (NULL
!= string
) {
11270 *valid_to
= calloc(1, sizeof(**valid_to
));
11275 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11277 if (err
== IE_NOTSUP
) {
11279 char *string_locale
= _isds_utf82locale(string
);
11280 isds_printf_message(context
,
11281 _("Invalid dmValidTo value: %s"), string_locale
);
11282 free(string_locale
);
11292 xmlXPathFreeObject(result
);
11293 xmlXPathFreeContext(xpath_ctx
);
11295 xmlFreeDoc(response
);
11296 xmlFreeNode(request
);
11297 #else /* not HAVE_LIBCURL */
11304 #undef INSERT_ELEMENT
11305 #undef CHECK_FOR_STRING_LENGTH
11306 #undef INSERT_STRING_ATTRIBUTE
11307 #undef INSERT_ULONGINTNOPTR
11308 #undef INSERT_ULONGINT
11309 #undef INSERT_LONGINT
11310 #undef INSERT_BOOLEAN
11311 #undef INSERT_SCALAR_BOOLEAN
11312 #undef INSERT_STRING
11313 #undef INSERT_STRING_WITH_NS
11314 #undef EXTRACT_STRING_ATTRIBUTE
11315 #undef EXTRACT_ULONGINT
11316 #undef EXTRACT_LONGINT
11317 #undef EXTRACT_BOOLEAN
11318 #undef EXTRACT_STRING
11321 /* Compute hash of message from raw representation and store it into envelope.
11322 * Original hash structure will be destroyed in envelope.
11323 * @context is session context
11324 * @message is message carrying raw XML message blob
11325 * @algorithm is desired hash algorithm to use */
11326 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11327 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11328 isds_error err
= IE_SUCCESS
;
11330 void *xml_stream
= NULL
;
11331 size_t xml_stream_length
;
11332 size_t phys_start
, phys_end
;
11333 char *phys_path
= NULL
;
11334 struct isds_hash
*new_hash
= NULL
;
11337 if (!context
) return IE_INVALID_CONTEXT
;
11338 zfree(context
->long_message
);
11339 if (!message
) return IE_INVAL
;
11341 if (!message
->raw
) {
11342 isds_log_message(context
,
11343 _("Message does not carry raw representation"));
11347 switch (message
->raw_type
) {
11348 case RAWTYPE_INCOMING_MESSAGE
:
11350 xml_stream
= message
->raw
;
11351 xml_stream_length
= message
->raw_length
;
11354 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11355 nsuri
= SISDS_INCOMING_NS
;
11356 xml_stream
= message
->raw
;
11357 xml_stream_length
= message
->raw_length
;
11360 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11361 nsuri
= SISDS_INCOMING_NS
;
11362 err
= _isds_extract_cms_data(context
,
11363 message
->raw
, message
->raw_length
,
11364 &xml_stream
, &xml_stream_length
);
11365 if (err
) goto leave
;
11368 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11369 nsuri
= SISDS_OUTGOING_NS
;
11370 xml_stream
= message
->raw
;
11371 xml_stream_length
= message
->raw_length
;
11374 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11375 nsuri
= SISDS_OUTGOING_NS
;
11376 err
= _isds_extract_cms_data(context
,
11377 message
->raw
, message
->raw_length
,
11378 &xml_stream
, &xml_stream_length
);
11379 if (err
) goto leave
;
11383 isds_log_message(context
, _("Bad raw representation type"));
11389 /* XXX: Hash is computed from original string representing isds:dmDm
11390 * subtree. That means no encoding, white space, xmlns attributes changes.
11391 * In other words, input for hash can be invalid XML stream. */
11392 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11393 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11394 PHYSXML_ELEMENT_SEPARATOR
,
11395 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11396 PHYSXML_ELEMENT_SEPARATOR
11397 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11401 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11402 phys_path
, &phys_start
, &phys_end
);
11405 isds_log_message(context
,
11406 _("Substring with isds:dmDM element could not be located "
11407 "in raw message"));
11413 new_hash
= calloc(1, sizeof(*new_hash
));
11418 new_hash
->algorithm
= algorithm
;
11419 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11422 isds_log_message(context
, _("Could not compute message hash"));
11426 /* Save computed hash */
11427 if (!message
->envelope
) {
11428 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11429 if (!message
->envelope
) {
11434 isds_hash_free(&message
->envelope
->hash
);
11435 message
->envelope
->hash
= new_hash
;
11439 isds_hash_free(&new_hash
);
11443 if (xml_stream
!= message
->raw
) free(xml_stream
);
11448 /* Compare two hashes.
11449 * @h1 is first hash
11450 * @h2 is another hash
11452 * IE_SUCCESS if hashes equal
11453 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11454 * IE_ENUM if not comparable, but both structures defined
11455 * IE_INVAL if some of the structures are undefined (NULL)
11456 * IE_ERROR if internal error occurs */
11457 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
11458 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
11459 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
11460 if (h1
->length
!= h2
->length
) return IE_ERROR
;
11461 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
11462 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
11464 for (int i
= 0; i
< h1
->length
; i
++) {
11465 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
11466 return IE_NOTEQUAL
;
11472 /* Check message has gone through ISDS by comparing message hash stored in
11473 * ISDS and locally computed hash. You must provide message with valid raw
11474 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11475 * This is convenient wrapper for isds_download_message_hash(),
11476 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11477 * @context is session context
11478 * @message is message with valid raw and envelope member; envelope->hash
11479 * member will be changed during function run. Use envelope on heap only.
11481 * IE_SUCCESS if message originates in ISDS
11482 * IE_NOTEQUAL if message is unknown to ISDS
11483 * other code for other errors */
11484 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
11485 struct isds_message
*message
) {
11486 isds_error err
= IE_SUCCESS
;
11487 struct isds_hash
*downloaded_hash
= NULL
;
11489 if (!context
) return IE_INVALID_CONTEXT
;
11490 zfree(context
->long_message
);
11491 if (!message
) return IE_INVAL
;
11493 if (!message
->envelope
) {
11494 isds_log_message(context
,
11495 _("Given message structure is missing envelope"));
11498 if (!message
->raw
) {
11499 isds_log_message(context
,
11500 _("Given message structure is missing raw representation"));
11504 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
11506 if (err
) goto leave
;
11508 err
= isds_compute_message_hash(context
, message
,
11509 downloaded_hash
->algorithm
);
11510 if (err
) goto leave
;
11512 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
11515 isds_hash_free(&downloaded_hash
);
11520 /* Search for document by document ID in list of documents. IDs are compared
11522 * @documents is list of isds_documents
11523 * @id is document identifier
11524 * @return first matching document or NULL. */
11525 const struct isds_document
*isds_find_document_by_id(
11526 const struct isds_list
*documents
, const char *id
) {
11527 const struct isds_list
*item
;
11528 const struct isds_document
*document
;
11530 for (item
= documents
; item
; item
= item
->next
) {
11531 document
= (struct isds_document
*) item
->data
;
11532 if (!document
) continue;
11534 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
11542 /* Normalize @mime_type to be proper MIME type.
11543 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11544 * guess regular MIME type (e.g. "application/pdf").
11545 * @mime_type is UTF-8 encoded MIME type to fix
11546 * @return original @mime_type if no better interpretation exists, or
11547 * constant static UTF-8 encoded string with proper MIME type. */
11548 const char *isds_normalize_mime_type(const char *mime_type
) {
11549 if (!mime_type
) return NULL
;
11551 for (int offset
= 0;
11552 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
11554 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
11555 extension_map_mime
[offset
]))
11556 return (const char *) extension_map_mime
[offset
+ 1];
11563 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11564 struct isds_message **message);
11565 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11566 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11567 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11568 struct isds_address **address);
11570 int isds_message_free(struct isds_message **message);
11571 int isds_address_free(struct isds_address **address);
11575 /* Makes known all relevant namespaces to given XPath context
11576 * @xpath_ctx is XPath context
11577 * @message_ns selects proper message name space. Unsigned and signed
11578 * messages and delivery info's differ in prefix and URI. */
11579 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
11580 const message_ns_type message_ns
) {
11581 const xmlChar
*message_namespace
= NULL
;
11583 if (!xpath_ctx
) return IE_ERROR
;
11585 switch(message_ns
) {
11587 message_namespace
= BAD_CAST ISDS1_NS
; break;
11588 case MESSAGE_NS_UNSIGNED
:
11589 message_namespace
= BAD_CAST ISDS_NS
; break;
11590 case MESSAGE_NS_SIGNED_INCOMING
:
11591 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
11592 case MESSAGE_NS_SIGNED_OUTGOING
:
11593 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
11594 case MESSAGE_NS_SIGNED_DELIVERY
:
11595 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
11600 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
11602 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
11604 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
11606 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
11608 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
11610 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))