8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
13 #include "validator.h"
15 #include <gpg-error.h> /* Because of ksba or gpgme */
20 * Allocated in isds_init() and deallocated in isds_cleanup(). */
21 unsigned int log_facilities
;
22 isds_log_level log_level
;
23 isds_log_callback log_callback
;
24 void *log_callback_data
;
25 const char *version_gpgme
;
26 const char *version_gcrypt
;
27 const char *version_expat
;
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 /* Initialize gpg-error because of gpgme and ksba */
694 if (gpg_err_init()) {
695 isds_log(ILF_ISDS
, ILL_CRIT
,
696 _("gpg-error library initialization failed\n"));
700 /* Initialize GPGME */
701 if (_isds_init_gpgme(&version_gpgme
)) {
702 isds_log(ILF_ISDS
, ILL_CRIT
,
703 _("GPGME library initialization failed\n"));
707 /* Initialize gcrypt */
708 if (_isds_init_gcrypt(&version_gcrypt
)) {
709 isds_log(ILF_ISDS
, ILL_CRIT
,
710 _("gcrypt library initialization failed\n"));
714 /* This can _exit() current program. Find not so assertive check. */
716 xmlSetGenericErrorFunc(NULL
, log_xml
);
719 if (_isds_init_expat(&version_expat
)) {
720 isds_log(ILF_ISDS
, ILL_CRIT
,
721 _("expat library initialization failed\n"));
725 /* Allocate global variables */
732 /* Deinitialize ISDS library.
733 * Global function, must be called as last library function. */
734 isds_error
isds_cleanup(void) {
740 curl_global_cleanup();
747 /* Return version string of this library. Version of dependencies can be
748 * embedded. Do no try to parse it. You must free it. */
749 char *isds_version(void) {
752 isds_asprintf(&buffer
,
754 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
756 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
762 version_gpgme
, version_gcrypt
,
763 version_expat
, xmlParserVersion
);
768 /* Return text description of ISDS error */
769 const char *isds_strerror(const isds_error error
) {
772 return(_("Success")); break;
774 return(_("Unspecified error")); break;
776 return(_("Not supported")); break;
778 return(_("Invalid value")); break;
779 case IE_INVALID_CONTEXT
:
780 return(_("Invalid context")); break;
781 case IE_NOT_LOGGED_IN
:
782 return(_("Not logged in")); break;
783 case IE_CONNECTION_CLOSED
:
784 return(_("Connection closed")); break;
786 return(_("Timed out")); break;
788 return(_("Not exist")); break;
790 return(_("Out of memory")); break;
792 return(_("Network problem")); break;
794 return(_("HTTP problem")); break;
796 return(_("SOAP problem")); break;
798 return(_("XML problem")); break;
800 return(_("ISDS server problem")); break;
802 return(_("Invalid enum value")); break;
804 return(_("Invalid date value")); break;
806 return(_("Too big")); break;
808 return(_("Too small")); break;
810 return(_("Value not unique")); break;
812 return(_("Values not equal")); break;
813 case IE_PARTIAL_SUCCESS
:
814 return(_("Some suboperations failed")); break;
816 return(_("Operation aborted")); break;
818 return(_("Security problem")); break;
820 return(_("Unknown error"));
825 /* Create ISDS context.
826 * Each context can be used for different sessions to (possibly) different
827 * ISDS server with different credentials. */
828 struct isds_ctx
*isds_ctx_create(void) {
829 struct isds_ctx
*context
;
830 context
= malloc(sizeof(*context
));
831 if (context
) memset(context
, 0, sizeof(*context
));
836 /* Close possibly opened connection to Czech POINT document deposit without
837 * resetting long_message buffer.
838 * XXX: Do not use czp_close_connection() if you do not want to destroy log
840 * @context is Czech POINT session context. */
841 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
842 if (!context
) return IE_INVALID_CONTEXT
;
843 _isds_close_connection(context
);
848 /* Discard credentials.
849 * @context is ISDS context
850 * @discard_saved_username is true for removing saved username, false for
852 * Only that. It does not cause log out, connection close or similar. */
853 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
854 _Bool discard_saved_username
) {
855 if(!context
) return IE_INVALID_CONTEXT
;
857 if (context
->username
) {
858 memset(context
->username
, 0, strlen(context
->username
));
859 zfree(context
->username
);
861 if (context
->password
) {
862 memset(context
->password
, 0, strlen(context
->password
));
863 zfree(context
->password
);
865 isds_pki_credentials_free(&context
->pki_credentials
);
866 if (discard_saved_username
&& context
->saved_username
) {
867 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
868 zfree(context
->saved_username
);
873 #endif /* HAVE_LIBCURL */
876 /* Destroy ISDS context and free memory.
877 * @context will be NULLed on success. */
878 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
879 if (!context
|| !*context
) {
880 return IE_INVALID_CONTEXT
;
884 /* Discard credentials and close connection */
885 switch ((*context
)->type
) {
886 case CTX_TYPE_NONE
: break;
887 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
889 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
890 czp_do_close_connection(*context
); break;
894 _isds_discard_credentials(*context
, 1);
896 /* Free other structures */
897 free((*context
)->tls_verify_server
);
898 free((*context
)->tls_ca_file
);
899 free((*context
)->tls_ca_dir
);
900 free((*context
)->tls_crl_file
);
901 #endif /* HAVE_LIBCURL */
902 free((*context
)->long_message
);
910 /* Return long message text produced by library function, e.g. detailed error
911 * message. Returned pointer is only valid until new library function is
912 * called for the same context. Could be NULL, especially if NULL context is
913 * supplied. Return string is locale encoded. */
914 char *isds_long_message(const struct isds_ctx
*context
) {
915 if (!context
) return NULL
;
916 return context
->long_message
;
920 /* Stores message into context' long_message buffer.
921 * Application can pick the message up using isds_long_message().
922 * NULL @message truncates the buffer but does not deallocate it.
923 * @message is coded in locale encoding */
924 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
925 const char *message
) {
929 if (!context
) return IE_INVALID_CONTEXT
;
931 /* FIXME: Check for integer overflow */
932 length
= 1 + ((message
) ? strlen(message
) : 0);
933 buffer
= realloc(context
->long_message
, length
);
934 if (!buffer
) return IE_NOMEM
;
937 strcpy(buffer
, message
);
941 context
->long_message
= buffer
;
946 /* Appends message into context' long_message buffer.
947 * Application can pick the message up using isds_long_message().
948 * NULL message has void effect. */
949 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
950 const char *message
) {
952 size_t old_length
, length
;
954 if (!context
) return IE_INVALID_CONTEXT
;
955 if (!message
) return IE_SUCCESS
;
956 if (!context
->long_message
)
957 return isds_log_message(context
, message
);
959 old_length
= strlen(context
->long_message
);
960 /* FIXME: Check for integer overflow */
961 length
= 1 + old_length
+ strlen(message
);
962 buffer
= realloc(context
->long_message
, length
);
963 if (!buffer
) return IE_NOMEM
;
965 strcpy(buffer
+ old_length
, message
);
967 context
->long_message
= buffer
;
972 /* Stores formatted message into context' long_message buffer.
973 * Application can pick the message up using isds_long_message(). */
974 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
975 const char *format
, ...) {
979 if (!context
) return IE_INVALID_CONTEXT
;
980 va_start(ap
, format
);
981 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
984 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
989 * @facilities is bit mask of isds_log_facility values,
990 * @level is verbosity level. */
991 void isds_set_logging(const unsigned int facilities
,
992 const isds_log_level level
) {
993 log_facilities
= facilities
;
998 /* Register callback function libisds calls when new global log message is
999 * produced by library. Library logs to stderr by default.
1000 * @callback is function provided by application libisds will call. See type
1001 * definition for @callback argument explanation. Pass NULL to revert logging to
1002 * default behaviour.
1003 * @data is application specific data @callback gets as last argument */
1004 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1005 log_callback
= callback
;
1006 log_callback_data
= data
;
1010 /* Log @message in class @facility with log @level into global log. @message
1011 * is printf(3) formatting string, variadic arguments may be necessary.
1012 * For debugging purposes. */
1013 _hidden isds_error
isds_log(const isds_log_facility facility
,
1014 const isds_log_level level
, const char *message
, ...) {
1016 char *buffer
= NULL
;
1019 if (level
> log_level
) return IE_SUCCESS
;
1020 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1021 if (!message
) return IE_INVAL
;
1024 /* Pass message to application supplied callback function */
1025 va_start(ap
, message
);
1026 length
= isds_vasprintf(&buffer
, message
, ap
);
1033 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1037 /* Default: Log it to stderr */
1038 va_start(ap
, message
);
1039 vfprintf(stderr
, message
, ap
);
1041 /* Line buffered printf is default.
1049 /* Set timeout in milliseconds for each network job like connecting to server
1050 * or sending message. Use 0 to disable timeout limits. */
1051 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1052 const unsigned int timeout
) {
1053 if (!context
) return IE_INVALID_CONTEXT
;
1054 zfree(context
->long_message
);
1057 context
->timeout
= timeout
;
1059 if (context
->curl
) {
1062 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1064 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1065 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1068 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1069 context
->timeout
/ 1000);
1070 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1071 if (curl_err
) return IE_ERROR
;
1075 #else /* not HAVE_LIBCURL */
1081 /* Register callback function libisds calls periodically during HTTP data
1083 * @context is session context
1084 * @callback is function provided by application libisds will call. See type
1085 * definition for @callback argument explanation.
1086 * @data is application specific data @callback gets as last argument */
1087 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1088 isds_progress_callback callback
, void *data
) {
1089 if (!context
) return IE_INVALID_CONTEXT
;
1090 zfree(context
->long_message
);
1093 context
->progress_callback
= callback
;
1094 context
->progress_callback_data
= data
;
1097 #else /* not HAVE_LIBCURL */
1103 /* Change context settings.
1104 * @context is context which setting will be applied to
1105 * @option is name of option. It determines the type of last argument. See
1106 * isds_option definition for more info.
1107 * @... is value of new setting. Type is determined by @option
1109 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1111 isds_error err
= IE_SUCCESS
;
1114 char *pointer
, *string
;
1117 if (!context
) return IE_INVALID_CONTEXT
;
1118 zfree(context
->long_message
);
1120 va_start(ap
, option
);
1122 #define REPLACE_VA_BOOLEAN(destination) { \
1123 if (!(destination)) { \
1124 (destination) = malloc(sizeof(*(destination))); \
1125 if (!(destination)) { \
1126 err = IE_NOMEM; goto leave; \
1129 *(destination) = (_Bool) !!va_arg(ap, int); \
1132 #define REPLACE_VA_STRING(destination) { \
1133 string = va_arg(ap, char *); \
1135 pointer = realloc((destination), 1 + strlen(string)); \
1136 if (!pointer) { err = IE_NOMEM; goto leave; } \
1137 strcpy(pointer, string); \
1138 (destination) = pointer; \
1140 free(destination); \
1141 (destination) = NULL; \
1146 case IOPT_TLS_VERIFY_SERVER
:
1148 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1150 err
= IE_NOTSUP
; goto leave
;
1153 case IOPT_TLS_CA_FILE
:
1155 REPLACE_VA_STRING(context
->tls_ca_file
);
1157 err
= IE_NOTSUP
; goto leave
;
1160 case IOPT_TLS_CA_DIRECTORY
:
1162 REPLACE_VA_STRING(context
->tls_ca_dir
);
1164 err
= IE_NOTSUP
; goto leave
;
1167 case IOPT_TLS_CRL_FILE
:
1169 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1170 REPLACE_VA_STRING(context
->tls_crl_file
);
1172 isds_log_message(context
,
1173 _("Curl library does not support CRL definition"));
1175 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1177 err
= IE_NOTSUP
; goto leave
;
1178 #endif /* not HAVE_LIBCURL */
1180 case IOPT_NORMALIZE_MIME_TYPE
:
1181 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1185 err
= IE_ENUM
; goto leave
;
1188 #undef REPLACE_VA_STRING
1189 #undef REPLACE_VA_BOOLEAN
1198 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1199 * Destination for NULL argument will not be touched.
1200 * Destination pointers must be freed before calling this function.
1201 * If @username is @context->saved_username, the saved_username will not be
1202 * replaced. The saved_username is clobbered only if context has set otp
1204 * Return IE_SUCCESS on success. */
1205 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1206 const char *username
, const char *password
,
1207 const struct isds_pki_credentials
*pki_credentials
) {
1208 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1210 /* FIXME: mlock password
1211 * (I have a library) */
1214 context
->username
= strdup(username
);
1215 if (context
->otp
&& context
->saved_username
!= username
)
1216 context
->saved_username
= strdup(username
);
1219 if (NULL
== context
->otp_credentials
)
1220 context
->password
= strdup(password
);
1222 context
->password
= _isds_astrcat(password
,
1223 context
->otp_credentials
->otp_code
);
1225 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1227 if ((NULL
!= username
&& NULL
== context
->username
) ||
1228 (NULL
!= password
&& NULL
== context
->password
) ||
1229 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1230 (context
->otp
&& NULL
!= context
->username
&&
1231 NULL
== context
->saved_username
)) {
1240 /* Connect and log into ISDS server.
1241 * All required arguments will be copied, you do not have to keep them after
1243 * ISDS supports six different authentication methods. Exact method is
1244 * selected on @username, @password, @pki_credentials, and @otp arguments:
1245 * - If @pki_credentials == NULL, @username and @password must be supplied
1247 * - If @otp == NULL, simple authentication by username and password will
1249 * - If @otp != NULL, authentication by username and password and OTP
1251 * - If @pki_credentials != NULL, then
1252 * - If @username == NULL, only certificate will be used
1253 * - If @username != NULL, then
1254 * - If @password == NULL, then certificate will be used and
1255 * @username shifts meaning to box ID. This is used for hosted
1257 * - Otherwise all three arguments will be used.
1258 * Please note, that different cases require different certificate type
1259 * (system qualified one or commercial non qualified one). This library
1260 * does not check such political issues. Please see ISDS Specification
1262 * @url is base address of ISDS web service. Pass extern isds_locator
1263 * variable to use production ISDS instance without client certificate
1264 * authentication (or extern isds_cert_locator with client certificate
1265 * authentication or extern isds_otp_locators with OTP authentication).
1266 * Passing NULL has the same effect, autoselection between isds_locator,
1267 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1268 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1269 * isds_otp_testing_locator) variable to select testing instance.
1270 * @username is user name of ISDS user or box ID
1271 * @password is user's secret password
1272 * @pki_credentials defines public key cryptographic material to use in client
1274 * @otp selects one-time password authentication method to use, defines OTP
1275 * code (if known) and returns fine grade resolution of OTP procedure.
1277 * IE_SUCCESS if authentication succeeds
1278 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1279 * requested, fine grade reason will be set into @otp->resolution. Error
1280 * message from server can be obtained by isds_long_message() call.
1281 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1282 * server has sent OTP code through side channel. Application is expected to
1283 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1284 * this call to complete second phase of TOTP authentication;
1285 * or other appropriate error. */
1286 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1287 const char *username
, const char *password
,
1288 const struct isds_pki_credentials
*pki_credentials
,
1289 struct isds_otp
*otp
) {
1291 isds_error err
= IE_NOT_LOGGED_IN
;
1292 isds_error soap_err
;
1293 xmlNsPtr isds_ns
= NULL
;
1294 xmlNodePtr request
= NULL
;
1295 xmlNodePtr response
= NULL
;
1296 #endif /* HAVE_LIBCURL */
1298 if (!context
) return IE_INVALID_CONTEXT
;
1299 zfree(context
->long_message
);
1302 /* Close connection if already logged in */
1303 if (context
->curl
) {
1304 _isds_close_connection(context
);
1307 /* Store configuration */
1308 context
->type
= CTX_TYPE_ISDS
;
1309 zfree(context
->url
);
1311 /* Mangle base URI according to requested authentication method */
1312 if (NULL
== pki_credentials
) {
1313 isds_log(ILF_SEC
, ILL_INFO
,
1314 _("Selected authentication method: no certificate, "
1315 "username and password\n"));
1316 if (!username
|| !password
) {
1317 isds_log_message(context
,
1318 _("Both username and password must be supplied"));
1321 context
->otp_credentials
= otp
;
1322 context
->otp
= (NULL
!= context
->otp_credentials
);
1324 if (!context
->otp
) {
1325 /* Default locator is official system (without certificate or
1327 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1329 const char *authenticator_uri
= NULL
;
1330 if (!url
) url
= isds_otp_locator
;
1331 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1332 switch (context
->otp_credentials
->method
) {
1334 isds_log(ILF_SEC
, ILL_INFO
,
1335 _("Selected authentication method: "
1336 "HMAC-based one-time password\n"));
1338 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1341 isds_log(ILF_SEC
, ILL_INFO
,
1342 _("Selected authentication method: "
1343 "Time-based one-time password\n"));
1344 if (context
->otp_credentials
->otp_code
== NULL
) {
1345 isds_log(ILF_SEC
, ILL_INFO
,
1346 _("OTP code has not been provided by "
1347 "application, requesting server for "
1350 "%1$sas/processLogin?type=totp&sendSms=true&"
1353 isds_log(ILF_SEC
, ILL_INFO
,
1354 _("OTP code has been provided by "
1355 "application, not requesting server "
1358 "%1$sas/processLogin?type=totp&"
1363 isds_log_message(context
,
1364 _("Unknown one-time password authentication "
1365 "method requested by application"));
1368 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1372 /* Default locator is official system (with client certificate) */
1374 context
->otp_credentials
= NULL
;
1375 if (!url
) url
= isds_cert_locator
;
1378 isds_log(ILF_SEC
, ILL_INFO
,
1379 _("Selected authentication method: system certificate, "
1380 "no username and no password\n"));
1382 context
->url
= _isds_astrcat(url
, "cert/");
1385 isds_log(ILF_SEC
, ILL_INFO
,
1386 _("Selected authentication method: system certificate, "
1387 "box ID and no password\n"));
1388 context
->url
= _isds_astrcat(url
, "hspis/");
1390 isds_log(ILF_SEC
, ILL_INFO
,
1391 _("Selected authentication method: commercial "
1392 "certificate, username and password\n"));
1393 context
->url
= _isds_astrcat(url
, "certds/");
1397 if (!(context
->url
))
1400 /* Prepare CURL handle */
1401 context
->curl
= curl_easy_init();
1402 if (!(context
->curl
))
1405 /* Build log-in request */
1406 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1408 isds_log_message(context
, _("Could not build ISDS log-in request"));
1411 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1413 isds_log_message(context
, _("Could not create ISDS name space"));
1414 xmlFreeNode(request
);
1417 xmlSetNs(request
, isds_ns
);
1419 /* Store credentials */
1420 _isds_discard_credentials(context
, 1);
1421 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1422 _isds_discard_credentials(context
, 1);
1423 xmlFreeNode(request
);
1427 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1430 /* Send log-in request */
1431 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1434 /* Revert context URL from OTP authentication service URL to OTP web
1435 * service base URL for subsequent calls. Potenial isds_login() retry
1436 * will re-set context URL again. */
1437 zfree(context
->url
);
1438 context
->url
= _isds_astrcat(url
, "apps/");
1439 if (context
->url
== NULL
) {
1440 soap_err
= IE_NOMEM
;
1442 /* Detach pointer to OTP credentials from context */
1443 context
->otp_credentials
= NULL
;
1446 /* Remove credentials */
1447 _isds_discard_credentials(context
, 0);
1449 /* Destroy log-in request */
1450 xmlFreeNode(request
);
1453 xmlFreeNodeList(response
);
1454 _isds_close_connection(context
);
1458 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1459 * authentication succeeded if soap_err == IE_SUCCESS */
1462 xmlFreeNodeList(response
);
1465 isds_log(ILF_ISDS
, ILL_DEBUG
,
1466 _("User %s has been logged into server %s successfully\n"),
1469 #else /* not HAVE_LIBCURL */
1475 /* Log out from ISDS server discards credentials and connection configuration. */
1476 isds_error
isds_logout(struct isds_ctx
*context
) {
1477 if (!context
) return IE_INVALID_CONTEXT
;
1478 zfree(context
->long_message
);
1481 if (context
->curl
) {
1483 isds_error err
= _isds_invalidate_otp_cookie(context
);
1484 if (err
) return err
;
1487 /* Close connection */
1488 _isds_close_connection(context
);
1490 /* Discard credentials for sure. They should not survive isds_login(),
1491 * even successful .*/
1492 _isds_discard_credentials(context
, 1);
1493 zfree(context
->url
);
1495 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1497 _isds_discard_credentials(context
, 1);
1500 #else /* not HAVE_LIBCURL */
1506 /* Verify connection to ISDS is alive and server is responding.
1507 * Send dummy request to ISDS and expect dummy response. */
1508 isds_error
isds_ping(struct isds_ctx
*context
) {
1510 isds_error soap_err
;
1511 xmlNsPtr isds_ns
= NULL
;
1512 xmlNodePtr request
= NULL
;
1513 xmlNodePtr response
= NULL
;
1514 #endif /* HAVE_LIBCURL */
1516 if (!context
) return IE_INVALID_CONTEXT
;
1517 zfree(context
->long_message
);
1520 /* Check if connection is established */
1521 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1524 /* Build dummy request */
1525 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1527 isds_log_message(context
, _("Could build ISDS dummy request"));
1530 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1532 isds_log_message(context
, _("Could not create ISDS name space"));
1533 xmlFreeNode(request
);
1536 xmlSetNs(request
, isds_ns
);
1538 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1540 /* Sent dummy request */
1541 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1543 /* Destroy log-in request */
1544 xmlFreeNode(request
);
1547 isds_log(ILF_ISDS
, ILL_DEBUG
,
1548 _("ISDS server could not be contacted\n"));
1549 xmlFreeNodeList(response
);
1553 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1554 * authentication succeeded if soap_err == IE_SUCCESS */
1555 /* TODO: ISDS documentation does not specify response body.
1556 * However real server sends back DummyOperationResponse */
1559 xmlFreeNodeList(response
);
1561 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1564 #else /* not HAVE_LIBCURL */
1570 /* Send bogus request to ISDS.
1571 * Just for test purposes */
1572 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1575 xmlNsPtr isds_ns
= NULL
;
1576 xmlNodePtr request
= NULL
;
1577 xmlDocPtr response
= NULL
;
1578 xmlChar
*code
= NULL
, *message
= NULL
;
1581 if (!context
) return IE_INVALID_CONTEXT
;
1582 zfree(context
->long_message
);
1585 /* Check if connection is established */
1586 if (!context
->curl
) {
1587 /* Testing printf message */
1588 isds_printf_message(context
, "%s", _("I said connection closed"));
1589 return IE_CONNECTION_CLOSED
;
1593 /* Build dummy request */
1594 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1596 isds_log_message(context
, _("Could build ISDS bogus request"));
1599 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1601 isds_log_message(context
, _("Could not create ISDS name space"));
1602 xmlFreeNode(request
);
1605 xmlSetNs(request
, isds_ns
);
1607 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1609 /* Sent bogus request */
1610 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1612 /* Destroy request */
1613 xmlFreeNode(request
);
1616 isds_log(ILF_ISDS
, ILL_DEBUG
,
1617 _("Processing ISDS response on bogus request failed\n"));
1618 xmlFreeDoc(response
);
1622 /* Check for response status */
1623 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1624 &code
, &message
, NULL
);
1626 isds_log(ILF_ISDS
, ILL_DEBUG
,
1627 _("ISDS response on bogus request is missing status\n"));
1630 xmlFreeDoc(response
);
1633 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1634 char *code_locale
= _isds_utf82locale((char*)code
);
1635 char *message_locale
= _isds_utf82locale((char*)message
);
1636 isds_log(ILF_ISDS
, ILL_DEBUG
,
1637 _("Server refused bogus request (code=%s, message=%s)\n"),
1638 code_locale
, message_locale
);
1639 /* XXX: Literal error messages from ISDS are Czech messages
1640 * (English sometimes) in UTF-8. It's hard to catch them for
1641 * translation. Successfully gettextized would return in locale
1642 * encoding, unsuccessfully translated would pass in UTF-8. */
1643 isds_log_message(context
, message_locale
);
1645 free(message_locale
);
1648 xmlFreeDoc(response
);
1655 xmlFreeDoc(response
);
1657 isds_log(ILF_ISDS
, ILL_DEBUG
,
1658 _("Bogus message accepted by server. This should not happen.\n"));
1661 #else /* not HAVE_LIBCURL */
1668 /* Serialize XML subtree to buffer preserving XML indentation.
1669 * @context is session context
1670 * @subtree is XML element to be serialized (with children)
1671 * @buffer is automatically reallocated buffer where serialize to
1672 * @length is size of serialized stream in bytes
1673 * @return standard error code, free @buffer in case of error */
1674 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1675 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1676 isds_error err
= IE_SUCCESS
;
1677 xmlBufferPtr xml_buffer
= NULL
;
1678 xmlSaveCtxtPtr save_ctx
= NULL
;
1679 xmlDocPtr subtree_doc
= NULL
;
1680 xmlNodePtr subtree_copy
;
1684 if (!context
) return IE_INVALID_CONTEXT
;
1685 if (!buffer
) return IE_INVAL
;
1687 if (!subtree
|| !length
) return IE_INVAL
;
1689 /* Make temporary XML document with @subtree root element */
1690 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1691 * It can result in not well-formed on invalid XML tree (e.g. name space
1692 * prefix definition can miss. */
1695 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1697 isds_log_message(context
, _("Could not build temporary document"));
1702 /* XXX: Copy subtree and attach the copy to document.
1703 * One node can not bee attached into more document at the same time.
1704 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1706 * XXX: Check xmlSaveTree() too. */
1707 subtree_copy
= xmlCopyNodeList(subtree
);
1708 if (!subtree_copy
) {
1709 isds_log_message(context
, _("Could not copy subtree"));
1713 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1715 /* Only this way we get namespace definition as @xmlns:isds,
1716 * otherwise we get namespace prefix without definition */
1717 /* FIXME: Don't overwrite original default namespace */
1718 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1720 isds_log_message(context
, _("Could not create ISDS name space"));
1724 xmlSetNs(subtree_copy
, isds_ns
);
1727 /* Serialize the document into buffer */
1728 xml_buffer
= xmlBufferCreate();
1730 isds_log_message(context
, _("Could not create xmlBuffer"));
1734 /* Last argument 0 means to not format the XML tree */
1735 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1737 isds_log_message(context
, _("Could not create XML serializer"));
1741 /* XXX: According LibXML documentation, this function does not return
1742 * meaningful value yet */
1743 xmlSaveDoc(save_ctx
, subtree_doc
);
1744 if (-1 == xmlSaveFlush(save_ctx
)) {
1745 isds_log_message(context
,
1746 _("Could not serialize XML subtree"));
1750 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1751 * even after xmlSaveFlush(). Thus close it here */
1752 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1755 /* Store and detach buffer from xml_buffer */
1756 *buffer
= xml_buffer
->content
;
1757 *length
= xml_buffer
->use
;
1758 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1761 new_buffer
= realloc(*buffer
, *length
);
1762 if (new_buffer
) *buffer
= new_buffer
;
1770 xmlSaveClose(save_ctx
);
1771 xmlBufferFree(xml_buffer
);
1772 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1775 #endif /* HAVE_LIBCURL */
1779 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1780 * @context is session context
1781 * @document is original document where @nodeset points to
1782 * @nodeset is XPath node set to dump (recursively)
1783 * @buffer is automatically reallocated buffer where serialize to
1784 * @length is size of serialized stream in bytes
1785 * @return standard error code, free @buffer in case of error */
1786 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1787 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1788 void **buffer
, size_t *length
) {
1789 isds_error err
= IE_SUCCESS
;
1790 xmlBufferPtr xml_buffer
= NULL
;
1793 if (!context
) return IE_INVALID_CONTEXT
;
1794 if (!buffer
) return IE_INVAL
;
1796 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1799 /* Empty node set results into NULL buffer */
1800 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1804 /* Resulting the document into buffer */
1805 xml_buffer
= xmlBufferCreate();
1807 isds_log_message(context
, _("Could not create xmlBuffer"));
1812 /* Iterate over all nodes */
1813 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1815 * XXX: xmlNodeDump() appends to xml_buffer. */
1817 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1818 isds_log_message(context
, _("Could not dump XML node"));
1824 /* Store and detach buffer from xml_buffer */
1825 *buffer
= xml_buffer
->content
;
1826 *length
= xml_buffer
->use
;
1827 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1830 new_buffer
= realloc(*buffer
, *length
);
1831 if (new_buffer
) *buffer
= new_buffer
;
1840 xmlBufferFree(xml_buffer
);
1846 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1847 * @context is session context
1848 * @document is original document where @nodeset points to
1849 * @nodeset is XPath node set to dump (recursively)
1850 * @buffer is automatically reallocated buffer where serialize to
1851 * @length is size of serialized stream in bytes
1852 * @return standard error code, free @buffer in case of error */
1853 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1854 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1855 void **buffer
, size_t *length
) {
1856 isds_error err
= IE_SUCCESS
;
1857 xmlBufferPtr xml_buffer
= NULL
;
1858 xmlSaveCtxtPtr save_ctx
= NULL
;
1861 if (!context
) return IE_INVALID_CONTEXT
;
1862 if (!buffer
) return IE_INVAL
;
1864 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1867 /* Empty node set results into NULL buffer */
1868 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1872 /* Resulting the document into buffer */
1873 xml_buffer
= xmlBufferCreate();
1875 isds_log_message(context
, _("Could not create xmlBuffer"));
1879 if (xmlSubstituteEntitiesDefault(1)) {
1880 isds_log_message(context
, _("Could not disable attribute escaping"));
1884 /* Last argument means:
1885 * 0 to not format the XML tree
1886 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1887 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1888 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1890 isds_log_message(context
, _("Could not create XML serializer"));
1894 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1895 isds_log_message(context, _("Could not disable attribute escaping"));
1901 /* Iterate over all nodes */
1902 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1904 * XXX: xmlNodeDump() appends to xml_buffer. */
1906 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1908 /* XXX: According LibXML documentation, this function does not return
1909 * meaningful value yet */
1910 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1911 if (-1 == xmlSaveFlush(save_ctx
)) {
1912 isds_log_message(context
,
1913 _("Could not serialize XML subtree"));
1919 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1920 * even after xmlSaveFlush(). Thus close it here */
1921 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1923 /* Store and detach buffer from xml_buffer */
1924 *buffer
= xml_buffer
->content
;
1925 *length
= xml_buffer
->use
;
1926 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1929 new_buffer
= realloc(*buffer
, *length
);
1930 if (new_buffer
) *buffer
= new_buffer
;
1938 xmlSaveClose(save_ctx
);
1939 xmlBufferFree(xml_buffer
);
1946 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1947 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1948 if (!string
|| !type
) return IE_INVAL
;
1950 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1952 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1954 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1955 *type
= DBTYPE_PFO_ADVOK
;
1956 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1957 *type
= DBTYPE_PFO_DANPOR
;
1958 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1959 *type
= DBTYPE_PFO_INSSPR
;
1960 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1962 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1963 *type
= DBTYPE_PO_ZAK
;
1964 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1965 *type
= DBTYPE_PO_REQ
;
1966 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1968 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1969 *type
= DBTYPE_OVM_NOTAR
;
1970 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1971 *type
= DBTYPE_OVM_EXEKUT
;
1972 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1973 *type
= DBTYPE_OVM_REQ
;
1980 /* Convert ISDS dbType enum @type to UTF-8 string.
1981 * @Return pointer to static string, or NULL if unknown enum value */
1982 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
1984 /* DBTYPE_SYSTEM is invalid value from point of view of public
1985 * SOAP interface. */
1986 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
1987 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
1988 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
1989 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
1990 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
1991 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
1992 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
1993 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
1994 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
1995 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
1996 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
1997 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
1998 default: return NULL
; break;
2003 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2004 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2005 if (!string
|| !type
) return IE_INVAL
;
2007 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2008 *type
= USERTYPE_PRIMARY
;
2009 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2010 *type
= USERTYPE_ENTRUSTED
;
2011 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2012 *type
= USERTYPE_ADMINISTRATOR
;
2013 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2014 *type
= USERTYPE_OFFICIAL
;
2015 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2016 *type
= USERTYPE_OFFICIAL_CERT
;
2017 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2018 *type
= USERTYPE_LIQUIDATOR
;
2025 /* Convert ISDS userType enum @type to UTF-8 string.
2026 * @Return pointer to static string, or NULL if unknown enum value */
2027 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2029 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2030 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2031 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2032 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2033 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2034 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2035 default: return NULL
; break;
2040 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2041 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2042 isds_sender_type
*type
) {
2043 if (!string
|| !type
) return IE_INVAL
;
2045 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2046 *type
= SENDERTYPE_PRIMARY
;
2047 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2048 *type
= SENDERTYPE_ENTRUSTED
;
2049 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2050 *type
= SENDERTYPE_ADMINISTRATOR
;
2051 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2052 *type
= SENDERTYPE_OFFICIAL
;
2053 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2054 *type
= SENDERTYPE_VIRTUAL
;
2055 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2056 *type
= SENDERTYPE_OFFICIAL_CERT
;
2057 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2058 *type
= SENDERTYPE_LIQUIDATOR
;
2065 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2066 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2067 isds_payment_type
*type
) {
2068 if (!string
|| !type
) return IE_INVAL
;
2070 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2071 *type
= PAYMENT_SENDER
;
2072 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2073 *type
= PAYMENT_RESPONSE
;
2074 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2075 *type
= PAYMENT_SPONSOR
;
2076 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2077 *type
= PAYMENT_SPONSOR_LIMITED
;
2078 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2079 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2080 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2081 *type
= PAYMENT_STAMP
;
2088 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2089 * ciEventType is integer but we convert it from string representation
2091 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2092 isds_credit_event_type
*type
) {
2093 if (!string
|| !type
) return IE_INVAL
;
2095 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2096 *type
= ISDS_CREDIT_CHARGED
;
2097 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2098 *type
= ISDS_CREDIT_DISCHARGED
;
2099 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2100 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2101 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2102 *type
= ISDS_CREDIT_STORAGE_SET
;
2103 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2104 *type
= ISDS_CREDIT_EXPIRED
;
2111 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2112 * @Return pointer to static string, or NULL if unknown enum value */
2113 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2115 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2116 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2117 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2118 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2119 default: return NULL
; break;
2122 #endif /* HAVE_LIBCURL */
2125 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2126 * @Return IE_ENUM if @string is not valid enum member */
2127 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2128 isds_FileMetaType
*type
) {
2129 if (!string
|| !type
) return IE_INVAL
;
2131 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2132 *type
= FILEMETATYPE_MAIN
;
2133 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2134 *type
= FILEMETATYPE_ENCLOSURE
;
2135 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2136 *type
= FILEMETATYPE_SIGNATURE
;
2137 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2138 *type
= FILEMETATYPE_META
;
2145 /* Convert UTF-8 @string to ISDS hash @algorithm.
2146 * @Return IE_ENUM if @string is not valid enum member */
2147 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2148 isds_hash_algorithm
*algorithm
) {
2149 if (!string
|| !algorithm
) return IE_INVAL
;
2151 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2152 *algorithm
= HASH_ALGORITHM_MD5
;
2153 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2154 *algorithm
= HASH_ALGORITHM_SHA_1
;
2155 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2156 *algorithm
= HASH_ALGORITHM_SHA_224
;
2157 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2158 *algorithm
= HASH_ALGORITHM_SHA_256
;
2159 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2160 *algorithm
= HASH_ALGORITHM_SHA_384
;
2161 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2162 *algorithm
= HASH_ALGORITHM_SHA_512
;
2170 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2171 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2172 if (!time
|| !string
) return IE_INVAL
;
2174 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2175 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2182 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2183 * respects the @time microseconds too. */
2184 static isds_error
timeval2timestring(const struct timeval
*time
,
2188 if (!time
|| !string
) return IE_INVAL
;
2190 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2191 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2193 /* TODO: small negative year should be formatted as "-0012". This is not
2194 * true for glibc "%04d". We should implement it.
2195 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2196 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2197 if (-1 == isds_asprintf((char **) string
,
2198 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2199 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2200 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2206 #endif /* HAVE_LIBCURL */
2209 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2210 * It respects microseconds too.
2211 * In case of error, @time will be freed. */
2212 static isds_error
timestring2timeval(const xmlChar
*string
,
2213 struct timeval
**time
) {
2215 char *offset
, *delim
, *endptr
;
2217 int offset_hours
, offset_minutes
;
2223 if (!time
) return IE_INVAL
;
2229 memset(&broken
, 0, sizeof(broken
));
2232 *time
= calloc(1, sizeof(**time
));
2233 if (!*time
) return IE_NOMEM
;
2235 memset(*time
, 0, sizeof(**time
));
2239 /* xsd:date is ISO 8601 string, thus ASCII */
2240 /*TODO: negative year */
2244 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2245 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2246 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2252 broken
.tm_year
-= 1900;
2254 offset
= (char*)string
+ i
;
2256 /* Parse date and time without subseconds and offset */
2257 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2264 /* Get subseconds */
2265 if (*offset
== '.' ) {
2268 /* Copy first 6 digits, pad it with zeros.
2269 * XXX: It truncates longer number, no round.
2270 * Current server implementation uses only millisecond resolution. */
2271 /* TODO: isdigit() is locale sensitive */
2273 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
2275 subseconds
[i
] = *offset
;
2277 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
2278 subseconds
[i
] = '0';
2280 subseconds
[6] = '\0';
2282 /* Convert it into integer */
2283 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
2284 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
2285 (*time
)->tv_usec
== LONG_MAX
) {
2290 /* move to the zone offset delimiter or signal NULL*/
2291 delim
= strchr(offset
, '-');
2293 delim
= strchr(offset
, '+');
2295 delim
= strchr(offset
, 'Z');
2299 /* Get zone offset */
2300 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2301 * "" equals to "Z" and it means UTC zone. */
2302 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2303 * colon separator */
2304 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2305 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2309 if (*offset
== '+') {
2310 broken
.tm_hour
-= offset_hours
;
2311 broken
.tm_min
-= offset_minutes
;
2313 broken
.tm_hour
+= offset_hours
;
2314 broken
.tm_min
+= offset_minutes
;
2318 /* Convert to time_t */
2319 (*time
)->tv_sec
= _isds_timegm(&broken
);
2320 if ((*time
)->tv_sec
== (time_t) -1) {
2329 /* Convert unsigned int into isds_message_status.
2330 * @context is session context
2331 * @number is pointer to number value. NULL will be treated as invalid value.
2332 * @status is automatically reallocated status
2333 * @return IE_SUCCESS, or error code and free status */
2334 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2335 const unsigned long int *number
, isds_message_status
**status
) {
2336 if (!context
) return IE_INVALID_CONTEXT
;
2337 if (!status
) return IE_INVAL
;
2339 free(*status
); *status
= NULL
;
2340 if (!number
) return IE_INVAL
;
2342 if (*number
< 1 || *number
> 10) {
2343 isds_printf_message(context
, _("Invalid message status value: %lu"),
2348 *status
= malloc(sizeof(**status
));
2349 if (!*status
) return IE_NOMEM
;
2351 **status
= 1 << *number
;
2356 /* Convert event description string into isds_event members type and
2358 * @string is raw event description starting with event prefix
2359 * @event is structure where to store type and stripped description to
2360 * @return standard error code, unknown prefix is not classified as an error.
2362 static isds_error
eventstring2event(const xmlChar
*string
,
2363 struct isds_event
* event
) {
2364 const xmlChar
*known_prefixes
[] = {
2375 const isds_event_type types
[] = {
2376 EVENT_ENTERED_SYSTEM
,
2377 EVENT_ACCEPTED_BY_RECIPIENT
,
2378 EVENT_ACCEPTED_BY_FICTION
,
2379 EVENT_UNDELIVERABLE
,
2380 EVENT_COMMERCIAL_ACCEPTED
,
2382 EVENT_PRIMARY_LOGIN
,
2383 EVENT_ENTRUSTED_LOGIN
,
2389 if (!string
|| !event
) return IE_INVAL
;
2392 event
->type
= malloc(sizeof(*event
->type
));
2393 if (!(event
->type
)) return IE_NOMEM
;
2395 zfree(event
->description
);
2397 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2399 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2401 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2402 /* Prefix is known */
2403 *event
->type
= types
[index
];
2405 /* Strip prefix from description and spaces */
2406 /* TODO: Recognize all white spaces from UCS blank class and
2407 * operate on UTF-8 chars. */
2408 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2409 event
->description
= strdup((char *) (string
+ length
));
2410 if (!(event
->description
)) return IE_NOMEM
;
2416 /* Unknown event prefix.
2417 * XSD allows any string */
2418 char *string_locale
= _isds_utf82locale((char *) string
);
2419 isds_log(ILF_ISDS
, ILL_WARNING
,
2420 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2421 free(string_locale
);
2423 *event
->type
= EVENT_UKNOWN
;
2424 event
->description
= strdup((char *) string
);
2425 if (!(event
->description
)) return IE_NOMEM
;
2431 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2432 * and leave label */
2433 #define EXTRACT_STRING(element, string) { \
2434 xmlXPathFreeObject(result); \
2435 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2436 if (NULL == (result)) { \
2440 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2441 if (result->nodesetval->nodeNr > 1) { \
2442 isds_printf_message(context, _("Multiple %s element"), element); \
2446 (string) = (char *) \
2447 xmlXPathCastNodeSetToString(result->nodesetval); \
2448 if (NULL == (string)) { \
2455 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2457 char *string = NULL; \
2458 EXTRACT_STRING(element, string); \
2461 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2462 if (!(booleanPtr)) { \
2468 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2469 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2470 *(booleanPtr) = 1; \
2471 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2472 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2473 *(booleanPtr) = 0; \
2475 char *string_locale = _isds_utf82locale((char*)string); \
2476 isds_printf_message(context, \
2477 _("%s value is not valid boolean: %s"), \
2478 element, string_locale); \
2479 free(string_locale); \
2489 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2491 char *string = NULL; \
2492 EXTRACT_STRING(element, string); \
2497 number = strtol((char*)string, &endptr, 10); \
2499 if (*endptr != '\0') { \
2500 char *string_locale = _isds_utf82locale((char *)string); \
2501 isds_printf_message(context, \
2502 _("%s is not valid integer: %s"), \
2503 element, string_locale); \
2504 free(string_locale); \
2510 if (number == LONG_MIN || number == LONG_MAX) { \
2511 char *string_locale = _isds_utf82locale((char *)string); \
2512 isds_printf_message(context, \
2513 _("%s value out of range of long int: %s"), \
2514 element, string_locale); \
2515 free(string_locale); \
2521 free(string); string = NULL; \
2523 if (!(preallocated)) { \
2524 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2525 if (!(longintPtr)) { \
2530 *(longintPtr) = number; \
2534 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2536 char *string = NULL; \
2537 EXTRACT_STRING(element, string); \
2542 number = strtol((char*)string, &endptr, 10); \
2544 if (*endptr != '\0') { \
2545 char *string_locale = _isds_utf82locale((char *)string); \
2546 isds_printf_message(context, \
2547 _("%s is not valid integer: %s"), \
2548 element, string_locale); \
2549 free(string_locale); \
2555 if (number == LONG_MIN || number == LONG_MAX) { \
2556 char *string_locale = _isds_utf82locale((char *)string); \
2557 isds_printf_message(context, \
2558 _("%s value out of range of long int: %s"), \
2559 element, string_locale); \
2560 free(string_locale); \
2566 free(string); string = NULL; \
2568 isds_printf_message(context, \
2569 _("%s value is negative: %ld"), element, number); \
2574 if (!(preallocated)) { \
2575 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2576 if (!(ulongintPtr)) { \
2581 *(ulongintPtr) = number; \
2585 #define EXTRACT_DATE(element, tmPtr) { \
2586 char *string = NULL; \
2587 EXTRACT_STRING(element, string); \
2588 if (NULL != string) { \
2589 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2590 if (NULL == (tmPtr)) { \
2595 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2597 if (err == IE_NOTSUP) { \
2599 char *string_locale = _isds_utf82locale(string); \
2600 char *element_locale = _isds_utf82locale(element); \
2601 isds_printf_message(context, _("Invalid %s value: %s"), \
2602 element_locale, string_locale); \
2603 free(string_locale); \
2604 free(element_locale); \
2613 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2614 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2616 if ((required) && (!string)) { \
2617 char *attribute_locale = _isds_utf82locale(attribute); \
2618 char *element_locale = \
2619 _isds_utf82locale((char *)xpath_ctx->node->name); \
2620 isds_printf_message(context, \
2621 _("Could not extract required %s attribute value from " \
2622 "%s element"), attribute_locale, element_locale); \
2623 free(element_locale); \
2624 free(attribute_locale); \
2631 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2633 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2634 (xmlChar *) (string)); \
2636 isds_printf_message(context, \
2637 _("Could not add %s child to %s element"), \
2638 element, (parent)->name); \
2644 #define INSERT_STRING(parent, element, string) \
2645 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2647 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2649 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2650 else { INSERT_STRING(parent, element, "false"); } \
2653 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2656 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2658 INSERT_STRING(parent, element, NULL); \
2662 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2663 if ((longintPtr)) { \
2664 /* FIXME: locale sensitive */ \
2665 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2669 INSERT_STRING(parent, element, buffer) \
2670 free(buffer); (buffer) = NULL; \
2671 } else { INSERT_STRING(parent, element, NULL) } \
2674 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2675 if ((ulongintPtr)) { \
2676 /* FIXME: locale sensitive */ \
2677 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2681 INSERT_STRING(parent, element, buffer) \
2682 free(buffer); (buffer) = NULL; \
2683 } else { INSERT_STRING(parent, element, NULL) } \
2686 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2688 /* FIXME: locale sensitive */ \
2689 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2693 INSERT_STRING(parent, element, buffer) \
2694 free(buffer); (buffer) = NULL; \
2697 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2699 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2701 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2702 (xmlChar *) (string)); \
2703 if (!attribute_node) { \
2704 isds_printf_message(context, _("Could not add %s " \
2705 "attribute to %s element"), \
2706 (attribute), (parent)->name); \
2712 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2714 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2715 if (length > (maximum)) { \
2716 isds_printf_message(context, \
2717 ngettext("%s has more than %d characters", \
2718 "%s has more than %d characters", (maximum)), \
2719 (name), (maximum)); \
2723 if (length < (minimum)) { \
2724 isds_printf_message(context, \
2725 ngettext("%s has less than %d characters", \
2726 "%s has less than %d characters", (minimum)), \
2727 (name), (minimum)); \
2734 #define INSERT_ELEMENT(child, parent, element) \
2736 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2738 isds_printf_message(context, \
2739 _("Could not add %s child to %s element"), \
2740 (element), (parent)->name); \
2747 /* Find child element by name in given XPath context and switch context onto
2748 * it. The child must be uniq and must exist. Otherwise fails.
2749 * @context is ISDS context
2750 * @child is child element name
2751 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2752 * into it child. In error case, the @xpath_ctx keeps original value. */
2753 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2754 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2755 isds_error err
= IE_SUCCESS
;
2756 xmlXPathObjectPtr result
= NULL
;
2758 if (!context
) return IE_INVALID_CONTEXT
;
2759 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2762 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2769 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2770 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2771 char *child_locale
= _isds_utf82locale((char*) child
);
2772 isds_printf_message(context
,
2773 _("%s element does not contain %s child"),
2774 parent_locale
, child_locale
);
2776 free(parent_locale
);
2782 if (result
->nodesetval
->nodeNr
> 1) {
2783 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2784 char *child_locale
= _isds_utf82locale((char*) child
);
2785 isds_printf_message(context
,
2786 _("%s element contains multiple %s children"),
2787 parent_locale
, child_locale
);
2789 free(parent_locale
);
2794 /* Switch context */
2795 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2798 xmlXPathFreeObject(result
);
2805 /* Find and convert XSD:gPersonName group in current node into structure
2806 * @context is ISDS context
2807 * @personName is automatically reallocated person name structure. If no member
2808 * value is found, will be freed.
2809 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2811 * In case of error @personName will be freed. */
2812 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2813 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2814 isds_error err
= IE_SUCCESS
;
2815 xmlXPathObjectPtr result
= NULL
;
2817 if (!context
) return IE_INVALID_CONTEXT
;
2818 if (!personName
) return IE_INVAL
;
2819 isds_PersonName_free(personName
);
2820 if (!xpath_ctx
) return IE_INVAL
;
2823 *personName
= calloc(1, sizeof(**personName
));
2829 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2830 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2831 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2832 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2834 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2835 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2836 isds_PersonName_free(personName
);
2839 if (err
) isds_PersonName_free(personName
);
2840 xmlXPathFreeObject(result
);
2845 /* Find and convert XSD:gAddress group in current node into structure
2846 * @context is ISDS context
2847 * @address is automatically reallocated address structure. If no member
2848 * value is found, will be freed.
2849 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2851 * In case of error @address will be freed. */
2852 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2853 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2854 isds_error err
= IE_SUCCESS
;
2855 xmlXPathObjectPtr result
= NULL
;
2857 if (!context
) return IE_INVALID_CONTEXT
;
2858 if (!address
) return IE_INVAL
;
2859 isds_Address_free(address
);
2860 if (!xpath_ctx
) return IE_INVAL
;
2863 *address
= calloc(1, sizeof(**address
));
2869 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2870 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2871 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2872 EXTRACT_STRING("isds:adNumberInMunicipality",
2873 (*address
)->adNumberInMunicipality
);
2874 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2875 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2877 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2878 !(*address
)->adNumberInStreet
&&
2879 !(*address
)->adNumberInMunicipality
&&
2880 !(*address
)->adZipCode
&& !(*address
)->adState
)
2881 isds_Address_free(address
);
2884 if (err
) isds_Address_free(address
);
2885 xmlXPathFreeObject(result
);
2890 /* Find and convert isds:biDate element in current node into structure
2891 * @context is ISDS context
2892 * @biDate is automatically reallocated birth date structure. If no member
2893 * value is found, will be freed.
2894 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2896 * In case of error @biDate will be freed. */
2897 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2898 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2899 isds_error err
= IE_SUCCESS
;
2900 xmlXPathObjectPtr result
= NULL
;
2901 char *string
= NULL
;
2903 if (!context
) return IE_INVALID_CONTEXT
;
2904 if (!biDate
) return IE_INVAL
;
2906 if (!xpath_ctx
) return IE_INVAL
;
2908 EXTRACT_STRING("isds:biDate", string
);
2910 *biDate
= calloc(1, sizeof(**biDate
));
2915 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
2917 if (err
== IE_NOTSUP
) {
2919 char *string_locale
= _isds_utf82locale(string
);
2920 isds_printf_message(context
,
2921 _("Invalid isds:biDate value: %s"), string_locale
);
2922 free(string_locale
);
2929 if (err
) zfree(*biDate
);
2931 xmlXPathFreeObject(result
);
2936 /* Convert isds:dBOwnerInfo XML tree into structure
2937 * @context is ISDS context
2938 * @db_owner_info is automatically reallocated box owner info structure
2939 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2940 * In case of error @db_owner_info will be freed. */
2941 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
2942 struct isds_DbOwnerInfo
**db_owner_info
,
2943 xmlXPathContextPtr xpath_ctx
) {
2944 isds_error err
= IE_SUCCESS
;
2945 xmlXPathObjectPtr result
= NULL
;
2946 char *string
= NULL
;
2948 if (!context
) return IE_INVALID_CONTEXT
;
2949 if (!db_owner_info
) return IE_INVAL
;
2950 isds_DbOwnerInfo_free(db_owner_info
);
2951 if (!xpath_ctx
) return IE_INVAL
;
2954 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
2955 if (!*db_owner_info
) {
2960 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
2962 EXTRACT_STRING("isds:dbType", string
);
2964 (*db_owner_info
)->dbType
=
2965 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
2966 if (!(*db_owner_info
)->dbType
) {
2970 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
2972 zfree((*db_owner_info
)->dbType
);
2973 if (err
== IE_ENUM
) {
2975 char *string_locale
= _isds_utf82locale(string
);
2976 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
2978 free(string_locale
);
2985 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
2987 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
2989 if (err
) goto leave
;
2991 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
2993 (*db_owner_info
)->birthInfo
=
2994 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
2995 if (!(*db_owner_info
)->birthInfo
) {
2999 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3001 if (err
) goto leave
;
3002 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3003 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3004 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3005 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3006 !(*db_owner_info
)->birthInfo
->biCity
&&
3007 !(*db_owner_info
)->birthInfo
->biCounty
&&
3008 !(*db_owner_info
)->birthInfo
->biState
)
3009 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3011 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3012 if (err
) goto leave
;
3014 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3015 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3016 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3017 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3018 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3020 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3022 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3023 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3024 (*db_owner_info
)->dbOpenAddressing
);
3027 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3029 xmlXPathFreeObject(result
);
3034 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3035 * @context is session context
3036 * @owner is libisds structure with box description
3037 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3038 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3039 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3041 isds_error err
= IE_SUCCESS
;
3043 xmlChar
*string
= NULL
;
3045 if (!context
) return IE_INVALID_CONTEXT
;
3046 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3049 /* Build XSD:tDbOwnerInfo */
3050 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3051 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3054 if (owner
->dbType
) {
3055 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3057 isds_printf_message(context
, _("Invalid dbType value: %d"),
3062 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3064 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3065 if (owner
->personName
) {
3066 INSERT_STRING(db_owner_info
, "pnFirstName",
3067 owner
->personName
->pnFirstName
);
3068 INSERT_STRING(db_owner_info
, "pnMiddleName",
3069 owner
->personName
->pnMiddleName
);
3070 INSERT_STRING(db_owner_info
, "pnLastName",
3071 owner
->personName
->pnLastName
);
3072 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3073 owner
->personName
->pnLastNameAtBirth
);
3075 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3076 if (owner
->birthInfo
) {
3077 if (owner
->birthInfo
->biDate
) {
3078 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3079 INSERT_STRING(db_owner_info
, "biDate", string
);
3080 free(string
); string
= NULL
;
3082 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3083 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3084 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3086 if (owner
->address
) {
3087 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3088 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3089 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3090 owner
->address
->adNumberInStreet
);
3091 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3092 owner
->address
->adNumberInMunicipality
);
3093 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3094 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3096 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3097 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3098 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3100 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3101 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3103 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3104 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3106 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3108 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3109 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3110 owner
->dbOpenAddressing
);
3118 /* Convert XSD:tDbUserInfo XML tree into structure
3119 * @context is ISDS context
3120 * @db_user_info is automatically reallocated user info structure
3121 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3122 * In case of error @db_user_info will be freed. */
3123 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3124 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3125 isds_error err
= IE_SUCCESS
;
3126 xmlXPathObjectPtr result
= NULL
;
3127 char *string
= NULL
;
3129 if (!context
) return IE_INVALID_CONTEXT
;
3130 if (!db_user_info
) return IE_INVAL
;
3131 isds_DbUserInfo_free(db_user_info
);
3132 if (!xpath_ctx
) return IE_INVAL
;
3135 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3136 if (!*db_user_info
) {
3141 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3143 EXTRACT_STRING("isds:userType", string
);
3145 (*db_user_info
)->userType
=
3146 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3147 if (!(*db_user_info
)->userType
) {
3151 err
= string2isds_UserType((xmlChar
*)string
,
3152 (*db_user_info
)->userType
);
3154 zfree((*db_user_info
)->userType
);
3155 if (err
== IE_ENUM
) {
3157 char *string_locale
= _isds_utf82locale(string
);
3158 isds_printf_message(context
,
3159 _("Unknown isds:userType value: %s"), string_locale
);
3160 free(string_locale
);
3167 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3169 (*db_user_info
)->personName
=
3170 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3171 if (!(*db_user_info
)->personName
) {
3176 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3178 if (err
) goto leave
;
3180 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3181 if (err
) goto leave
;
3183 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3184 if (err
) goto leave
;
3186 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3187 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3189 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3190 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3191 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3193 /* ???: Default value is "CZ" according specification. Should we provide
3195 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3198 if (err
) isds_DbUserInfo_free(db_user_info
);
3200 xmlXPathFreeObject(result
);
3205 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3206 * @context is session context
3207 * @user is libisds structure with user description
3208 * @db_user_info is XML element of XSD:tDbUserInfo */
3209 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3210 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3212 isds_error err
= IE_SUCCESS
;
3214 xmlChar
*string
= NULL
;
3216 if (!context
) return IE_INVALID_CONTEXT
;
3217 if (!user
|| !db_user_info
) return IE_INVAL
;
3219 /* Build XSD:tDbUserInfo */
3220 if (user
->personName
) {
3221 INSERT_STRING(db_user_info
, "pnFirstName",
3222 user
->personName
->pnFirstName
);
3223 INSERT_STRING(db_user_info
, "pnMiddleName",
3224 user
->personName
->pnMiddleName
);
3225 INSERT_STRING(db_user_info
, "pnLastName",
3226 user
->personName
->pnLastName
);
3227 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3228 user
->personName
->pnLastNameAtBirth
);
3230 if (user
->address
) {
3231 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3232 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3233 INSERT_STRING(db_user_info
, "adNumberInStreet",
3234 user
->address
->adNumberInStreet
);
3235 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3236 user
->address
->adNumberInMunicipality
);
3237 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3238 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3241 if (!tm2datestring(user
->biDate
, &string
))
3242 INSERT_STRING(db_user_info
, "biDate", string
);
3245 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3246 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3249 if (user
->userType
) {
3250 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3252 isds_printf_message(context
, _("Invalid userType value: %d"),
3257 INSERT_STRING(db_user_info
, "userType", type_string
);
3260 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3261 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3262 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3263 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3264 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3265 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3266 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3267 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3268 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3276 /* Convert XSD:tPDZRec XML tree into structure
3277 * @context is ISDS context
3278 * @permission is automatically reallocated commercial permission structure
3279 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3280 * In case of error @permission will be freed. */
3281 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3282 struct isds_commercial_permission
**permission
,
3283 xmlXPathContextPtr xpath_ctx
) {
3284 isds_error err
= IE_SUCCESS
;
3285 xmlXPathObjectPtr result
= NULL
;
3286 char *string
= NULL
;
3288 if (!context
) return IE_INVALID_CONTEXT
;
3289 if (!permission
) return IE_INVAL
;
3290 isds_commercial_permission_free(permission
);
3291 if (!xpath_ctx
) return IE_INVAL
;
3294 *permission
= calloc(1, sizeof(**permission
));
3300 EXTRACT_STRING("isds:PDZType", string
);
3302 err
= string2isds_payment_type((xmlChar
*)string
,
3303 &(*permission
)->type
);
3305 if (err
== IE_ENUM
) {
3307 char *string_locale
= _isds_utf82locale(string
);
3308 isds_printf_message(context
,
3309 _("Unknown isds:PDZType value: %s"), string_locale
);
3310 free(string_locale
);
3317 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3318 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3320 EXTRACT_STRING("isds:PDZExpire", string
);
3322 err
= timestring2timeval((xmlChar
*) string
,
3323 &((*permission
)->expiration
));
3325 char *string_locale
= _isds_utf82locale(string
);
3326 if (err
== IE_DATE
) err
= IE_ISDS
;
3327 isds_printf_message(context
,
3328 _("Could not convert PDZExpire as ISO time: %s"),
3330 free(string_locale
);
3336 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3337 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3340 if (err
) isds_commercial_permission_free(permission
);
3342 xmlXPathFreeObject(result
);
3347 /* Convert XSD:tCiRecord XML tree into structure
3348 * @context is ISDS context
3349 * @event is automatically reallocated commercial credit event structure
3350 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3351 * In case of error @event will be freed. */
3352 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3353 struct isds_credit_event
**event
,
3354 xmlXPathContextPtr xpath_ctx
) {
3355 isds_error err
= IE_SUCCESS
;
3356 xmlXPathObjectPtr result
= NULL
;
3357 char *string
= NULL
;
3358 long int *number_ptr
;
3360 if (!context
) return IE_INVALID_CONTEXT
;
3361 if (!event
) return IE_INVAL
;
3362 isds_credit_event_free(event
);
3363 if (!xpath_ctx
) return IE_INVAL
;
3366 *event
= calloc(1, sizeof(**event
));
3372 EXTRACT_STRING("isds:ciEventTime", string
);
3374 err
= timestring2timeval((xmlChar
*) string
,
3377 char *string_locale
= _isds_utf82locale(string
);
3378 if (err
== IE_DATE
) err
= IE_ISDS
;
3379 isds_printf_message(context
,
3380 _("Could not convert ciEventTime as ISO time: %s"),
3382 free(string_locale
);
3388 EXTRACT_STRING("isds:ciEventType", string
);
3390 err
= string2isds_credit_event_type((xmlChar
*)string
,
3393 if (err
== IE_ENUM
) {
3395 char *string_locale
= _isds_utf82locale(string
);
3396 isds_printf_message(context
,
3397 _("Unknown isds:ciEventType value: %s"), string_locale
);
3398 free(string_locale
);
3405 number_ptr
= &((*event
)->credit_change
);
3406 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3407 number_ptr
= &(*event
)->new_credit
;
3408 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3410 switch((*event
)->type
) {
3411 case ISDS_CREDIT_CHARGED
:
3412 EXTRACT_STRING("isds:ciTransID",
3413 (*event
)->details
.charged
.transaction
);
3415 case ISDS_CREDIT_DISCHARGED
:
3416 EXTRACT_STRING("isds:ciTransID",
3417 (*event
)->details
.discharged
.transaction
);
3419 case ISDS_CREDIT_MESSAGE_SENT
:
3420 EXTRACT_STRING("isds:ciRecipientID",
3421 (*event
)->details
.message_sent
.recipient
);
3422 EXTRACT_STRING("isds:ciPDZID",
3423 (*event
)->details
.message_sent
.message_id
);
3425 case ISDS_CREDIT_STORAGE_SET
:
3426 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3427 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3428 EXTRACT_DATE("isds:ciNewFrom",
3429 (*event
)->details
.storage_set
.new_valid_from
);
3430 EXTRACT_DATE("isds:ciNewTo",
3431 (*event
)->details
.storage_set
.new_valid_to
);
3432 EXTRACT_LONGINT("isds:ciOldCapacity",
3433 (*event
)->details
.storage_set
.old_capacity
, 0);
3434 EXTRACT_DATE("isds:ciOldFrom",
3435 (*event
)->details
.storage_set
.old_valid_from
);
3436 EXTRACT_DATE("isds:ciOldTo",
3437 (*event
)->details
.storage_set
.old_valid_to
);
3438 EXTRACT_STRING("isds:ciDoneBy",
3439 (*event
)->details
.storage_set
.initiator
);
3441 case ISDS_CREDIT_EXPIRED
:
3446 if (err
) isds_credit_event_free(event
);
3448 xmlXPathFreeObject(result
);
3453 #endif /* HAVE_LIBCURL */
3456 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3457 * isds_envelope structure. The envelope is automatically allocated but not
3458 * reallocated. The date are just appended into envelope structure.
3459 * @context is ISDS context
3460 * @envelope is automatically allocated message envelope structure
3461 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3462 * In case of error @envelope will be freed. */
3463 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3464 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3465 isds_error err
= IE_SUCCESS
;
3466 xmlXPathObjectPtr result
= NULL
;
3468 if (!context
) return IE_INVALID_CONTEXT
;
3469 if (!envelope
) return IE_INVAL
;
3470 if (!xpath_ctx
) return IE_INVAL
;
3474 /* Allocate envelope */
3475 *envelope
= calloc(1, sizeof(**envelope
));
3481 /* Else free former data */
3482 zfree((*envelope
)->dmSenderOrgUnit
);
3483 zfree((*envelope
)->dmSenderOrgUnitNum
);
3484 zfree((*envelope
)->dbIDRecipient
);
3485 zfree((*envelope
)->dmRecipientOrgUnit
);
3486 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3487 zfree((*envelope
)->dmToHands
);
3488 zfree((*envelope
)->dmAnnotation
);
3489 zfree((*envelope
)->dmRecipientRefNumber
);
3490 zfree((*envelope
)->dmSenderRefNumber
);
3491 zfree((*envelope
)->dmRecipientIdent
);
3492 zfree((*envelope
)->dmSenderIdent
);
3493 zfree((*envelope
)->dmLegalTitleLaw
);
3494 zfree((*envelope
)->dmLegalTitleYear
);
3495 zfree((*envelope
)->dmLegalTitleSect
);
3496 zfree((*envelope
)->dmLegalTitlePar
);
3497 zfree((*envelope
)->dmLegalTitlePoint
);
3498 zfree((*envelope
)->dmPersonalDelivery
);
3499 zfree((*envelope
)->dmAllowSubstDelivery
);
3502 /* Extract envelope elements added by sender or ISDS
3503 * (XSD: gMessageEnvelopeSub type) */
3504 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3505 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3506 (*envelope
)->dmSenderOrgUnitNum
, 0);
3507 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3508 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3509 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3510 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3511 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3512 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3513 EXTRACT_STRING("isds:dmRecipientRefNumber",
3514 (*envelope
)->dmRecipientRefNumber
);
3515 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3516 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3517 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3519 /* Extract envelope elements regarding law reference */
3520 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3521 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3522 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3523 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3524 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3526 /* Extract envelope other elements */
3527 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3528 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3529 (*envelope
)->dmAllowSubstDelivery
);
3532 if (err
) isds_envelope_free(envelope
);
3533 xmlXPathFreeObject(result
);
3539 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3540 * isds_envelope structure. The envelope is automatically allocated but not
3541 * reallocated. The date are just appended into envelope structure.
3542 * @context is ISDS context
3543 * @envelope is automatically allocated message envelope structure
3544 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3545 * In case of error @envelope will be freed. */
3546 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3547 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3548 isds_error err
= IE_SUCCESS
;
3549 xmlXPathObjectPtr result
= NULL
;
3551 if (!context
) return IE_INVALID_CONTEXT
;
3552 if (!envelope
) return IE_INVAL
;
3553 if (!xpath_ctx
) return IE_INVAL
;
3557 /* Allocate envelope */
3558 *envelope
= calloc(1, sizeof(**envelope
));
3564 /* Else free former data */
3565 zfree((*envelope
)->dmID
);
3566 zfree((*envelope
)->dbIDSender
);
3567 zfree((*envelope
)->dmSender
);
3568 zfree((*envelope
)->dmSenderAddress
);
3569 zfree((*envelope
)->dmSenderType
);
3570 zfree((*envelope
)->dmRecipient
);
3571 zfree((*envelope
)->dmRecipientAddress
);
3572 zfree((*envelope
)->dmAmbiguousRecipient
);
3575 /* Extract envelope elements added by ISDS
3576 * (XSD: gMessageEnvelope type) */
3577 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3578 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3579 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3580 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3581 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3582 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3583 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3584 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3585 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3586 (*envelope
)->dmAmbiguousRecipient
);
3588 /* Extract envelope elements added by sender and ISDS
3589 * (XSD: gMessageEnvelope type) */
3590 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3591 if (err
) goto leave
;
3594 if (err
) isds_envelope_free(envelope
);
3595 xmlXPathFreeObject(result
);
3600 /* Convert other envelope elements from XML tree into isds_envelope structure:
3601 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3602 * The envelope is automatically allocated but not reallocated.
3603 * The data are just appended into envelope structure.
3604 * @context is ISDS context
3605 * @envelope is automatically allocated message envelope structure
3606 * @xpath_ctx is XPath context with current node as parent desired elements
3607 * In case of error @envelope will be freed. */
3608 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3609 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3610 isds_error err
= IE_SUCCESS
;
3611 xmlXPathObjectPtr result
= NULL
;
3612 char *string
= NULL
;
3613 unsigned long int *unumber
= NULL
;
3615 if (!context
) return IE_INVALID_CONTEXT
;
3616 if (!envelope
) return IE_INVAL
;
3617 if (!xpath_ctx
) return IE_INVAL
;
3622 *envelope
= calloc(1, sizeof(**envelope
));
3629 zfree((*envelope
)->dmMessageStatus
);
3630 zfree((*envelope
)->dmAttachmentSize
);
3631 zfree((*envelope
)->dmDeliveryTime
);
3632 zfree((*envelope
)->dmAcceptanceTime
);
3636 /* dmMessageStatus element is mandatory */
3637 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3639 isds_log_message(context
,
3640 _("Missing mandatory sisds:dmMessageStatus integer"));
3644 err
= uint2isds_message_status(context
, unumber
,
3645 &((*envelope
)->dmMessageStatus
));
3647 if (err
== IE_ENUM
) err
= IE_ISDS
;
3650 free(unumber
); unumber
= NULL
;
3652 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3655 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3657 err
= timestring2timeval((xmlChar
*) string
,
3658 &((*envelope
)->dmDeliveryTime
));
3660 char *string_locale
= _isds_utf82locale(string
);
3661 if (err
== IE_DATE
) err
= IE_ISDS
;
3662 isds_printf_message(context
,
3663 _("Could not convert dmDeliveryTime as ISO time: %s"),
3665 free(string_locale
);
3671 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3673 err
= timestring2timeval((xmlChar
*) string
,
3674 &((*envelope
)->dmAcceptanceTime
));
3676 char *string_locale
= _isds_utf82locale(string
);
3677 if (err
== IE_DATE
) err
= IE_ISDS
;
3678 isds_printf_message(context
,
3679 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3681 free(string_locale
);
3688 if (err
) isds_envelope_free(envelope
);
3691 xmlXPathFreeObject(result
);
3696 /* Convert message type attribute of current element into isds_envelope
3698 * TODO: This function can be incorporated into append_status_size_times() as
3699 * they are called always together.
3700 * The envelope is automatically allocated but not reallocated.
3701 * The data are just appended into envelope structure.
3702 * @context is ISDS context
3703 * @envelope is automatically allocated message envelope structure
3704 * @xpath_ctx is XPath context with current node as parent of attribute
3705 * carrying message type
3706 * In case of error @envelope will be freed. */
3707 static isds_error
append_message_type(struct isds_ctx
*context
,
3708 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3709 isds_error err
= IE_SUCCESS
;
3711 if (!context
) return IE_INVALID_CONTEXT
;
3712 if (!envelope
) return IE_INVAL
;
3713 if (!xpath_ctx
) return IE_INVAL
;
3718 *envelope
= calloc(1, sizeof(**envelope
));
3725 zfree((*envelope
)->dmType
);
3729 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3731 if (!(*envelope
)->dmType
) {
3732 /* Use default value */
3733 (*envelope
)->dmType
= strdup("V");
3734 if (!(*envelope
)->dmType
) {
3738 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3739 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3740 isds_printf_message(context
,
3741 _("Message type in dmType attribute is not 1 character long: "
3750 if (err
) isds_envelope_free(envelope
);
3756 /* Convert dmType isds_envelope member into XML attribute and append it to
3758 * @context is ISDS context
3759 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3760 * @dm_envelope is XML element the resulting attribute will be appended to.
3761 * @return error code, in case of error context' message is filled. */
3762 static isds_error
insert_message_type(struct isds_ctx
*context
,
3763 const char *type
, xmlNodePtr dm_envelope
) {
3764 isds_error err
= IE_SUCCESS
;
3765 xmlAttrPtr attribute_node
;
3767 if (!context
) return IE_INVALID_CONTEXT
;
3768 if (!dm_envelope
) return IE_INVAL
;
3770 /* Insert optional message type */
3772 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3773 char *type_locale
= _isds_utf82locale(type
);
3774 isds_printf_message(context
,
3775 _("Message type in envelope is not 1 character long: %s"),
3781 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3787 #endif /* HAVE_LIBCURL */
3790 /* Extract message document into reallocated document structure
3791 * @context is ISDS context
3792 * @document is automatically reallocated message documents structure
3793 * @xpath_ctx is XPath context with current node as isds:dmFile
3794 * In case of error @document will be freed. */
3795 static isds_error
extract_document(struct isds_ctx
*context
,
3796 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3797 isds_error err
= IE_SUCCESS
;
3798 xmlXPathObjectPtr result
= NULL
;
3799 xmlNodePtr file_node
;
3800 char *string
= NULL
;
3802 if (!context
) return IE_INVALID_CONTEXT
;
3803 if (!document
) return IE_INVAL
;
3804 isds_document_free(document
);
3805 if (!xpath_ctx
) return IE_INVAL
;
3806 file_node
= xpath_ctx
->node
;
3808 *document
= calloc(1, sizeof(**document
));
3814 /* Extract document meta data */
3815 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3816 if (context
->normalize_mime_type
) {
3817 const char *normalized_type
=
3818 isds_normalize_mime_type((*document
)->dmMimeType
);
3819 if (NULL
!= normalized_type
&&
3820 normalized_type
!= (*document
)->dmMimeType
) {
3821 char *new_type
= strdup(normalized_type
);
3822 if (NULL
== new_type
) {
3823 isds_printf_message(context
,
3824 _("Not enough memory to normalize document MIME type"));
3828 free((*document
)->dmMimeType
);
3829 (*document
)->dmMimeType
= new_type
;
3833 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3834 err
= string2isds_FileMetaType((xmlChar
*)string
,
3835 &((*document
)->dmFileMetaType
));
3837 char *meta_type_locale
= _isds_utf82locale(string
);
3838 isds_printf_message(context
,
3839 _("Document has invalid dmFileMetaType attribute value: %s"),
3841 free(meta_type_locale
);
3847 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3848 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3849 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3850 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3853 /* Extract document data.
3854 * Base64 encoded blob or XML subtree must be presented. */
3856 /* Check for dmEncodedContent */
3857 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3864 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3865 /* Here we have Base64 blob */
3866 (*document
)->is_xml
= 0;
3868 if (result
->nodesetval
->nodeNr
> 1) {
3869 isds_printf_message(context
,
3870 _("Document has more dmEncodedContent elements"));
3875 xmlXPathFreeObject(result
); result
= NULL
;
3876 EXTRACT_STRING("isds:dmEncodedContent", string
);
3878 /* Decode non-empty document */
3879 if (string
&& string
[0] != '\0') {
3880 (*document
)->data_length
=
3881 _isds_b64decode(string
, &((*document
)->data
));
3882 if ((*document
)->data_length
== (size_t) -1) {
3883 isds_printf_message(context
,
3884 _("Error while Base64-decoding document content"));
3890 /* No Base64 blob, try XML document */
3891 xmlXPathFreeObject(result
); result
= NULL
;
3892 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3899 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3900 /* Here we have XML document */
3901 (*document
)->is_xml
= 1;
3903 if (result
->nodesetval
->nodeNr
> 1) {
3904 isds_printf_message(context
,
3905 _("Document has more dmXMLContent elements"));
3910 /* XXX: We cannot serialize the content simply because:
3911 * - XML document may point out of its scope (e.g. to message
3913 * - isds:dmXMLContent can contain more elements, no element,
3915 * - it's not the XML way
3916 * Thus we provide the only right solution: XML DOM. Let's
3917 * application to cope with this hot potato :) */
3918 (*document
)->xml_node_list
=
3919 result
->nodesetval
->nodeTab
[0]->children
;
3921 /* No base64 blob, nor XML document */
3922 isds_printf_message(context
,
3923 _("Document has no dmEncodedContent, nor dmXMLContent "
3932 if (err
) isds_document_free(document
);
3934 xmlXPathFreeObject(result
);
3935 xpath_ctx
->node
= file_node
;
3941 /* Extract message documents into reallocated list of documents
3942 * @context is ISDS context
3943 * @documents is automatically reallocated message documents list structure
3944 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3945 * In case of error @documents will be freed. */
3946 static isds_error
extract_documents(struct isds_ctx
*context
,
3947 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
3948 isds_error err
= IE_SUCCESS
;
3949 xmlXPathObjectPtr result
= NULL
;
3950 xmlNodePtr files_node
;
3951 struct isds_list
*document
, *prev_document
= NULL
;
3953 if (!context
) return IE_INVALID_CONTEXT
;
3954 if (!documents
) return IE_INVAL
;
3955 isds_list_free(documents
);
3956 if (!xpath_ctx
) return IE_INVAL
;
3957 files_node
= xpath_ctx
->node
;
3959 /* Find documents */
3960 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
3967 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3968 isds_printf_message(context
,
3969 _("Message does not contain any document"));
3975 /* Iterate over documents */
3976 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3978 /* Allocate and append list item */
3979 document
= calloc(1, sizeof(*document
));
3984 document
->destructor
= (void (*)(void **))isds_document_free
;
3985 if (i
== 0) *documents
= document
;
3986 else prev_document
->next
= document
;
3987 prev_document
= document
;
3989 /* Extract document */
3990 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3991 err
= extract_document(context
,
3992 (struct isds_document
**) &(document
->data
), xpath_ctx
);
3993 if (err
) goto leave
;
3998 if (err
) isds_list_free(documents
);
3999 xmlXPathFreeObject(result
);
4000 xpath_ctx
->node
= files_node
;
4006 /* Convert isds:dmRecord XML tree into structure
4007 * @context is ISDS context
4008 * @envelope is automatically reallocated message envelope structure
4009 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4010 * In case of error @envelope will be freed. */
4011 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4012 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4013 isds_error err
= IE_SUCCESS
;
4014 xmlXPathObjectPtr result
= NULL
;
4016 if (!context
) return IE_INVALID_CONTEXT
;
4017 if (!envelope
) return IE_INVAL
;
4018 isds_envelope_free(envelope
);
4019 if (!xpath_ctx
) return IE_INVAL
;
4022 *envelope
= calloc(1, sizeof(**envelope
));
4029 /* Extract tRecord data */
4030 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4032 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4033 * dmAcceptanceTime. */
4034 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4035 if (err
) goto leave
;
4037 /* Extract envelope elements added by sender and ISDS
4038 * (XSD: gMessageEnvelope type) */
4039 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4040 if (err
) goto leave
;
4042 /* Get message type */
4043 err
= append_message_type(context
, envelope
, xpath_ctx
);
4044 if (err
) goto leave
;
4048 if (err
) isds_envelope_free(envelope
);
4049 xmlXPathFreeObject(result
);
4054 /* Convert XSD:tStateChangesRecord type XML tree into structure
4055 * @context is ISDS context
4056 * @changed_status is automatically reallocated message state change structure
4057 * @xpath_ctx is XPath context with current node as element of
4058 * XSD:tStateChangesRecord type
4059 * In case of error @changed_status will be freed. */
4060 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4061 struct isds_message_status_change
**changed_status
,
4062 xmlXPathContextPtr xpath_ctx
) {
4063 isds_error err
= IE_SUCCESS
;
4064 xmlXPathObjectPtr result
= NULL
;
4065 unsigned long int *unumber
= NULL
;
4066 char *string
= NULL
;
4068 if (!context
) return IE_INVALID_CONTEXT
;
4069 if (!changed_status
) return IE_INVAL
;
4070 isds_message_status_change_free(changed_status
);
4071 if (!xpath_ctx
) return IE_INVAL
;
4074 *changed_status
= calloc(1, sizeof(**changed_status
));
4075 if (!*changed_status
) {
4081 /* Extract tGetStateChangesInput data */
4082 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4084 /* dmEventTime is mandatory */
4085 EXTRACT_STRING("isds:dmEventTime", string
);
4087 err
= timestring2timeval((xmlChar
*) string
,
4088 &((*changed_status
)->time
));
4090 char *string_locale
= _isds_utf82locale(string
);
4091 if (err
== IE_DATE
) err
= IE_ISDS
;
4092 isds_printf_message(context
,
4093 _("Could not convert dmEventTime as ISO time: %s"),
4095 free(string_locale
);
4101 /* dmMessageStatus element is mandatory */
4102 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4104 isds_log_message(context
,
4105 _("Missing mandatory isds:dmMessageStatus integer"));
4109 err
= uint2isds_message_status(context
, unumber
,
4110 &((*changed_status
)->dmMessageStatus
));
4112 if (err
== IE_ENUM
) err
= IE_ISDS
;
4121 if (err
) isds_message_status_change_free(changed_status
);
4122 xmlXPathFreeObject(result
);
4125 #endif /* HAVE_LIBCURL */
4128 /* Find and convert isds:dmHash XML tree into structure
4129 * @context is ISDS context
4130 * @envelope is automatically reallocated message hash structure
4131 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4132 * In case of error @hash will be freed. */
4133 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4134 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4135 isds_error err
= IE_SUCCESS
;
4136 xmlNodePtr old_ctx_node
;
4137 xmlXPathObjectPtr result
= NULL
;
4138 char *string
= NULL
;
4140 if (!context
) return IE_INVALID_CONTEXT
;
4141 if (!hash
) return IE_INVAL
;
4142 isds_hash_free(hash
);
4143 if (!xpath_ctx
) return IE_INVAL
;
4145 old_ctx_node
= xpath_ctx
->node
;
4147 *hash
= calloc(1, sizeof(**hash
));
4154 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4155 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4164 /* Get hash algorithm */
4165 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4166 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4168 if (err
== IE_ENUM
) {
4169 char *string_locale
= _isds_utf82locale(string
);
4170 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4172 free(string_locale
);
4178 /* Get hash value */
4179 EXTRACT_STRING(".", string
);
4181 isds_printf_message(context
,
4182 _("sisds:dmHash element is missing hash value"));
4186 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4187 if ((*hash
)->length
== (size_t) -1) {
4188 isds_printf_message(context
,
4189 _("Error while Base64-decoding hash value"));
4195 if (err
) isds_hash_free(hash
);
4197 xmlXPathFreeObject(result
);
4198 xpath_ctx
->node
= old_ctx_node
;
4203 /* Find and append isds:dmQTimestamp XML tree into envelope.
4204 * Because one service is allowed to miss time-stamp content, and we think
4205 * other could too (flaw in specification), this function is deliberated and
4206 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4207 * @context is ISDS context
4208 * @envelope is automatically allocated envelope structure
4209 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4211 * In case of error @envelope will be freed. */
4212 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4213 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4214 isds_error err
= IE_SUCCESS
;
4215 xmlXPathObjectPtr result
= NULL
;
4216 char *string
= NULL
;
4218 if (!context
) return IE_INVALID_CONTEXT
;
4219 if (!envelope
) return IE_INVAL
;
4221 isds_envelope_free(envelope
);
4226 *envelope
= calloc(1, sizeof(**envelope
));
4232 zfree((*envelope
)->timestamp
);
4233 (*envelope
)->timestamp_length
= 0;
4236 /* Get dmQTimestamp */
4237 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4239 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4242 (*envelope
)->timestamp_length
=
4243 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4244 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4245 isds_printf_message(context
,
4246 _("Error while Base64-decoding time stamp value"));
4252 if (err
) isds_envelope_free(envelope
);
4254 xmlXPathFreeObject(result
);
4259 /* Convert XSD tReturnedMessage XML tree into message structure.
4260 * It does not store serialized XML tree into message->raw.
4261 * It does store (pointer to) parsed XML tree into message->xml if needed.
4262 * @context is ISDS context
4263 * @include_documents Use true if documents must be extracted
4264 * (tReturnedMessage XSD type), use false if documents shall be omitted
4265 * (tReturnedMessageEnvelope).
4266 * @message is automatically reallocated message structure
4267 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4269 * In case of error @message will be freed. */
4270 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4271 const _Bool include_documents
, struct isds_message
**message
,
4272 xmlXPathContextPtr xpath_ctx
) {
4273 isds_error err
= IE_SUCCESS
;
4274 xmlNodePtr message_node
;
4276 if (!context
) return IE_INVALID_CONTEXT
;
4277 if (!message
) return IE_INVAL
;
4278 isds_message_free(message
);
4279 if (!xpath_ctx
) return IE_INVAL
;
4282 *message
= calloc(1, sizeof(**message
));
4288 /* Save message XPATH context node */
4289 message_node
= xpath_ctx
->node
;
4293 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4294 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4295 if (err
) { err
= IE_ERROR
; goto leave
; }
4296 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4297 if (err
) goto leave
;
4299 if (include_documents
) {
4300 struct isds_list
*item
;
4302 /* Extract dmFiles */
4303 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4305 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4306 err
= IE_ISDS
; goto leave
;
4308 if (err
) { err
= IE_ERROR
; goto leave
; }
4309 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4310 if (err
) goto leave
;
4312 /* Store xmlDoc of this message if needed */
4313 /* Only if we got a XML document in all the documents. */
4314 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4315 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4316 (*message
)->xml
= xpath_ctx
->doc
;
4323 /* Restore context to message */
4324 xpath_ctx
->node
= message_node
;
4326 /* Extract dmHash */
4327 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4329 if (err
) goto leave
;
4331 /* Extract dmQTimestamp, */
4332 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4334 if (err
) goto leave
;
4336 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4337 * dmAcceptanceTime. */
4338 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4339 if (err
) goto leave
;
4341 /* Get message type */
4342 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4343 if (err
) goto leave
;
4346 if (err
) isds_message_free(message
);
4351 /* Extract message event into reallocated isds_event structure
4352 * @context is ISDS context
4353 * @event is automatically reallocated message event structure
4354 * @xpath_ctx is XPath context with current node as isds:dmEvent
4355 * In case of error @event will be freed. */
4356 static isds_error
extract_event(struct isds_ctx
*context
,
4357 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4358 isds_error err
= IE_SUCCESS
;
4359 xmlXPathObjectPtr result
= NULL
;
4360 xmlNodePtr event_node
;
4361 char *string
= NULL
;
4363 if (!context
) return IE_INVALID_CONTEXT
;
4364 if (!event
) return IE_INVAL
;
4365 isds_event_free(event
);
4366 if (!xpath_ctx
) return IE_INVAL
;
4367 event_node
= xpath_ctx
->node
;
4369 *event
= calloc(1, sizeof(**event
));
4375 /* Extract event data.
4376 * All elements are optional according XSD. That's funny. */
4377 EXTRACT_STRING("sisds:dmEventTime", string
);
4379 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4381 char *string_locale
= _isds_utf82locale(string
);
4382 if (err
== IE_DATE
) err
= IE_ISDS
;
4383 isds_printf_message(context
,
4384 _("Could not convert dmEventTime as ISO time: %s"),
4386 free(string_locale
);
4392 /* dmEventDescr element has prefix and the rest */
4393 EXTRACT_STRING("sisds:dmEventDescr", string
);
4395 err
= eventstring2event((xmlChar
*) string
, *event
);
4396 if (err
) goto leave
;
4401 if (err
) isds_event_free(event
);
4403 xmlXPathFreeObject(result
);
4404 xpath_ctx
->node
= event_node
;
4409 /* Convert element of XSD tEventsArray type from XML tree into
4410 * isds_list of isds_event's structure. The list is automatically reallocated.
4411 * @context is ISDS context
4412 * @events is automatically reallocated list of event structures
4413 * @xpath_ctx is XPath context with current node as tEventsArray
4414 * In case of error @events will be freed. */
4415 static isds_error
extract_events(struct isds_ctx
*context
,
4416 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4417 isds_error err
= IE_SUCCESS
;
4418 xmlXPathObjectPtr result
= NULL
;
4419 xmlNodePtr events_node
;
4420 struct isds_list
*event
, *prev_event
= NULL
;
4422 if (!context
) return IE_INVALID_CONTEXT
;
4423 if (!events
) return IE_INVAL
;
4424 if (!xpath_ctx
) return IE_INVAL
;
4425 events_node
= xpath_ctx
->node
;
4428 isds_list_free(events
);
4431 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4438 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4439 isds_printf_message(context
,
4440 _("Delivery info does not contain any event"));
4446 /* Iterate over events */
4447 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4449 /* Allocate and append list item */
4450 event
= calloc(1, sizeof(*event
));
4455 event
->destructor
= (void (*)(void **))isds_event_free
;
4456 if (i
== 0) *events
= event
;
4457 else prev_event
->next
= event
;
4461 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4462 err
= extract_event(context
,
4463 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4464 if (err
) goto leave
;
4469 if (err
) isds_list_free(events
);
4470 xmlXPathFreeObject(result
);
4471 xpath_ctx
->node
= events_node
;
4477 /* Insert Base64 encoded data as element with text child.
4478 * @context is session context
4479 * @parent is XML node to append @element with @data as child
4480 * @ns is XML namespace of @element, use NULL to inherit from @parent
4481 * @element is UTF-8 encoded name of new element
4482 * @data is bit stream to encode into @element
4483 * @length is size of @data in bytes
4484 * @return standard error code and fill long error message if needed */
4485 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4486 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4487 const void *data
, size_t length
) {
4488 isds_error err
= IE_SUCCESS
;
4491 if (!context
) return IE_INVALID_CONTEXT
;
4492 if (!data
&& length
> 0) return IE_INVAL
;
4493 if (!parent
|| !element
) return IE_INVAL
;
4495 xmlChar
*base64data
= NULL
;
4496 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4498 isds_printf_message(context
,
4499 ngettext("Not enough memory to encode %zd byte into Base64",
4500 "Not enough memory to encode %zd bytes into Base64",
4506 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4514 /* Convert isds_document structure into XML tree and append to dmFiles node.
4515 * @context is session context
4516 * @document is ISDS document
4517 * @dm_files is XML element the resulting tree will be appended to as a child.
4518 * @return error code, in case of error context' message is filled. */
4519 static isds_error
insert_document(struct isds_ctx
*context
,
4520 struct isds_document
*document
, xmlNodePtr dm_files
) {
4521 isds_error err
= IE_SUCCESS
;
4522 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4523 xmlAttrPtr attribute_node
;
4525 if (!context
) return IE_INVALID_CONTEXT
;
4526 if (!document
|| !dm_files
) return IE_INVAL
;
4528 /* Allocate new dmFile */
4529 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4531 isds_printf_message(context
, _("Could not allocate main dmFile"));
4535 /* Append the new dmFile.
4536 * XXX: Main document must go first */
4537 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4538 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4540 file
= xmlAddChild(dm_files
, new_file
);
4543 xmlFreeNode(new_file
); new_file
= NULL
;
4544 isds_printf_message(context
, _("Could not add dmFile child to "
4545 "%s element"), dm_files
->name
);
4550 /* @dmMimeType is required */
4551 if (!document
->dmMimeType
) {
4552 isds_log_message(context
,
4553 _("Document is missing mandatory MIME type definition"));
4557 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4559 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4561 isds_printf_message(context
,
4562 _("Document has unknown dmFileMetaType: %ld"),
4563 document
->dmFileMetaType
);
4567 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4569 if (document
->dmFileGuid
) {
4570 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4572 if (document
->dmUpFileGuid
) {
4573 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4576 /* @dmFileDescr is required */
4577 if (!document
->dmFileDescr
) {
4578 isds_log_message(context
,
4579 _("Document is missing mandatory description (title)"));
4583 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4585 if (document
->dmFormat
) {
4586 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4590 /* Insert content (body) of the document. */
4591 if (document
->is_xml
) {
4592 /* XML document requested */
4594 /* Allocate new dmXMLContent */
4595 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4597 isds_printf_message(context
,
4598 _("Could not allocate dmXMLContent element"));
4603 node
= xmlAddChild(file
, xmlcontent
);
4605 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4606 isds_printf_message(context
,
4607 _("Could not add dmXMLContent child to %s element"),
4613 /* Copy non-empty node list */
4614 if (document
->xml_node_list
) {
4615 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4616 document
->xml_node_list
);
4618 isds_printf_message(context
,
4619 _("Not enough memory to copy XML document"));
4624 if (!xmlAddChildList(node
, content
)) {
4625 xmlFreeNodeList(content
);
4626 isds_printf_message(context
,
4627 _("Error while adding XML document into dmXMLContent"));
4631 /* XXX: We cannot free the content here because it's part of node's
4632 * document since now. It will be freed with it automatically. */
4635 /* Binary document requested */
4636 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4637 document
->data
, document
->data_length
);
4638 if (err
) goto leave
;
4646 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4647 * The copy must be preallocated, the date are just appended into structure.
4648 * @context is ISDS context
4649 * @copy is message copy structure
4650 * @xpath_ctx is XPath context with current node as tMStatus */
4651 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4652 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4653 isds_error err
= IE_SUCCESS
;
4654 xmlXPathObjectPtr result
= NULL
;
4655 char *code
= NULL
, *message
= NULL
;
4657 if (!context
) return IE_INVALID_CONTEXT
;
4658 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4660 /* Free old values */
4661 zfree(copy
->dmStatus
);
4664 /* Get error specific to this copy */
4665 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4667 isds_log_message(context
,
4668 _("Missing isds:dmStatusCode under "
4669 "XSD:tMStatus type element"));
4674 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4675 /* This copy failed */
4676 copy
->error
= IE_ISDS
;
4677 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4679 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4680 if (!copy
->dmStatus
) {
4681 copy
->dmStatus
= code
;
4685 copy
->dmStatus
= code
;
4689 /* This copy succeeded. In this case only, message ID is valid */
4690 copy
->error
= IE_SUCCESS
;
4692 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4694 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4695 "but did not returned assigned message ID\n"));
4703 xmlXPathFreeObject(result
);
4708 /* Insert struct isds_approval data (box approval) into XML tree
4709 * @context is session context
4710 * @approval is libisds structure with approval description. NULL is
4712 * @parent is XML element to append @approval to */
4713 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4714 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4716 isds_error err
= IE_SUCCESS
;
4719 if (!context
) return IE_INVALID_CONTEXT
;
4720 if (!parent
) return IE_INVAL
;
4722 if (!approval
) return IE_SUCCESS
;
4724 /* Build XSD:gExtApproval */
4725 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4726 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4733 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4735 * @context is session context
4736 * @service_name is name of SERVICE_DB_ACCESS
4737 * @response is reallocated server SOAP body response as XML document
4738 * @raw_response is reallocated bit stream with response body. Use
4739 * NULL if you don't care
4740 * @raw_response_length is size of @raw_response in bytes
4741 * @code is reallocated ISDS status code
4742 * @status_message is reallocated ISDS status message
4743 * @return error coded from lower layer, context message will be set up
4745 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4746 const xmlChar
*service_name
,
4747 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4748 xmlChar
**code
, xmlChar
**status_message
) {
4750 isds_error err
= IE_SUCCESS
;
4751 char *service_name_locale
= NULL
;
4752 xmlNodePtr request
= NULL
, node
;
4753 xmlNsPtr isds_ns
= NULL
;
4755 if (!context
) return IE_INVALID_CONTEXT
;
4756 if (!service_name
) return IE_INVAL
;
4757 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4758 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4760 /* Free output argument */
4761 xmlFreeDoc(*response
); *response
= NULL
;
4762 if (raw_response
) zfree(*raw_response
);
4764 zfree(*status_message
);
4767 /* Check if connection is established
4768 * TODO: This check should be done downstairs. */
4769 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4771 service_name_locale
= _isds_utf82locale((char*)service_name
);
4772 if (!service_name_locale
) {
4778 request
= xmlNewNode(NULL
, service_name
);
4780 isds_printf_message(context
,
4781 _("Could not build %s request"), service_name_locale
);
4785 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4787 isds_log_message(context
, _("Could not create ISDS name space"));
4791 xmlSetNs(request
, isds_ns
);
4794 /* Add XSD:tDummyInput child */
4795 INSERT_STRING(request
, "dbDummy", NULL
);
4798 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4799 service_name_locale
);
4802 err
= isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4803 raw_response
, raw_response_length
);
4804 xmlFreeNode(request
); request
= NULL
;
4807 isds_log(ILF_ISDS
, ILL_DEBUG
,
4808 _("Processing ISDS response on %s request failed\n"),
4809 service_name_locale
);
4813 /* Check for response status */
4814 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4815 code
, status_message
, NULL
);
4817 isds_log(ILF_ISDS
, ILL_DEBUG
,
4818 _("ISDS response on %s request is missing status\n"),
4819 service_name_locale
);
4823 /* Request processed, but nothing found */
4824 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4825 char *code_locale
= _isds_utf82locale((char*) *code
);
4826 char *status_message_locale
=
4827 _isds_utf82locale((char*) *status_message
);
4828 isds_log(ILF_ISDS
, ILL_DEBUG
,
4829 _("Server refused %s request (code=%s, message=%s)\n"),
4830 service_name_locale
, code_locale
, status_message_locale
);
4831 isds_log_message(context
, status_message_locale
);
4833 free(status_message_locale
);
4839 free(service_name_locale
);
4840 xmlFreeNode(request
);
4846 /* Get data about logged in user and his box. */
4847 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4848 struct isds_DbOwnerInfo
**db_owner_info
) {
4849 isds_error err
= IE_SUCCESS
;
4851 xmlDocPtr response
= NULL
;
4852 xmlChar
*code
= NULL
, *message
= NULL
;
4853 xmlXPathContextPtr xpath_ctx
= NULL
;
4854 xmlXPathObjectPtr result
= NULL
;
4855 char *string
= NULL
;
4858 if (!context
) return IE_INVALID_CONTEXT
;
4859 zfree(context
->long_message
);
4860 if (!db_owner_info
) return IE_INVAL
;
4861 isds_DbOwnerInfo_free(db_owner_info
);
4864 /* Check if connection is established */
4865 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4868 /* Do request and check for success */
4869 err
= build_send_check_dbdummy_request(context
,
4870 BAD_CAST
"GetOwnerInfoFromLogin",
4871 &response
, NULL
, NULL
, &code
, &message
);
4872 if (err
) goto leave
;
4876 /* Prepare structure */
4877 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4878 if (!*db_owner_info
) {
4882 xpath_ctx
= xmlXPathNewContext(response
);
4887 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4892 /* Set context node */
4893 result
= xmlXPathEvalExpression(BAD_CAST
4894 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4899 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4900 isds_log_message(context
, _("Missing dbOwnerInfo element"));
4904 if (result
->nodesetval
->nodeNr
> 1) {
4905 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
4909 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4910 xmlXPathFreeObject(result
); result
= NULL
;
4913 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
4918 isds_DbOwnerInfo_free(db_owner_info
);
4922 xmlXPathFreeObject(result
);
4923 xmlXPathFreeContext(xpath_ctx
);
4927 xmlFreeDoc(response
);
4930 isds_log(ILF_ISDS
, ILL_DEBUG
,
4931 _("GetOwnerInfoFromLogin request processed by server "
4932 "successfully.\n"));
4933 #else /* not HAVE_LIBCURL */
4941 /* Get data about logged in user. */
4942 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
4943 struct isds_DbUserInfo
**db_user_info
) {
4944 isds_error err
= IE_SUCCESS
;
4946 xmlDocPtr response
= NULL
;
4947 xmlChar
*code
= NULL
, *message
= NULL
;
4948 xmlXPathContextPtr xpath_ctx
= NULL
;
4949 xmlXPathObjectPtr result
= NULL
;
4952 if (!context
) return IE_INVALID_CONTEXT
;
4953 zfree(context
->long_message
);
4954 if (!db_user_info
) return IE_INVAL
;
4955 isds_DbUserInfo_free(db_user_info
);
4958 /* Check if connection is established */
4959 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4962 /* Do request and check for success */
4963 err
= build_send_check_dbdummy_request(context
,
4964 BAD_CAST
"GetUserInfoFromLogin",
4965 &response
, NULL
, NULL
, &code
, &message
);
4966 if (err
) goto leave
;
4970 /* Prepare structure */
4971 *db_user_info
= calloc(1, sizeof(**db_user_info
));
4972 if (!*db_user_info
) {
4976 xpath_ctx
= xmlXPathNewContext(response
);
4981 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4986 /* Set context node */
4987 result
= xmlXPathEvalExpression(BAD_CAST
4988 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
4993 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4994 isds_log_message(context
, _("Missing dbUserInfo element"));
4998 if (result
->nodesetval
->nodeNr
> 1) {
4999 isds_log_message(context
, _("Multiple dbUserInfo element"));
5003 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5004 xmlXPathFreeObject(result
); result
= NULL
;
5007 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5011 isds_DbUserInfo_free(db_user_info
);
5014 xmlXPathFreeObject(result
);
5015 xmlXPathFreeContext(xpath_ctx
);
5019 xmlFreeDoc(response
);
5022 isds_log(ILF_ISDS
, ILL_DEBUG
,
5023 _("GetUserInfoFromLogin request processed by server "
5024 "successfully.\n"));
5025 #else /* not HAVE_LIBCURL */
5033 /* Get expiration time of current password
5034 * @context is session context
5035 * @expiration is automatically reallocated time when password expires. If
5036 * password expiration is disables, NULL will be returned. In case of error
5037 * it will be nulled too. */
5038 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5039 struct timeval
**expiration
) {
5040 isds_error err
= IE_SUCCESS
;
5042 xmlDocPtr response
= NULL
;
5043 xmlChar
*code
= NULL
, *message
= NULL
;
5044 xmlXPathContextPtr xpath_ctx
= NULL
;
5045 xmlXPathObjectPtr result
= NULL
;
5046 char *string
= NULL
;
5049 if (!context
) return IE_INVALID_CONTEXT
;
5050 zfree(context
->long_message
);
5051 if (!expiration
) return IE_INVAL
;
5055 /* Check if connection is established */
5056 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5059 /* Do request and check for success */
5060 err
= build_send_check_dbdummy_request(context
,
5061 BAD_CAST
"GetPasswordInfo",
5062 &response
, NULL
, NULL
, &code
, &message
);
5063 if (err
) goto leave
;
5067 xpath_ctx
= xmlXPathNewContext(response
);
5072 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5077 /* Set context node */
5078 result
= xmlXPathEvalExpression(BAD_CAST
5079 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5084 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5085 isds_log_message(context
,
5086 _("Missing GetPasswordInfoResponse element"));
5090 if (result
->nodesetval
->nodeNr
> 1) {
5091 isds_log_message(context
,
5092 _("Multiple GetPasswordInfoResponse element"));
5096 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5097 xmlXPathFreeObject(result
); result
= NULL
;
5099 /* Extract expiration date */
5100 EXTRACT_STRING("isds:pswExpDate", string
);
5102 /* And convert it if any returned. Otherwise expiration is disabled. */
5103 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5105 char *string_locale
= _isds_utf82locale(string
);
5106 if (err
== IE_DATE
) err
= IE_ISDS
;
5107 isds_printf_message(context
,
5108 _("Could not convert pswExpDate as ISO time: %s"),
5110 free(string_locale
);
5123 xmlXPathFreeObject(result
);
5124 xmlXPathFreeContext(xpath_ctx
);
5128 xmlFreeDoc(response
);
5131 isds_log(ILF_ISDS
, ILL_DEBUG
,
5132 _("GetPasswordInfo request processed by server "
5133 "successfully.\n"));
5134 #else /* not HAVE_LIBCURL */
5143 /* Request delivering new TOTP code from ISDS through side channel before
5144 * changing password.
5145 * @context is session context
5146 * @password is current password.
5147 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5148 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5149 * function for more details.
5150 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5151 * NULL, if you don't care.
5152 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5154 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5155 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5156 isds_error err
= IE_SUCCESS
;
5157 char *saved_url
= NULL
; /* No copy */
5158 #if HAVE_CURL_REAUTHORIZATION_BUG
5159 CURL
*saved_curl
= NULL
; /* No copy */
5161 xmlNsPtr isds_ns
= NULL
;
5162 xmlNodePtr request
= NULL
;
5163 xmlDocPtr response
= NULL
;
5164 xmlChar
*code
= NULL
, *message
= NULL
;
5165 const xmlChar
*codes
[] = {
5170 const char *meanings
[] = {
5171 N_("Unexpected error"),
5172 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5173 N_("One-time code could not been sent. Try later again.")
5175 const isds_otp_resolution resolutions
[] = {
5176 OTP_RESOLUTION_UNKNOWN
,
5177 OTP_RESOLUTION_TO_FAST
,
5178 OTP_RESOLUTION_TOTP_NOT_SENT
5181 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5182 zfree(context
->long_message
);
5183 if (NULL
== password
) {
5184 isds_log_message(context
,
5185 _("Second argument (password) of isds_change_password() "
5190 /* Check if connection is established
5191 * TODO: This check should be done downstairs. */
5192 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5194 if (!context
->otp
) {
5195 isds_log_message(context
, _("This function requires OTP-authenticated "
5197 return IE_INVALID_CONTEXT
;
5200 isds_log_message(context
, _("If one-time password authentication "
5201 "method is in use, requesting new OTP code requires "
5202 "one-time credentials argument either"));
5205 if (otp
->method
!= OTP_TIME
) {
5206 isds_log_message(context
, _("Requesting new time-based OTP code from "
5207 "server requires one-time password authentication "
5211 if (otp
->otp_code
!= NULL
) {
5212 isds_log_message(context
, _("Requesting new time-based OTP code from "
5213 "server requires undefined OTP code member in "
5214 "one-time credentials argument"));
5220 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5222 isds_log_message(context
, _("Could not build SendSMSCode request"));
5225 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5227 isds_log_message(context
, _("Could not create ISDS name space"));
5228 xmlFreeNode(request
);
5231 xmlSetNs(request
, isds_ns
);
5233 /* Change URL temporarily for sending this request only */
5235 char *new_url
= NULL
;
5236 if ((err
= _isds_build_url_from_context(context
,
5237 "%1$.*2$sasws/changePassword", &new_url
))) {
5240 saved_url
= context
->url
;
5241 context
->url
= new_url
;
5244 /* Store credentials for sending this request only */
5245 context
->otp_credentials
= otp
;
5246 _isds_discard_credentials(context
, 0);
5247 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5249 _isds_discard_credentials(context
, 0);
5252 #if HAVE_CURL_REAUTHORIZATION_BUG
5253 saved_curl
= context
->curl
;
5254 context
->curl
= curl_easy_init();
5255 if (NULL
== context
->curl
) {
5259 if (context
->timeout
) {
5260 err
= isds_set_timeout(context
, context
->timeout
);
5261 if (err
) goto leave
;
5265 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5268 err
= isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5270 /* Remove temporal credentials */
5271 _isds_discard_credentials(context
, 0);
5272 /* Detach pointer to OTP credentials from context */
5273 context
->otp_credentials
= NULL
;
5274 /* Keep context->otp true to keep signaling this is OTP session */
5276 /* Destroy request */
5277 xmlFreeNode(request
); request
= NULL
;
5280 isds_log(ILF_ISDS
, ILL_DEBUG
,
5281 _("Processing ISDS response on SendSMSCode request failed\n"));
5285 /* Check for response status */
5286 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5287 &code
, &message
, (xmlChar
**)refnumber
);
5289 isds_log(ILF_ISDS
, ILL_DEBUG
,
5290 _("ISDS response on SendSMSCode request is missing "
5295 /* Check for error */
5296 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5297 char *code_locale
= _isds_utf82locale((char*)code
);
5298 char *message_locale
= _isds_utf82locale((char*)message
);
5300 isds_log(ILF_ISDS
, ILL_DEBUG
,
5301 _("Server refused to send new code on SendSMSCode "
5302 "request (code=%s, message=%s)\n"),
5303 code_locale
, message_locale
);
5305 /* Check for known error codes */
5306 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5307 if (!xmlStrcmp(code
, codes
[i
])) break;
5309 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5310 isds_log_message(context
, _(meanings
[i
]));
5311 /* Mimic otp->resolution according to the code, specification does
5312 * prescribe OTP header to be available. */
5313 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5314 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5315 otp
->resolution
= resolutions
[i
];
5317 isds_log_message(context
, message_locale
);
5320 free(message_locale
);
5326 /* Otherwise new code sent successfully */
5327 /* Mimic otp->resolution according to the code, specification does
5328 * prescribe OTP header to be available. */
5329 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5330 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5333 if (NULL
!= saved_url
) {
5334 /* Revert URL to original one */
5335 zfree(context
->url
);
5336 context
->url
= saved_url
;
5338 #if HAVE_CURL_REAUTHORIZATION_BUG
5339 if (NULL
!= saved_curl
) {
5340 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5341 context
->curl
= saved_curl
;
5347 xmlFreeDoc(response
);
5348 xmlFreeNode(request
);
5351 isds_log(ILF_ISDS
, ILL_DEBUG
,
5352 _("New OTP code has been sent successfully on SendSMSCode "
5358 /* Convert response status code to isds_error code and set long message
5359 * @context is context to save long message to
5360 * @map is mapping from codes to errors and messages. Pass NULL for generic
5362 * @code is status code to translate
5363 * @message is non-localized status message to put into long message in case
5364 * of uknown error. It can be NULL if server did not provide any.
5365 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5366 * invalid invocation. */
5367 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5368 const struct code_map_isds_error
*map
,
5369 const xmlChar
*code
, const xmlChar
*message
) {
5371 isds_log_message(context
,
5372 _("NULL status code passed to statuscode2isds_error()"));
5377 /* Check for known error codes */
5378 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5379 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5380 isds_log_message(context
, _(map
->meanings
[i
]));
5381 return map
->errors
[i
];
5387 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5388 char *message_locale
= _isds_utf82locale((char*)message
);
5389 if (NULL
== message_locale
)
5390 isds_log_message(context
, _("ISDS server returned unknown error"));
5392 isds_log_message(context
, message_locale
);
5393 free(message_locale
);
5402 /* Change user password in ISDS.
5403 * User must supply old password, new password will takes effect after some
5404 * time, current session can continue. Password must fulfill some constraints.
5405 * @context is session context
5406 * @old_password is current password.
5407 * @new_password is requested new password
5408 * @otp auxiliary data required if one-time password authentication is in use,
5409 * defines OTP code (if known) and returns fine grade resolution of OTP
5410 * procedure. Pass NULL, if one-time password authentication is not needed.
5411 * Please note the @otp argument must match OTP method used at log-in time. See
5412 * isds_login() function for more details.
5413 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5414 * NULL, if you don't care.
5415 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5416 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5417 * awaiting OTP code that has been delivered by side channel to the user. */
5418 isds_error
isds_change_password(struct isds_ctx
*context
,
5419 const char *old_password
, const char *new_password
,
5420 struct isds_otp
*otp
, char **refnumber
) {
5421 isds_error err
= IE_SUCCESS
;
5423 char *saved_url
= NULL
; /* No copy */
5424 #if HAVE_CURL_REAUTHORIZATION_BUG
5425 CURL
*saved_curl
= NULL
; /* No copy */
5427 xmlNsPtr isds_ns
= NULL
;
5428 xmlNodePtr request
= NULL
, node
;
5429 xmlDocPtr response
= NULL
;
5430 xmlChar
*code
= NULL
, *message
= NULL
;
5431 const xmlChar
*codes
[] = {
5444 const char *meanings
[] = {
5445 N_("Password length must be between 8 and 32 characters"),
5446 N_("Password cannot be reused"), /* Server does not distinguish 1067
5447 and 1091 on ChangePasswordOTP */
5448 N_("Password contains forbidden character"),
5449 N_("Password must contain at least one upper-case letter, "
5450 "one lower-case, and one digit"),
5451 N_("Password cannot contain sequence of three identical characters"),
5452 N_("Password cannot contain user identifier"),
5453 N_("Password is too simmple"),
5454 N_("Old password is not valid"),
5455 N_("Password cannot be reused"),
5456 N_("Unexpected error"),
5457 N_("LDAP update error")
5461 if (!context
) return IE_INVALID_CONTEXT
;
5462 zfree(context
->long_message
);
5463 if (NULL
!= refnumber
)
5465 if (NULL
== old_password
) {
5466 isds_log_message(context
,
5467 _("Second argument (old password) of isds_change_password() "
5471 if (NULL
== otp
&& NULL
== new_password
) {
5472 isds_log_message(context
,
5473 _("Third argument (new password) of isds_change_password() "
5479 /* Check if connection is established
5480 * TODO: This check should be done downstairs. */
5481 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5483 if (context
->otp
&& NULL
== otp
) {
5484 isds_log_message(context
, _("If one-time password authentication "
5485 "method is in use, changing password requires one-time "
5486 "credentials either"));
5490 /* Build ChangeISDSPassword request */
5491 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5492 BAD_CAST
"ChangePasswordOTP");
5494 isds_log_message(context
, (NULL
== otp
) ?
5495 _("Could not build ChangeISDSPassword request") :
5496 _("Could not build ChangePasswordOTP request"));
5499 isds_ns
= xmlNewNs(request
,
5500 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5503 isds_log_message(context
, _("Could not create ISDS name space"));
5504 xmlFreeNode(request
);
5507 xmlSetNs(request
, isds_ns
);
5509 INSERT_STRING(request
, "dbOldPassword", old_password
);
5510 INSERT_STRING(request
, "dbNewPassword", new_password
);
5513 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5514 switch (otp
->method
) {
5516 isds_log(ILF_SEC
, ILL_INFO
,
5517 _("Selected authentication method: "
5518 "HMAC-based one-time password\n"));
5519 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5522 isds_log(ILF_SEC
, ILL_INFO
,
5523 _("Selected authentication method: "
5524 "Time-based one-time password\n"));
5525 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5526 if (otp
->otp_code
== NULL
) {
5527 isds_log(ILF_SEC
, ILL_INFO
,
5528 _("OTP code has not been provided by "
5529 "application, requesting server for "
5531 err
= _isds_request_totp_code(context
, old_password
, otp
,
5533 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5537 isds_log(ILF_SEC
, ILL_INFO
,
5538 _("OTP code has been provided by "
5539 "application, not requesting server "
5544 isds_log_message(context
,
5545 _("Unknown one-time password authentication "
5546 "method requested by application"));
5551 /* Change URL temporarily for sending this request only */
5553 char *new_url
= NULL
;
5554 if ((err
= _isds_build_url_from_context(context
,
5555 "%1$.*2$sasws/changePassword", &new_url
))) {
5558 saved_url
= context
->url
;
5559 context
->url
= new_url
;
5562 /* Store credentials for sending this request only */
5563 context
->otp_credentials
= otp
;
5564 _isds_discard_credentials(context
, 0);
5565 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5566 old_password
, NULL
))) {
5567 _isds_discard_credentials(context
, 0);
5570 #if HAVE_CURL_REAUTHORIZATION_BUG
5571 saved_curl
= context
->curl
;
5572 context
->curl
= curl_easy_init();
5573 if (NULL
== context
->curl
) {
5577 if (context
->timeout
) {
5578 err
= isds_set_timeout(context
, context
->timeout
);
5579 if (err
) goto leave
;
5584 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5585 _("Sending ChangeISDSPassword request to ISDS\n") :
5586 _("Sending ChangePasswordOTP request to ISDS\n"));
5589 err
= isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5590 request
, &response
, NULL
, NULL
);
5593 /* Remove temporal credentials */
5594 _isds_discard_credentials(context
, 0);
5595 /* Detach pointer to OTP credentials from context */
5596 context
->otp_credentials
= NULL
;
5597 /* Keep context->otp true to keep signaling this is OTP session */
5600 /* Destroy request */
5601 xmlFreeNode(request
); request
= NULL
;
5604 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5605 _("Processing ISDS response on ChangeISDSPassword "
5606 "request failed\n") :
5607 _("Processing ISDS response on ChangePasswordOTP "
5608 "request failed\n"));
5612 /* Check for response status */
5613 err
= isds_response_status(context
,
5614 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5615 &code
, &message
, (xmlChar
**)refnumber
);
5617 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5618 _("ISDS response on ChangeISDSPassword request is missing "
5620 _("ISDS response on ChangePasswordOTP request is missing "
5625 /* Check for known error codes */
5626 for (int i
=0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5627 if (!xmlStrcmp(code
, codes
[i
])) {
5628 char *code_locale
= _isds_utf82locale((char*)code
);
5629 char *message_locale
= _isds_utf82locale((char*)message
);
5630 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5631 _("Server refused to change password on ChangeISDSPassword "
5632 "request (code=%s, message=%s)\n") :
5633 _("Server refused to change password on ChangePasswordOTP "
5634 "request (code=%s, message=%s)\n"),
5635 code_locale
, message_locale
);
5637 free(message_locale
);
5638 isds_log_message(context
, _(meanings
[i
]));
5645 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5646 char *code_locale
= _isds_utf82locale((char*)code
);
5647 char *message_locale
= _isds_utf82locale((char*)message
);
5648 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5649 _("Server refused to change password on ChangeISDSPassword "
5650 "request (code=%s, message=%s)\n") :
5651 _("Server refused to change password on ChangePasswordOTP "
5652 "request (code=%s, message=%s)\n"),
5653 code_locale
, message_locale
);
5654 isds_log_message(context
, message_locale
);
5656 free(message_locale
);
5661 /* Otherwise password changed successfully */
5664 if (NULL
!= saved_url
) {
5665 /* Revert URL to original one */
5666 zfree(context
->url
);
5667 context
->url
= saved_url
;
5669 #if HAVE_CURL_REAUTHORIZATION_BUG
5670 if (NULL
!= saved_curl
) {
5671 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5672 context
->curl
= saved_curl
;
5678 xmlFreeDoc(response
);
5679 xmlFreeNode(request
);
5682 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5683 _("Password changed successfully on ChangeISDSPassword "
5685 _("Password changed successfully on ChangePasswordOTP "
5687 #else /* not HAVE_LIBCURL */
5696 /* Generic middle part with request sending and response check.
5697 * It sends prepared request and checks for error code.
5698 * @context is ISDS session context.
5699 * @service is ISDS service handler
5700 * @service_name is name in scope of given @service
5701 * @request is XML tree with request. Will be freed to save memory.
5702 * @response is XML document outputting ISDS response.
5703 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5704 * @map is mapping from status code to library error. Pass NULL if no special
5705 * handling is requested.
5706 * NULL, if you don't care. */
5707 static isds_error
send_destroy_request_check_response(
5708 struct isds_ctx
*context
,
5709 const isds_service service
, const xmlChar
*service_name
,
5710 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5711 const struct code_map_isds_error
*map
) {
5712 isds_error err
= IE_SUCCESS
;
5713 char *service_name_locale
= NULL
;
5714 xmlChar
*code
= NULL
, *message
= NULL
;
5717 if (!context
) return IE_INVALID_CONTEXT
;
5718 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5722 /* Check if connection is established
5723 * TODO: This check should be done downstairs. */
5724 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5726 service_name_locale
= _isds_utf82locale((char*) service_name
);
5727 if (!service_name_locale
) {
5732 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5733 service_name_locale
);
5736 err
= isds(context
, service
, *request
, response
, NULL
, NULL
);
5737 xmlFreeNode(*request
); *request
= NULL
;
5740 isds_log(ILF_ISDS
, ILL_DEBUG
,
5741 _("Processing ISDS response on %s request failed\n"),
5742 service_name_locale
);
5746 /* Check for response status */
5747 err
= isds_response_status(context
, service
, *response
,
5748 &code
, &message
, refnumber
);
5750 isds_log(ILF_ISDS
, ILL_DEBUG
,
5751 _("ISDS response on %s request is missing status\n"),
5752 service_name_locale
);
5756 err
= statuscode2isds_error(context
, map
, code
, message
);
5758 /* Request processed, but server failed */
5759 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5760 char *code_locale
= _isds_utf82locale((char*) code
);
5761 char *message_locale
= _isds_utf82locale((char*) message
);
5762 isds_log(ILF_ISDS
, ILL_DEBUG
,
5763 _("Server refused %s request (code=%s, message=%s)\n"),
5764 service_name_locale
, code_locale
, message_locale
);
5766 free(message_locale
);
5774 if (err
&& *response
) {
5775 xmlFreeDoc(*response
);
5779 xmlFreeNode(*request
);
5782 free(service_name_locale
);
5788 /* Generic bottom half with request sending.
5789 * It sends prepared request, checks for error code, destroys response and
5790 * request and log success or failure.
5791 * @context is ISDS session context.
5792 * @service is ISDS service handler
5793 * @service_name is name in scope of given @service
5794 * @request is XML tree with request. Will be freed to save memory.
5795 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5796 * NULL, if you don't care. */
5797 static isds_error
send_request_check_drop_response(
5798 struct isds_ctx
*context
,
5799 const isds_service service
, const xmlChar
*service_name
,
5800 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5801 isds_error err
= IE_SUCCESS
;
5802 xmlDocPtr response
= NULL
;
5805 if (!context
) return IE_INVALID_CONTEXT
;
5806 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5809 /* Send request and check response*/
5810 err
= send_destroy_request_check_response(context
,
5811 service
, service_name
, request
, &response
, refnumber
, NULL
);
5813 xmlFreeDoc(response
);
5816 xmlFreeNode(*request
);
5821 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5822 isds_log(ILF_ISDS
, ILL_DEBUG
,
5823 _("%s request processed by server successfully.\n"),
5824 service_name_locale
);
5825 free(service_name_locale
);
5832 /* Insert isds_credentials_delivery structure into XML request if not NULL
5833 * @context is session context
5834 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5835 * credentials delivery. The email field is passed.
5836 * @parent is XML element where to insert */
5837 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5838 const struct isds_credentials_delivery
*credentials_delivery
,
5839 xmlNodePtr parent
) {
5840 isds_error err
= IE_SUCCESS
;
5843 if (!context
) return IE_INVALID_CONTEXT
;
5844 if (!parent
) return IE_INVAL
;
5846 if (credentials_delivery
) {
5847 /* Following elements are valid only for services:
5848 * NewAccessData, AddDataBoxUser, CreateDataBox */
5849 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5850 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5858 /* Extract credentials delivery from ISDS response.
5859 * @context is session context
5860 * @credentials_delivery is pointer to valid structure to fill in returned
5861 * user's password (and new log-in name). If NULL, do not extract the data.
5862 * @response is pointer to XML document with ISDS response
5863 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5864 * @return IE_SUCCESS even if new user name has not been found because it's not
5865 * clear whether it's returned always. */
5866 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5867 struct isds_credentials_delivery
*credentials_delivery
,
5868 xmlDocPtr response
, const char *request_name
) {
5869 isds_error err
= IE_SUCCESS
;
5870 xmlXPathContextPtr xpath_ctx
= NULL
;
5871 xmlXPathObjectPtr result
= NULL
;
5872 char *xpath_query
= NULL
;
5874 if (!context
) return IE_INVALID_CONTEXT
;
5875 if (credentials_delivery
) {
5876 zfree(credentials_delivery
->token
);
5877 zfree(credentials_delivery
->new_user_name
);
5879 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5882 /* Extract optional token */
5883 if (credentials_delivery
) {
5884 xpath_ctx
= xmlXPathNewContext(response
);
5889 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5894 /* Verify root element */
5895 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5900 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
5905 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5906 char *request_name_locale
= _isds_utf82locale(request_name
);
5907 isds_log(ILF_ISDS
, ILL_WARNING
,
5908 _("Wrong element in ISDS response for %s request "
5909 "while extracting credentials delivery details\n"),
5910 request_name_locale
);
5911 free(request_name_locale
);
5915 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5918 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5920 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
5922 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
5923 if (!credentials_delivery
->token
) {
5924 char *request_name_locale
= _isds_utf82locale(request_name
);
5925 isds_log(ILF_ISDS
, ILL_ERR
,
5926 _("ISDS did not return token on %s request "
5927 "even if requested\n"), request_name_locale
);
5928 free(request_name_locale
);
5935 xmlXPathFreeObject(result
);
5936 xmlXPathFreeContext(xpath_ctx
);
5942 /* Build XSD:tCreateDBInput request type for box creating.
5943 * @context is session context
5944 * @request outputs built XML tree
5945 * @service_name is request name of SERVICE_DB_MANIPULATION service
5946 * @box is box description to create including single primary user (in case of
5948 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5949 * box, or contact address of PFO box owner)
5950 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5951 * @upper_box_id is optional ID of supper box if currently created box is
5953 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5955 * @credentials_delivery is valid pointer if ISDS should return token that box
5956 * owner can use to obtain his new credentials in on-line way. Then valid email
5957 * member value should be supplied.
5958 * @approval is optional external approval of box manipulation */
5959 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
5960 xmlNodePtr
*request
, const xmlChar
*service_name
,
5961 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5962 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
5963 const xmlChar
*ceo_label
,
5964 const struct isds_credentials_delivery
*credentials_delivery
,
5965 const struct isds_approval
*approval
) {
5966 isds_error err
= IE_SUCCESS
;
5967 xmlNsPtr isds_ns
= NULL
;
5968 xmlNodePtr node
, dbPrimaryUsers
;
5969 xmlChar
*string
= NULL
;
5970 const struct isds_list
*item
;
5973 if (!context
) return IE_INVALID_CONTEXT
;
5974 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
5978 /* Build CreateDataBox-similar request */
5979 *request
= xmlNewNode(NULL
, service_name
);
5981 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
5982 isds_printf_message(context
, _("Could build %s request"),
5983 service_name_locale
);
5984 free(service_name_locale
);
5987 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
5988 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
5990 isds_log_message(context
, _("Could not create ISDS1 name space"));
5991 xmlFreeNode(*request
);
5995 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
5997 isds_log_message(context
, _("Could not create ISDS name space"));
5998 xmlFreeNode(*request
);
6002 xmlSetNs(*request
, isds_ns
);
6004 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6005 err
= insert_DbOwnerInfo(context
, box
, node
);
6006 if (err
) goto leave
;
6009 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6010 * verbose documentation allows none dbUserInfo */
6011 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6012 for (item
= users
; item
; item
= item
->next
) {
6014 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6015 err
= insert_DbUserInfo(context
,
6016 (struct isds_DbUserInfo
*) item
->data
, node
);
6017 if (err
) goto leave
;
6021 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6022 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6023 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6025 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6026 if (err
) goto leave
;
6028 err
= insert_GExtApproval(context
, approval
, *request
);
6029 if (err
) goto leave
;
6033 xmlFreeNode(*request
);
6039 #endif /* HAVE_LIBCURL */
6043 * @context is session context
6044 * @box is box description to create including single primary user (in case of
6045 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6046 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6047 * box, or contact address of PFO box owner)
6048 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6049 * @upper_box_id is optional ID of supper box if currently created box is
6051 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6052 * @credentials_delivery is NULL if new password should be delivered off-line
6053 * to box owner. It is valid pointer if owner should obtain new password on-line
6054 * on dedicated web server. Then input @credentials_delivery.email value is
6055 * his e-mail address he must provide to dedicated web server together
6056 * with output reallocated @credentials_delivery.token member. Output
6057 * member @credentials_delivery.new_user_name is unused up on this call.
6058 * @approval is optional external approval of box manipulation
6059 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6060 * NULL, if you don't care.*/
6061 isds_error
isds_add_box(struct isds_ctx
*context
,
6062 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6063 const char *former_names
, const char *upper_box_id
,
6064 const char *ceo_label
,
6065 struct isds_credentials_delivery
*credentials_delivery
,
6066 const struct isds_approval
*approval
, char **refnumber
) {
6067 isds_error err
= IE_SUCCESS
;
6069 xmlNodePtr request
= NULL
;
6070 xmlDocPtr response
= NULL
;
6071 xmlXPathContextPtr xpath_ctx
= NULL
;
6072 xmlXPathObjectPtr result
= NULL
;
6076 if (!context
) return IE_INVALID_CONTEXT
;
6077 zfree(context
->long_message
);
6078 if (credentials_delivery
) {
6079 zfree(credentials_delivery
->token
);
6080 zfree(credentials_delivery
->new_user_name
);
6082 if (!box
) return IE_INVAL
;
6085 /* Scratch box ID */
6088 /* Build CreateDataBox request */
6089 err
= build_CreateDBInput_request(context
,
6090 &request
, BAD_CAST
"CreateDataBox",
6091 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6092 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6093 if (err
) goto leave
;
6095 /* Send it to server and process response */
6096 err
= send_destroy_request_check_response(context
,
6097 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6098 &response
, (xmlChar
**) refnumber
, NULL
);
6100 /* Extract box ID */
6101 xpath_ctx
= xmlXPathNewContext(response
);
6106 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6110 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6112 /* Extract optional token */
6113 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6117 xmlXPathFreeObject(result
);
6118 xmlXPathFreeContext(xpath_ctx
);
6119 xmlFreeDoc(response
);
6120 xmlFreeNode(request
);
6123 isds_log(ILF_ISDS
, ILL_DEBUG
,
6124 _("CreateDataBox request processed by server successfully.\n"));
6126 #else /* not HAVE_LIBCURL */
6134 /* Notify ISDS about new PFO entity.
6135 * This function has no real effect.
6136 * @context is session context
6137 * @box is PFO description including single primary user.
6138 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6139 * @former_names is optional undocumented string. Pass NULL if you don't care.
6140 * @upper_box_id is optional ID of supper box if currently created box is
6142 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6143 * @approval is optional external approval of box manipulation
6144 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6145 * NULL, if you don't care.*/
6146 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6147 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6148 const char *former_names
, const char *upper_box_id
,
6149 const char *ceo_label
, const struct isds_approval
*approval
,
6151 isds_error err
= IE_SUCCESS
;
6153 xmlNodePtr request
= NULL
;
6156 if (!context
) return IE_INVALID_CONTEXT
;
6157 zfree(context
->long_message
);
6158 if (!box
) return IE_INVAL
;
6161 /* Build CreateDataBoxPFOInfo request */
6162 err
= build_CreateDBInput_request(context
,
6163 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6164 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6165 (xmlChar
*) ceo_label
, NULL
, approval
);
6166 if (err
) goto leave
;
6168 /* Send it to server and process response */
6169 err
= send_request_check_drop_response(context
,
6170 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6171 (xmlChar
**) refnumber
);
6172 /* XXX: XML Schema names output dbID element but textual documentation
6173 * states no box identifier is returned. */
6175 xmlFreeNode(request
);
6176 #else /* not HAVE_LIBCURL */
6183 /* Common implementation for removing given box.
6184 * @context is session context
6185 * @service_name is UTF-8 encoded name fo ISDS service
6186 * @box is box description to delete
6187 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6188 * carry sane value. If NULL, do not inject this information into request.
6189 * @approval is optional external approval of box manipulation
6190 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6191 * NULL, if you don't care.*/
6192 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6193 const xmlChar
*service_name
,
6194 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6195 const struct isds_approval
*approval
, char **refnumber
) {
6196 isds_error err
= IE_SUCCESS
;
6198 xmlNsPtr isds_ns
= NULL
;
6199 xmlNodePtr request
= NULL
;
6201 xmlChar
*string
= NULL
;
6205 if (!context
) return IE_INVALID_CONTEXT
;
6206 zfree(context
->long_message
);
6207 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6211 /* Build DeleteDataBox(Promptly) request */
6212 request
= xmlNewNode(NULL
, service_name
);
6214 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6215 isds_printf_message(context
,
6216 _("Could build %s request"), service_name_locale
);
6217 free(service_name_locale
);
6220 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6222 isds_log_message(context
, _("Could not create ISDS name space"));
6223 xmlFreeNode(request
);
6226 xmlSetNs(request
, isds_ns
);
6228 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6229 err
= insert_DbOwnerInfo(context
, box
, node
);
6230 if (err
) goto leave
;
6233 err
= tm2datestring(since
, &string
);
6235 isds_log_message(context
,
6236 _("Could not convert `since' argument to ISO date string"));
6239 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6243 err
= insert_GExtApproval(context
, approval
, request
);
6244 if (err
) goto leave
;
6247 /* Send it to server and process response */
6248 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6249 service_name
, &request
, (xmlChar
**) refnumber
);
6252 xmlFreeNode(request
);
6254 #else /* not HAVE_LIBCURL */
6261 /* Remove given box permanently.
6262 * @context is session context
6263 * @box is box description to delete
6264 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6266 * @approval is optional external approval of box manipulation
6267 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6268 * NULL, if you don't care.*/
6269 isds_error
isds_delete_box(struct isds_ctx
*context
,
6270 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6271 const struct isds_approval
*approval
, char **refnumber
) {
6272 if (!context
) return IE_INVALID_CONTEXT
;
6273 zfree(context
->long_message
);
6274 if (!box
|| !since
) return IE_INVAL
;
6276 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6277 box
, since
, approval
, refnumber
);
6281 /* Undocumented function.
6282 * @context is session context
6283 * @box is box description to delete
6284 * @approval is optional external approval of box manipulation
6285 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6286 * NULL, if you don't care.*/
6287 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6288 const struct isds_DbOwnerInfo
*box
,
6289 const struct isds_approval
*approval
, char **refnumber
) {
6290 if (!context
) return IE_INVALID_CONTEXT
;
6291 zfree(context
->long_message
);
6292 if (!box
) return IE_INVAL
;
6294 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6295 box
, NULL
, approval
, refnumber
);
6299 /* Update data about given box.
6300 * @context is session context
6301 * @old_box current box description
6302 * @new_box are updated data about @old_box
6303 * @approval is optional external approval of box manipulation
6304 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6305 * NULL, if you don't care.*/
6306 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6307 const struct isds_DbOwnerInfo
*old_box
,
6308 const struct isds_DbOwnerInfo
*new_box
,
6309 const struct isds_approval
*approval
, char **refnumber
) {
6310 isds_error err
= IE_SUCCESS
;
6312 xmlNsPtr isds_ns
= NULL
;
6313 xmlNodePtr request
= NULL
;
6318 if (!context
) return IE_INVALID_CONTEXT
;
6319 zfree(context
->long_message
);
6320 if (!old_box
|| !new_box
) return IE_INVAL
;
6324 /* Build UpdateDataBoxDescr request */
6325 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6327 isds_log_message(context
,
6328 _("Could build UpdateDataBoxDescr request"));
6331 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6333 isds_log_message(context
, _("Could not create ISDS name space"));
6334 xmlFreeNode(request
);
6337 xmlSetNs(request
, isds_ns
);
6339 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6340 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6341 if (err
) goto leave
;
6343 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6344 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6345 if (err
) goto leave
;
6347 err
= insert_GExtApproval(context
, approval
, request
);
6348 if (err
) goto leave
;
6351 /* Send it to server and process response */
6352 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6353 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6356 xmlFreeNode(request
);
6357 #else /* not HAVE_LIBCURL */
6366 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6368 * @context is session context
6369 * @service is SOAP service
6370 * @service_name is name of request in @service
6371 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6372 * @box_id is box ID of interest
6373 * @approval is optional external approval of box manipulation
6374 * @response is server SOAP body response as XML document
6375 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6376 * NULL, if you don't care.
6377 * @return error coded from lower layer, context message will be set up
6379 static isds_error
build_send_dbid_request_check_response(
6380 struct isds_ctx
*context
, const isds_service service
,
6381 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6382 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6383 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6385 isds_error err
= IE_SUCCESS
;
6386 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6387 xmlNodePtr request
= NULL
, node
;
6388 xmlNsPtr isds_ns
= NULL
;
6390 if (!context
) return IE_INVALID_CONTEXT
;
6391 if (!service_name
|| !box_id
) return IE_INVAL
;
6392 if (!response
) return IE_INVAL
;
6394 /* Free output argument */
6395 xmlFreeDoc(*response
); *response
= NULL
;
6397 /* Prepare strings */
6398 service_name_locale
= _isds_utf82locale((char*)service_name
);
6399 if (!service_name_locale
) {
6403 box_id_locale
= _isds_utf82locale((char*)box_id
);
6404 if (!box_id_locale
) {
6410 request
= xmlNewNode(NULL
, service_name
);
6412 isds_printf_message(context
,
6413 _("Could not build %s request for %s box"), service_name_locale
,
6418 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6420 isds_log_message(context
, _("Could not create ISDS name space"));
6424 xmlSetNs(request
, isds_ns
);
6426 /* Add XSD:tIdDbInput children */
6427 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6428 INSERT_STRING(request
, box_id_element
, box_id
);
6429 err
= insert_GExtApproval(context
, approval
, request
);
6430 if (err
) goto leave
;
6432 /* Send request and check response*/
6433 err
= send_destroy_request_check_response(context
,
6434 service
, service_name
, &request
, response
, refnumber
, NULL
);
6437 free(service_name_locale
);
6438 free(box_id_locale
);
6439 xmlFreeNode(request
);
6442 #endif /* HAVE_LIBCURL */
6445 /* Get data about all users assigned to given box.
6446 * @context is session context
6448 * @users is automatically reallocated list of struct isds_DbUserInfo */
6449 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6450 struct isds_list
**users
) {
6451 isds_error err
= IE_SUCCESS
;
6453 xmlDocPtr response
= NULL
;
6454 xmlXPathContextPtr xpath_ctx
= NULL
;
6455 xmlXPathObjectPtr result
= NULL
;
6457 struct isds_list
*item
, *prev_item
= NULL
;
6460 if (!context
) return IE_INVALID_CONTEXT
;
6461 zfree(context
->long_message
);
6462 if (!users
|| !box_id
) return IE_INVAL
;
6463 isds_list_free(users
);
6467 /* Do request and check for success */
6468 err
= build_send_dbid_request_check_response(context
,
6469 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6470 BAD_CAST box_id
, NULL
, &response
, NULL
);
6471 if (err
) goto leave
;
6475 /* Prepare structure */
6476 xpath_ctx
= xmlXPathNewContext(response
);
6481 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6486 /* Set context node */
6487 result
= xmlXPathEvalExpression(BAD_CAST
6488 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6494 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6495 /* Iterate over all users */
6496 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6498 /* Prepare structure */
6499 item
= calloc(1, sizeof(*item
));
6504 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6505 if (i
== 0) *users
= item
;
6506 else prev_item
->next
= item
;
6510 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6511 err
= extract_DbUserInfo(context
,
6512 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6513 if (err
) goto leave
;
6519 isds_list_free(users
);
6522 xmlXPathFreeObject(result
);
6523 xmlXPathFreeContext(xpath_ctx
);
6524 xmlFreeDoc(response
);
6527 isds_log(ILF_ISDS
, ILL_DEBUG
,
6528 _("GetDataBoxUsers request processed by server "
6529 "successfully.\n"));
6530 #else /* not HAVE_LIBCURL */
6538 /* Update data about user assigned to given box.
6539 * @context is session context
6540 * @box is box identification
6541 * @old_user identifies user to update
6542 * @new_user are updated data about @old_user
6543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6544 * NULL, if you don't care.*/
6545 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6546 const struct isds_DbOwnerInfo
*box
,
6547 const struct isds_DbUserInfo
*old_user
,
6548 const struct isds_DbUserInfo
*new_user
,
6550 isds_error err
= IE_SUCCESS
;
6552 xmlNsPtr isds_ns
= NULL
;
6553 xmlNodePtr request
= NULL
;
6558 if (!context
) return IE_INVALID_CONTEXT
;
6559 zfree(context
->long_message
);
6560 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6564 /* Build UpdateDataBoxUser request */
6565 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6567 isds_log_message(context
,
6568 _("Could build UpdateDataBoxUser request"));
6571 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6573 isds_log_message(context
, _("Could not create ISDS name space"));
6574 xmlFreeNode(request
);
6577 xmlSetNs(request
, isds_ns
);
6579 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6580 err
= insert_DbOwnerInfo(context
, box
, node
);
6581 if (err
) goto leave
;
6583 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6584 err
= insert_DbUserInfo(context
, old_user
, node
);
6585 if (err
) goto leave
;
6587 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6588 err
= insert_DbUserInfo(context
, new_user
, node
);
6589 if (err
) goto leave
;
6591 /* Send it to server and process response */
6592 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6593 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6596 xmlFreeNode(request
);
6597 #else /* not HAVE_LIBCURL */
6605 /* Undocumented function.
6606 * @context is session context
6607 * @box_id is UTF-8 encoded box identifier
6608 * @token is UTF-8 encoded temporary password
6609 * @user_id outputs UTF-8 encoded reallocated user identifier
6610 * @password outpus UTF-8 encoded reallocated user password
6611 * Output arguments will be nulled in case of error */
6612 isds_error
isds_activate(struct isds_ctx
*context
,
6613 const char *box_id
, const char *token
,
6614 char **user_id
, char **password
) {
6615 isds_error err
= IE_SUCCESS
;
6617 xmlNsPtr isds_ns
= NULL
;
6618 xmlNodePtr request
= NULL
, node
;
6619 xmlDocPtr response
= NULL
;
6620 xmlXPathContextPtr xpath_ctx
= NULL
;
6621 xmlXPathObjectPtr result
= NULL
;
6625 if (!context
) return IE_INVALID_CONTEXT
;
6626 zfree(context
->long_message
);
6628 if (user_id
) zfree(*user_id
);
6629 if (password
) zfree(*password
);
6631 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6635 /* Build Activate request */
6636 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6638 isds_log_message(context
, _("Could build Activate request"));
6641 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6643 isds_log_message(context
, _("Could not create ISDS name space"));
6644 xmlFreeNode(request
);
6647 xmlSetNs(request
, isds_ns
);
6649 INSERT_STRING(request
, "dbAccessDataId", token
);
6650 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6651 INSERT_STRING(request
, "dbID", box_id
);
6654 /* Send request and check response*/
6655 err
= send_destroy_request_check_response(context
,
6656 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6657 &response
, NULL
, NULL
);
6658 if (err
) goto leave
;
6662 xpath_ctx
= xmlXPathNewContext(response
);
6667 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6671 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6677 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6678 isds_log_message(context
, _("Missing ActivateResponse element"));
6682 if (result
->nodesetval
->nodeNr
> 1) {
6683 isds_log_message(context
, _("Multiple ActivateResponse element"));
6687 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6688 xmlXPathFreeObject(result
); result
= NULL
;
6690 EXTRACT_STRING("isds:userId", *user_id
);
6692 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6693 "but did not return `userId' element.\n"));
6695 EXTRACT_STRING("isds:password", *password
);
6697 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6698 "but did not return `password' element.\n"));
6701 xmlXPathFreeObject(result
);
6702 xmlXPathFreeContext(xpath_ctx
);
6703 xmlFreeDoc(response
);
6704 xmlFreeNode(request
);
6707 isds_log(ILF_ISDS
, ILL_DEBUG
,
6708 _("Activate request processed by server successfully.\n"));
6709 #else /* not HAVE_LIBCURL */
6717 /* Reset credentials of user assigned to given box.
6718 * @context is session context
6719 * @box is box identification
6720 * @user identifies user to reset password
6721 * @fee_paid is true if fee has been paid, false otherwise
6722 * @approval is optional external approval of box manipulation
6723 * @credentials_delivery is NULL if new password should be delivered off-line
6724 * to the user. It is valid pointer if user should obtain new password on-line
6725 * on dedicated web server. Then input @credentials_delivery.email value is
6726 * user's e-mail address user must provide to dedicated web server together
6727 * with @credentials_delivery.token. The output reallocated token user needs
6728 * to use to authorize on the web server to view his new password. Output
6729 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6730 * ISDS changed up on this call. (No reason why server could change the name
6732 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6733 * NULL, if you don't care.*/
6734 isds_error
isds_reset_password(struct isds_ctx
*context
,
6735 const struct isds_DbOwnerInfo
*box
,
6736 const struct isds_DbUserInfo
*user
,
6737 const _Bool fee_paid
, const struct isds_approval
*approval
,
6738 struct isds_credentials_delivery
*credentials_delivery
,
6740 isds_error err
= IE_SUCCESS
;
6742 xmlNsPtr isds_ns
= NULL
;
6743 xmlNodePtr request
= NULL
, node
;
6744 xmlDocPtr response
= NULL
;
6748 if (!context
) return IE_INVALID_CONTEXT
;
6749 zfree(context
->long_message
);
6751 if (credentials_delivery
) {
6752 zfree(credentials_delivery
->token
);
6753 zfree(credentials_delivery
->new_user_name
);
6755 if (!box
|| !user
) return IE_INVAL
;
6759 /* Build NewAccessData request */
6760 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6762 isds_log_message(context
,
6763 _("Could build NewAccessData request"));
6766 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6768 isds_log_message(context
, _("Could not create ISDS name space"));
6769 xmlFreeNode(request
);
6772 xmlSetNs(request
, isds_ns
);
6774 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6775 err
= insert_DbOwnerInfo(context
, box
, node
);
6776 if (err
) goto leave
;
6778 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6779 err
= insert_DbUserInfo(context
, user
, node
);
6780 if (err
) goto leave
;
6782 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6784 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6785 if (err
) goto leave
;
6787 err
= insert_GExtApproval(context
, approval
, request
);
6788 if (err
) goto leave
;
6790 /* Send request and check response*/
6791 err
= send_destroy_request_check_response(context
,
6792 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6793 &response
, (xmlChar
**) refnumber
, NULL
);
6794 if (err
) goto leave
;
6797 /* Extract optional token */
6798 err
= extract_credentials_delivery(context
, credentials_delivery
,
6799 response
, "NewAccessData");
6802 xmlFreeDoc(response
);
6803 xmlFreeNode(request
);
6806 isds_log(ILF_ISDS
, ILL_DEBUG
,
6807 _("NewAccessData request processed by server "
6808 "successfully.\n"));
6809 #else /* not HAVE_LIBCURL */
6817 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6818 * code, destroy response and log success.
6819 * @context is ISDS session context.
6820 * @service_name is name of SERVICE_DB_MANIPULATION service
6821 * @box is box identification
6822 * @user identifies user to remove
6823 * @credentials_delivery is NULL if new user's password should be delivered
6824 * off-line to the user. It is valid pointer if user should obtain new
6825 * password on-line on dedicated web server. Then input
6826 * @credentials_delivery.email value is user's e-mail address user must
6827 * provide to dedicated web server together with @credentials_delivery.token.
6828 * The output reallocated token user needs to use to authorize on the web
6829 * server to view his new password. Output reallocated
6830 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6831 * assingned or changed up on this call.
6832 * @approval is optional external approval of box manipulation
6833 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6834 * NULL, if you don't care. */
6835 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6836 struct isds_ctx
*context
, const xmlChar
*service_name
,
6837 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6838 struct isds_credentials_delivery
*credentials_delivery
,
6839 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6840 isds_error err
= IE_SUCCESS
;
6842 xmlNsPtr isds_ns
= NULL
;
6843 xmlNodePtr request
= NULL
, node
;
6844 xmlDocPtr response
= NULL
;
6848 if (!context
) return IE_INVALID_CONTEXT
;
6849 zfree(context
->long_message
);
6850 if (credentials_delivery
) {
6851 zfree(credentials_delivery
->token
);
6852 zfree(credentials_delivery
->new_user_name
);
6854 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6859 /* Build NewAccessData or similar request */
6860 request
= xmlNewNode(NULL
, service_name
);
6862 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6863 isds_printf_message(context
, _("Could not build %s request"),
6864 service_name_locale
);
6865 free(service_name_locale
);
6868 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6870 isds_log_message(context
, _("Could not create ISDS name space"));
6871 xmlFreeNode(request
);
6874 xmlSetNs(request
, isds_ns
);
6876 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6877 err
= insert_DbOwnerInfo(context
, box
, node
);
6878 if (err
) goto leave
;
6880 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6881 err
= insert_DbUserInfo(context
, user
, node
);
6882 if (err
) goto leave
;
6884 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6885 if (err
) goto leave
;
6887 err
= insert_GExtApproval(context
, approval
, request
);
6888 if (err
) goto leave
;
6891 /* Send request and check response*/
6892 err
= send_destroy_request_check_response(context
,
6893 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6896 xmlFreeNode(request
);
6899 /* Pick up credentials_delivery if requested */
6900 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6901 (char *)service_name
);
6904 xmlFreeDoc(response
);
6905 if (request
) xmlFreeNode(request
);
6908 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6909 isds_log(ILF_ISDS
, ILL_DEBUG
,
6910 _("%s request processed by server successfully.\n"),
6911 service_name_locale
);
6912 free(service_name_locale
);
6914 #else /* not HAVE_LIBCURL */
6922 /* Assign new user to given box.
6923 * @context is session context
6924 * @box is box identification
6925 * @user defines new user to add
6926 * @credentials_delivery is NULL if new user's password should be delivered
6927 * off-line to the user. It is valid pointer if user should obtain new
6928 * password on-line on dedicated web server. Then input
6929 * @credentials_delivery.email value is user's e-mail address user must
6930 * provide to dedicated web server together with @credentials_delivery.token.
6931 * The output reallocated token user needs to use to authorize on the web
6932 * server to view his new password. Output reallocated
6933 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6934 * assingned up on this call.
6935 * @approval is optional external approval of box manipulation
6936 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6937 * NULL, if you don't care.*/
6938 isds_error
isds_add_user(struct isds_ctx
*context
,
6939 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6940 struct isds_credentials_delivery
*credentials_delivery
,
6941 const struct isds_approval
*approval
, char **refnumber
) {
6942 return build_send_manipulationboxuser_request_check_drop_response(context
,
6943 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
6944 approval
, (xmlChar
**) refnumber
);
6948 /* Remove user assigned to given box.
6949 * @context is session context
6950 * @box is box identification
6951 * @user identifies user to remove
6952 * @approval is optional external approval of box manipulation
6953 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6954 * NULL, if you don't care.*/
6955 isds_error
isds_delete_user(struct isds_ctx
*context
,
6956 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6957 const struct isds_approval
*approval
, char **refnumber
) {
6958 return build_send_manipulationboxuser_request_check_drop_response(context
,
6959 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
6960 (xmlChar
**) refnumber
);
6964 /* Get list of boxes in ZIP archive.
6965 * @context is session context
6966 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6967 * System recognizes following values currently: ALL (all boxes), UPG
6968 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6969 * receiving commercial messages). This argument is a string because
6970 * specification states new values can appear in the future. Not all list
6971 * types are available to all users.
6972 * @buffer is automatically reallocated memory to store the list of boxes. The
6973 * list is zipped CSV file.
6974 * @buffer_length is size of @buffer data in bytes.
6975 * In case of error @buffer will be freed and @buffer_length will be
6977 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
6978 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
6979 isds_error err
= IE_SUCCESS
;
6981 xmlNsPtr isds_ns
= NULL
;
6982 xmlNodePtr request
= NULL
, node
;
6983 xmlDocPtr response
= NULL
;
6984 xmlXPathContextPtr xpath_ctx
= NULL
;
6985 xmlXPathObjectPtr result
= NULL
;
6986 char *string
= NULL
;
6990 if (!context
) return IE_INVALID_CONTEXT
;
6991 zfree(context
->long_message
);
6992 if (buffer
) zfree(*buffer
);
6993 if (!buffer
|| !buffer_length
) return IE_INVAL
;
6997 /* Check if connection is established
6998 * TODO: This check should be done downstairs. */
6999 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7002 /* Build AuthenticateMessage request */
7003 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7005 isds_log_message(context
,
7006 _("Could not build GetDataBoxList request"));
7009 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7011 isds_log_message(context
, _("Could not create ISDS name space"));
7012 xmlFreeNode(request
);
7015 xmlSetNs(request
, isds_ns
);
7016 INSERT_STRING(request
, "dblType", list_identifier
);
7018 /* Send request to server and process response */
7019 err
= send_destroy_request_check_response(context
,
7020 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7021 &response
, NULL
, NULL
);
7022 if (err
) goto leave
;
7025 /* Extract Base-64 encoded ZIP file */
7026 xpath_ctx
= xmlXPathNewContext(response
);
7031 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7035 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7037 /* Decode non-empty archive */
7038 if (string
&& string
[0] != '\0') {
7039 *buffer_length
= _isds_b64decode(string
, buffer
);
7040 if (*buffer_length
== (size_t) -1) {
7041 isds_printf_message(context
,
7042 _("Error while Base64-decoding box list archive"));
7051 xmlXPathFreeObject(result
);
7052 xmlXPathFreeContext(xpath_ctx
);
7053 xmlFreeDoc(response
);
7054 xmlFreeNode(request
);
7057 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7058 "processed by server successfully.\n"));
7060 #else /* not HAVE_LIBCURL */
7068 /* Find boxes suiting given criteria.
7069 * @criteria is filter. You should fill in at least some members.
7070 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7071 * possibly empty. Input NULL or valid old structure.
7073 * IE_SUCCESS if search succeeded, @boxes contains useful data
7074 * IE_NOEXIST if no such box exists, @boxes will be NULL
7075 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7076 * contains still valid data
7077 * other code if something bad happens. @boxes will be NULL. */
7078 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7079 const struct isds_DbOwnerInfo
*criteria
,
7080 struct isds_list
**boxes
) {
7081 isds_error err
= IE_SUCCESS
;
7083 _Bool truncated
= 0;
7084 xmlNsPtr isds_ns
= NULL
;
7085 xmlNodePtr request
= NULL
;
7086 xmlDocPtr response
= NULL
;
7087 xmlChar
*code
= NULL
, *message
= NULL
;
7088 xmlNodePtr db_owner_info
;
7089 xmlXPathContextPtr xpath_ctx
= NULL
;
7090 xmlXPathObjectPtr result
= NULL
;
7091 xmlChar
*string
= NULL
;
7095 if (!context
) return IE_INVALID_CONTEXT
;
7096 zfree(context
->long_message
);
7097 if (!boxes
) return IE_INVAL
;
7098 isds_list_free(boxes
);
7105 /* Check if connection is established
7106 * TODO: This check should be done downstairs. */
7107 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7110 /* Build FindDataBox request */
7111 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7113 isds_log_message(context
,
7114 _("Could build FindDataBox request"));
7117 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7119 isds_log_message(context
, _("Could not create ISDS name space"));
7120 xmlFreeNode(request
);
7123 xmlSetNs(request
, isds_ns
);
7124 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7125 if (!db_owner_info
) {
7126 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7127 "FindDataBox element"));
7128 xmlFreeNode(request
);
7132 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7133 if (err
) goto leave
;
7136 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7139 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7141 /* Destroy request */
7142 xmlFreeNode(request
); request
= NULL
;
7145 isds_log(ILF_ISDS
, ILL_DEBUG
,
7146 _("Processing ISDS response on FindDataBox "
7147 "request failed\n"));
7151 /* Check for response status */
7152 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7153 &code
, &message
, NULL
);
7155 isds_log(ILF_ISDS
, ILL_DEBUG
,
7156 _("ISDS response on FindDataBox request is missing status\n"));
7160 /* Request processed, but nothing found */
7161 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7162 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7163 char *code_locale
= _isds_utf82locale((char*)code
);
7164 char *message_locale
= _isds_utf82locale((char*)message
);
7165 isds_log(ILF_ISDS
, ILL_DEBUG
,
7166 _("Server did not found any box on FindDataBox request "
7167 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7168 isds_log_message(context
, message_locale
);
7170 free(message_locale
);
7175 /* Warning, not a error */
7176 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7177 char *code_locale
= _isds_utf82locale((char*)code
);
7178 char *message_locale
= _isds_utf82locale((char*)message
);
7179 isds_log(ILF_ISDS
, ILL_DEBUG
,
7180 _("Server truncated response on FindDataBox request "
7181 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7182 isds_log_message(context
, message_locale
);
7184 free(message_locale
);
7189 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7190 char *code_locale
= _isds_utf82locale((char*)code
);
7191 char *message_locale
= _isds_utf82locale((char*)message
);
7192 isds_log(ILF_ISDS
, ILL_DEBUG
,
7193 _("Server refused FindDataBox request "
7194 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7195 isds_log_message(context
, message_locale
);
7197 free(message_locale
);
7202 xpath_ctx
= xmlXPathNewContext(response
);
7207 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7212 /* Extract boxes if they present */
7213 result
= xmlXPathEvalExpression(BAD_CAST
7214 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7220 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7221 struct isds_list
*item
, *prev_item
= NULL
;
7222 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7223 item
= calloc(1, sizeof(*item
));
7229 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7230 if (i
== 0) *boxes
= item
;
7231 else prev_item
->next
= item
;
7234 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7235 err
= extract_DbOwnerInfo(context
,
7236 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7237 if (err
) goto leave
;
7243 isds_list_free(boxes
);
7245 if (truncated
) err
= IE_2BIG
;
7249 xmlFreeNode(request
);
7250 xmlXPathFreeObject(result
);
7251 xmlXPathFreeContext(xpath_ctx
);
7255 xmlFreeDoc(response
);
7258 isds_log(ILF_ISDS
, ILL_DEBUG
,
7259 _("FindDataBox request processed by server successfully.\n"));
7260 #else /* not HAVE_LIBCURL */
7268 /* Get status of a box.
7269 * @context is ISDS session context.
7270 * @box_id is UTF-8 encoded box identifier as zero terminated string
7271 * @box_status is return value of box status.
7273 * IE_SUCCESS if box has been found and its status retrieved
7274 * IE_NOEXIST if box is not known to ISDS server
7275 * or other appropriate error.
7276 * You can use isds_DbState to enumerate box status. However out of enum
7277 * range value can be returned too. This is feature because ISDS
7278 * specification leaves the set of values open.
7279 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7280 * the box has been deleted, but ISDS still lists its former existence. */
7281 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7282 long int *box_status
) {
7283 isds_error err
= IE_SUCCESS
;
7285 xmlNsPtr isds_ns
= NULL
;
7286 xmlNodePtr request
= NULL
, db_id
;
7287 xmlDocPtr response
= NULL
;
7288 xmlXPathContextPtr xpath_ctx
= NULL
;
7289 xmlXPathObjectPtr result
= NULL
;
7290 xmlChar
*string
= NULL
;
7292 const xmlChar
*codes
[] = {
7298 const char *meanings
[] = {
7299 "The box does not exist",
7300 "Box ID is malformed",
7303 const isds_error errors
[] = {
7308 struct code_map_isds_error map
= {
7310 .meanings
= meanings
,
7315 if (!context
) return IE_INVALID_CONTEXT
;
7316 zfree(context
->long_message
);
7317 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7320 /* Check if connection is established
7321 * TODO: This check should be done downstairs. */
7322 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7325 /* Build CheckDataBox request */
7326 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7328 isds_log_message(context
,
7329 _("Could build CheckDataBox request"));
7332 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7334 isds_log_message(context
, _("Could not create ISDS name space"));
7335 xmlFreeNode(request
);
7338 xmlSetNs(request
, isds_ns
);
7339 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7341 isds_log_message(context
, _("Could not add dbID child to "
7342 "CheckDataBox element"));
7343 xmlFreeNode(request
);
7348 /* Send request and check response*/
7349 err
= send_destroy_request_check_response(context
,
7350 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7351 &request
, &response
, NULL
, &map
);
7352 if (err
) goto leave
;
7356 xpath_ctx
= xmlXPathNewContext(response
);
7361 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7365 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7371 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7372 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7376 if (result
->nodesetval
->nodeNr
> 1) {
7377 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7381 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7382 xmlXPathFreeObject(result
); result
= NULL
;
7384 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7389 xmlXPathFreeObject(result
);
7390 xmlXPathFreeContext(xpath_ctx
);
7392 xmlFreeDoc(response
);
7395 isds_log(ILF_ISDS
, ILL_DEBUG
,
7396 _("CheckDataBox request processed by server successfully.\n"));
7397 #else /* not HAVE_LIBCURL */
7405 /* Get list of permissions to send commercial messages.
7406 * @context is ISDS session context.
7407 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7408 * @permissions is a reallocated list of permissions (struct
7409 * isds_commercial_permission*) to send commercial messages from @box_id. The
7410 * order of permissions is significant as the server applies the permissions
7411 * and associated pre-paid credits in the order. Empty list means no
7414 * IE_SUCCESS if the list has been obtained correctly,
7415 * or other appropriate error. */
7416 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7417 const char *box_id
, struct isds_list
**permissions
) {
7418 isds_error err
= IE_SUCCESS
;
7420 xmlDocPtr response
= NULL
;
7421 xmlXPathContextPtr xpath_ctx
= NULL
;
7422 xmlXPathObjectPtr result
= NULL
;
7425 if (!context
) return IE_INVALID_CONTEXT
;
7426 zfree(context
->long_message
);
7427 if (NULL
== permissions
) return IE_INVAL
;
7428 isds_list_free(permissions
);
7429 if (NULL
== box_id
) return IE_INVAL
;
7432 /* Check if connection is established */
7433 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7435 /* Do request and check for success */
7436 err
= build_send_dbid_request_check_response(context
,
7437 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7438 BAD_CAST box_id
, NULL
, &response
, NULL
);
7440 isds_log(ILF_ISDS
, ILL_DEBUG
,
7441 _("PDZInfo request processed by server successfully.\n"));
7445 /* Prepare structure */
7446 xpath_ctx
= xmlXPathNewContext(response
);
7451 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7456 /* Set context node */
7457 result
= xmlXPathEvalExpression(BAD_CAST
7458 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7464 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7465 struct isds_list
*prev_item
= NULL
;
7467 /* Iterate over all permission records */
7468 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7469 struct isds_list
*item
;
7471 /* Prepare structure */
7472 item
= calloc(1, sizeof(*item
));
7477 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
7478 if (i
== 0) *permissions
= item
;
7479 else prev_item
->next
= item
;
7483 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7484 err
= extract_DbPDZRecord(context
,
7485 (struct isds_commercial_permission
**) (&item
->data
),
7487 if (err
) goto leave
;
7493 isds_list_free(permissions
);
7496 xmlXPathFreeObject(result
);
7497 xmlXPathFreeContext(xpath_ctx
);
7498 xmlFreeDoc(response
);
7500 #else /* not HAVE_LIBCURL */
7508 /* Get details about credit for sending pre-paid commercial messages.
7509 * @context is ISDS session context.
7510 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7511 * @from_date is first day of credit history to return in @history. Only
7512 * tm_year, tm_mon and tm_mday carry sane value.
7513 * @to_date is last day of credit history to return in @history. Only
7514 * tm_year, tm_mon and tm_mday carry sane value.
7515 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7516 * if you don't care. This and all other credit values are integers in
7517 * hundredths of Czech Crowns.
7518 * @email outputs notification e-mail address where notifications about credit
7519 * are sent. This is automatically reallocated string. Pass NULL if you don't
7520 * care. It can return NULL if no address is defined.
7521 * @history outputs auto-reallocated list of pointers to struct
7522 * isds_credit_event. Events in closed interval @from_time to @to_time are
7523 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7524 * are sorted by time.
7526 * IE_SUCCESS if the credit details have been obtained correctly,
7527 * or other appropriate error. Please note that server allows to retrieve
7528 * only limited history of events. */
7529 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
7531 const struct tm
*from_date
, const struct tm
*to_date
,
7532 long int *credit
, char **email
, struct isds_list
**history
) {
7533 isds_error err
= IE_SUCCESS
;
7535 char *box_id_locale
= NULL
;
7536 xmlNodePtr request
= NULL
, node
;
7537 xmlNsPtr isds_ns
= NULL
;
7538 xmlChar
*string
= NULL
;
7540 xmlDocPtr response
= NULL
;
7541 xmlXPathContextPtr xpath_ctx
= NULL
;
7542 xmlXPathObjectPtr result
= NULL
;
7544 const xmlChar
*codes
[] = {
7552 const char *meanings
[] = {
7553 "Insufficient priviledges for the box",
7554 "The box does not exist",
7555 "Date is too long (history is not available after 15 months)",
7556 "Interval is too long (limit is 3 months)",
7559 const isds_error errors
[] = {
7566 struct code_map_isds_error map
= {
7568 .meanings
= meanings
,
7573 if (!context
) return IE_INVALID_CONTEXT
;
7574 zfree(context
->long_message
);
7576 /* Free output argument */
7577 if (NULL
!= credit
) *credit
= 0;
7578 if (NULL
!= email
) zfree(*email
);
7579 isds_list_free(history
);
7581 if (NULL
== box_id
) return IE_INVAL
;
7584 /* Check if connection is established */
7585 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7587 box_id_locale
= _isds_utf82locale((char*)box_id
);
7588 if (NULL
== box_id_locale
) {
7594 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
7595 if (NULL
== request
) {
7596 isds_printf_message(context
,
7597 _("Could not build DataBoxCreditInfo request for %s box"),
7602 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7604 isds_log_message(context
, _("Could not create ISDS name space"));
7608 xmlSetNs(request
, isds_ns
);
7610 /* Add mandatory XSD:tIdDbInput child */
7611 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
7612 /* Add mandatory dates elements with optional values */
7614 err
= tm2datestring(from_date
, &string
);
7616 isds_log_message(context
,
7617 _("Could not convert `from_date' argument to ISO date "
7621 INSERT_STRING(request
, "ciFromDate", string
);
7624 INSERT_STRING(request
, "ciFromDate", NULL
);
7627 err
= tm2datestring(to_date
, &string
);
7629 isds_log_message(context
,
7630 _("Could not convert `to_date' argument to ISO date "
7634 INSERT_STRING(request
, "ciTodate", string
);
7637 INSERT_STRING(request
, "ciTodate", NULL
);
7640 /* Send request and check response*/
7641 err
= send_destroy_request_check_response(context
,
7642 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
7643 &request
, &response
, NULL
, &map
);
7644 if (err
) goto leave
;
7648 /* Set context to the root */
7649 xpath_ctx
= xmlXPathNewContext(response
);
7654 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7658 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
7664 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7665 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
7669 if (result
->nodesetval
->nodeNr
> 1) {
7670 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
7674 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7675 xmlXPathFreeObject(result
); result
= NULL
;
7677 /* Extract common data */
7678 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
7679 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
7681 /* Extract records */
7682 if (NULL
== history
) goto leave
;
7683 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
7689 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7690 struct isds_list
*prev_item
= NULL
;
7692 /* Iterate over all records */
7693 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7694 struct isds_list
*item
;
7696 /* Prepare structure */
7697 item
= calloc(1, sizeof(*item
));
7702 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
7703 if (i
== 0) *history
= item
;
7704 else prev_item
->next
= item
;
7708 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7709 err
= extract_CiRecord(context
,
7710 (struct isds_credit_event
**) (&item
->data
),
7712 if (err
) goto leave
;
7718 isds_log(ILF_ISDS
, ILL_DEBUG
,
7719 _("DataBoxCreditInfo request processed by server successfully.\n"));
7722 isds_list_free(history
);
7723 if (NULL
!= email
) zfree(*email
)
7726 free(box_id_locale
);
7727 xmlXPathFreeObject(result
);
7728 xmlXPathFreeContext(xpath_ctx
);
7729 xmlFreeDoc(response
);
7731 #else /* not HAVE_LIBCURL */
7739 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7740 * code, destroy response and log success.
7741 * @context is ISDS session context.
7742 * @service_name is name of SERVICE_DB_MANIPULATION service
7743 * @box_id is UTF-8 encoded box identifier as zero terminated string
7744 * @approval is optional external approval of box manipulation
7745 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7746 * NULL, if you don't care. */
7747 static isds_error
build_send_manipulationdbid_request_check_drop_response(
7748 struct isds_ctx
*context
, const xmlChar
*service_name
,
7749 const xmlChar
*box_id
, const struct isds_approval
*approval
,
7750 xmlChar
**refnumber
) {
7751 isds_error err
= IE_SUCCESS
;
7753 xmlDocPtr response
= NULL
;
7756 if (!context
) return IE_INVALID_CONTEXT
;
7757 zfree(context
->long_message
);
7758 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
7761 /* Check if connection is established */
7762 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7764 /* Do request and check for success */
7765 err
= build_send_dbid_request_check_response(context
,
7766 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
7767 &response
, refnumber
);
7768 xmlFreeDoc(response
);
7771 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7772 isds_log(ILF_ISDS
, ILL_DEBUG
,
7773 _("%s request processed by server successfully.\n"),
7774 service_name_locale
);
7775 free(service_name_locale
);
7777 #else /* not HAVE_LIBCURL */
7785 /* Switch box into state where box can receive commercial messages (off by
7787 * @context is ISDS session context.
7788 * @box_id is UTF-8 encoded box identifier as zero terminated string
7789 * @allow is true for enable, false for disable commercial messages income
7790 * @approval is optional external approval of box manipulation
7791 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7792 * NULL, if you don't care. */
7793 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
7794 const char *box_id
, const _Bool allow
,
7795 const struct isds_approval
*approval
, char **refnumber
) {
7796 return build_send_manipulationdbid_request_check_drop_response(context
,
7797 (allow
) ? BAD_CAST
"SetOpenAddressing" :
7798 BAD_CAST
"ClearOpenAddressing",
7799 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7803 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7804 * message acceptance). This is just a box permission. Sender must apply
7805 * such role by sending each message.
7806 * @context is ISDS session context.
7807 * @box_id is UTF-8 encoded box identifier as zero terminated string
7808 * @allow is true for enable, false for disable OVM role permission
7809 * @approval is optional external approval of box manipulation
7810 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7811 * NULL, if you don't care. */
7812 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
7813 const char *box_id
, const _Bool allow
,
7814 const struct isds_approval
*approval
, char **refnumber
) {
7815 return build_send_manipulationdbid_request_check_drop_response(context
,
7816 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
7817 BAD_CAST
"ClearEffectiveOVM",
7818 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7822 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7823 * code, destroy response and log success.
7824 * @context is ISDS session context.
7825 * @service_name is name of SERVICE_DB_MANIPULATION service
7826 * @owner is structure describing box
7827 * @approval is optional external approval of box manipulation
7828 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7829 * NULL, if you don't care. */
7830 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
7831 struct isds_ctx
*context
, const xmlChar
*service_name
,
7832 const struct isds_DbOwnerInfo
*owner
,
7833 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7834 isds_error err
= IE_SUCCESS
;
7836 char *service_name_locale
= NULL
;
7837 xmlNodePtr request
= NULL
, db_owner_info
;
7838 xmlNsPtr isds_ns
= NULL
;
7842 if (!context
) return IE_INVALID_CONTEXT
;
7843 zfree(context
->long_message
);
7844 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
7847 service_name_locale
= _isds_utf82locale((char*)service_name
);
7848 if (!service_name_locale
) {
7854 request
= xmlNewNode(NULL
, service_name
);
7856 isds_printf_message(context
,
7857 _("Could not build %s request"), service_name_locale
);
7861 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7863 isds_log_message(context
, _("Could not create ISDS name space"));
7867 xmlSetNs(request
, isds_ns
);
7870 /* Add XSD:tOwnerInfoInput child*/
7871 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
7872 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
7873 if (err
) goto leave
;
7875 /* Add XSD:gExtApproval*/
7876 err
= insert_GExtApproval(context
, approval
, request
);
7877 if (err
) goto leave
;
7879 /* Send it to server and process response */
7880 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7881 service_name
, &request
, refnumber
);
7884 xmlFreeNode(request
);
7885 free(service_name_locale
);
7886 #else /* not HAVE_LIBCURL */
7894 /* Switch box accessibility state on request of box owner.
7895 * Despite the name, owner must do the request off-line. This function is
7896 * designed for such off-line meeting points (e.g. Czech POINT).
7897 * @context is ISDS session context.
7898 * @box identifies box to switch accessibility state.
7899 * @allow is true for making accessible, false to disallow access.
7900 * @approval is optional external approval of box manipulation
7901 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7902 * NULL, if you don't care. */
7903 isds_error
isds_switch_box_accessibility_on_owner_request(
7904 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7905 const _Bool allow
, const struct isds_approval
*approval
,
7907 return build_send_manipulationdbowner_request_check_drop_response(context
,
7908 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
7909 BAD_CAST
"DisableOwnDataBox",
7910 box
, approval
, (xmlChar
**) refnumber
);
7914 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7916 * @context is ISDS session context.
7917 * @box identifies box to switch accessibility state.
7918 * @since is date since accessibility has been denied. This can be past too.
7919 * Only tm_year, tm_mon and tm_mday carry sane value.
7920 * @approval is optional external approval of box manipulation
7921 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7922 * NULL, if you don't care. */
7923 isds_error
isds_disable_box_accessibility_externaly(
7924 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7925 const struct tm
*since
, const struct isds_approval
*approval
,
7927 isds_error err
= IE_SUCCESS
;
7929 char *service_name_locale
= NULL
;
7930 xmlNodePtr request
= NULL
, node
;
7931 xmlNsPtr isds_ns
= NULL
;
7932 xmlChar
*string
= NULL
;
7936 if (!context
) return IE_INVALID_CONTEXT
;
7937 zfree(context
->long_message
);
7938 if (!box
|| !since
) return IE_INVAL
;
7942 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
7944 isds_printf_message(context
,
7945 _("Could not build %s request"), "DisableDataBoxExternally");
7949 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7951 isds_log_message(context
, _("Could not create ISDS name space"));
7955 xmlSetNs(request
, isds_ns
);
7958 /* Add @box identification */
7959 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7960 err
= insert_DbOwnerInfo(context
, box
, node
);
7961 if (err
) goto leave
;
7963 /* Add @since date */
7964 err
= tm2datestring(since
, &string
);
7966 isds_log_message(context
,
7967 _("Could not convert `since' argument to ISO date string"));
7970 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
7974 err
= insert_GExtApproval(context
, approval
, request
);
7975 if (err
) goto leave
;
7977 /* Send it to server and process response */
7978 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7979 BAD_CAST
"DisableDataBoxExternally", &request
,
7980 (xmlChar
**) refnumber
);
7984 xmlFreeNode(request
);
7985 free(service_name_locale
);
7986 #else /* not HAVE_LIBCURL */
7995 /* Insert struct isds_message data (envelope (recipient data optional) and
7996 * documents into XML tree
7997 * @context is session context
7998 * @outgoing_message is libisds structure with message data
7999 * @create_message is XML CreateMessage or CreateMultipleMessage element
8000 * @process_recipient true for recipient data serialization, false for no
8002 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8003 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8004 const _Bool process_recipient
) {
8006 isds_error err
= IE_SUCCESS
;
8007 xmlNodePtr envelope
, dm_files
, node
;
8008 xmlChar
*string
= NULL
;
8010 if (!context
) return IE_INVALID_CONTEXT
;
8011 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8014 /* Build envelope */
8015 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8017 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8018 "%s element"), create_message
->name
);
8022 if (!outgoing_message
->envelope
) {
8023 isds_log_message(context
, _("Outgoing message is missing envelope"));
8028 /* Insert optional message type */
8029 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8031 if (err
) goto leave
;
8033 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8034 outgoing_message
->envelope
->dmSenderOrgUnit
);
8035 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8036 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8038 if (process_recipient
) {
8039 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8040 isds_log_message(context
,
8041 _("Outgoing message is missing recipient box identifier"));
8045 INSERT_STRING(envelope
, "dbIDRecipient",
8046 outgoing_message
->envelope
->dbIDRecipient
);
8048 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8049 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8050 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8051 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8052 INSERT_STRING(envelope
, "dmToHands",
8053 outgoing_message
->envelope
->dmToHands
);
8056 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8058 INSERT_STRING(envelope
, "dmAnnotation",
8059 outgoing_message
->envelope
->dmAnnotation
);
8061 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8062 0, 50, "dmRecipientRefNumber");
8063 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8064 outgoing_message
->envelope
->dmRecipientRefNumber
);
8066 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8067 0, 50, "dmSenderRefNumber");
8068 INSERT_STRING(envelope
, "dmSenderRefNumber",
8069 outgoing_message
->envelope
->dmSenderRefNumber
);
8071 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8072 0, 50, "dmRecipientIdent");
8073 INSERT_STRING(envelope
, "dmRecipientIdent",
8074 outgoing_message
->envelope
->dmRecipientIdent
);
8076 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8077 0, 50, "dmSenderIdent");
8078 INSERT_STRING(envelope
, "dmSenderIdent",
8079 outgoing_message
->envelope
->dmSenderIdent
);
8081 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8082 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8083 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8084 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8085 INSERT_STRING(envelope
, "dmLegalTitleSect",
8086 outgoing_message
->envelope
->dmLegalTitleSect
);
8087 INSERT_STRING(envelope
, "dmLegalTitlePar",
8088 outgoing_message
->envelope
->dmLegalTitlePar
);
8089 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8090 outgoing_message
->envelope
->dmLegalTitlePoint
);
8092 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8093 outgoing_message
->envelope
->dmPersonalDelivery
);
8094 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8095 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8097 /* ???: Should we require value for dbEffectiveOVM sender?
8098 * ISDS has default as true */
8099 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8100 INSERT_BOOLEAN(envelope
, "dmOVM",
8101 outgoing_message
->envelope
->dmPublishOwnID
);
8104 /* Append dmFiles */
8105 if (!outgoing_message
->documents
) {
8106 isds_log_message(context
,
8107 _("Outgoing message is missing list of documents"));
8111 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8113 isds_printf_message(context
, _("Could not add dmFiles child to "
8114 "%s element"), create_message
->name
);
8119 /* Check for document hierarchy */
8120 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8121 if (err
) goto leave
;
8123 /* Process each document */
8124 for (struct isds_list
*item
=
8125 (struct isds_list
*) outgoing_message
->documents
;
8126 item
; item
= item
->next
) {
8128 isds_log_message(context
,
8129 _("List of documents contains empty item"));
8133 /* FIXME: Check for dmFileMetaType and for document references.
8134 * Only first document can be of MAIN type */
8135 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8138 if (err
) goto leave
;
8145 #endif /* HAVE_LIBCURL */
8148 /* Send a message via ISDS to a recipient
8149 * @context is session context
8150 * @outgoing_message is message to send; Some members are mandatory (like
8151 * dbIDRecipient), some are optional and some are irrelevant (especially data
8152 * about sender). Included pointer to isds_list documents must contain at
8153 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8154 * members will be filled with valid data from ISDS. Exact list of write
8155 * members is subject to change. Currently dmID is changed.
8156 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8157 isds_error
isds_send_message(struct isds_ctx
*context
,
8158 struct isds_message
*outgoing_message
) {
8160 isds_error err
= IE_SUCCESS
;
8162 xmlNsPtr isds_ns
= NULL
;
8163 xmlNodePtr request
= NULL
;
8164 xmlDocPtr response
= NULL
;
8165 xmlChar
*code
= NULL
, *message
= NULL
;
8166 xmlXPathContextPtr xpath_ctx
= NULL
;
8167 xmlXPathObjectPtr result
= NULL
;
8168 /*_Bool message_is_complete = 0;*/
8171 if (!context
) return IE_INVALID_CONTEXT
;
8172 zfree(context
->long_message
);
8173 if (!outgoing_message
) return IE_INVAL
;
8176 /* Check if connection is established
8177 * TODO: This check should be done downstairs. */
8178 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8181 /* Build CreateMessage request */
8182 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8184 isds_log_message(context
,
8185 _("Could not build CreateMessage request"));
8188 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8190 isds_log_message(context
, _("Could not create ISDS name space"));
8191 xmlFreeNode(request
);
8194 xmlSetNs(request
, isds_ns
);
8196 /* Append envelope and files */
8197 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8198 if (err
) goto leave
;
8201 /* Signal we can serialize message since now */
8202 /*message_is_complete = 1;*/
8205 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8208 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8210 /* Don't' destroy request, we want to provide it to application later */
8213 isds_log(ILF_ISDS
, ILL_DEBUG
,
8214 _("Processing ISDS response on CreateMessage "
8215 "request failed\n"));
8219 /* Check for response status */
8220 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8221 &code
, &message
, NULL
);
8223 isds_log(ILF_ISDS
, ILL_DEBUG
,
8224 _("ISDS response on CreateMessage request "
8225 "is missing status\n"));
8229 /* Request processed, but refused by server or server failed */
8230 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8231 char *box_id_locale
=
8232 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8233 char *code_locale
= _isds_utf82locale((char*)code
);
8234 char *message_locale
= _isds_utf82locale((char*)message
);
8235 isds_log(ILF_ISDS
, ILL_DEBUG
,
8236 _("Server did not accept message for %s on CreateMessage "
8237 "request (code=%s, message=%s)\n"),
8238 box_id_locale
, code_locale
, message_locale
);
8239 isds_log_message(context
, message_locale
);
8240 free(box_id_locale
);
8242 free(message_locale
);
8249 xpath_ctx
= xmlXPathNewContext(response
);
8254 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8258 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8264 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8265 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8269 if (result
->nodesetval
->nodeNr
> 1) {
8270 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8274 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8275 xmlXPathFreeObject(result
); result
= NULL
;
8277 if (outgoing_message
->envelope
->dmID
) {
8278 free(outgoing_message
->envelope
->dmID
);
8279 outgoing_message
->envelope
->dmID
= NULL
;
8281 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8282 if (!outgoing_message
->envelope
->dmID
) {
8283 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8284 "but did not return assigned message ID\n"));
8288 /* TODO: Serialize message into structure member raw */
8289 /* XXX: Each web service transport message in different format.
8290 * Therefore it's not possible to save them directly.
8291 * To save them, one must figure out common format.
8292 * We can leave it on application, or we can implement the ESS format. */
8293 /*if (message_is_complete) {
8294 if (outgoing_message->envelope->dmID) {
8296 /* Add assigned message ID as first child*/
8297 /*xmlNodePtr dmid_text = xmlNewText(
8298 (xmlChar *) outgoing_message->envelope->dmID);
8299 if (!dmid_text) goto serialization_failed;
8301 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8303 if (!dmid_element) {
8304 xmlFreeNode(dmid_text);
8305 goto serialization_failed;
8308 xmlNodePtr dmid_element_with_text =
8309 xmlAddChild(dmid_element, dmid_text);
8310 if (!dmid_element_with_text) {
8311 xmlFreeNode(dmid_element);
8312 xmlFreeNode(dmid_text);
8313 goto serialization_failed;
8316 node = xmlAddPrevSibling(envelope->childern,
8317 dmid_element_with_text);
8319 xmlFreeNodeList(dmid_element_with_text);
8320 goto serialization_failed;
8324 /* Serialize message with ID into raw */
8325 /*buffer = serialize_element(envelope)*/
8328 serialization_failed:
8333 xmlXPathFreeObject(result
);
8334 xmlXPathFreeContext(xpath_ctx
);
8338 xmlFreeDoc(response
);
8339 xmlFreeNode(request
);
8342 isds_log(ILF_ISDS
, ILL_DEBUG
,
8343 _("CreateMessage request processed by server "
8344 "successfully.\n"));
8345 #else /* not HAVE_LIBCURL */
8353 /* Send a message via ISDS to a multiple recipients
8354 * @context is session context
8355 * @outgoing_message is message to send; Some members are mandatory,
8356 * some are optional and some are irrelevant (especially data
8357 * about sender). Data about recipient will be substituted by ISDS from
8358 * @copies. Included pointer to isds_list documents must
8359 * contain at least one document of FILEMETATYPE_MAIN.
8360 * @copies is list of isds_message_copy structures addressing all desired
8361 * recipients. This is read-write structure, some members will be filled with
8362 * valid data from ISDS (message IDs, error codes, error descriptions).
8364 * ISDS_SUCCESS if all messages have been sent
8365 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8366 * succeeded messages can be identified by copies->data->error),
8367 * or other error code if something other goes wrong. */
8368 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8369 const struct isds_message
*outgoing_message
,
8370 struct isds_list
*copies
) {
8372 isds_error err
= IE_SUCCESS
;
8374 isds_error append_err
;
8375 xmlNsPtr isds_ns
= NULL
;
8376 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8377 struct isds_list
*item
;
8378 struct isds_message_copy
*copy
;
8379 xmlDocPtr response
= NULL
;
8380 xmlChar
*code
= NULL
, *message
= NULL
;
8381 xmlXPathContextPtr xpath_ctx
= NULL
;
8382 xmlXPathObjectPtr result
= NULL
;
8383 xmlChar
*string
= NULL
;
8387 if (!context
) return IE_INVALID_CONTEXT
;
8388 zfree(context
->long_message
);
8389 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8392 /* Check if connection is established
8393 * TODO: This check should be done downstairs. */
8394 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8397 /* Build CreateMultipleMessage request */
8398 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8400 isds_log_message(context
,
8401 _("Could not build CreateMultipleMessage request"));
8404 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8406 isds_log_message(context
, _("Could not create ISDS name space"));
8407 xmlFreeNode(request
);
8410 xmlSetNs(request
, isds_ns
);
8413 /* Build recipients */
8414 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8416 isds_log_message(context
, _("Could not add dmRecipients child to "
8417 "CreateMultipleMessage element"));
8418 xmlFreeNode(request
);
8422 /* Insert each recipient */
8423 for (item
= copies
; item
; item
= item
->next
) {
8424 copy
= (struct isds_message_copy
*) item
->data
;
8426 isds_log_message(context
,
8427 _("`copies' list item contains empty data"));
8432 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8434 isds_log_message(context
, _("Could not add dmRecipient child to "
8435 "dmRecipients element"));
8440 if (!copy
->dbIDRecipient
) {
8441 isds_log_message(context
,
8442 _("Message copy is missing recipient box identifier"));
8446 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
8447 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
8448 copy
->dmRecipientOrgUnit
);
8449 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
8450 copy
->dmRecipientOrgUnitNum
, string
);
8451 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
8454 /* Append envelope and files */
8455 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
8456 if (err
) goto leave
;
8459 isds_log(ILF_ISDS
, ILL_DEBUG
,
8460 _("Sending CreateMultipleMessage request to ISDS\n"));
8463 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8465 isds_log(ILF_ISDS
, ILL_DEBUG
,
8466 _("Processing ISDS response on CreateMultipleMessage "
8467 "request failed\n"));
8471 /* Check for response status */
8472 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8473 &code
, &message
, NULL
);
8475 isds_log(ILF_ISDS
, ILL_DEBUG
,
8476 _("ISDS response on CreateMultipleMessage request "
8477 "is missing status\n"));
8481 /* Request processed, but some copies failed */
8482 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
8483 char *box_id_locale
=
8484 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8485 char *code_locale
= _isds_utf82locale((char*)code
);
8486 char *message_locale
= _isds_utf82locale((char*)message
);
8487 isds_log(ILF_ISDS
, ILL_DEBUG
,
8488 _("Server did accept message for multiple recipients "
8489 "on CreateMultipleMessage request but delivery to "
8490 "some of them failed (code=%s, message=%s)\n"),
8491 box_id_locale
, code_locale
, message_locale
);
8492 isds_log_message(context
, message_locale
);
8493 free(box_id_locale
);
8495 free(message_locale
);
8496 err
= IE_PARTIAL_SUCCESS
;
8499 /* Request refused by server as whole */
8500 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8501 char *box_id_locale
=
8502 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8503 char *code_locale
= _isds_utf82locale((char*)code
);
8504 char *message_locale
= _isds_utf82locale((char*)message
);
8505 isds_log(ILF_ISDS
, ILL_DEBUG
,
8506 _("Server did not accept message for multiple recipients "
8507 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8508 box_id_locale
, code_locale
, message_locale
);
8509 isds_log_message(context
, message_locale
);
8510 free(box_id_locale
);
8512 free(message_locale
);
8519 xpath_ctx
= xmlXPathNewContext(response
);
8524 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8528 result
= xmlXPathEvalExpression(
8529 BAD_CAST
"/isds:CreateMultipleMessageResponse"
8530 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8536 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8537 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
8542 /* Extract message ID and delivery status for each copy */
8543 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
8544 item
= item
->next
, i
++) {
8545 copy
= (struct isds_message_copy
*) item
->data
;
8546 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8548 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
8554 if (item
|| i
< result
->nodesetval
->nodeNr
) {
8555 isds_printf_message(context
, _("ISDS returned unexpected number of "
8556 "message copy delivery states: %d"),
8557 result
->nodesetval
->nodeNr
);
8566 xmlXPathFreeObject(result
);
8567 xmlXPathFreeContext(xpath_ctx
);
8571 xmlFreeDoc(response
);
8572 xmlFreeNode(request
);
8575 isds_log(ILF_ISDS
, ILL_DEBUG
,
8576 _("CreateMultipleMessageResponse request processed by server "
8577 "successfully.\n"));
8578 #else /* not HAVE_LIBCURL */
8586 /* Get list of messages. This is common core for getting sent or received
8588 * Any criterion argument can be NULL, if you don't care about it.
8589 * @context is session context. Must not be NULL.
8590 * @outgoing_direction is true if you want list of outgoing messages,
8591 * it's false if you want incoming messages.
8592 * @from_time is minimal time and date of message sending inclusive.
8593 * @to_time is maximal time and date of message sending inclusive
8594 * @organization_unit_number is number of sender/recipient respectively.
8595 * @status_filter is bit field of isds_message_status values. Use special
8596 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8597 * all values, you can use bit-wise arithmetic if you want.)
8598 * @offset is index of first message we are interested in. First message is 1.
8599 * Set to 0 (or 1) if you don't care.
8600 * @number is maximal length of list you want to get as input value, outputs
8601 * number of messages matching these criteria. Can be NULL if you don't care
8602 * (applies to output value either).
8603 * @messages is automatically reallocated list of isds_message's. Be ware that
8604 * it returns only brief overview (envelope and some other fields) about each
8605 * message, not the complete message. FIXME: Specify exact fields.
8606 * The list is sorted by delivery time in ascending order.
8607 * Use NULL if you don't care about don't need the data (useful if you want to
8608 * know only the @number). If you provide &NULL, list will be allocated on
8609 * heap, if you provide pointer to non-NULL, list will be freed automatically
8610 * at first. Also in case of error the list will be NULLed.
8611 * @return IE_SUCCESS or appropriate error code. */
8612 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
8613 _Bool outgoing_direction
,
8614 const struct timeval
*from_time
, const struct timeval
*to_time
,
8615 const long int *organization_unit_number
,
8616 const unsigned int status_filter
,
8617 const unsigned long int offset
, unsigned long int *number
,
8618 struct isds_list
**messages
) {
8620 isds_error err
= IE_SUCCESS
;
8622 xmlNsPtr isds_ns
= NULL
;
8623 xmlNodePtr request
= NULL
, node
;
8624 xmlDocPtr response
= NULL
;
8625 xmlChar
*code
= NULL
, *message
= NULL
;
8626 xmlXPathContextPtr xpath_ctx
= NULL
;
8627 xmlXPathObjectPtr result
= NULL
;
8628 xmlChar
*string
= NULL
;
8629 long unsigned int count
= 0;
8632 if (!context
) return IE_INVALID_CONTEXT
;
8633 zfree(context
->long_message
);
8635 /* Free former message list if any */
8636 if (messages
) isds_list_free(messages
);
8639 /* Check if connection is established
8640 * TODO: This check should be done downstairs. */
8641 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8643 /* Build GetListOf*Messages request */
8644 request
= xmlNewNode(NULL
,
8645 (outgoing_direction
) ?
8646 BAD_CAST
"GetListOfSentMessages" :
8647 BAD_CAST
"GetListOfReceivedMessages"
8650 isds_log_message(context
,
8651 (outgoing_direction
) ?
8652 _("Could not build GetListOfSentMessages request") :
8653 _("Could not build GetListOfReceivedMessages request")
8657 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8659 isds_log_message(context
, _("Could not create ISDS name space"));
8660 xmlFreeNode(request
);
8663 xmlSetNs(request
, isds_ns
);
8667 err
= timeval2timestring(from_time
, &string
);
8668 if (err
) goto leave
;
8670 INSERT_STRING(request
, "dmFromTime", string
);
8671 free(string
); string
= NULL
;
8674 err
= timeval2timestring(to_time
, &string
);
8675 if (err
) goto leave
;
8677 INSERT_STRING(request
, "dmToTime", string
);
8678 free(string
); string
= NULL
;
8680 if (outgoing_direction
) {
8681 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
8682 organization_unit_number
, string
);
8684 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
8685 organization_unit_number
, string
);
8688 if (status_filter
> MESSAGESTATE_ANY
) {
8689 isds_printf_message(context
,
8690 _("Invalid message state filter value: %ld"), status_filter
);
8694 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
8697 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
8699 INSERT_STRING(request
, "dmOffset", "1");
8702 /* number 0 means no limit */
8703 if (number
&& *number
== 0) {
8704 INSERT_STRING(request
, "dmLimit", NULL
);
8706 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
8710 isds_log(ILF_ISDS
, ILL_DEBUG
,
8711 (outgoing_direction
) ?
8712 _("Sending GetListOfSentMessages request to ISDS\n") :
8713 _("Sending GetListOfReceivedMessages request to ISDS\n")
8717 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
8718 xmlFreeNode(request
); request
= NULL
;
8721 isds_log(ILF_ISDS
, ILL_DEBUG
,
8722 (outgoing_direction
) ?
8723 _("Processing ISDS response on GetListOfSentMessages "
8724 "request failed\n") :
8725 _("Processing ISDS response on GetListOfReceivedMessages "
8731 /* Check for response status */
8732 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
8733 &code
, &message
, NULL
);
8735 isds_log(ILF_ISDS
, ILL_DEBUG
,
8736 (outgoing_direction
) ?
8737 _("ISDS response on GetListOfSentMessages request "
8738 "is missing status\n") :
8739 _("ISDS response on GetListOfReceivedMessages request "
8740 "is missing status\n")
8745 /* Request processed, but nothing found */
8746 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8747 char *code_locale
= _isds_utf82locale((char*)code
);
8748 char *message_locale
= _isds_utf82locale((char*)message
);
8749 isds_log(ILF_ISDS
, ILL_DEBUG
,
8750 (outgoing_direction
) ?
8751 _("Server refused GetListOfSentMessages request "
8752 "(code=%s, message=%s)\n") :
8753 _("Server refused GetListOfReceivedMessages request "
8754 "(code=%s, message=%s)\n"),
8755 code_locale
, message_locale
);
8756 isds_log_message(context
, message_locale
);
8758 free(message_locale
);
8765 xpath_ctx
= xmlXPathNewContext(response
);
8770 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8774 result
= xmlXPathEvalExpression(
8775 (outgoing_direction
) ?
8776 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
8777 "isds:dmRecords/isds:dmRecord" :
8778 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
8779 "isds:dmRecords/isds:dmRecord",
8786 /* Fill output arguments in */
8787 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8788 struct isds_envelope
*envelope
;
8789 struct isds_list
*item
= NULL
, *last_item
= NULL
;
8791 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
8792 /* Create new message */
8793 item
= calloc(1, sizeof(*item
));
8798 item
->destructor
= (void(*)(void**)) &isds_message_free
;
8799 item
->data
= calloc(1, sizeof(struct isds_message
));
8801 isds_list_free(&item
);
8806 /* Extract envelope data */
8807 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
8809 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
8811 isds_list_free(&item
);
8815 /* Attach extracted envelope */
8816 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
8818 /* Append new message into the list */
8820 *messages
= last_item
= item
;
8822 last_item
->next
= item
;
8827 if (number
) *number
= count
;
8831 isds_list_free(messages
);
8835 xmlXPathFreeObject(result
);
8836 xmlXPathFreeContext(xpath_ctx
);
8840 xmlFreeDoc(response
);
8841 xmlFreeNode(request
);
8844 isds_log(ILF_ISDS
, ILL_DEBUG
,
8845 (outgoing_direction
) ?
8846 _("GetListOfSentMessages request processed by server "
8847 "successfully.\n") :
8848 _("GetListOfReceivedMessages request processed by server "
8851 #else /* not HAVE_LIBCURL */
8858 /* Get list of outgoing (already sent) messages.
8859 * Any criterion argument can be NULL, if you don't care about it.
8860 * @context is session context. Must not be NULL.
8861 * @from_time is minimal time and date of message sending inclusive.
8862 * @to_time is maximal time and date of message sending inclusive
8863 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8864 * @status_filter is bit field of isds_message_status values. Use special
8865 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8866 * all values, you can use bit-wise arithmetic if you want.)
8867 * @offset is index of first message we are interested in. First message is 1.
8868 * Set to 0 (or 1) if you don't care.
8869 * @number is maximal length of list you want to get as input value, outputs
8870 * number of messages matching these criteria. Can be NULL if you don't care
8871 * (applies to output value either).
8872 * @messages is automatically reallocated list of isds_message's. Be ware that
8873 * it returns only brief overview (envelope and some other fields) about each
8874 * message, not the complete message. FIXME: Specify exact fields.
8875 * The list is sorted by delivery time in ascending order.
8876 * Use NULL if you don't care about the meta data (useful if you want to know
8877 * only the @number). If you provide &NULL, list will be allocated on heap,
8878 * if you provide pointer to non-NULL, list will be freed automatically at
8879 * first. Also in case of error the list will be NULLed.
8880 * @return IE_SUCCESS or appropriate error code. */
8881 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
8882 const struct timeval
*from_time
, const struct timeval
*to_time
,
8883 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
8884 const unsigned long int offset
, unsigned long int *number
,
8885 struct isds_list
**messages
) {
8887 return isds_get_list_of_messages(
8889 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
8895 /* Get list of incoming (addressed to you) messages.
8896 * Any criterion argument can be NULL, if you don't care about it.
8897 * @context is session context. Must not be NULL.
8898 * @from_time is minimal time and date of message sending inclusive.
8899 * @to_time is maximal time and date of message sending inclusive
8900 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8901 * @status_filter is bit field of isds_message_status values. Use special
8902 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8903 * all values, you can use bit-wise arithmetic if you want.)
8904 * @offset is index of first message we are interested in. First message is 1.
8905 * Set to 0 (or 1) if you don't care.
8906 * @number is maximal length of list you want to get as input value, outputs
8907 * number of messages matching these criteria. Can be NULL if you don't care
8908 * (applies to output value either).
8909 * @messages is automatically reallocated list of isds_message's. Be ware that
8910 * it returns only brief overview (envelope and some other fields) about each
8911 * message, not the complete message. FIXME: Specify exact fields.
8912 * Use NULL if you don't care about the meta data (useful if you want to know
8913 * only the @number). If you provide &NULL, list will be allocated on heap,
8914 * if you provide pointer to non-NULL, list will be freed automatically at
8915 * first. Also in case of error the list will be NULLed.
8916 * @return IE_SUCCESS or appropriate error code. */
8917 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
8918 const struct timeval
*from_time
, const struct timeval
*to_time
,
8919 const long int *dmRecipientOrgUnitNum
,
8920 const unsigned int status_filter
,
8921 const unsigned long int offset
, unsigned long int *number
,
8922 struct isds_list
**messages
) {
8924 return isds_get_list_of_messages(
8926 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
8932 /* Get list of sent message state changes.
8933 * Any criterion argument can be NULL, if you don't care about it.
8934 * @context is session context. Must not be NULL.
8935 * @from_time is minimal time and date of status changes inclusive
8936 * @to_time is maximal time and date of status changes inclusive
8937 * @changed_states is automatically reallocated list of
8938 * isds_message_status_change's. If you provide &NULL, list will be allocated
8939 * on heap, if you provide pointer to non-NULL, list will be freed
8940 * automatically at first. Also in case of error the list will be NULLed.
8941 * XXX: The list item ordering is not specified.
8942 * XXX: Server provides only `recent' changes.
8943 * @return IE_SUCCESS or appropriate error code. */
8944 isds_error
isds_get_list_of_sent_message_state_changes(
8945 struct isds_ctx
*context
,
8946 const struct timeval
*from_time
, const struct timeval
*to_time
,
8947 struct isds_list
**changed_states
) {
8949 isds_error err
= IE_SUCCESS
;
8951 xmlNsPtr isds_ns
= NULL
;
8952 xmlNodePtr request
= NULL
, node
;
8953 xmlDocPtr response
= NULL
;
8954 xmlXPathContextPtr xpath_ctx
= NULL
;
8955 xmlXPathObjectPtr result
= NULL
;
8956 xmlChar
*string
= NULL
;
8957 long unsigned int count
= 0;
8960 if (!context
) return IE_INVALID_CONTEXT
;
8961 zfree(context
->long_message
);
8963 /* Free former message list if any */
8964 isds_list_free(changed_states
);
8967 /* Check if connection is established
8968 * TODO: This check should be done downstairs. */
8969 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8971 /* Build GetMessageStateChanges request */
8972 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
8974 isds_log_message(context
,
8975 _("Could not build GetMessageStateChanges request"));
8978 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8980 isds_log_message(context
, _("Could not create ISDS name space"));
8981 xmlFreeNode(request
);
8984 xmlSetNs(request
, isds_ns
);
8988 err
= timeval2timestring(from_time
, &string
);
8989 if (err
) goto leave
;
8991 INSERT_STRING(request
, "dmFromTime", string
);
8995 err
= timeval2timestring(to_time
, &string
);
8996 if (err
) goto leave
;
8998 INSERT_STRING(request
, "dmToTime", string
);
9003 err
= send_destroy_request_check_response(context
,
9004 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9005 &response
, NULL
, NULL
);
9006 if (err
) goto leave
;
9010 xpath_ctx
= xmlXPathNewContext(response
);
9015 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9019 result
= xmlXPathEvalExpression(
9020 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9021 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9027 /* Fill output arguments in */
9028 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9029 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9031 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9032 /* Create new status change */
9033 item
= calloc(1, sizeof(*item
));
9039 (void(*)(void**)) &isds_message_status_change_free
;
9041 /* Extract message status change */
9042 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9043 err
= extract_StateChangesRecord(context
,
9044 (struct isds_message_status_change
**) &item
->data
,
9047 isds_list_free(&item
);
9051 /* Append new message status change into the list */
9052 if (!*changed_states
) {
9053 *changed_states
= last_item
= item
;
9055 last_item
->next
= item
;
9063 isds_list_free(changed_states
);
9067 xmlXPathFreeObject(result
);
9068 xmlXPathFreeContext(xpath_ctx
);
9069 xmlFreeDoc(response
);
9070 xmlFreeNode(request
);
9073 isds_log(ILF_ISDS
, ILL_DEBUG
,
9074 _("GetMessageStateChanges request processed by server "
9075 "successfully.\n"));
9076 #else /* not HAVE_LIBCURL */
9084 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9086 * @context is session context
9087 * @service is ISDS WS service handler
9088 * @service_name is name of SERVICE_DM_OPERATIONS
9089 * @message_id is message ID to send as service argument to ISDS
9090 * @response is reallocated server SOAP body response as XML document
9091 * @raw_response is reallocated bit stream with response body. Use
9092 * NULL if you don't care
9093 * @raw_response_length is size of @raw_response in bytes
9094 * @code is reallocated ISDS status code
9095 * @status_message is reallocated ISDS status message
9096 * @return error coded from lower layer, context message will be set up
9098 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9099 const isds_service service
, const xmlChar
*service_name
,
9100 const char *message_id
,
9101 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9102 xmlChar
**code
, xmlChar
**status_message
) {
9104 isds_error err
= IE_SUCCESS
;
9105 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9106 xmlNodePtr request
= NULL
, node
;
9107 xmlNsPtr isds_ns
= NULL
;
9109 if (!context
) return IE_INVALID_CONTEXT
;
9110 if (!service_name
|| !message_id
) return IE_INVAL
;
9111 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9112 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9114 /* Free output argument */
9115 xmlFreeDoc(*response
); *response
= NULL
;
9116 if (raw_response
) zfree(*raw_response
);
9118 zfree(*status_message
);
9121 /* Check if connection is established
9122 * TODO: This check should be done downstairs. */
9123 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9125 service_name_locale
= _isds_utf82locale((char*)service_name
);
9126 message_id_locale
= _isds_utf82locale(message_id
);
9127 if (!service_name_locale
|| !message_id_locale
) {
9133 request
= xmlNewNode(NULL
, service_name
);
9135 isds_printf_message(context
,
9136 _("Could not build %s request for %s message ID"),
9137 service_name_locale
, message_id_locale
);
9141 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9143 isds_log_message(context
, _("Could not create ISDS name space"));
9147 xmlSetNs(request
, isds_ns
);
9150 /* Add requested ID */
9151 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9152 if (err
) goto leave
;
9153 INSERT_STRING(request
, "dmID", message_id
);
9156 isds_log(ILF_ISDS
, ILL_DEBUG
,
9157 _("Sending %s request for %s message ID to ISDS\n"),
9158 service_name_locale
, message_id_locale
);
9161 err
= isds(context
, service
, request
, response
,
9162 raw_response
, raw_response_length
);
9163 xmlFreeNode(request
); request
= NULL
;
9166 isds_log(ILF_ISDS
, ILL_DEBUG
,
9167 _("Processing ISDS response on %s request failed\n"),
9168 service_name_locale
);
9172 /* Check for response status */
9173 err
= isds_response_status(context
, service
, *response
,
9174 code
, status_message
, NULL
);
9176 isds_log(ILF_ISDS
, ILL_DEBUG
,
9177 _("ISDS response on %s request is missing status\n"),
9178 service_name_locale
);
9182 /* Request processed, but nothing found */
9183 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9184 char *code_locale
= _isds_utf82locale((char*) *code
);
9185 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9186 isds_log(ILF_ISDS
, ILL_DEBUG
,
9187 _("Server refused %s request for %s message ID "
9188 "(code=%s, message=%s)\n"),
9189 service_name_locale
, message_id_locale
,
9190 code_locale
, status_message_locale
);
9191 isds_log_message(context
, status_message_locale
);
9193 free(status_message_locale
);
9199 free(message_id_locale
);
9200 free(service_name_locale
);
9201 xmlFreeNode(request
);
9206 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9207 * signed data and free ISDS response.
9208 * @context is session context
9209 * @message_id is UTF-8 encoded message ID for logging purpose
9210 * @response is parsed XML document. It will be freed and NULLed in the middle
9211 * of function run to save memory. This is not guaranteed in case of error.
9212 * @request_name is name of ISDS request used to construct response root
9213 * element name and for logging purpose.
9214 * @raw is reallocated output buffer with DER encoded CMS data
9215 * @raw_length is size of @raw buffer in bytes
9216 * @returns standard error codes, in case of error, @raw will be freed and
9217 * NULLed, @response sometimes. */
9218 static isds_error
find_extract_signed_data_free_response(
9219 struct isds_ctx
*context
, const xmlChar
*message_id
,
9220 xmlDocPtr
*response
, const xmlChar
*request_name
,
9221 void **raw
, size_t *raw_length
) {
9223 isds_error err
= IE_SUCCESS
;
9224 char *xpath_expression
= NULL
;
9225 xmlXPathContextPtr xpath_ctx
= NULL
;
9226 xmlXPathObjectPtr result
= NULL
;
9227 char *encoded_structure
= NULL
;
9229 if (!context
) return IE_INVALID_CONTEXT
;
9230 if (!raw
) return IE_INVAL
;
9232 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9235 /* Build XPath expression */
9236 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9237 "Response/isds:dmSignature");
9238 if (!xpath_expression
) return IE_NOMEM
;
9241 xpath_ctx
= xmlXPathNewContext(*response
);
9246 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9250 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9255 /* Empty response */
9256 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9257 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9258 isds_printf_message(context
,
9259 _("Server did not return any signed data for message ID `%s' "
9261 message_id_locale
, request_name
);
9262 free(message_id_locale
);
9266 /* More responses */
9267 if (result
->nodesetval
->nodeNr
> 1) {
9268 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9269 isds_printf_message(context
,
9270 _("Server did return more signed data for message ID `%s' "
9272 message_id_locale
, request_name
);
9273 free(message_id_locale
);
9278 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9280 /* Extract PKCS#7 structure */
9281 EXTRACT_STRING(".", encoded_structure
);
9282 if (!encoded_structure
) {
9283 isds_log_message(context
, _("dmSignature element is empty"));
9286 /* Here we have delivery info as standalone CMS in encoded_structure.
9287 * We don't need any other data, free them: */
9288 xmlXPathFreeObject(result
); result
= NULL
;
9289 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9290 xmlFreeDoc(*response
); *response
= NULL
;
9293 /* Decode PKCS#7 to DER format */
9294 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9295 if (*raw_length
== (size_t) -1) {
9296 isds_log_message(context
,
9297 _("Error while Base64-decoding PKCS#7 structure"));
9308 free(encoded_structure
);
9309 xmlXPathFreeObject(result
);
9310 xmlXPathFreeContext(xpath_ctx
);
9311 free(xpath_expression
);
9315 #endif /* HAVE_LIBCURL */
9318 /* Download incoming message envelope identified by ID.
9319 * @context is session context
9320 * @message_id is message identifier (you can get them from
9321 * isds_get_list_of_received_messages())
9322 * @message is automatically reallocated message retrieved from ISDS.
9323 * It will miss documents per se. Use isds_get_received_message(), if you are
9324 * interested in documents (content) too.
9325 * Returned hash and timestamp require documents to be verifiable. */
9326 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9327 const char *message_id
, struct isds_message
**message
) {
9329 isds_error err
= IE_SUCCESS
;
9331 xmlDocPtr response
= NULL
;
9332 xmlChar
*code
= NULL
, *status_message
= NULL
;
9333 xmlXPathContextPtr xpath_ctx
= NULL
;
9334 xmlXPathObjectPtr result
= NULL
;
9337 if (!context
) return IE_INVALID_CONTEXT
;
9338 zfree(context
->long_message
);
9340 /* Free former message if any */
9341 if (!message
) return IE_INVAL
;
9342 isds_message_free(message
);
9345 /* Do request and check for success */
9346 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9347 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9348 &response
, NULL
, NULL
, &code
, &status_message
);
9349 if (err
) goto leave
;
9352 xpath_ctx
= xmlXPathNewContext(response
);
9357 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9361 result
= xmlXPathEvalExpression(
9362 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9363 "isds:dmReturnedMessageEnvelope",
9369 /* Empty response */
9370 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9371 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9372 isds_printf_message(context
,
9373 _("Server did not return any envelope for ID `%s' "
9374 "on MessageEnvelopeDownload request"), message_id_locale
);
9375 free(message_id_locale
);
9380 if (result
->nodesetval
->nodeNr
> 1) {
9381 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9382 isds_printf_message(context
,
9383 _("Server did return more envelopes for ID `%s' "
9384 "on MessageEnvelopeDownload request"), message_id_locale
);
9385 free(message_id_locale
);
9390 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9392 /* Extract the envelope (= message without documents, hence 0) */
9393 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9394 if (err
) goto leave
;
9397 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9398 &(*message
)->raw_length
);
9402 isds_message_free(message
);
9405 xmlXPathFreeObject(result
);
9406 xmlXPathFreeContext(xpath_ctx
);
9409 free(status_message
);
9410 if (!*message
|| !(*message
)->xml
) {
9411 xmlFreeDoc(response
);
9415 isds_log(ILF_ISDS
, ILL_DEBUG
,
9416 _("MessageEnvelopeDownload request processed by server "
9419 #else /* not HAVE_LIBCURL */
9426 /* Load delivery info of any format from buffer.
9427 * @context is session context
9428 * @raw_type advertises format of @buffer content. Only delivery info types
9430 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9431 * retrieve such data from message->raw after calling
9432 * isds_get_signed_delivery_info().
9433 * @length is length of buffer in bytes.
9434 * @message is automatically reallocated message parsed from @buffer.
9435 * @strategy selects how buffer will be attached into raw isds_message member.
9437 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9438 const isds_raw_type raw_type
,
9439 const void *buffer
, const size_t length
,
9440 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9442 isds_error err
= IE_SUCCESS
;
9443 message_ns_type message_ns
;
9444 xmlDocPtr message_doc
= NULL
;
9445 xmlXPathContextPtr xpath_ctx
= NULL
;
9446 xmlXPathObjectPtr result
= NULL
;
9447 void *xml_stream
= NULL
;
9448 size_t xml_stream_length
= 0;
9450 if (!context
) return IE_INVALID_CONTEXT
;
9451 zfree(context
->long_message
);
9452 if (!message
) return IE_INVAL
;
9453 isds_message_free(message
);
9454 if (!buffer
) return IE_INVAL
;
9457 /* Select buffer format and extract XML from CMS*/
9459 case RAWTYPE_DELIVERYINFO
:
9460 message_ns
= MESSAGE_NS_UNSIGNED
;
9461 xml_stream
= (void *) buffer
;
9462 xml_stream_length
= length
;
9465 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
9466 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9467 xml_stream
= (void *) buffer
;
9468 xml_stream_length
= length
;
9471 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
9472 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9473 err
= _isds_extract_cms_data(context
, buffer
, length
,
9474 &xml_stream
, &xml_stream_length
);
9475 if (err
) goto leave
;
9479 isds_log_message(context
, _("Bad raw delivery representation type"));
9484 isds_log(ILF_ISDS
, ILL_DEBUG
,
9485 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9486 xml_stream_length
, xml_stream
);
9488 /* Convert delivery info XML stream into XPath context */
9489 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9494 xpath_ctx
= xmlXPathNewContext(message_doc
);
9499 /* XXX: Name spaces mangled for signed delivery info:
9500 * http://isds.czechpoint.cz/v20/delivery:
9502 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9504 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9505 * <p:dmID>170272</p:dmID>
9508 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9510 * </q:dmEvents>...</q:dmEvents>
9512 * </q:GetDeliveryInfoResponse>
9514 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
9518 result
= xmlXPathEvalExpression(
9519 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9525 /* Empty delivery info */
9526 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9527 isds_printf_message(context
,
9528 _("XML document is not sisds:dmDelivery document"));
9532 /* More delivery info's */
9533 if (result
->nodesetval
->nodeNr
> 1) {
9534 isds_printf_message(context
,
9535 _("XML document has more sisds:dmDelivery elements"));
9539 /* One delivery info */
9540 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9542 /* Extract the envelope (= message without documents, hence 0).
9543 * XXX: extract_TReturnedMessage() can obtain attachments size,
9544 * but delivery info carries none. It's coded as option elements,
9545 * so it should work. */
9546 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9547 if (err
) goto leave
;
9549 /* Extract events */
9550 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
9551 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
9552 if (err
) { err
= IE_ERROR
; goto leave
; }
9553 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
9554 if (err
) goto leave
;
9556 /* Append raw CMS structure into message */
9557 (*message
)->raw_type
= raw_type
;
9559 case BUFFER_DONT_STORE
:
9562 (*message
)->raw
= malloc(length
);
9563 if (!(*message
)->raw
) {
9567 memcpy((*message
)->raw
, buffer
, length
);
9568 (*message
)->raw_length
= length
;
9571 (*message
)->raw
= (void *) buffer
;
9572 (*message
)->raw_length
= length
;
9581 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
9582 isds_message_free(message
);
9585 xmlXPathFreeObject(result
);
9586 xmlXPathFreeContext(xpath_ctx
);
9587 if (!*message
|| !(*message
)->xml
) {
9588 xmlFreeDoc(message_doc
);
9590 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9593 isds_log(ILF_ISDS
, ILL_DEBUG
,
9594 _("Delivery info loaded successfully.\n"));
9599 /* Download signed delivery info-sheet of given message identified by ID.
9600 * @context is session context
9601 * @message_id is message identifier (you can get them from
9602 * isds_get_list_of_{sent,received}_messages())
9603 * @message is automatically reallocated message retrieved from ISDS.
9604 * It will miss documents per se. Use isds_get_signed_received_message(),
9605 * if you are interested in documents (content). OTOH, only this function
9606 * can get list events message has gone through. */
9607 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
9608 const char *message_id
, struct isds_message
**message
) {
9610 isds_error err
= IE_SUCCESS
;
9612 xmlDocPtr response
= NULL
;
9613 xmlChar
*code
= NULL
, *status_message
= NULL
;
9615 size_t raw_length
= 0;
9618 if (!context
) return IE_INVALID_CONTEXT
;
9619 zfree(context
->long_message
);
9621 /* Free former message if any */
9622 if (!message
) return IE_INVAL
;
9623 isds_message_free(message
);
9626 /* Do request and check for success */
9627 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9628 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
9629 &response
, NULL
, NULL
, &code
, &status_message
);
9630 if (err
) goto leave
;
9632 /* Find signed delivery info, extract it into raw and maybe free
9634 err
= find_extract_signed_data_free_response(context
,
9635 (xmlChar
*)message_id
, &response
,
9636 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
9637 if (err
) goto leave
;
9639 /* Parse delivery info */
9640 err
= isds_load_delivery_info(context
,
9641 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
9642 message
, BUFFER_MOVE
);
9643 if (err
) goto leave
;
9649 isds_message_free(message
);
9654 free(status_message
);
9655 xmlFreeDoc(response
);
9658 isds_log(ILF_ISDS
, ILL_DEBUG
,
9659 _("GetSignedDeliveryInfo request processed by server "
9662 #else /* not HAVE_LIBCURL */
9669 /* Download delivery info-sheet of given message identified by ID.
9670 * @context is session context
9671 * @message_id is message identifier (you can get them from
9672 * isds_get_list_of_{sent,received}_messages())
9673 * @message is automatically reallocated message retrieved from ISDS.
9674 * It will miss documents per se. Use isds_get_received_message(), if you are
9675 * interested in documents (content). OTOH, only this function can get list
9676 * of events message has gone through. */
9677 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
9678 const char *message_id
, struct isds_message
**message
) {
9680 isds_error err
= IE_SUCCESS
;
9682 xmlDocPtr response
= NULL
;
9683 xmlChar
*code
= NULL
, *status_message
= NULL
;
9684 xmlNodePtr delivery_node
= NULL
;
9686 size_t raw_length
= 0;
9689 if (!context
) return IE_INVALID_CONTEXT
;
9690 zfree(context
->long_message
);
9692 /* Free former message if any */
9693 if (!message
) return IE_INVAL
;
9694 isds_message_free(message
);
9697 /* Do request and check for success */
9698 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9699 BAD_CAST
"GetDeliveryInfo", message_id
,
9700 &response
, NULL
, NULL
, &code
, &status_message
);
9701 if (err
) goto leave
;
9704 /* Serialize delivery info */
9705 delivery_node
= xmlDocGetRootElement(response
);
9706 if (!delivery_node
) {
9707 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9708 isds_printf_message(context
,
9709 _("Server did not return any delivery info for ID `%s' "
9710 "on GetDeliveryInfo request"), message_id_locale
);
9711 free(message_id_locale
);
9715 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
9716 if (err
) goto leave
;
9718 /* Parse delivery info */
9719 /* TODO: Here we parse the response second time. We could single delivery
9720 * parser from isds_load_delivery_info() to make things faster. */
9721 err
= isds_load_delivery_info(context
,
9722 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
9723 message
, BUFFER_MOVE
);
9724 if (err
) goto leave
;
9731 isds_message_free(message
);
9736 free(status_message
);
9737 xmlFreeDoc(response
);
9740 isds_log(ILF_ISDS
, ILL_DEBUG
,
9741 _("GetDeliveryInfo request processed by server "
9744 #else /* not HAVE_LIBCURL */
9751 /* Download incoming message identified by ID.
9752 * @context is session context
9753 * @message_id is message identifier (you can get them from
9754 * isds_get_list_of_received_messages())
9755 * @message is automatically reallocated message retrieved from ISDS */
9756 isds_error
isds_get_received_message(struct isds_ctx
*context
,
9757 const char *message_id
, struct isds_message
**message
) {
9759 isds_error err
= IE_SUCCESS
;
9761 xmlDocPtr response
= NULL
;
9762 void *xml_stream
= NULL
;
9763 size_t xml_stream_length
;
9764 xmlChar
*code
= NULL
, *status_message
= NULL
;
9765 xmlXPathContextPtr xpath_ctx
= NULL
;
9766 xmlXPathObjectPtr result
= NULL
;
9767 char *phys_path
= NULL
;
9768 size_t phys_start
, phys_end
;
9771 if (!context
) return IE_INVALID_CONTEXT
;
9772 zfree(context
->long_message
);
9774 /* Free former message if any */
9775 if (NULL
== message
) return IE_INVAL
;
9776 if (message
) isds_message_free(message
);
9779 /* Do request and check for success */
9780 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
9781 BAD_CAST
"MessageDownload", message_id
,
9782 &response
, &xml_stream
, &xml_stream_length
,
9783 &code
, &status_message
);
9784 if (err
) goto leave
;
9787 xpath_ctx
= xmlXPathNewContext(response
);
9792 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9796 result
= xmlXPathEvalExpression(
9797 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9803 /* Empty response */
9804 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9805 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9806 isds_printf_message(context
,
9807 _("Server did not return any message for ID `%s' "
9808 "on MessageDownload request"), message_id_locale
);
9809 free(message_id_locale
);
9814 if (result
->nodesetval
->nodeNr
> 1) {
9815 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9816 isds_printf_message(context
,
9817 _("Server did return more messages for ID `%s' "
9818 "on MessageDownload request"), message_id_locale
);
9819 free(message_id_locale
);
9824 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9826 /* Extract the message */
9827 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
9828 if (err
) goto leave
;
9830 /* Locate raw XML blob */
9832 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
9833 PHYSXML_ELEMENT_SEPARATOR
9834 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
9835 PHYSXML_ELEMENT_SEPARATOR
9836 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
9842 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
9843 phys_path
, &phys_start
, &phys_end
);
9846 isds_log_message(context
,
9847 _("Substring with isds:MessageDownloadResponse element "
9848 "could not be located in raw SOAP message"));
9852 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9853 &(*message)->raw_length);*/
9854 /* TODO: Store name space declarations from ancestors */
9855 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9856 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
9857 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
9858 (*message
)->raw
= malloc((*message
)->raw_length
);
9859 if (!(*message
)->raw
) {
9863 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
9868 isds_message_free(message
);
9873 xmlXPathFreeObject(result
);
9874 xmlXPathFreeContext(xpath_ctx
);
9877 free(status_message
);
9879 if (!*message
|| !(*message
)->xml
) {
9880 xmlFreeDoc(response
);
9884 isds_log(ILF_ISDS
, ILL_DEBUG
,
9885 _("MessageDownload request processed by server "
9888 #else /* not HAVE_LIBCURL */
9895 /* Load message of any type from buffer.
9896 * @context is session context
9897 * @raw_type defines content type of @buffer. Only message types are allowed.
9898 * @buffer is message raw representation. Format (CMS, plain signed,
9899 * message direction) is defined in @raw_type. You can retrieve such data
9900 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9901 * @length is length of buffer in bytes.
9902 * @message is automatically reallocated message parsed from @buffer.
9903 * @strategy selects how buffer will be attached into raw isds_message member.
9905 isds_error
isds_load_message(struct isds_ctx
*context
,
9906 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
9907 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9909 isds_error err
= IE_SUCCESS
;
9910 void *xml_stream
= NULL
;
9911 size_t xml_stream_length
= 0;
9912 message_ns_type message_ns
;
9913 xmlDocPtr message_doc
= NULL
;
9914 xmlXPathContextPtr xpath_ctx
= NULL
;
9915 xmlXPathObjectPtr result
= NULL
;
9917 if (!context
) return IE_INVALID_CONTEXT
;
9918 zfree(context
->long_message
);
9919 if (!message
) return IE_INVAL
;
9920 isds_message_free(message
);
9921 if (!buffer
) return IE_INVAL
;
9924 /* Select buffer format and extract XML from CMS*/
9926 case RAWTYPE_INCOMING_MESSAGE
:
9927 message_ns
= MESSAGE_NS_UNSIGNED
;
9928 xml_stream
= (void *) buffer
;
9929 xml_stream_length
= length
;
9932 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
9933 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9934 xml_stream
= (void *) buffer
;
9935 xml_stream_length
= length
;
9938 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
9939 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9940 err
= _isds_extract_cms_data(context
, buffer
, length
,
9941 &xml_stream
, &xml_stream_length
);
9942 if (err
) goto leave
;
9945 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
9946 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9947 xml_stream
= (void *) buffer
;
9948 xml_stream_length
= length
;
9951 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
9952 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9953 err
= _isds_extract_cms_data(context
, buffer
, length
,
9954 &xml_stream
, &xml_stream_length
);
9955 if (err
) goto leave
;
9959 isds_log_message(context
, _("Bad raw message representation type"));
9964 isds_log(ILF_ISDS
, ILL_DEBUG
,
9965 _("Loading message:\n%.*s\nEnd of message\n"),
9966 xml_stream_length
, xml_stream
);
9968 /* Convert messages XML stream into XPath context */
9969 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9974 xpath_ctx
= xmlXPathNewContext(message_doc
);
9979 /* XXX: Standard name space for unsigned incoming direction:
9980 * http://isds.czechpoint.cz/v20/
9982 * XXX: Name spaces mangled for signed outgoing direction:
9983 * http://isds.czechpoint.cz/v20/SentMessage:
9985 * <q:MessageDownloadResponse
9986 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9987 * <q:dmReturnedMessage>
9988 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9989 * <p:dmID>151916</p:dmID>
9992 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9994 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9995 * </q:dmReturnedMessage>
9996 * </q:MessageDownloadResponse>
9998 * XXX: Name spaces mangled for signed incoming direction:
9999 * http://isds.czechpoint.cz/v20/message:
10001 * <q:MessageDownloadResponse
10002 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10003 * <q:dmReturnedMessage>
10004 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10005 * <p:dmID>151916</p:dmID>
10008 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10010 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10011 * </q:dmReturnedMessage>
10012 * </q:MessageDownloadResponse>
10014 * Stupidity of ISDS developers is unlimited */
10015 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10019 result
= xmlXPathEvalExpression(
10020 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10026 /* Empty message */
10027 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10028 isds_printf_message(context
,
10029 _("XML document does not contain "
10030 "sisds:dmReturnedMessage element"));
10034 /* More messages */
10035 if (result
->nodesetval
->nodeNr
> 1) {
10036 isds_printf_message(context
,
10037 _("XML document has more sisds:dmReturnedMessage elements"));
10042 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10044 /* Extract the message */
10045 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10046 if (err
) goto leave
;
10048 /* Append raw buffer into message */
10049 (*message
)->raw_type
= raw_type
;
10050 switch (strategy
) {
10051 case BUFFER_DONT_STORE
:
10054 (*message
)->raw
= malloc(length
);
10055 if (!(*message
)->raw
) {
10059 memcpy((*message
)->raw
, buffer
, length
);
10060 (*message
)->raw_length
= length
;
10063 (*message
)->raw
= (void *) buffer
;
10064 (*message
)->raw_length
= length
;
10074 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10075 isds_message_free(message
);
10078 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10079 xmlXPathFreeObject(result
);
10080 xmlXPathFreeContext(xpath_ctx
);
10081 if (!*message
|| !(*message
)->xml
) {
10082 xmlFreeDoc(message_doc
);
10086 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10091 /* Determine type of raw message or delivery info according some heuristics.
10092 * It does not validate the raw blob.
10093 * @context is session context
10094 * @raw_type returns content type of @buffer. Valid only if exit code of this
10095 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10096 * reallocated memory.
10097 * @buffer is message raw representation.
10098 * @length is length of buffer in bytes. */
10099 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10100 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10102 void *xml_stream
= NULL
;
10103 size_t xml_stream_length
= 0;
10104 xmlDocPtr document
= NULL
;
10105 xmlNodePtr root
= NULL
;
10107 if (!context
) return IE_INVALID_CONTEXT
;
10108 zfree(context
->long_message
);
10109 if (length
== 0 || !buffer
) return IE_INVAL
;
10110 if (!raw_type
) return IE_INVAL
;
10113 err
= _isds_extract_cms_data(context
, buffer
, length
,
10114 &xml_stream
, &xml_stream_length
);
10116 xml_stream
= (void *) buffer
;
10117 xml_stream_length
= (size_t) length
;
10122 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10124 isds_printf_message(context
,
10125 _("Could not parse data as XML document"));
10130 /* Get root element */
10131 root
= xmlDocGetRootElement(document
);
10133 isds_printf_message(context
,
10134 _("XML document is missing root element"));
10139 if (!root
->ns
|| !root
->ns
->href
) {
10140 isds_printf_message(context
,
10141 _("Root element does not belong to any name space"));
10146 /* Test name space */
10147 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10148 if (xml_stream
== buffer
)
10149 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10151 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10152 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10153 if (xml_stream
== buffer
)
10154 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10156 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10157 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10158 if (xml_stream
== buffer
)
10159 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10161 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10162 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10163 if (xml_stream
!= buffer
) {
10164 isds_printf_message(context
,
10165 _("Document in ISDS name space is encapsulated into CMS" ));
10167 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10168 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10169 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10170 *raw_type
= RAWTYPE_DELIVERYINFO
;
10172 isds_printf_message(context
,
10173 _("Unknown root element in ISDS name space"));
10177 isds_printf_message(context
,
10178 _("Unknown name space"));
10183 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10184 xmlFreeDoc(document
);
10189 /* Download signed incoming/outgoing message identified by ID.
10190 * @context is session context
10191 * @output is true for outgoing message, false for incoming message
10192 * @message_id is message identifier (you can get them from
10193 * isds_get_list_of_{sent,received}_messages())
10194 * @message is automatically reallocated message retrieved from ISDS. The raw
10195 * member will be filled with PKCS#7 structure in DER format. */
10196 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10197 const _Bool outgoing
, const char *message_id
,
10198 struct isds_message
**message
) {
10200 isds_error err
= IE_SUCCESS
;
10202 xmlDocPtr response
= NULL
;
10203 xmlChar
*code
= NULL
, *status_message
= NULL
;
10204 xmlXPathContextPtr xpath_ctx
= NULL
;
10205 xmlXPathObjectPtr result
= NULL
;
10206 char *encoded_structure
= NULL
;
10208 size_t raw_length
= 0;
10211 if (!context
) return IE_INVALID_CONTEXT
;
10212 zfree(context
->long_message
);
10213 if (!message
) return IE_INVAL
;
10214 isds_message_free(message
);
10217 /* Do request and check for success */
10218 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10219 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10220 BAD_CAST
"SignedMessageDownload",
10221 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10222 if (err
) goto leave
;
10224 /* Find signed message, extract it into raw and maybe free
10226 err
= find_extract_signed_data_free_response(context
,
10227 (xmlChar
*)message_id
, &response
,
10228 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10229 BAD_CAST
"SignedMessageDownload",
10230 &raw
, &raw_length
);
10231 if (err
) goto leave
;
10233 /* Parse message */
10234 err
= isds_load_message(context
,
10235 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10236 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10237 raw
, raw_length
, message
, BUFFER_MOVE
);
10238 if (err
) goto leave
;
10244 isds_message_free(message
);
10247 free(encoded_structure
);
10248 xmlXPathFreeObject(result
);
10249 xmlXPathFreeContext(xpath_ctx
);
10253 free(status_message
);
10254 xmlFreeDoc(response
);
10257 isds_log(ILF_ISDS
, ILL_DEBUG
,
10259 _("SignedSentMessageDownload request processed by server "
10260 "successfully.\n") :
10261 _("SignedMessageDownload request processed by server "
10264 #else /* not HAVE_LIBCURL */
10271 /* Download signed incoming message identified by ID.
10272 * @context is session context
10273 * @message_id is message identifier (you can get them from
10274 * isds_get_list_of_received_messages())
10275 * @message is automatically reallocated message retrieved from ISDS. The raw
10276 * member will be filled with PKCS#7 structure in DER format. */
10277 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10278 const char *message_id
, struct isds_message
**message
) {
10279 return isds_get_signed_message(context
, 0, message_id
, message
);
10283 /* Download signed outgoing message identified by ID.
10284 * @context is session context
10285 * @message_id is message identifier (you can get them from
10286 * isds_get_list_of_sent_messages())
10287 * @message is automatically reallocated message retrieved from ISDS. The raw
10288 * member will be filled with PKCS#7 structure in DER format. */
10289 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10290 const char *message_id
, struct isds_message
**message
) {
10291 return isds_get_signed_message(context
, 1, message_id
, message
);
10295 /* Get type and name of user who sent a message identified by ID.
10296 * @context is session context
10297 * @message_id is message identifier
10298 * @sender_type is pointer to automatically allocated type of sender detected
10299 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10300 * library or to the server, NULL will be returned. Pass NULL if you don't
10302 * @raw_sender_type is automatically reallocated UTF-8 string describing
10303 * sender type or NULL if not known to server. Pass NULL if you don't care.
10304 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10305 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10306 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10307 const char *message_id
, isds_sender_type
**sender_type
,
10308 char **raw_sender_type
, char **sender_name
) {
10309 isds_error err
= IE_SUCCESS
;
10311 xmlDocPtr response
= NULL
;
10312 xmlChar
*code
= NULL
, *status_message
= NULL
;
10313 xmlXPathContextPtr xpath_ctx
= NULL
;
10314 xmlXPathObjectPtr result
= NULL
;
10315 char *type_string
= NULL
;
10318 if (!context
) return IE_INVALID_CONTEXT
;
10319 zfree(context
->long_message
);
10320 if (sender_type
) zfree(*sender_type
);
10321 if (raw_sender_type
) zfree(*raw_sender_type
);
10322 if (sender_name
) zfree(*sender_name
);
10323 if (!message_id
) return IE_INVAL
;
10326 /* Do request and check for success */
10327 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10328 BAD_CAST
"GetMessageAuthor",
10329 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10330 if (err
) goto leave
;
10333 xpath_ctx
= xmlXPathNewContext(response
);
10338 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10342 result
= xmlXPathEvalExpression(
10343 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10348 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10349 isds_log_message(context
,
10350 _("Missing GetMessageAuthorResponse element"));
10354 if (result
->nodesetval
->nodeNr
> 1) {
10355 isds_log_message(context
,
10356 _("Multiple GetMessageAuthorResponse element"));
10360 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10361 xmlXPathFreeObject(result
); result
= NULL
;
10363 /* Fill output arguments in */
10364 EXTRACT_STRING("isds:userType", type_string
);
10365 if (NULL
!= type_string
) {
10366 if (NULL
!= sender_type
) {
10367 *sender_type
= calloc(1, sizeof(**sender_type
));
10368 if (NULL
== *sender_type
) {
10373 err
= string2isds_sender_type((xmlChar
*)type_string
,
10376 zfree(*sender_type
);
10377 if (err
== IE_ENUM
) {
10379 char *type_string_locale
= _isds_utf82locale(type_string
);
10380 isds_log(ILF_ISDS
, ILL_WARNING
,
10381 _("Unknown isds:userType value: %s"),
10382 type_string_locale
);
10383 free(type_string_locale
);
10388 if (NULL
!= sender_name
)
10389 EXTRACT_STRING("isds:authorName", *sender_name
);
10393 if (NULL
!= sender_type
) zfree(*sender_type
);
10394 zfree(type_string
);
10395 if (NULL
!= sender_name
) zfree(*sender_name
);
10397 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10399 xmlXPathFreeObject(result
);
10400 xmlXPathFreeContext(xpath_ctx
);
10403 free(status_message
);
10404 xmlFreeDoc(response
);
10407 isds_log(ILF_ISDS
, ILL_DEBUG
,
10408 _("GetMessageAuthor request processed by server "
10409 "successfully.\n"));
10410 #else /* not HAVE_LIBCURL */
10417 /* Retrieve hash of message identified by ID stored in ISDS.
10418 * @context is session context
10419 * @message_id is message identifier
10420 * @hash is automatically reallocated message hash downloaded from ISDS.
10421 * Message must exist in system and must not be deleted. */
10422 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10423 const char *message_id
, struct isds_hash
**hash
) {
10425 isds_error err
= IE_SUCCESS
;
10427 xmlDocPtr response
= NULL
;
10428 xmlChar
*code
= NULL
, *status_message
= NULL
;
10429 xmlXPathContextPtr xpath_ctx
= NULL
;
10430 xmlXPathObjectPtr result
= NULL
;
10433 if (!context
) return IE_INVALID_CONTEXT
;
10434 zfree(context
->long_message
);
10436 isds_hash_free(hash
);
10439 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10440 BAD_CAST
"VerifyMessage", message_id
,
10441 &response
, NULL
, NULL
, &code
, &status_message
);
10442 if (err
) goto leave
;
10446 xpath_ctx
= xmlXPathNewContext(response
);
10451 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10455 result
= xmlXPathEvalExpression(
10456 BAD_CAST
"/isds:VerifyMessageResponse",
10462 /* Empty response */
10463 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10464 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10465 isds_printf_message(context
,
10466 _("Server did not return any response for ID `%s' "
10467 "on VerifyMessage request"), message_id_locale
);
10468 free(message_id_locale
);
10472 /* More responses */
10473 if (result
->nodesetval
->nodeNr
> 1) {
10474 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10475 isds_printf_message(context
,
10476 _("Server did return more responses for ID `%s' "
10477 "on VerifyMessage request"), message_id_locale
);
10478 free(message_id_locale
);
10483 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10485 /* Extract the hash */
10486 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
10490 isds_hash_free(hash
);
10493 xmlXPathFreeObject(result
);
10494 xmlXPathFreeContext(xpath_ctx
);
10497 free(status_message
);
10498 xmlFreeDoc(response
);
10501 isds_log(ILF_ISDS
, ILL_DEBUG
,
10502 _("VerifyMessage request processed by server "
10505 #else /* not HAVE_LIBCURL */
10512 /* Erase message specified by @message_id from long term storage. Other
10513 * message cannot be erased on user request.
10514 * @context is session context
10515 * @message_id is message identifier.
10516 * @incoming is true for incoming message, false for outgoing message.
10518 * IE_SUCCESS if message has ben removed
10519 * IE_INVAL if message does not exist in long term storage or message
10520 * belongs to different box
10521 * TODO: IE_NOEPRM if user has no permission to erase a message */
10522 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
10523 const char *message_id
, _Bool incoming
) {
10524 isds_error err
= IE_SUCCESS
;
10526 xmlNodePtr request
= NULL
, node
;
10527 xmlNsPtr isds_ns
= NULL
;
10528 xmlDocPtr response
= NULL
;
10529 xmlChar
*code
= NULL
, *status_message
= NULL
;
10532 if (!context
) return IE_INVALID_CONTEXT
;
10533 zfree(context
->long_message
);
10534 if (NULL
== message_id
) return IE_INVAL
;
10536 /* Check if connection is established
10537 * TODO: This check should be done downstairs. */
10538 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10541 /* Build request */
10542 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
10544 isds_log_message(context
,
10545 _("Could build EraseMessage request"));
10548 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10550 isds_log_message(context
, _("Could not create ISDS name space"));
10551 xmlFreeNode(request
);
10554 xmlSetNs(request
, isds_ns
);
10556 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
10557 if (err
) goto leave
;
10558 INSERT_STRING(request
, "dmID", message_id
);
10560 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
10564 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
10565 "message ID %s to ISDS\n"), message_id
);
10566 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
10567 xmlFreeNode(request
); request
= NULL
;
10570 isds_log(ILF_ISDS
, ILL_DEBUG
,
10571 _("Processing ISDS response on EraseMessage request "
10576 /* Check for response status */
10577 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
10578 &code
, &status_message
, NULL
);
10580 isds_log(ILF_ISDS
, ILL_DEBUG
,
10581 _("ISDS response on EraseMessage request is missing "
10586 /* Check server status code */
10587 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
10588 isds_log_message(context
, _("Message to erase belongs to other box"));
10590 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
10591 isds_log_message(context
, _("Message to erase is not saved in "
10592 "long term storage or the direction does not match"));
10594 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
10595 char *code_locale
= _isds_utf82locale((char*) code
);
10596 char *message_locale
= _isds_utf82locale((char*) status_message
);
10597 isds_log(ILF_ISDS
, ILL_DEBUG
,
10598 _("Server refused EraseMessage request "
10599 "(code=%s, message=%s)\n"),
10600 code_locale
, message_locale
);
10601 isds_log_message(context
, message_locale
);
10603 free(message_locale
);
10610 free(status_message
);
10611 xmlFreeDoc(response
);
10612 xmlFreeNode(request
);
10615 isds_log(ILF_ISDS
, ILL_DEBUG
,
10616 _("EraseMessage request processed by server "
10619 #else /* not HAVE_LIBCURL */
10626 /* Mark message as read. This is a transactional commit function to acknowledge
10627 * to ISDS the message has been downloaded and processed by client properly.
10628 * @context is session context
10629 * @message_id is message identifier. */
10630 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
10631 const char *message_id
) {
10633 isds_error err
= IE_SUCCESS
;
10635 xmlDocPtr response
= NULL
;
10636 xmlChar
*code
= NULL
, *status_message
= NULL
;
10639 if (!context
) return IE_INVALID_CONTEXT
;
10640 zfree(context
->long_message
);
10643 /* Do request and check for success */
10644 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10645 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
10646 &response
, NULL
, NULL
, &code
, &status_message
);
10649 free(status_message
);
10650 xmlFreeDoc(response
);
10653 isds_log(ILF_ISDS
, ILL_DEBUG
,
10654 _("MarkMessageAsDownloaded request processed by server "
10657 #else /* not HAVE_LIBCURL */
10664 /* Mark message as received by recipient. This is applicable only to
10665 * commercial message. Use envelope->dmType message member to distinguish
10666 * commercial message from government message. Government message is
10667 * received automatically (by law), commercial message on recipient request.
10668 * @context is session context
10669 * @message_id is message identifier. */
10670 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
10671 const char *message_id
) {
10673 isds_error err
= IE_SUCCESS
;
10675 xmlDocPtr response
= NULL
;
10676 xmlChar
*code
= NULL
, *status_message
= NULL
;
10679 if (!context
) return IE_INVALID_CONTEXT
;
10680 zfree(context
->long_message
);
10683 /* Do request and check for success */
10684 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10685 BAD_CAST
"ConfirmDelivery", message_id
,
10686 &response
, NULL
, NULL
, &code
, &status_message
);
10689 free(status_message
);
10690 xmlFreeDoc(response
);
10693 isds_log(ILF_ISDS
, ILL_DEBUG
,
10694 _("ConfirmDelivery request processed by server "
10697 #else /* not HAVE_LIBCURL */
10704 /* Send document for authorized conversion into Czech POINT system.
10705 * This is public anonymous service, no log-in necessary. Special context is
10706 * used to reuse keep-a-live HTTPS connection.
10707 * @context is Czech POINT session context. DO NOT use context connected to
10708 * ISDS server. Use new context or context used by this function previously.
10709 * @document is document to convert. Only data, data_length, dmFileDescr and
10710 * is_xml members are significant. Be ware that not all document formats can be
10711 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10712 * @id is reallocated identifier assigned by Czech POINT system to
10713 * your document on submit. Use is to tell it to Czech POINT officer.
10714 * @date is reallocated document submit date (submitted documents
10715 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10717 isds_error
czp_convert_document(struct isds_ctx
*context
,
10718 const struct isds_document
*document
,
10719 char **id
, struct tm
**date
) {
10720 isds_error err
= IE_SUCCESS
;
10722 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
10723 xmlNodePtr request
= NULL
, node
;
10724 xmlDocPtr response
= NULL
;
10726 xmlXPathContextPtr xpath_ctx
= NULL
;
10727 xmlXPathObjectPtr result
= NULL
;
10728 long int status
= -1;
10729 long int *status_ptr
= &status
;
10730 char *string
= NULL
;
10734 if (!context
) return IE_INVALID_CONTEXT
;
10735 zfree(context
->long_message
);
10736 if (!document
|| !id
|| !date
) return IE_INVAL
;
10738 if (document
->is_xml
) {
10739 isds_log_message(context
,
10740 _("XML documents cannot be submitted to conversion"));
10744 /* Free output arguments */
10749 /* Store configuration */
10750 context
->type
= CTX_TYPE_CZP
;
10751 free(context
->url
);
10752 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
10753 if (!(context
->url
))
10756 /* Prepare CURL handle if not yet connected */
10757 if (!context
->curl
) {
10758 context
->curl
= curl_easy_init();
10759 if (!(context
->curl
))
10763 /* Build conversion request */
10764 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
10766 isds_log_message(context
,
10767 _("Could not build Czech POINT conversion request"));
10770 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
10772 isds_log_message(context
,
10773 _("Could not create Czech POINT deposit name space"));
10774 xmlFreeNode(request
);
10777 xmlSetNs(request
, deposit_ns
);
10779 /* Insert children. They are in empty namespace! */
10780 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
10782 isds_log_message(context
, _("Could not create empty name space"));
10786 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
10787 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
10788 document
->dmFileDescr
);
10790 /* Document encoded in Base64 */
10791 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
10792 document
->data
, document
->data_length
);
10793 if (err
) goto leave
;
10795 isds_log(ILF_ISDS
, ILL_DEBUG
,
10796 _("Submitting document for conversion into Czech POINT deposit"));
10798 /* Send conversion request */
10799 err
= _czp_czpdeposit(context
, request
, &response
);
10800 xmlFreeNode(request
); request
= NULL
;
10803 czp_do_close_connection(context
);
10808 /* Extract response */
10809 xpath_ctx
= xmlXPathNewContext(response
);
10814 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10818 result
= xmlXPathEvalExpression(
10819 BAD_CAST
"/deposit:saveDocumentResponse/return",
10825 /* Empty response */
10826 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10827 isds_printf_message(context
,
10828 _("Missing `return' element in Czech POINT deposit response"));
10832 /* More responses */
10833 if (result
->nodesetval
->nodeNr
> 1) {
10834 isds_printf_message(context
,
10835 _("Multiple `return' element in Czech POINT deposit response"));
10840 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10843 EXTRACT_LONGINT("status", status_ptr
, 1);
10845 EXTRACT_STRING("statusMsg", string
);
10846 char *string_locale
= _isds_utf82locale(string
);
10847 isds_printf_message(context
,
10848 _("Czech POINT deposit refused document for conversion "
10849 "(code=%ld, message=%s)"),
10850 status
, string_locale
);
10851 free(string_locale
);
10856 /* Get document ID */
10857 EXTRACT_STRING("documentID", *id
);
10859 /* Get submit date */
10860 EXTRACT_STRING("dateInserted", string
);
10862 *date
= calloc(1, sizeof(**date
));
10867 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
10869 if (err
== IE_NOTSUP
) {
10871 char *string_locale
= _isds_utf82locale(string
);
10872 isds_printf_message(context
,
10873 _("Invalid dateInserted value: %s"), string_locale
);
10874 free(string_locale
);
10882 xmlXPathFreeObject(result
);
10883 xmlXPathFreeContext(xpath_ctx
);
10885 xmlFreeDoc(response
);
10886 xmlFreeNode(request
);
10889 char *id_locale
= _isds_utf82locale((char *) *id
);
10890 isds_log(ILF_ISDS
, ILL_DEBUG
,
10891 _("Document %s has been submitted for conversion "
10892 "to server successfully\n"), id_locale
);
10895 #else /* not HAVE_LIBCURL */
10902 /* Close possibly opened connection to Czech POINT document deposit.
10903 * @context is Czech POINT session context. */
10904 isds_error
czp_close_connection(struct isds_ctx
*context
) {
10905 if (!context
) return IE_INVALID_CONTEXT
;
10906 zfree(context
->long_message
);
10908 return czp_do_close_connection(context
);
10915 /* Send request for new box creation in testing ISDS instance.
10916 * It's not possible to request for a production box currently, as it
10917 * communicates via e-mail.
10918 * XXX: This function does not work either. Server complains about invalid
10920 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10922 * @context is special session context for box creation request. DO NOT use
10923 * standard context as it could reveal your password. Use fresh new context or
10924 * context previously used by this function.
10925 * @box is box description to create including single primary user (in case of
10926 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10927 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10928 * box, or contact address of PFO box owner). The email member is mandatory as
10929 * it will be used to deliver credentials.
10930 * @former_names is former name of box owner. Pass NULL if you don't care.
10931 * @approval is optional external approval of box manipulation
10932 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10933 * NULL, if you don't care.*/
10934 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
10935 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
10936 const char *former_names
, const struct isds_approval
*approval
,
10937 char **refnumber
) {
10938 isds_error err
= IE_SUCCESS
;
10940 xmlNodePtr request
= NULL
;
10941 xmlDocPtr response
= NULL
;
10942 xmlXPathContextPtr xpath_ctx
= NULL
;
10943 xmlXPathObjectPtr result
= NULL
;
10947 if (!context
) return IE_INVALID_CONTEXT
;
10948 zfree(context
->long_message
);
10949 if (!box
) return IE_INVAL
;
10952 if (!box
->email
|| box
->email
[0] == '\0') {
10953 isds_log_message(context
, _("E-mail field is mandatory"));
10957 /* Scratch box ID */
10960 /* Store configuration */
10961 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
10962 free(context
->url
);
10963 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
10964 if (!(context
->url
))
10967 /* Prepare CURL handle if not yet connected */
10968 if (!context
->curl
) {
10969 context
->curl
= curl_easy_init();
10970 if (!(context
->curl
))
10974 /* Build CreateDataBox request */
10975 err
= build_CreateDBInput_request(context
,
10976 &request
, BAD_CAST
"CreateDataBox",
10977 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
10978 if (err
) goto leave
;
10980 /* Send it to server and process response */
10981 err
= send_destroy_request_check_response(context
,
10982 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
10983 &response
, (xmlChar
**) refnumber
, NULL
);
10984 if (err
) goto leave
;
10986 /* Extract box ID */
10987 xpath_ctx
= xmlXPathNewContext(response
);
10992 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10996 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
10999 xmlXPathFreeObject(result
);
11000 xmlXPathFreeContext(xpath_ctx
);
11001 xmlFreeDoc(response
);
11002 xmlFreeNode(request
);
11005 isds_log(ILF_ISDS
, ILL_DEBUG
,
11006 _("CreateDataBox request processed by server successfully.\n"));
11008 #else /* not HAVE_LIBCURL */
11016 /* Submit CMS signed message to ISDS to verify its originality. This is
11017 * stronger form of isds_verify_message_hash() because ISDS does more checks
11018 * than simple one (potentialy old weak) hash comparison.
11019 * @context is session context
11020 * @message is memory with raw CMS signed message bit stream
11021 * @length is @message size in bytes
11023 * IE_SUCCESS if message originates in ISDS
11024 * IE_NOTEQUAL if message is unknown to ISDS
11025 * other code for other errors */
11026 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11027 const void *message
, size_t length
) {
11028 isds_error err
= IE_SUCCESS
;
11030 xmlNsPtr isds_ns
= NULL
;
11031 xmlNodePtr request
= NULL
;
11032 xmlDocPtr response
= NULL
;
11033 xmlXPathContextPtr xpath_ctx
= NULL
;
11034 xmlXPathObjectPtr result
= NULL
;
11035 _Bool
*authentic
= NULL
;
11038 if (!context
) return IE_INVALID_CONTEXT
;
11039 zfree(context
->long_message
);
11040 if (!message
|| length
== 0) return IE_INVAL
;
11043 /* Check if connection is established
11044 * TODO: This check should be done downstairs. */
11045 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11048 /* Build AuthenticateMessage request */
11049 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11051 isds_log_message(context
,
11052 _("Could not build AuthenticateMessage request"));
11055 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11057 isds_log_message(context
, _("Could not create ISDS name space"));
11058 xmlFreeNode(request
);
11061 xmlSetNs(request
, isds_ns
);
11063 /* Insert Base64 encoded message */
11064 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11066 if (err
) goto leave
;
11068 /* Send request to server and process response */
11069 err
= send_destroy_request_check_response(context
,
11070 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11071 &response
, NULL
, NULL
);
11072 if (err
) goto leave
;
11075 /* ISDS has decided */
11076 xpath_ctx
= xmlXPathNewContext(response
);
11081 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11086 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11089 isds_log_message(context
,
11090 _("Server did not return any response on "
11091 "AuthenticateMessage request"));
11096 isds_log(ILF_ISDS
, ILL_DEBUG
,
11097 _("ISDS authenticated the message successfully\n"));
11099 isds_log_message(context
, _("ISDS does not know the message"));
11106 xmlXPathFreeObject(result
);
11107 xmlXPathFreeContext(xpath_ctx
);
11109 xmlFreeDoc(response
);
11110 xmlFreeNode(request
);
11111 #else /* not HAVE_LIBCURL */
11119 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11120 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11122 * @context is session context
11123 * @input_data is memory with raw CMS signed message or delivery info bit
11124 * stream to re-sign
11125 * @input_length is @input_data size in bytes
11126 * @output_data is pointer to auto-allocated memory where to store re-signed
11127 * input data blob. Caller must free it.
11128 * @output_data is pointer where to store @output_data size in bytes
11129 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11130 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11132 * IE_SUCCESS if CMS blob has been re-signed successfully
11133 * other code for other errors */
11134 isds_error
isds_resign_message(struct isds_ctx
*context
,
11135 const void *input_data
, size_t input_length
,
11136 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11137 isds_error err
= IE_SUCCESS
;
11139 xmlNsPtr isds_ns
= NULL
;
11140 xmlNodePtr request
= NULL
;
11141 xmlDocPtr response
= NULL
;
11142 xmlXPathContextPtr xpath_ctx
= NULL
;
11143 xmlXPathObjectPtr result
= NULL
;
11144 char *string
= NULL
;
11145 const xmlChar
*codes
[] = {
11152 const char *meanings
[] = {
11154 "Message is not original",
11155 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11156 "Time stamp could not been generated in time"
11158 const isds_error errors
[] = {
11164 struct code_map_isds_error map
= {
11166 .meanings
= meanings
,
11171 if (NULL
!= output_data
) *output_data
= NULL
;
11172 if (NULL
!= output_length
) *output_length
= 0;
11173 if (NULL
!= valid_to
) *valid_to
= NULL
;
11175 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11176 zfree(context
->long_message
);
11177 if (NULL
== input_data
|| 0 == input_length
) {
11178 isds_log_message(context
, _("Empty CMS blob on input"));
11181 if (NULL
== output_data
|| NULL
== output_length
) {
11182 isds_log_message(context
,
11183 _("NULL pointer provided for output CMS blob"));
11188 /* Check if connection is established
11189 * TODO: This check should be done downstairs. */
11190 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11193 /* Build Re-signISDSDocument request */
11194 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11196 isds_log_message(context
,
11197 _("Could not build Re-signISDSDocument request"));
11200 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11202 isds_log_message(context
, _("Could not create ISDS name space"));
11203 xmlFreeNode(request
);
11206 xmlSetNs(request
, isds_ns
);
11208 /* Insert Base64 encoded CMS blob */
11209 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11210 input_data
, input_length
);
11211 if (err
) goto leave
;
11213 /* Send request to server and process response */
11214 err
= send_destroy_request_check_response(context
,
11215 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11216 &response
, NULL
, &map
);
11217 if (err
) goto leave
;
11220 /* Extract re-signed data */
11221 xpath_ctx
= xmlXPathNewContext(response
);
11226 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11230 result
= xmlXPathEvalExpression(
11231 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11236 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11237 isds_log_message(context
,
11238 _("Missing Re-signISDSDocumentResponse element"));
11242 if (result
->nodesetval
->nodeNr
> 1) {
11243 isds_log_message(context
,
11244 _("Multiple Re-signISDSDocumentResponse element"));
11248 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11249 xmlXPathFreeObject(result
); result
= NULL
;
11251 EXTRACT_STRING("isds:dmResultDoc", string
);
11252 /* Decode non-empty data */
11253 if (NULL
!= string
&& string
[0] != '\0') {
11254 *output_length
= _isds_b64decode(string
, output_data
);
11255 if (*output_length
== (size_t) -1) {
11256 isds_log_message(context
,
11257 _("Error while Base64-decoding re-signed data"));
11262 isds_log_message(context
, _("Server did not send re-signed data"));
11268 if (NULL
!= valid_to
) {
11269 /* Get time stamp expiration date */
11270 EXTRACT_STRING("isds:dmValidTo", string
);
11271 if (NULL
!= string
) {
11272 *valid_to
= calloc(1, sizeof(**valid_to
));
11277 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11279 if (err
== IE_NOTSUP
) {
11281 char *string_locale
= _isds_utf82locale(string
);
11282 isds_printf_message(context
,
11283 _("Invalid dmValidTo value: %s"), string_locale
);
11284 free(string_locale
);
11294 xmlXPathFreeObject(result
);
11295 xmlXPathFreeContext(xpath_ctx
);
11297 xmlFreeDoc(response
);
11298 xmlFreeNode(request
);
11299 #else /* not HAVE_LIBCURL */
11306 #undef INSERT_ELEMENT
11307 #undef CHECK_FOR_STRING_LENGTH
11308 #undef INSERT_STRING_ATTRIBUTE
11309 #undef INSERT_ULONGINTNOPTR
11310 #undef INSERT_ULONGINT
11311 #undef INSERT_LONGINT
11312 #undef INSERT_BOOLEAN
11313 #undef INSERT_SCALAR_BOOLEAN
11314 #undef INSERT_STRING
11315 #undef INSERT_STRING_WITH_NS
11316 #undef EXTRACT_STRING_ATTRIBUTE
11317 #undef EXTRACT_ULONGINT
11318 #undef EXTRACT_LONGINT
11319 #undef EXTRACT_BOOLEAN
11320 #undef EXTRACT_STRING
11323 /* Compute hash of message from raw representation and store it into envelope.
11324 * Original hash structure will be destroyed in envelope.
11325 * @context is session context
11326 * @message is message carrying raw XML message blob
11327 * @algorithm is desired hash algorithm to use */
11328 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11329 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11330 isds_error err
= IE_SUCCESS
;
11332 void *xml_stream
= NULL
;
11333 size_t xml_stream_length
;
11334 size_t phys_start
, phys_end
;
11335 char *phys_path
= NULL
;
11336 struct isds_hash
*new_hash
= NULL
;
11339 if (!context
) return IE_INVALID_CONTEXT
;
11340 zfree(context
->long_message
);
11341 if (!message
) return IE_INVAL
;
11343 if (!message
->raw
) {
11344 isds_log_message(context
,
11345 _("Message does not carry raw representation"));
11349 switch (message
->raw_type
) {
11350 case RAWTYPE_INCOMING_MESSAGE
:
11352 xml_stream
= message
->raw
;
11353 xml_stream_length
= message
->raw_length
;
11356 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11357 nsuri
= SISDS_INCOMING_NS
;
11358 xml_stream
= message
->raw
;
11359 xml_stream_length
= message
->raw_length
;
11362 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11363 nsuri
= SISDS_INCOMING_NS
;
11364 err
= _isds_extract_cms_data(context
,
11365 message
->raw
, message
->raw_length
,
11366 &xml_stream
, &xml_stream_length
);
11367 if (err
) goto leave
;
11370 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11371 nsuri
= SISDS_OUTGOING_NS
;
11372 xml_stream
= message
->raw
;
11373 xml_stream_length
= message
->raw_length
;
11376 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11377 nsuri
= SISDS_OUTGOING_NS
;
11378 err
= _isds_extract_cms_data(context
,
11379 message
->raw
, message
->raw_length
,
11380 &xml_stream
, &xml_stream_length
);
11381 if (err
) goto leave
;
11385 isds_log_message(context
, _("Bad raw representation type"));
11391 /* XXX: Hash is computed from original string representing isds:dmDm
11392 * subtree. That means no encoding, white space, xmlns attributes changes.
11393 * In other words, input for hash can be invalid XML stream. */
11394 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11395 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11396 PHYSXML_ELEMENT_SEPARATOR
,
11397 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11398 PHYSXML_ELEMENT_SEPARATOR
11399 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11403 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11404 phys_path
, &phys_start
, &phys_end
);
11407 isds_log_message(context
,
11408 _("Substring with isds:dmDM element could not be located "
11409 "in raw message"));
11415 new_hash
= calloc(1, sizeof(*new_hash
));
11420 new_hash
->algorithm
= algorithm
;
11421 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11424 isds_log_message(context
, _("Could not compute message hash"));
11428 /* Save computed hash */
11429 if (!message
->envelope
) {
11430 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11431 if (!message
->envelope
) {
11436 isds_hash_free(&message
->envelope
->hash
);
11437 message
->envelope
->hash
= new_hash
;
11441 isds_hash_free(&new_hash
);
11445 if (xml_stream
!= message
->raw
) free(xml_stream
);
11450 /* Compare two hashes.
11451 * @h1 is first hash
11452 * @h2 is another hash
11454 * IE_SUCCESS if hashes equal
11455 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11456 * IE_ENUM if not comparable, but both structures defined
11457 * IE_INVAL if some of the structures are undefined (NULL)
11458 * IE_ERROR if internal error occurs */
11459 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
11460 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
11461 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
11462 if (h1
->length
!= h2
->length
) return IE_ERROR
;
11463 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
11464 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
11466 for (int i
= 0; i
< h1
->length
; i
++) {
11467 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
11468 return IE_NOTEQUAL
;
11474 /* Check message has gone through ISDS by comparing message hash stored in
11475 * ISDS and locally computed hash. You must provide message with valid raw
11476 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11477 * This is convenient wrapper for isds_download_message_hash(),
11478 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11479 * @context is session context
11480 * @message is message with valid raw and envelope member; envelope->hash
11481 * member will be changed during function run. Use envelope on heap only.
11483 * IE_SUCCESS if message originates in ISDS
11484 * IE_NOTEQUAL if message is unknown to ISDS
11485 * other code for other errors */
11486 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
11487 struct isds_message
*message
) {
11488 isds_error err
= IE_SUCCESS
;
11489 struct isds_hash
*downloaded_hash
= NULL
;
11491 if (!context
) return IE_INVALID_CONTEXT
;
11492 zfree(context
->long_message
);
11493 if (!message
) return IE_INVAL
;
11495 if (!message
->envelope
) {
11496 isds_log_message(context
,
11497 _("Given message structure is missing envelope"));
11500 if (!message
->raw
) {
11501 isds_log_message(context
,
11502 _("Given message structure is missing raw representation"));
11506 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
11508 if (err
) goto leave
;
11510 err
= isds_compute_message_hash(context
, message
,
11511 downloaded_hash
->algorithm
);
11512 if (err
) goto leave
;
11514 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
11517 isds_hash_free(&downloaded_hash
);
11522 /* Search for document by document ID in list of documents. IDs are compared
11524 * @documents is list of isds_documents
11525 * @id is document identifier
11526 * @return first matching document or NULL. */
11527 const struct isds_document
*isds_find_document_by_id(
11528 const struct isds_list
*documents
, const char *id
) {
11529 const struct isds_list
*item
;
11530 const struct isds_document
*document
;
11532 for (item
= documents
; item
; item
= item
->next
) {
11533 document
= (struct isds_document
*) item
->data
;
11534 if (!document
) continue;
11536 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
11544 /* Normalize @mime_type to be proper MIME type.
11545 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11546 * guess regular MIME type (e.g. "application/pdf").
11547 * @mime_type is UTF-8 encoded MIME type to fix
11548 * @return original @mime_type if no better interpretation exists, or
11549 * constant static UTF-8 encoded string with proper MIME type. */
11550 const char *isds_normalize_mime_type(const char *mime_type
) {
11551 if (!mime_type
) return NULL
;
11553 for (int offset
= 0;
11554 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
11556 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
11557 extension_map_mime
[offset
]))
11558 return (const char *) extension_map_mime
[offset
+ 1];
11565 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11566 struct isds_message **message);
11567 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11568 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11569 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11570 struct isds_address **address);
11572 int isds_message_free(struct isds_message **message);
11573 int isds_address_free(struct isds_address **address);
11577 /* Makes known all relevant namespaces to given XPath context
11578 * @xpath_ctx is XPath context
11579 * @message_ns selects proper message name space. Unsigned and signed
11580 * messages and delivery info's differ in prefix and URI. */
11581 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
11582 const message_ns_type message_ns
) {
11583 const xmlChar
*message_namespace
= NULL
;
11585 if (!xpath_ctx
) return IE_ERROR
;
11587 switch(message_ns
) {
11589 message_namespace
= BAD_CAST ISDS1_NS
; break;
11590 case MESSAGE_NS_UNSIGNED
:
11591 message_namespace
= BAD_CAST ISDS_NS
; break;
11592 case MESSAGE_NS_SIGNED_INCOMING
:
11593 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
11594 case MESSAGE_NS_SIGNED_OUTGOING
:
11595 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
11596 case MESSAGE_NS_SIGNED_DELIVERY
:
11597 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
11602 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
11604 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
11606 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
11608 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
11610 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
11612 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))