8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
13 #include "validator.h"
19 * Allocated in isds_init() and deallocated in isds_cleanup(). */
20 unsigned int log_facilities
;
21 isds_log_level log_level
;
22 isds_log_callback log_callback
;
23 void *log_callback_data
;
24 const char *version_gpgme
= N_("n/a");
25 const char *version_gcrypt
= N_("n/a");
26 const char *version_openssl
= N_("n/a");
27 const char *version_expat
= N_("n/a");
30 /* Base URL of production ISDS instance */
31 const char isds_locator
[] = "https://ws1.mojedatovaschranka.cz/";
32 const char isds_cert_locator
[] = "https://ws1c.mojedatovaschranka.cz/";
33 const char isds_otp_locator
[] = "https://www.mojedatovaschranka.cz/";
35 /* Base URL of production ISDS instance */
36 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
37 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
38 const char isds_otp_testing_locator
[] = "https://www.czebox.cz/";
40 /* Extension to MIME type map */
41 static const xmlChar
*extension_map_mime
[] = {
42 BAD_CAST
"cer", BAD_CAST
"application/x-x509-ca-cert",
43 BAD_CAST
"crt", BAD_CAST
"application/x-x509-ca-cert",
44 BAD_CAST
"der", BAD_CAST
"application/x-x509-ca-cert",
45 BAD_CAST
"doc", BAD_CAST
"application/msword",
46 BAD_CAST
"docx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
47 "wordprocessingml.document",
48 BAD_CAST
"dbf", BAD_CAST
"application/octet-stream",
49 BAD_CAST
"prj", BAD_CAST
"application/octet-stream",
50 BAD_CAST
"qix", BAD_CAST
"application/octet-stream",
51 BAD_CAST
"sbn", BAD_CAST
"application/octet-stream",
52 BAD_CAST
"sbx", BAD_CAST
"application/octet-stream",
53 BAD_CAST
"shp", BAD_CAST
"application/octet-stream",
54 BAD_CAST
"shx", BAD_CAST
"application/octet-stream",
55 BAD_CAST
"dgn", BAD_CAST
"application/octet-stream",
56 BAD_CAST
"dwg", BAD_CAST
"image/vnd.dwg",
57 BAD_CAST
"edi", BAD_CAST
"application/edifact",
58 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.form+xml",
59 BAD_CAST
"gfs", BAD_CAST
"application/xml",
60 BAD_CAST
"gml", BAD_CAST
"application/xml",
61 BAD_CAST
"gif", BAD_CAST
"image/gif",
62 BAD_CAST
"htm", BAD_CAST
"text/html",
63 BAD_CAST
"html", BAD_CAST
"text/html",
64 BAD_CAST
"isdoc", BAD_CAST
"text/isdoc",
65 BAD_CAST
"isdocx", BAD_CAST
"text/isdocx",
66 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
67 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
68 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
69 BAD_CAST
"mpeg", BAD_CAST
"video/mpeg",
70 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
71 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg",
72 BAD_CAST
"mpg", BAD_CAST
"video/mpeg",
73 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
74 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
75 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
76 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
77 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
78 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
79 BAD_CAST
"p7b", BAD_CAST
"application/pkcs7-certificates",
80 BAD_CAST
"p7c", BAD_CAST
"application/pkcs7-mime",
81 BAD_CAST
"p7m", BAD_CAST
"application/pkcs7-mime",
82 BAD_CAST
"p7f", BAD_CAST
"application/pkcs7-signature",
83 BAD_CAST
"p7s", BAD_CAST
"application/pkcs7-signature",
84 BAD_CAST
"pk7", BAD_CAST
"application/pkcs7-mime",
85 BAD_CAST
"png", BAD_CAST
"image/png",
86 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
87 BAD_CAST
"pptx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
88 "presentationml.presentation",
89 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
90 BAD_CAST
"tif", BAD_CAST
"image/tiff",
91 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
92 BAD_CAST
"tsr", BAD_CAST
"application/timestamp-reply",
93 BAD_CAST
"tst", BAD_CAST
"application/timestamp-reply",
94 BAD_CAST
"txt", BAD_CAST
"text/plain",
95 BAD_CAST
"wav", BAD_CAST
"audio/wav",
96 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
97 BAD_CAST
"xlsx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
98 "spreadsheetml.sheet",
99 BAD_CAST
"xml", BAD_CAST
"application/xml",
100 BAD_CAST
"xsd", BAD_CAST
"application/xml",
101 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.form-xml-zip"
104 /* Structure type to hold conversion table from status code to isds_error and
106 struct code_map_isds_error
{
107 const xmlChar
**codes
; /* NULL terminated array of status codes */
108 const char **meanings
; /* Mapping to non-localized long messages */
109 const isds_error
*errors
; /* Mapping to isds_error code */
112 /* Deallocate structure isds_pki_credentials and NULL it.
113 * Pass-phrase is discarded.
114 * @pki credentials to to free */
115 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
116 if(!pki
|| !*pki
) return;
118 free((*pki
)->engine
);
119 free((*pki
)->certificate
);
122 if ((*pki
)->passphrase
) {
123 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
124 free((*pki
)->passphrase
);
131 /* Free isds_list with all member data.
132 * @list list to free, on return will be NULL */
133 void isds_list_free(struct isds_list
**list
) {
134 struct isds_list
*item
, *next_item
;
136 if (!list
|| !*list
) return;
138 for(item
= *list
; item
; item
= next_item
) {
139 if (item
->destructor
) (item
->destructor
)(&(item
->data
));
140 next_item
= item
->next
;
148 /* Deallocate structure isds_hash and NULL it.
149 * @hash hash to to free */
150 void isds_hash_free(struct isds_hash
**hash
) {
151 if(!hash
|| !*hash
) return;
152 free((*hash
)->value
);
157 /* Deallocate structure isds_PersonName recursively and NULL it */
158 void isds_PersonName_free(struct isds_PersonName
**person_name
) {
159 if (!person_name
|| !*person_name
) return;
161 free((*person_name
)->pnFirstName
);
162 free((*person_name
)->pnMiddleName
);
163 free((*person_name
)->pnLastName
);
164 free((*person_name
)->pnLastNameAtBirth
);
171 /* Deallocate structure isds_BirthInfo recursively and NULL it */
172 void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
173 if (!birth_info
|| !*birth_info
) return;
175 free((*birth_info
)->biDate
);
176 free((*birth_info
)->biCity
);
177 free((*birth_info
)->biCounty
);
178 free((*birth_info
)->biState
);
185 /* Deallocate structure isds_Address recursively and NULL it */
186 void isds_Address_free(struct isds_Address
**address
) {
187 if (!address
|| !*address
) return;
189 free((*address
)->adCity
);
190 free((*address
)->adStreet
);
191 free((*address
)->adNumberInStreet
);
192 free((*address
)->adNumberInMunicipality
);
193 free((*address
)->adZipCode
);
194 free((*address
)->adState
);
201 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
202 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
203 if (!db_owner_info
|| !*db_owner_info
) return;
205 free((*db_owner_info
)->dbID
);
206 free((*db_owner_info
)->dbType
);
207 free((*db_owner_info
)->ic
);
208 isds_PersonName_free(&((*db_owner_info
)->personName
));
209 free((*db_owner_info
)->firmName
);
210 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
211 isds_Address_free(&((*db_owner_info
)->address
));
212 free((*db_owner_info
)->nationality
);
213 free((*db_owner_info
)->email
);
214 free((*db_owner_info
)->telNumber
);
215 free((*db_owner_info
)->identifier
);
216 free((*db_owner_info
)->registryCode
);
217 free((*db_owner_info
)->dbState
);
218 free((*db_owner_info
)->dbEffectiveOVM
);
219 free((*db_owner_info
)->dbOpenAddressing
);
221 free(*db_owner_info
);
222 *db_owner_info
= NULL
;
225 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
226 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
227 if (!db_user_info
|| !*db_user_info
) return;
229 free((*db_user_info
)->userID
);
230 free((*db_user_info
)->userType
);
231 free((*db_user_info
)->userPrivils
);
232 isds_PersonName_free(&((*db_user_info
)->personName
));
233 isds_Address_free(&((*db_user_info
)->address
));
234 free((*db_user_info
)->biDate
);
235 free((*db_user_info
)->ic
);
236 free((*db_user_info
)->firmName
);
237 free((*db_user_info
)->caStreet
);
238 free((*db_user_info
)->caCity
);
239 free((*db_user_info
)->caZipCode
);
240 free((*db_user_info
)->caState
);
242 zfree(*db_user_info
);
246 /* Deallocate struct isds_event recursively and NULL it */
247 void isds_event_free(struct isds_event
**event
) {
248 if (!event
|| !*event
) return;
250 free((*event
)->time
);
251 free((*event
)->type
);
252 free((*event
)->description
);
257 /* Deallocate struct isds_envelope recursively and NULL it */
258 void isds_envelope_free(struct isds_envelope
**envelope
) {
259 if (!envelope
|| !*envelope
) return;
261 free((*envelope
)->dmID
);
262 free((*envelope
)->dbIDSender
);
263 free((*envelope
)->dmSender
);
264 free((*envelope
)->dmSenderAddress
);
265 free((*envelope
)->dmSenderType
);
266 free((*envelope
)->dmRecipient
);
267 free((*envelope
)->dmRecipientAddress
);
268 free((*envelope
)->dmAmbiguousRecipient
);
269 free((*envelope
)->dmType
);
271 free((*envelope
)->dmOrdinal
);
272 free((*envelope
)->dmMessageStatus
);
273 free((*envelope
)->dmDeliveryTime
);
274 free((*envelope
)->dmAcceptanceTime
);
275 isds_hash_free(&(*envelope
)->hash
);
276 free((*envelope
)->timestamp
);
277 isds_list_free(&(*envelope
)->events
);
279 free((*envelope
)->dmSenderOrgUnit
);
280 free((*envelope
)->dmSenderOrgUnitNum
);
281 free((*envelope
)->dbIDRecipient
);
282 free((*envelope
)->dmRecipientOrgUnit
);
283 free((*envelope
)->dmRecipientOrgUnitNum
);
284 free((*envelope
)->dmToHands
);
285 free((*envelope
)->dmAnnotation
);
286 free((*envelope
)->dmRecipientRefNumber
);
287 free((*envelope
)->dmSenderRefNumber
);
288 free((*envelope
)->dmRecipientIdent
);
289 free((*envelope
)->dmSenderIdent
);
291 free((*envelope
)->dmLegalTitleLaw
);
292 free((*envelope
)->dmLegalTitleYear
);
293 free((*envelope
)->dmLegalTitleSect
);
294 free((*envelope
)->dmLegalTitlePar
);
295 free((*envelope
)->dmLegalTitlePoint
);
297 free((*envelope
)->dmPersonalDelivery
);
298 free((*envelope
)->dmAllowSubstDelivery
);
300 free((*envelope
)->dmOVM
);
301 free((*envelope
)->dmPublishOwnID
);
308 /* Deallocate struct isds_message recursively and NULL it */
309 void isds_message_free(struct isds_message
**message
) {
310 if (!message
|| !*message
) return;
312 free((*message
)->raw
);
313 isds_envelope_free(&((*message
)->envelope
));
314 isds_list_free(&((*message
)->documents
));
315 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
322 /* Deallocate struct isds_document recursively and NULL it */
323 void isds_document_free(struct isds_document
**document
) {
324 if (!document
|| !*document
) return;
326 if (!(*document
)->is_xml
) {
327 free((*document
)->data
);
329 free((*document
)->dmMimeType
);
330 free((*document
)->dmFileGuid
);
331 free((*document
)->dmUpFileGuid
);
332 free((*document
)->dmFileDescr
);
333 free((*document
)->dmFormat
);
340 /* Deallocate struct isds_message_copy recursively and NULL it */
341 void isds_message_copy_free(struct isds_message_copy
**copy
) {
342 if (!copy
|| !*copy
) return;
344 free((*copy
)->dbIDRecipient
);
345 free((*copy
)->dmRecipientOrgUnit
);
346 free((*copy
)->dmRecipientOrgUnitNum
);
347 free((*copy
)->dmToHands
);
349 free((*copy
)->dmStatus
);
356 /* Deallocate struct isds_message_status_change recursively and NULL it */
357 void isds_message_status_change_free(
358 struct isds_message_status_change
**message_status_change
) {
359 if (!message_status_change
|| !*message_status_change
) return;
361 free((*message_status_change
)->dmID
);
362 free((*message_status_change
)->time
);
363 free((*message_status_change
)->dmMessageStatus
);
365 zfree(*message_status_change
);
369 /* Deallocate struct isds_approval recursively and NULL it */
370 void isds_approval_free(struct isds_approval
**approval
) {
371 if (!approval
|| !*approval
) return;
373 free((*approval
)->refference
);
379 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
380 * The email string is deallocated too. */
381 void isds_credentials_delivery_free(
382 struct isds_credentials_delivery
**credentials_delivery
) {
383 if (!credentials_delivery
|| !*credentials_delivery
) return;
385 free((*credentials_delivery
)->email
);
386 free((*credentials_delivery
)->token
);
387 free((*credentials_delivery
)->new_user_name
);
389 zfree(*credentials_delivery
);
393 /* Deallocate struct isds_commercial_permission recursively and NULL it */
394 void isds_commercial_permission_free(
395 struct isds_commercial_permission
**permission
) {
396 if (NULL
== permission
|| NULL
== *permission
) return;
398 free((*permission
)->recipient
);
399 free((*permission
)->payer
);
400 free((*permission
)->expiration
);
401 free((*permission
)->count
);
402 free((*permission
)->reply_identifier
);
408 /* Deallocate struct isds_credit_event recursively and NULL it */
409 void isds_credit_event_free(struct isds_credit_event
**event
) {
410 if (NULL
== event
|| NULL
== *event
) return;
412 free((*event
)->time
);
413 switch ((*event
)->type
) {
414 case ISDS_CREDIT_CHARGED
:
415 free((*event
)->details
.charged
.transaction
);
417 case ISDS_CREDIT_DISCHARGED
:
418 free((*event
)->details
.discharged
.transaction
);
420 case ISDS_CREDIT_MESSAGE_SENT
:
421 free((*event
)->details
.message_sent
.recipient
);
422 free((*event
)->details
.message_sent
.message_id
);
424 case ISDS_CREDIT_STORAGE_SET
:
425 free((*event
)->details
.storage_set
.new_valid_from
);
426 free((*event
)->details
.storage_set
.new_valid_to
);
427 free((*event
)->details
.storage_set
.old_capacity
);
428 free((*event
)->details
.storage_set
.old_valid_from
);
429 free((*event
)->details
.storage_set
.old_valid_to
);
430 free((*event
)->details
.storage_set
.initiator
);
432 case ISDS_CREDIT_EXPIRED
:
440 /* *DUP_OR_ERROR macros needs error label */
441 #define STRDUP_OR_ERROR(new, template) { \
445 (new) = strdup(template); \
446 if (!new) goto error; \
450 #define FLATDUP_OR_ERROR(new, template) { \
454 (new) = malloc(sizeof(*(new))); \
455 if (!new) goto error; \
456 memcpy((new), (template), sizeof(*(template))); \
460 /* Copy structure isds_pki_credentials recursively. */
461 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
462 const struct isds_pki_credentials
*template) {
463 struct isds_pki_credentials
*new = NULL
;
465 if(!template) return NULL
;
467 new = calloc(1, sizeof(*new));
468 if (!new) return NULL
;
470 STRDUP_OR_ERROR(new->engine
, template->engine
);
471 new->certificate_format
= template->certificate_format
;
472 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
473 new->key_format
= template->key_format
;
474 STRDUP_OR_ERROR(new->key
, template->key
);
475 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
480 isds_pki_credentials_free(&new);
485 /* Copy structure isds_PersonName recursively */
486 struct isds_PersonName
*isds_PersonName_duplicate(
487 const struct isds_PersonName
*src
) {
488 struct isds_PersonName
*new = NULL
;
490 if (!src
) return NULL
;
492 new = calloc(1, sizeof(*new));
493 if (!new) return NULL
;
495 STRDUP_OR_ERROR(new->pnFirstName
, src
->pnFirstName
);
496 STRDUP_OR_ERROR(new->pnMiddleName
, src
->pnMiddleName
);
497 STRDUP_OR_ERROR(new->pnLastName
, src
->pnLastName
);
498 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, src
->pnLastNameAtBirth
);
503 isds_PersonName_free(&new);
508 /* Copy structure isds_BirthInfo recursively */
509 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
510 const struct isds_BirthInfo
*template) {
511 struct isds_BirthInfo
*new = NULL
;
513 if (!template) return NULL
;
515 new = calloc(1, sizeof(*new));
516 if (!new) return NULL
;
518 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
519 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
520 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
521 STRDUP_OR_ERROR(new->biState
, template->biState
);
526 isds_BirthInfo_free(&new);
531 /* Copy structure isds_Address recursively */
532 struct isds_Address
*isds_Address_duplicate(
533 const struct isds_Address
*src
) {
534 struct isds_Address
*new = NULL
;
536 if (!src
) return NULL
;
538 new = calloc(1, sizeof(*new));
539 if (!new) return NULL
;
541 STRDUP_OR_ERROR(new->adCity
, src
->adCity
);
542 STRDUP_OR_ERROR(new->adStreet
, src
->adStreet
);
543 STRDUP_OR_ERROR(new->adNumberInStreet
, src
->adNumberInStreet
);
544 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
545 src
->adNumberInMunicipality
);
546 STRDUP_OR_ERROR(new->adZipCode
, src
->adZipCode
);
547 STRDUP_OR_ERROR(new->adState
, src
->adState
);
552 isds_Address_free(&new);
557 /* Copy structure isds_DbOwnerInfo recursively */
558 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
559 const struct isds_DbOwnerInfo
*src
) {
560 struct isds_DbOwnerInfo
*new = NULL
;
561 if (!src
) return NULL
;
563 new = calloc(1, sizeof(*new));
564 if (!new) return NULL
;
566 STRDUP_OR_ERROR(new->dbID
, src
->dbID
);
567 FLATDUP_OR_ERROR(new->dbType
, src
->dbType
);
568 STRDUP_OR_ERROR(new->ic
, src
->ic
);
570 if (src
->personName
) {
571 if (!(new->personName
=
572 isds_PersonName_duplicate(src
->personName
)))
576 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
578 if (src
->birthInfo
) {
579 if (!(new->birthInfo
=
580 isds_BirthInfo_duplicate(src
->birthInfo
)))
585 if (!(new->address
= isds_Address_duplicate(src
->address
)))
589 STRDUP_OR_ERROR(new->nationality
, src
->nationality
);
590 STRDUP_OR_ERROR(new->email
, src
->email
);
591 STRDUP_OR_ERROR(new->telNumber
, src
->telNumber
);
592 STRDUP_OR_ERROR(new->identifier
, src
->identifier
);
593 STRDUP_OR_ERROR(new->registryCode
, src
->registryCode
);
594 FLATDUP_OR_ERROR(new->dbState
, src
->dbState
);
595 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, src
->dbEffectiveOVM
);
596 FLATDUP_OR_ERROR(new->dbOpenAddressing
, src
->dbOpenAddressing
);
601 isds_DbOwnerInfo_free(&new);
606 /* Copy structure isds_DbUserInfo recursively */
607 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
608 const struct isds_DbUserInfo
*src
) {
609 struct isds_DbUserInfo
*new = NULL
;
610 if (!src
) return NULL
;
612 new = calloc(1, sizeof(*new));
613 if (!new) return NULL
;
615 STRDUP_OR_ERROR(new->userID
, src
->userID
);
616 FLATDUP_OR_ERROR(new->userType
, src
->userType
);
617 FLATDUP_OR_ERROR(new->userPrivils
, src
->userPrivils
);
619 if (src
->personName
) {
620 if (!(new->personName
=
621 isds_PersonName_duplicate(src
->personName
)))
626 if (!(new->address
= isds_Address_duplicate(src
->address
)))
630 FLATDUP_OR_ERROR(new->biDate
, src
->biDate
);
631 STRDUP_OR_ERROR(new->ic
, src
->ic
);
632 STRDUP_OR_ERROR(new->firmName
, src
->firmName
);
633 STRDUP_OR_ERROR(new->caStreet
, src
->caStreet
);
634 STRDUP_OR_ERROR(new->caCity
, src
->caCity
);
635 STRDUP_OR_ERROR(new->caZipCode
, src
->caZipCode
);
636 STRDUP_OR_ERROR(new->caState
, src
->caState
);
641 isds_DbUserInfo_free(&new);
645 #undef FLATDUP_OR_ERROR
646 #undef STRDUP_OR_ERROR
649 /* Logs libxml2 errors. Should be registered to libxml2 library.
650 * @ctx is unused currently
651 * @msg is printf-like formated message from libxml2 (UTF-8?)
652 * @... are variadic arguments for @msg */
653 static void log_xml(void *ctx
, const char *msg
, ...) {
660 isds_vasprintf(&text
, msg
, ap
);
664 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
669 /* Initialize ISDS library.
670 * Global function, must be called before other functions.
671 * If it fails you can not use ISDS library and must call isds_cleanup() to
672 * free partially initialized global variables. */
673 isds_error
isds_init(void) {
674 /* NULL global variables */
675 log_facilities
= ILF_ALL
;
676 log_level
= ILL_WARNING
;
678 log_callback_data
= NULL
;
681 /* Initialize gettext */
682 bindtextdomain(PACKAGE
, LOCALEDIR
);
686 /* Initialize CURL */
687 if (curl_global_init(CURL_GLOBAL_ALL
)) {
688 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
691 #endif /* HAVE_LIBCURL */
693 /* Initialise cryptographic back-ends. */
694 if (IE_SUCCESS
!= _isds_init_crypto()) {
695 isds_log(ILF_ISDS
, ILL_CRIT
,
696 _("initialisation of cryptographic back-end failed\n"));
700 /* This can _exit() current program. Find not so assertive check. */
702 xmlSetGenericErrorFunc(NULL
, log_xml
);
705 if (_isds_init_expat(&version_expat
)) {
706 isds_log(ILF_ISDS
, ILL_CRIT
,
707 _("expat library initialization failed\n"));
711 /* Allocate global variables */
718 /* Deinitialize ISDS library.
719 * Global function, must be called as last library function. */
720 isds_error
isds_cleanup(void) {
726 curl_global_cleanup();
733 /* Return version string of this library. Version of dependencies can be
734 * embedded. Do no try to parse it. You must free it. */
735 char *isds_version(void) {
738 isds_asprintf(&buffer
,
740 # ifndef USE_OPENSSL_BACKEND
741 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
743 _("%s (%s, %s, %s, libxml2 %s)"),
746 # ifndef USE_OPENSSL_BACKEND
747 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
749 _("%s (%s, %s, libxml2 %s)"),
756 #ifndef USE_OPENSSL_BACKEND
757 version_gpgme
, version_gcrypt
,
761 version_expat
, xmlParserVersion
);
766 /* Return text description of ISDS error */
767 const char *isds_strerror(const isds_error error
) {
770 return(_("Success")); break;
772 return(_("Unspecified error")); break;
774 return(_("Not supported")); break;
776 return(_("Invalid value")); break;
777 case IE_INVALID_CONTEXT
:
778 return(_("Invalid context")); break;
779 case IE_NOT_LOGGED_IN
:
780 return(_("Not logged in")); break;
781 case IE_CONNECTION_CLOSED
:
782 return(_("Connection closed")); break;
784 return(_("Timed out")); break;
786 return(_("Not exist")); break;
788 return(_("Out of memory")); break;
790 return(_("Network problem")); break;
792 return(_("HTTP problem")); break;
794 return(_("SOAP problem")); break;
796 return(_("XML problem")); break;
798 return(_("ISDS server problem")); break;
800 return(_("Invalid enum value")); break;
802 return(_("Invalid date value")); break;
804 return(_("Too big")); break;
806 return(_("Too small")); break;
808 return(_("Value not unique")); break;
810 return(_("Values not equal")); break;
811 case IE_PARTIAL_SUCCESS
:
812 return(_("Some suboperations failed")); break;
814 return(_("Operation aborted")); break;
816 return(_("Security problem")); break;
818 return(_("Unknown error"));
823 /* Create ISDS context.
824 * Each context can be used for different sessions to (possibly) different
825 * ISDS server with different credentials. */
826 struct isds_ctx
*isds_ctx_create(void) {
827 struct isds_ctx
*context
;
828 context
= malloc(sizeof(*context
));
829 if (context
) memset(context
, 0, sizeof(*context
));
834 /* Close possibly opened connection to Czech POINT document deposit without
835 * resetting long_message buffer.
836 * XXX: Do not use czp_close_connection() if you do not want to destroy log
838 * @context is Czech POINT session context. */
839 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
840 if (!context
) return IE_INVALID_CONTEXT
;
841 _isds_close_connection(context
);
846 /* Discard credentials.
847 * @context is ISDS context
848 * @discard_saved_username is true for removing saved username, false for
850 * Only that. It does not cause log out, connection close or similar. */
851 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
852 _Bool discard_saved_username
) {
853 if(!context
) return IE_INVALID_CONTEXT
;
855 if (context
->username
) {
856 memset(context
->username
, 0, strlen(context
->username
));
857 zfree(context
->username
);
859 if (context
->password
) {
860 memset(context
->password
, 0, strlen(context
->password
));
861 zfree(context
->password
);
863 isds_pki_credentials_free(&context
->pki_credentials
);
864 if (discard_saved_username
&& context
->saved_username
) {
865 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
866 zfree(context
->saved_username
);
871 #endif /* HAVE_LIBCURL */
874 /* Destroy ISDS context and free memory.
875 * @context will be NULLed on success. */
876 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
877 if (!context
|| !*context
) {
878 return IE_INVALID_CONTEXT
;
882 /* Discard credentials and close connection */
883 switch ((*context
)->type
) {
884 case CTX_TYPE_NONE
: break;
885 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
887 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
888 czp_do_close_connection(*context
); break;
892 _isds_discard_credentials(*context
, 1);
894 /* Free other structures */
895 free((*context
)->tls_verify_server
);
896 free((*context
)->tls_ca_file
);
897 free((*context
)->tls_ca_dir
);
898 free((*context
)->tls_crl_file
);
899 #endif /* HAVE_LIBCURL */
900 free((*context
)->long_message
);
908 /* Return long message text produced by library function, e.g. detailed error
909 * message. Returned pointer is only valid until new library function is
910 * called for the same context. Could be NULL, especially if NULL context is
911 * supplied. Return string is locale encoded. */
912 char *isds_long_message(const struct isds_ctx
*context
) {
913 if (!context
) return NULL
;
914 return context
->long_message
;
918 /* Stores message into context' long_message buffer.
919 * Application can pick the message up using isds_long_message().
920 * NULL @message truncates the buffer but does not deallocate it.
921 * @message is coded in locale encoding */
922 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
923 const char *message
) {
927 if (!context
) return IE_INVALID_CONTEXT
;
929 /* FIXME: Check for integer overflow */
930 length
= 1 + ((message
) ? strlen(message
) : 0);
931 buffer
= realloc(context
->long_message
, length
);
932 if (!buffer
) return IE_NOMEM
;
935 strcpy(buffer
, message
);
939 context
->long_message
= buffer
;
944 /* Appends message into context' long_message buffer.
945 * Application can pick the message up using isds_long_message().
946 * NULL message has void effect. */
947 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
948 const char *message
) {
950 size_t old_length
, length
;
952 if (!context
) return IE_INVALID_CONTEXT
;
953 if (!message
) return IE_SUCCESS
;
954 if (!context
->long_message
)
955 return isds_log_message(context
, message
);
957 old_length
= strlen(context
->long_message
);
958 /* FIXME: Check for integer overflow */
959 length
= 1 + old_length
+ strlen(message
);
960 buffer
= realloc(context
->long_message
, length
);
961 if (!buffer
) return IE_NOMEM
;
963 strcpy(buffer
+ old_length
, message
);
965 context
->long_message
= buffer
;
970 /* Stores formatted message into context' long_message buffer.
971 * Application can pick the message up using isds_long_message(). */
972 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
973 const char *format
, ...) {
977 if (!context
) return IE_INVALID_CONTEXT
;
978 va_start(ap
, format
);
979 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
982 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
987 * @facilities is bit mask of isds_log_facility values,
988 * @level is verbosity level. */
989 void isds_set_logging(const unsigned int facilities
,
990 const isds_log_level level
) {
991 log_facilities
= facilities
;
996 /* Register callback function libisds calls when new global log message is
997 * produced by library. Library logs to stderr by default.
998 * @callback is function provided by application libisds will call. See type
999 * definition for @callback argument explanation. Pass NULL to revert logging to
1000 * default behaviour.
1001 * @data is application specific data @callback gets as last argument */
1002 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
1003 log_callback
= callback
;
1004 log_callback_data
= data
;
1008 /* Log @message in class @facility with log @level into global log. @message
1009 * is printf(3) formatting string, variadic arguments may be necessary.
1010 * For debugging purposes. */
1011 _hidden isds_error
isds_log(const isds_log_facility facility
,
1012 const isds_log_level level
, const char *message
, ...) {
1014 char *buffer
= NULL
;
1017 if (level
> log_level
) return IE_SUCCESS
;
1018 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
1019 if (!message
) return IE_INVAL
;
1022 /* Pass message to application supplied callback function */
1023 va_start(ap
, message
);
1024 length
= isds_vasprintf(&buffer
, message
, ap
);
1031 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
1035 /* Default: Log it to stderr */
1036 va_start(ap
, message
);
1037 vfprintf(stderr
, message
, ap
);
1039 /* Line buffered printf is default.
1047 /* Set timeout in milliseconds for each network job like connecting to server
1048 * or sending message. Use 0 to disable timeout limits. */
1049 isds_error
isds_set_timeout(struct isds_ctx
*context
,
1050 const unsigned int timeout
) {
1051 if (!context
) return IE_INVALID_CONTEXT
;
1052 zfree(context
->long_message
);
1055 context
->timeout
= timeout
;
1057 if (context
->curl
) {
1060 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1062 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1063 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1066 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1067 context
->timeout
/ 1000);
1068 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1069 if (curl_err
) return IE_ERROR
;
1073 #else /* not HAVE_LIBCURL */
1079 /* Register callback function libisds calls periodically during HTTP data
1081 * @context is session context
1082 * @callback is function provided by application libisds will call. See type
1083 * definition for @callback argument explanation.
1084 * @data is application specific data @callback gets as last argument */
1085 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1086 isds_progress_callback callback
, void *data
) {
1087 if (!context
) return IE_INVALID_CONTEXT
;
1088 zfree(context
->long_message
);
1091 context
->progress_callback
= callback
;
1092 context
->progress_callback_data
= data
;
1095 #else /* not HAVE_LIBCURL */
1101 /* Change context settings.
1102 * @context is context which setting will be applied to
1103 * @option is name of option. It determines the type of last argument. See
1104 * isds_option definition for more info.
1105 * @... is value of new setting. Type is determined by @option
1107 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1109 isds_error err
= IE_SUCCESS
;
1112 char *pointer
, *string
;
1115 if (!context
) return IE_INVALID_CONTEXT
;
1116 zfree(context
->long_message
);
1118 va_start(ap
, option
);
1120 #define REPLACE_VA_BOOLEAN(destination) { \
1121 if (!(destination)) { \
1122 (destination) = malloc(sizeof(*(destination))); \
1123 if (!(destination)) { \
1124 err = IE_NOMEM; goto leave; \
1127 *(destination) = (_Bool) !!va_arg(ap, int); \
1130 #define REPLACE_VA_STRING(destination) { \
1131 string = va_arg(ap, char *); \
1133 pointer = realloc((destination), 1 + strlen(string)); \
1134 if (!pointer) { err = IE_NOMEM; goto leave; } \
1135 strcpy(pointer, string); \
1136 (destination) = pointer; \
1138 free(destination); \
1139 (destination) = NULL; \
1144 case IOPT_TLS_VERIFY_SERVER
:
1146 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1148 err
= IE_NOTSUP
; goto leave
;
1151 case IOPT_TLS_CA_FILE
:
1153 REPLACE_VA_STRING(context
->tls_ca_file
);
1155 err
= IE_NOTSUP
; goto leave
;
1158 case IOPT_TLS_CA_DIRECTORY
:
1160 REPLACE_VA_STRING(context
->tls_ca_dir
);
1162 err
= IE_NOTSUP
; goto leave
;
1165 case IOPT_TLS_CRL_FILE
:
1167 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1168 REPLACE_VA_STRING(context
->tls_crl_file
);
1170 isds_log_message(context
,
1171 _("Curl library does not support CRL definition"));
1173 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1175 err
= IE_NOTSUP
; goto leave
;
1176 #endif /* not HAVE_LIBCURL */
1178 case IOPT_NORMALIZE_MIME_TYPE
:
1179 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1183 err
= IE_ENUM
; goto leave
;
1186 #undef REPLACE_VA_STRING
1187 #undef REPLACE_VA_BOOLEAN
1196 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1197 * Destination for NULL argument will not be touched.
1198 * Destination pointers must be freed before calling this function.
1199 * If @username is @context->saved_username, the saved_username will not be
1200 * replaced. The saved_username is clobbered only if context has set otp
1202 * Return IE_SUCCESS on success. */
1203 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1204 const char *username
, const char *password
,
1205 const struct isds_pki_credentials
*pki_credentials
) {
1206 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1208 /* FIXME: mlock password
1209 * (I have a library) */
1212 context
->username
= strdup(username
);
1213 if (context
->otp
&& context
->saved_username
!= username
)
1214 context
->saved_username
= strdup(username
);
1217 if (NULL
== context
->otp_credentials
)
1218 context
->password
= strdup(password
);
1220 context
->password
= _isds_astrcat(password
,
1221 context
->otp_credentials
->otp_code
);
1223 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1225 if ((NULL
!= username
&& NULL
== context
->username
) ||
1226 (NULL
!= password
&& NULL
== context
->password
) ||
1227 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1228 (context
->otp
&& NULL
!= context
->username
&&
1229 NULL
== context
->saved_username
)) {
1238 /* Connect and log into ISDS server.
1239 * All required arguments will be copied, you do not have to keep them after
1241 * ISDS supports six different authentication methods. Exact method is
1242 * selected on @username, @password, @pki_credentials, and @otp arguments:
1243 * - If @pki_credentials == NULL, @username and @password must be supplied
1245 * - If @otp == NULL, simple authentication by username and password will
1247 * - If @otp != NULL, authentication by username and password and OTP
1249 * - If @pki_credentials != NULL, then
1250 * - If @username == NULL, only certificate will be used
1251 * - If @username != NULL, then
1252 * - If @password == NULL, then certificate will be used and
1253 * @username shifts meaning to box ID. This is used for hosted
1255 * - Otherwise all three arguments will be used.
1256 * Please note, that different cases require different certificate type
1257 * (system qualified one or commercial non qualified one). This library
1258 * does not check such political issues. Please see ISDS Specification
1260 * @url is base address of ISDS web service. Pass extern isds_locator
1261 * variable to use production ISDS instance without client certificate
1262 * authentication (or extern isds_cert_locator with client certificate
1263 * authentication or extern isds_otp_locators with OTP authentication).
1264 * Passing NULL has the same effect, autoselection between isds_locator,
1265 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1266 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1267 * isds_otp_testing_locator) variable to select testing instance.
1268 * @username is user name of ISDS user or box ID
1269 * @password is user's secret password
1270 * @pki_credentials defines public key cryptographic material to use in client
1272 * @otp selects one-time password authentication method to use, defines OTP
1273 * code (if known) and returns fine grade resolution of OTP procedure.
1275 * IE_SUCCESS if authentication succeeds
1276 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1277 * requested, fine grade reason will be set into @otp->resolution. Error
1278 * message from server can be obtained by isds_long_message() call.
1279 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1280 * server has sent OTP code through side channel. Application is expected to
1281 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1282 * this call to complete second phase of TOTP authentication;
1283 * or other appropriate error. */
1284 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1285 const char *username
, const char *password
,
1286 const struct isds_pki_credentials
*pki_credentials
,
1287 struct isds_otp
*otp
) {
1289 isds_error err
= IE_NOT_LOGGED_IN
;
1290 isds_error soap_err
;
1291 xmlNsPtr isds_ns
= NULL
;
1292 xmlNodePtr request
= NULL
;
1293 xmlNodePtr response
= NULL
;
1294 #endif /* HAVE_LIBCURL */
1296 if (!context
) return IE_INVALID_CONTEXT
;
1297 zfree(context
->long_message
);
1300 /* Close connection if already logged in */
1301 if (context
->curl
) {
1302 _isds_close_connection(context
);
1305 /* Store configuration */
1306 context
->type
= CTX_TYPE_ISDS
;
1307 zfree(context
->url
);
1309 /* Mangle base URI according to requested authentication method */
1310 if (NULL
== pki_credentials
) {
1311 isds_log(ILF_SEC
, ILL_INFO
,
1312 _("Selected authentication method: no certificate, "
1313 "username and password\n"));
1314 if (!username
|| !password
) {
1315 isds_log_message(context
,
1316 _("Both username and password must be supplied"));
1319 context
->otp_credentials
= otp
;
1320 context
->otp
= (NULL
!= context
->otp_credentials
);
1322 if (!context
->otp
) {
1323 /* Default locator is official system (without certificate or
1325 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1327 const char *authenticator_uri
= NULL
;
1328 if (!url
) url
= isds_otp_locator
;
1329 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1330 switch (context
->otp_credentials
->method
) {
1332 isds_log(ILF_SEC
, ILL_INFO
,
1333 _("Selected authentication method: "
1334 "HMAC-based one-time password\n"));
1336 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1339 isds_log(ILF_SEC
, ILL_INFO
,
1340 _("Selected authentication method: "
1341 "Time-based one-time password\n"));
1342 if (context
->otp_credentials
->otp_code
== NULL
) {
1343 isds_log(ILF_SEC
, ILL_INFO
,
1344 _("OTP code has not been provided by "
1345 "application, requesting server for "
1348 "%1$sas/processLogin?type=totp&sendSms=true&"
1351 isds_log(ILF_SEC
, ILL_INFO
,
1352 _("OTP code has been provided by "
1353 "application, not requesting server "
1356 "%1$sas/processLogin?type=totp&"
1361 isds_log_message(context
,
1362 _("Unknown one-time password authentication "
1363 "method requested by application"));
1366 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1370 /* Default locator is official system (with client certificate) */
1372 context
->otp_credentials
= NULL
;
1373 if (!url
) url
= isds_cert_locator
;
1376 isds_log(ILF_SEC
, ILL_INFO
,
1377 _("Selected authentication method: system certificate, "
1378 "no username and no password\n"));
1380 context
->url
= _isds_astrcat(url
, "cert/");
1383 isds_log(ILF_SEC
, ILL_INFO
,
1384 _("Selected authentication method: system certificate, "
1385 "box ID and no password\n"));
1386 context
->url
= _isds_astrcat(url
, "hspis/");
1388 isds_log(ILF_SEC
, ILL_INFO
,
1389 _("Selected authentication method: commercial "
1390 "certificate, username and password\n"));
1391 context
->url
= _isds_astrcat(url
, "certds/");
1395 if (!(context
->url
))
1398 /* Prepare CURL handle */
1399 context
->curl
= curl_easy_init();
1400 if (!(context
->curl
))
1403 /* Build log-in request */
1404 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1406 isds_log_message(context
, _("Could not build ISDS log-in request"));
1409 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1411 isds_log_message(context
, _("Could not create ISDS name space"));
1412 xmlFreeNode(request
);
1415 xmlSetNs(request
, isds_ns
);
1417 /* Store credentials */
1418 _isds_discard_credentials(context
, 1);
1419 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1420 _isds_discard_credentials(context
, 1);
1421 xmlFreeNode(request
);
1425 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1428 /* XXX: ISDS documentation does not specify response body for
1429 * DummyOperation request. However real server sends back
1430 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1431 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1432 * SOAP body content, e.g. the dmStatus element. */
1434 /* Send log-in request */
1435 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1438 /* Revert context URL from OTP authentication service URL to OTP web
1439 * service base URL for subsequent calls. Potenial isds_login() retry
1440 * will re-set context URL again. */
1441 zfree(context
->url
);
1442 context
->url
= _isds_astrcat(url
, "apps/");
1443 if (context
->url
== NULL
) {
1444 soap_err
= IE_NOMEM
;
1446 /* Detach pointer to OTP credentials from context */
1447 context
->otp_credentials
= NULL
;
1450 /* Remove credentials */
1451 _isds_discard_credentials(context
, 0);
1453 /* Destroy log-in request */
1454 xmlFreeNode(request
);
1457 xmlFreeNodeList(response
);
1458 _isds_close_connection(context
);
1462 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1463 * authentication succeeded if soap_err == IE_SUCCESS */
1466 xmlFreeNodeList(response
);
1469 isds_log(ILF_ISDS
, ILL_DEBUG
,
1470 _("User %s has been logged into server %s successfully\n"),
1473 #else /* not HAVE_LIBCURL */
1479 /* Log out from ISDS server discards credentials and connection configuration. */
1480 isds_error
isds_logout(struct isds_ctx
*context
) {
1481 if (!context
) return IE_INVALID_CONTEXT
;
1482 zfree(context
->long_message
);
1485 if (context
->curl
) {
1487 isds_error err
= _isds_invalidate_otp_cookie(context
);
1488 if (err
) return err
;
1491 /* Close connection */
1492 _isds_close_connection(context
);
1494 /* Discard credentials for sure. They should not survive isds_login(),
1495 * even successful .*/
1496 _isds_discard_credentials(context
, 1);
1497 zfree(context
->url
);
1499 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1501 _isds_discard_credentials(context
, 1);
1504 #else /* not HAVE_LIBCURL */
1510 /* Verify connection to ISDS is alive and server is responding.
1511 * Send dummy request to ISDS and expect dummy response. */
1512 isds_error
isds_ping(struct isds_ctx
*context
) {
1514 isds_error soap_err
;
1515 xmlNsPtr isds_ns
= NULL
;
1516 xmlNodePtr request
= NULL
;
1517 xmlNodePtr response
= NULL
;
1518 #endif /* HAVE_LIBCURL */
1520 if (!context
) return IE_INVALID_CONTEXT
;
1521 zfree(context
->long_message
);
1524 /* Check if connection is established */
1525 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1528 /* Build dummy request */
1529 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1531 isds_log_message(context
, _("Could build ISDS dummy request"));
1534 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1536 isds_log_message(context
, _("Could not create ISDS name space"));
1537 xmlFreeNode(request
);
1540 xmlSetNs(request
, isds_ns
);
1542 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1544 /* XXX: ISDS documentation does not specify response body for
1545 * DummyOperation request. However real server sends back
1546 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1547 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1548 * SOAP body content, e.g. the dmStatus element. */
1550 /* Send dummy request */
1551 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1553 /* Destroy log-in request */
1554 xmlFreeNode(request
);
1557 isds_log(ILF_ISDS
, ILL_DEBUG
,
1558 _("ISDS server could not be contacted\n"));
1559 xmlFreeNodeList(response
);
1563 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1564 * authentication succeeded if soap_err == IE_SUCCESS */
1567 xmlFreeNodeList(response
);
1569 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1572 #else /* not HAVE_LIBCURL */
1578 /* Send bogus request to ISDS.
1579 * Just for test purposes */
1580 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1583 xmlNsPtr isds_ns
= NULL
;
1584 xmlNodePtr request
= NULL
;
1585 xmlDocPtr response
= NULL
;
1586 xmlChar
*code
= NULL
, *message
= NULL
;
1589 if (!context
) return IE_INVALID_CONTEXT
;
1590 zfree(context
->long_message
);
1593 /* Check if connection is established */
1594 if (!context
->curl
) {
1595 /* Testing printf message */
1596 isds_printf_message(context
, "%s", _("I said connection closed"));
1597 return IE_CONNECTION_CLOSED
;
1601 /* Build dummy request */
1602 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1604 isds_log_message(context
, _("Could build ISDS bogus request"));
1607 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1609 isds_log_message(context
, _("Could not create ISDS name space"));
1610 xmlFreeNode(request
);
1613 xmlSetNs(request
, isds_ns
);
1615 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1617 /* Sent bogus request */
1618 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1620 /* Destroy request */
1621 xmlFreeNode(request
);
1624 isds_log(ILF_ISDS
, ILL_DEBUG
,
1625 _("Processing ISDS response on bogus request failed\n"));
1626 xmlFreeDoc(response
);
1630 /* Check for response status */
1631 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1632 &code
, &message
, NULL
);
1634 isds_log(ILF_ISDS
, ILL_DEBUG
,
1635 _("ISDS response on bogus request is missing status\n"));
1638 xmlFreeDoc(response
);
1641 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1642 char *code_locale
= _isds_utf82locale((char*)code
);
1643 char *message_locale
= _isds_utf82locale((char*)message
);
1644 isds_log(ILF_ISDS
, ILL_DEBUG
,
1645 _("Server refused bogus request (code=%s, message=%s)\n"),
1646 code_locale
, message_locale
);
1647 /* XXX: Literal error messages from ISDS are Czech messages
1648 * (English sometimes) in UTF-8. It's hard to catch them for
1649 * translation. Successfully gettextized would return in locale
1650 * encoding, unsuccessfully translated would pass in UTF-8. */
1651 isds_log_message(context
, message_locale
);
1653 free(message_locale
);
1656 xmlFreeDoc(response
);
1663 xmlFreeDoc(response
);
1665 isds_log(ILF_ISDS
, ILL_DEBUG
,
1666 _("Bogus message accepted by server. This should not happen.\n"));
1669 #else /* not HAVE_LIBCURL */
1676 /* Serialize XML subtree to buffer preserving XML indentation.
1677 * @context is session context
1678 * @subtree is XML element to be serialized (with children)
1679 * @buffer is automatically reallocated buffer where serialize to
1680 * @length is size of serialized stream in bytes
1681 * @return standard error code, free @buffer in case of error */
1682 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1683 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1684 isds_error err
= IE_SUCCESS
;
1685 xmlBufferPtr xml_buffer
= NULL
;
1686 xmlSaveCtxtPtr save_ctx
= NULL
;
1687 xmlDocPtr subtree_doc
= NULL
;
1688 xmlNodePtr subtree_copy
;
1692 if (!context
) return IE_INVALID_CONTEXT
;
1693 if (!buffer
) return IE_INVAL
;
1695 if (!subtree
|| !length
) return IE_INVAL
;
1697 /* Make temporary XML document with @subtree root element */
1698 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1699 * It can result in not well-formed on invalid XML tree (e.g. name space
1700 * prefix definition can miss. */
1703 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1705 isds_log_message(context
, _("Could not build temporary document"));
1710 /* XXX: Copy subtree and attach the copy to document.
1711 * One node can not bee attached into more document at the same time.
1712 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1714 * XXX: Check xmlSaveTree() too. */
1715 subtree_copy
= xmlCopyNodeList(subtree
);
1716 if (!subtree_copy
) {
1717 isds_log_message(context
, _("Could not copy subtree"));
1721 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1723 /* Only this way we get namespace definition as @xmlns:isds,
1724 * otherwise we get namespace prefix without definition */
1725 /* FIXME: Don't overwrite original default namespace */
1726 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1728 isds_log_message(context
, _("Could not create ISDS name space"));
1732 xmlSetNs(subtree_copy
, isds_ns
);
1735 /* Serialize the document into buffer */
1736 xml_buffer
= xmlBufferCreate();
1738 isds_log_message(context
, _("Could not create xmlBuffer"));
1742 /* Last argument 0 means to not format the XML tree */
1743 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1745 isds_log_message(context
, _("Could not create XML serializer"));
1749 /* XXX: According LibXML documentation, this function does not return
1750 * meaningful value yet */
1751 xmlSaveDoc(save_ctx
, subtree_doc
);
1752 if (-1 == xmlSaveFlush(save_ctx
)) {
1753 isds_log_message(context
,
1754 _("Could not serialize XML subtree"));
1758 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1759 * even after xmlSaveFlush(). Thus close it here */
1760 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1763 /* Store and detach buffer from xml_buffer */
1764 *buffer
= xml_buffer
->content
;
1765 *length
= xml_buffer
->use
;
1766 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1769 new_buffer
= realloc(*buffer
, *length
);
1770 if (new_buffer
) *buffer
= new_buffer
;
1778 xmlSaveClose(save_ctx
);
1779 xmlBufferFree(xml_buffer
);
1780 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1783 #endif /* HAVE_LIBCURL */
1787 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1788 * @context is session context
1789 * @document is original document where @nodeset points to
1790 * @nodeset is XPath node set to dump (recursively)
1791 * @buffer is automatically reallocated buffer where serialize to
1792 * @length is size of serialized stream in bytes
1793 * @return standard error code, free @buffer in case of error */
1794 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1795 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1796 void **buffer
, size_t *length
) {
1797 isds_error err
= IE_SUCCESS
;
1798 xmlBufferPtr xml_buffer
= NULL
;
1801 if (!context
) return IE_INVALID_CONTEXT
;
1802 if (!buffer
) return IE_INVAL
;
1804 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1807 /* Empty node set results into NULL buffer */
1808 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1812 /* Resulting the document into buffer */
1813 xml_buffer
= xmlBufferCreate();
1815 isds_log_message(context
, _("Could not create xmlBuffer"));
1820 /* Iterate over all nodes */
1821 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1823 * XXX: xmlNodeDump() appends to xml_buffer. */
1825 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1826 isds_log_message(context
, _("Could not dump XML node"));
1832 /* Store and detach buffer from xml_buffer */
1833 *buffer
= xml_buffer
->content
;
1834 *length
= xml_buffer
->use
;
1835 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1838 new_buffer
= realloc(*buffer
, *length
);
1839 if (new_buffer
) *buffer
= new_buffer
;
1848 xmlBufferFree(xml_buffer
);
1854 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1855 * @context is session context
1856 * @document is original document where @nodeset points to
1857 * @nodeset is XPath node set to dump (recursively)
1858 * @buffer is automatically reallocated buffer where serialize to
1859 * @length is size of serialized stream in bytes
1860 * @return standard error code, free @buffer in case of error */
1861 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1862 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1863 void **buffer
, size_t *length
) {
1864 isds_error err
= IE_SUCCESS
;
1865 xmlBufferPtr xml_buffer
= NULL
;
1866 xmlSaveCtxtPtr save_ctx
= NULL
;
1869 if (!context
) return IE_INVALID_CONTEXT
;
1870 if (!buffer
) return IE_INVAL
;
1872 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1875 /* Empty node set results into NULL buffer */
1876 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1880 /* Resulting the document into buffer */
1881 xml_buffer
= xmlBufferCreate();
1883 isds_log_message(context
, _("Could not create xmlBuffer"));
1887 if (xmlSubstituteEntitiesDefault(1)) {
1888 isds_log_message(context
, _("Could not disable attribute escaping"));
1892 /* Last argument means:
1893 * 0 to not format the XML tree
1894 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1895 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1896 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1898 isds_log_message(context
, _("Could not create XML serializer"));
1902 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1903 isds_log_message(context, _("Could not disable attribute escaping"));
1909 /* Iterate over all nodes */
1910 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1912 * XXX: xmlNodeDump() appends to xml_buffer. */
1914 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1916 /* XXX: According LibXML documentation, this function does not return
1917 * meaningful value yet */
1918 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1919 if (-1 == xmlSaveFlush(save_ctx
)) {
1920 isds_log_message(context
,
1921 _("Could not serialize XML subtree"));
1927 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1928 * even after xmlSaveFlush(). Thus close it here */
1929 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1931 /* Store and detach buffer from xml_buffer */
1932 *buffer
= xml_buffer
->content
;
1933 *length
= xml_buffer
->use
;
1934 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1937 new_buffer
= realloc(*buffer
, *length
);
1938 if (new_buffer
) *buffer
= new_buffer
;
1946 xmlSaveClose(save_ctx
);
1947 xmlBufferFree(xml_buffer
);
1954 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1955 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1956 if (!string
|| !type
) return IE_INVAL
;
1958 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1960 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1962 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1963 *type
= DBTYPE_PFO_ADVOK
;
1964 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1965 *type
= DBTYPE_PFO_DANPOR
;
1966 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1967 *type
= DBTYPE_PFO_INSSPR
;
1968 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1970 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1971 *type
= DBTYPE_PO_ZAK
;
1972 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1973 *type
= DBTYPE_PO_REQ
;
1974 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1976 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1977 *type
= DBTYPE_OVM_NOTAR
;
1978 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1979 *type
= DBTYPE_OVM_EXEKUT
;
1980 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1981 *type
= DBTYPE_OVM_REQ
;
1988 /* Convert ISDS dbType enum @type to UTF-8 string.
1989 * @Return pointer to static string, or NULL if unknown enum value */
1990 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
1992 /* DBTYPE_SYSTEM is invalid value from point of view of public
1993 * SOAP interface. */
1994 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
1995 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
1996 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
1997 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
1998 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
1999 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
2000 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
2001 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
2002 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
2003 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
2004 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
2005 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
2006 default: return NULL
; break;
2011 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2012 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
2013 if (!string
|| !type
) return IE_INVAL
;
2015 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2016 *type
= USERTYPE_PRIMARY
;
2017 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2018 *type
= USERTYPE_ENTRUSTED
;
2019 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2020 *type
= USERTYPE_ADMINISTRATOR
;
2021 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2022 *type
= USERTYPE_OFFICIAL
;
2023 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2024 *type
= USERTYPE_OFFICIAL_CERT
;
2025 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2026 *type
= USERTYPE_LIQUIDATOR
;
2033 /* Convert ISDS userType enum @type to UTF-8 string.
2034 * @Return pointer to static string, or NULL if unknown enum value */
2035 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
2037 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
2038 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
2039 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
2040 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
2041 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
2042 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
2043 default: return NULL
; break;
2048 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2049 static isds_error
string2isds_sender_type(const xmlChar
*string
,
2050 isds_sender_type
*type
) {
2051 if (!string
|| !type
) return IE_INVAL
;
2053 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
2054 *type
= SENDERTYPE_PRIMARY
;
2055 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
2056 *type
= SENDERTYPE_ENTRUSTED
;
2057 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
2058 *type
= SENDERTYPE_ADMINISTRATOR
;
2059 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
2060 *type
= SENDERTYPE_OFFICIAL
;
2061 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
2062 *type
= SENDERTYPE_VIRTUAL
;
2063 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
2064 *type
= SENDERTYPE_OFFICIAL_CERT
;
2065 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
2066 *type
= SENDERTYPE_LIQUIDATOR
;
2073 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2074 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2075 isds_payment_type
*type
) {
2076 if (!string
|| !type
) return IE_INVAL
;
2078 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2079 *type
= PAYMENT_SENDER
;
2080 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2081 *type
= PAYMENT_RESPONSE
;
2082 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2083 *type
= PAYMENT_SPONSOR
;
2084 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2085 *type
= PAYMENT_SPONSOR_LIMITED
;
2086 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2087 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2088 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2089 *type
= PAYMENT_STAMP
;
2096 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2097 * ciEventType is integer but we convert it from string representation
2099 static isds_error
string2isds_credit_event_type(const xmlChar
*string
,
2100 isds_credit_event_type
*type
) {
2101 if (!string
|| !type
) return IE_INVAL
;
2103 if (!xmlStrcmp(string
, BAD_CAST
"1"))
2104 *type
= ISDS_CREDIT_CHARGED
;
2105 else if (!xmlStrcmp(string
, BAD_CAST
"2"))
2106 *type
= ISDS_CREDIT_DISCHARGED
;
2107 else if (!xmlStrcmp(string
, BAD_CAST
"3"))
2108 *type
= ISDS_CREDIT_MESSAGE_SENT
;
2109 else if (!xmlStrcmp(string
, BAD_CAST
"4"))
2110 *type
= ISDS_CREDIT_STORAGE_SET
;
2111 else if (!xmlStrcmp(string
, BAD_CAST
"5"))
2112 *type
= ISDS_CREDIT_EXPIRED
;
2119 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2120 * @Return pointer to static string, or NULL if unknown enum value */
2121 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2123 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2124 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2125 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2126 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2127 default: return NULL
; break;
2130 #endif /* HAVE_LIBCURL */
2133 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2134 * @Return IE_ENUM if @string is not valid enum member */
2135 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2136 isds_FileMetaType
*type
) {
2137 if (!string
|| !type
) return IE_INVAL
;
2139 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2140 *type
= FILEMETATYPE_MAIN
;
2141 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2142 *type
= FILEMETATYPE_ENCLOSURE
;
2143 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2144 *type
= FILEMETATYPE_SIGNATURE
;
2145 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2146 *type
= FILEMETATYPE_META
;
2153 /* Convert UTF-8 @string to ISDS hash @algorithm.
2154 * @Return IE_ENUM if @string is not valid enum member */
2155 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2156 isds_hash_algorithm
*algorithm
) {
2157 if (!string
|| !algorithm
) return IE_INVAL
;
2159 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2160 *algorithm
= HASH_ALGORITHM_MD5
;
2161 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2162 *algorithm
= HASH_ALGORITHM_SHA_1
;
2163 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2164 *algorithm
= HASH_ALGORITHM_SHA_224
;
2165 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2166 *algorithm
= HASH_ALGORITHM_SHA_256
;
2167 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2168 *algorithm
= HASH_ALGORITHM_SHA_384
;
2169 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2170 *algorithm
= HASH_ALGORITHM_SHA_512
;
2178 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2179 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2180 if (!time
|| !string
) return IE_INVAL
;
2182 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2183 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2190 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2191 * respects the @time microseconds too. */
2192 static isds_error
timeval2timestring(const struct timeval
*time
,
2196 if (!time
|| !string
) return IE_INVAL
;
2198 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2199 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2201 /* TODO: small negative year should be formatted as "-0012". This is not
2202 * true for glibc "%04d". We should implement it.
2203 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2204 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2205 if (-1 == isds_asprintf((char **) string
,
2206 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2207 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2208 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2214 #endif /* HAVE_LIBCURL */
2217 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2218 * It respects microseconds too.
2219 * In case of error, @time will be freed. */
2220 static isds_error
timestring2timeval(const xmlChar
*string
,
2221 struct timeval
**time
) {
2223 char *offset
, *delim
, *endptr
;
2225 int offset_hours
, offset_minutes
;
2231 if (!time
) return IE_INVAL
;
2237 memset(&broken
, 0, sizeof(broken
));
2240 *time
= calloc(1, sizeof(**time
));
2241 if (!*time
) return IE_NOMEM
;
2243 memset(*time
, 0, sizeof(**time
));
2247 /* xsd:date is ISO 8601 string, thus ASCII */
2248 /*TODO: negative year */
2252 if ((tmp
= sscanf((const char*)string
, "%d-%d-%dT%d:%d:%d%n",
2253 &broken
.tm_year
, &broken
.tm_mon
, &broken
.tm_mday
,
2254 &broken
.tm_hour
, &broken
.tm_min
, &broken
.tm_sec
,
2260 broken
.tm_year
-= 1900;
2262 offset
= (char*)string
+ i
;
2264 /* Parse date and time without subseconds and offset */
2265 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2272 /* Get subseconds */
2273 if (*offset
== '.' ) {
2276 /* Copy first 6 digits, pad it with zeros.
2277 * XXX: It truncates longer number, no round.
2278 * Current server implementation uses only millisecond resolution. */
2279 /* TODO: isdigit() is locale sensitive */
2281 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
2283 subseconds
[i
] = *offset
;
2285 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
2286 subseconds
[i
] = '0';
2288 subseconds
[6] = '\0';
2290 /* Convert it into integer */
2291 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
2292 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
2293 (*time
)->tv_usec
== LONG_MAX
) {
2298 /* move to the zone offset delimiter or signal NULL*/
2299 delim
= strchr(offset
, '-');
2301 delim
= strchr(offset
, '+');
2303 delim
= strchr(offset
, 'Z');
2307 /* Get zone offset */
2308 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2309 * "" equals to "Z" and it means UTC zone. */
2310 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2311 * colon separator */
2312 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2313 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2317 if (*offset
== '+') {
2318 broken
.tm_hour
-= offset_hours
;
2319 broken
.tm_min
-= offset_minutes
;
2321 broken
.tm_hour
+= offset_hours
;
2322 broken
.tm_min
+= offset_minutes
;
2326 /* Convert to time_t */
2327 (*time
)->tv_sec
= _isds_timegm(&broken
);
2328 if ((*time
)->tv_sec
== (time_t) -1) {
2337 /* Convert unsigned int into isds_message_status.
2338 * @context is session context
2339 * @number is pointer to number value. NULL will be treated as invalid value.
2340 * @status is automatically reallocated status
2341 * @return IE_SUCCESS, or error code and free status */
2342 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2343 const unsigned long int *number
, isds_message_status
**status
) {
2344 if (!context
) return IE_INVALID_CONTEXT
;
2345 if (!status
) return IE_INVAL
;
2347 free(*status
); *status
= NULL
;
2348 if (!number
) return IE_INVAL
;
2350 if (*number
< 1 || *number
> 10) {
2351 isds_printf_message(context
, _("Invalid message status value: %lu"),
2356 *status
= malloc(sizeof(**status
));
2357 if (!*status
) return IE_NOMEM
;
2359 **status
= 1 << *number
;
2364 /* Convert event description string into isds_event members type and
2366 * @string is raw event description starting with event prefix
2367 * @event is structure where to store type and stripped description to
2368 * @return standard error code, unknown prefix is not classified as an error.
2370 static isds_error
eventstring2event(const xmlChar
*string
,
2371 struct isds_event
* event
) {
2372 const xmlChar
*known_prefixes
[] = {
2383 const isds_event_type types
[] = {
2384 EVENT_ENTERED_SYSTEM
,
2385 EVENT_ACCEPTED_BY_RECIPIENT
,
2386 EVENT_ACCEPTED_BY_FICTION
,
2387 EVENT_UNDELIVERABLE
,
2388 EVENT_COMMERCIAL_ACCEPTED
,
2390 EVENT_PRIMARY_LOGIN
,
2391 EVENT_ENTRUSTED_LOGIN
,
2397 if (!string
|| !event
) return IE_INVAL
;
2400 event
->type
= malloc(sizeof(*event
->type
));
2401 if (!(event
->type
)) return IE_NOMEM
;
2403 zfree(event
->description
);
2405 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2407 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2409 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2410 /* Prefix is known */
2411 *event
->type
= types
[index
];
2413 /* Strip prefix from description and spaces */
2414 /* TODO: Recognize all white spaces from UCS blank class and
2415 * operate on UTF-8 chars. */
2416 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2417 event
->description
= strdup((char *) (string
+ length
));
2418 if (!(event
->description
)) return IE_NOMEM
;
2424 /* Unknown event prefix.
2425 * XSD allows any string */
2426 char *string_locale
= _isds_utf82locale((char *) string
);
2427 isds_log(ILF_ISDS
, ILL_WARNING
,
2428 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2429 free(string_locale
);
2431 *event
->type
= EVENT_UKNOWN
;
2432 event
->description
= strdup((char *) string
);
2433 if (!(event
->description
)) return IE_NOMEM
;
2439 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2440 * and leave label */
2441 #define EXTRACT_STRING(element, string) { \
2442 xmlXPathFreeObject(result); \
2443 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2444 if (NULL == (result)) { \
2448 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2449 if (result->nodesetval->nodeNr > 1) { \
2450 isds_printf_message(context, _("Multiple %s element"), element); \
2454 (string) = (char *) \
2455 xmlXPathCastNodeSetToString(result->nodesetval); \
2456 if (NULL == (string)) { \
2463 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2465 char *string = NULL; \
2466 EXTRACT_STRING(element, string); \
2469 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2470 if (!(booleanPtr)) { \
2476 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2477 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2478 *(booleanPtr) = 1; \
2479 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2480 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2481 *(booleanPtr) = 0; \
2483 char *string_locale = _isds_utf82locale((char*)string); \
2484 isds_printf_message(context, \
2485 _("%s value is not valid boolean: %s"), \
2486 element, string_locale); \
2487 free(string_locale); \
2497 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2499 char *string = NULL; \
2500 EXTRACT_STRING(element, string); \
2505 number = strtol((char*)string, &endptr, 10); \
2507 if (*endptr != '\0') { \
2508 char *string_locale = _isds_utf82locale((char *)string); \
2509 isds_printf_message(context, \
2510 _("%s is not valid integer: %s"), \
2511 element, string_locale); \
2512 free(string_locale); \
2518 if (number == LONG_MIN || number == LONG_MAX) { \
2519 char *string_locale = _isds_utf82locale((char *)string); \
2520 isds_printf_message(context, \
2521 _("%s value out of range of long int: %s"), \
2522 element, string_locale); \
2523 free(string_locale); \
2529 free(string); string = NULL; \
2531 if (!(preallocated)) { \
2532 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2533 if (!(longintPtr)) { \
2538 *(longintPtr) = number; \
2542 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2544 char *string = NULL; \
2545 EXTRACT_STRING(element, string); \
2550 number = strtol((char*)string, &endptr, 10); \
2552 if (*endptr != '\0') { \
2553 char *string_locale = _isds_utf82locale((char *)string); \
2554 isds_printf_message(context, \
2555 _("%s is not valid integer: %s"), \
2556 element, string_locale); \
2557 free(string_locale); \
2563 if (number == LONG_MIN || number == LONG_MAX) { \
2564 char *string_locale = _isds_utf82locale((char *)string); \
2565 isds_printf_message(context, \
2566 _("%s value out of range of long int: %s"), \
2567 element, string_locale); \
2568 free(string_locale); \
2574 free(string); string = NULL; \
2576 isds_printf_message(context, \
2577 _("%s value is negative: %ld"), element, number); \
2582 if (!(preallocated)) { \
2583 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2584 if (!(ulongintPtr)) { \
2589 *(ulongintPtr) = number; \
2593 #define EXTRACT_DATE(element, tmPtr) { \
2594 char *string = NULL; \
2595 EXTRACT_STRING(element, string); \
2596 if (NULL != string) { \
2597 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2598 if (NULL == (tmPtr)) { \
2603 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2605 if (err == IE_NOTSUP) { \
2607 char *string_locale = _isds_utf82locale(string); \
2608 char *element_locale = _isds_utf82locale(element); \
2609 isds_printf_message(context, _("Invalid %s value: %s"), \
2610 element_locale, string_locale); \
2611 free(string_locale); \
2612 free(element_locale); \
2621 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2622 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2624 if ((required) && (!string)) { \
2625 char *attribute_locale = _isds_utf82locale(attribute); \
2626 char *element_locale = \
2627 _isds_utf82locale((char *)xpath_ctx->node->name); \
2628 isds_printf_message(context, \
2629 _("Could not extract required %s attribute value from " \
2630 "%s element"), attribute_locale, element_locale); \
2631 free(element_locale); \
2632 free(attribute_locale); \
2639 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2641 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2642 (xmlChar *) (string)); \
2644 isds_printf_message(context, \
2645 _("Could not add %s child to %s element"), \
2646 element, (parent)->name); \
2652 #define INSERT_STRING(parent, element, string) \
2653 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2655 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2657 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2658 else { INSERT_STRING(parent, element, "false"); } \
2661 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2664 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2666 INSERT_STRING(parent, element, NULL); \
2670 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2671 if ((longintPtr)) { \
2672 /* FIXME: locale sensitive */ \
2673 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2677 INSERT_STRING(parent, element, buffer) \
2678 free(buffer); (buffer) = NULL; \
2679 } else { INSERT_STRING(parent, element, NULL) } \
2682 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2683 if ((ulongintPtr)) { \
2684 /* FIXME: locale sensitive */ \
2685 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2689 INSERT_STRING(parent, element, buffer) \
2690 free(buffer); (buffer) = NULL; \
2691 } else { INSERT_STRING(parent, element, NULL) } \
2694 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2696 /* FIXME: locale sensitive */ \
2697 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2701 INSERT_STRING(parent, element, buffer) \
2702 free(buffer); (buffer) = NULL; \
2705 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2707 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2709 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2710 (xmlChar *) (string)); \
2711 if (!attribute_node) { \
2712 isds_printf_message(context, _("Could not add %s " \
2713 "attribute to %s element"), \
2714 (attribute), (parent)->name); \
2720 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2722 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2723 if (length > (maximum)) { \
2724 isds_printf_message(context, \
2725 ngettext("%s has more than %d characters", \
2726 "%s has more than %d characters", (maximum)), \
2727 (name), (maximum)); \
2731 if (length < (minimum)) { \
2732 isds_printf_message(context, \
2733 ngettext("%s has less than %d characters", \
2734 "%s has less than %d characters", (minimum)), \
2735 (name), (minimum)); \
2742 #define INSERT_ELEMENT(child, parent, element) \
2744 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2746 isds_printf_message(context, \
2747 _("Could not add %s child to %s element"), \
2748 (element), (parent)->name); \
2755 /* Find child element by name in given XPath context and switch context onto
2756 * it. The child must be uniq and must exist. Otherwise fails.
2757 * @context is ISDS context
2758 * @child is child element name
2759 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2760 * into it child. In error case, the @xpath_ctx keeps original value. */
2761 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2762 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2763 isds_error err
= IE_SUCCESS
;
2764 xmlXPathObjectPtr result
= NULL
;
2766 if (!context
) return IE_INVALID_CONTEXT
;
2767 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2770 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2777 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2778 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2779 char *child_locale
= _isds_utf82locale((char*) child
);
2780 isds_printf_message(context
,
2781 _("%s element does not contain %s child"),
2782 parent_locale
, child_locale
);
2784 free(parent_locale
);
2790 if (result
->nodesetval
->nodeNr
> 1) {
2791 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2792 char *child_locale
= _isds_utf82locale((char*) child
);
2793 isds_printf_message(context
,
2794 _("%s element contains multiple %s children"),
2795 parent_locale
, child_locale
);
2797 free(parent_locale
);
2802 /* Switch context */
2803 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2806 xmlXPathFreeObject(result
);
2813 /* Find and convert XSD:gPersonName group in current node into structure
2814 * @context is ISDS context
2815 * @personName is automatically reallocated person name structure. If no member
2816 * value is found, will be freed.
2817 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2819 * In case of error @personName will be freed. */
2820 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2821 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2822 isds_error err
= IE_SUCCESS
;
2823 xmlXPathObjectPtr result
= NULL
;
2825 if (!context
) return IE_INVALID_CONTEXT
;
2826 if (!personName
) return IE_INVAL
;
2827 isds_PersonName_free(personName
);
2828 if (!xpath_ctx
) return IE_INVAL
;
2831 *personName
= calloc(1, sizeof(**personName
));
2837 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2838 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2839 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2840 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2842 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2843 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2844 isds_PersonName_free(personName
);
2847 if (err
) isds_PersonName_free(personName
);
2848 xmlXPathFreeObject(result
);
2853 /* Find and convert XSD:gAddress group in current node into structure
2854 * @context is ISDS context
2855 * @address is automatically reallocated address structure. If no member
2856 * value is found, will be freed.
2857 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2859 * In case of error @address will be freed. */
2860 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2861 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2862 isds_error err
= IE_SUCCESS
;
2863 xmlXPathObjectPtr result
= NULL
;
2865 if (!context
) return IE_INVALID_CONTEXT
;
2866 if (!address
) return IE_INVAL
;
2867 isds_Address_free(address
);
2868 if (!xpath_ctx
) return IE_INVAL
;
2871 *address
= calloc(1, sizeof(**address
));
2877 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2878 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2879 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2880 EXTRACT_STRING("isds:adNumberInMunicipality",
2881 (*address
)->adNumberInMunicipality
);
2882 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2883 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2885 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2886 !(*address
)->adNumberInStreet
&&
2887 !(*address
)->adNumberInMunicipality
&&
2888 !(*address
)->adZipCode
&& !(*address
)->adState
)
2889 isds_Address_free(address
);
2892 if (err
) isds_Address_free(address
);
2893 xmlXPathFreeObject(result
);
2898 /* Find and convert isds:biDate element in current node into structure
2899 * @context is ISDS context
2900 * @biDate is automatically reallocated birth date structure. If no member
2901 * value is found, will be freed.
2902 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2904 * In case of error @biDate will be freed. */
2905 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2906 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2907 isds_error err
= IE_SUCCESS
;
2908 xmlXPathObjectPtr result
= NULL
;
2909 char *string
= NULL
;
2911 if (!context
) return IE_INVALID_CONTEXT
;
2912 if (!biDate
) return IE_INVAL
;
2914 if (!xpath_ctx
) return IE_INVAL
;
2916 EXTRACT_STRING("isds:biDate", string
);
2918 *biDate
= calloc(1, sizeof(**biDate
));
2923 err
= _isds_datestring2tm((xmlChar
*)string
, *biDate
);
2925 if (err
== IE_NOTSUP
) {
2927 char *string_locale
= _isds_utf82locale(string
);
2928 isds_printf_message(context
,
2929 _("Invalid isds:biDate value: %s"), string_locale
);
2930 free(string_locale
);
2937 if (err
) zfree(*biDate
);
2939 xmlXPathFreeObject(result
);
2944 /* Convert isds:dBOwnerInfo XML tree into structure
2945 * @context is ISDS context
2946 * @db_owner_info is automatically reallocated box owner info structure
2947 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2948 * In case of error @db_owner_info will be freed. */
2949 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
2950 struct isds_DbOwnerInfo
**db_owner_info
,
2951 xmlXPathContextPtr xpath_ctx
) {
2952 isds_error err
= IE_SUCCESS
;
2953 xmlXPathObjectPtr result
= NULL
;
2954 char *string
= NULL
;
2956 if (!context
) return IE_INVALID_CONTEXT
;
2957 if (!db_owner_info
) return IE_INVAL
;
2958 isds_DbOwnerInfo_free(db_owner_info
);
2959 if (!xpath_ctx
) return IE_INVAL
;
2962 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
2963 if (!*db_owner_info
) {
2968 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
2970 EXTRACT_STRING("isds:dbType", string
);
2972 (*db_owner_info
)->dbType
=
2973 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
2974 if (!(*db_owner_info
)->dbType
) {
2978 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
2980 zfree((*db_owner_info
)->dbType
);
2981 if (err
== IE_ENUM
) {
2983 char *string_locale
= _isds_utf82locale(string
);
2984 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
2986 free(string_locale
);
2993 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
2995 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
2997 if (err
) goto leave
;
2999 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
3001 (*db_owner_info
)->birthInfo
=
3002 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
3003 if (!(*db_owner_info
)->birthInfo
) {
3007 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
3009 if (err
) goto leave
;
3010 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
3011 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
3012 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
3013 if (!(*db_owner_info
)->birthInfo
->biDate
&&
3014 !(*db_owner_info
)->birthInfo
->biCity
&&
3015 !(*db_owner_info
)->birthInfo
->biCounty
&&
3016 !(*db_owner_info
)->birthInfo
->biState
)
3017 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
3019 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
3020 if (err
) goto leave
;
3022 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
3023 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
3024 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
3025 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
3026 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
3028 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
3030 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
3031 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3032 (*db_owner_info
)->dbOpenAddressing
);
3035 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
3037 xmlXPathFreeObject(result
);
3042 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3043 * @context is session context
3044 * @owner is libisds structure with box description
3045 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3046 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
3047 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
3049 isds_error err
= IE_SUCCESS
;
3051 xmlChar
*string
= NULL
;
3053 if (!context
) return IE_INVALID_CONTEXT
;
3054 if (!owner
|| !db_owner_info
) return IE_INVAL
;
3057 /* Build XSD:tDbOwnerInfo */
3058 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
3059 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
3062 if (owner
->dbType
) {
3063 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
3065 isds_printf_message(context
, _("Invalid dbType value: %d"),
3070 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3072 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
3073 if (owner
->personName
) {
3074 INSERT_STRING(db_owner_info
, "pnFirstName",
3075 owner
->personName
->pnFirstName
);
3076 INSERT_STRING(db_owner_info
, "pnMiddleName",
3077 owner
->personName
->pnMiddleName
);
3078 INSERT_STRING(db_owner_info
, "pnLastName",
3079 owner
->personName
->pnLastName
);
3080 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3081 owner
->personName
->pnLastNameAtBirth
);
3083 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
3084 if (owner
->birthInfo
) {
3085 if (owner
->birthInfo
->biDate
) {
3086 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
3087 INSERT_STRING(db_owner_info
, "biDate", string
);
3088 free(string
); string
= NULL
;
3090 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
3091 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
3092 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
3094 if (owner
->address
) {
3095 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
3096 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
3097 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3098 owner
->address
->adNumberInStreet
);
3099 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3100 owner
->address
->adNumberInMunicipality
);
3101 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
3102 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
3104 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
3105 INSERT_STRING(db_owner_info
, "email", owner
->email
);
3106 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
3108 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
3109 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
3111 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
3112 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3114 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3116 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3117 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3118 owner
->dbOpenAddressing
);
3126 /* Convert XSD:tDbUserInfo XML tree into structure
3127 * @context is ISDS context
3128 * @db_user_info is automatically reallocated user info structure
3129 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3130 * In case of error @db_user_info will be freed. */
3131 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3132 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3133 isds_error err
= IE_SUCCESS
;
3134 xmlXPathObjectPtr result
= NULL
;
3135 char *string
= NULL
;
3137 if (!context
) return IE_INVALID_CONTEXT
;
3138 if (!db_user_info
) return IE_INVAL
;
3139 isds_DbUserInfo_free(db_user_info
);
3140 if (!xpath_ctx
) return IE_INVAL
;
3143 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3144 if (!*db_user_info
) {
3149 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3151 EXTRACT_STRING("isds:userType", string
);
3153 (*db_user_info
)->userType
=
3154 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3155 if (!(*db_user_info
)->userType
) {
3159 err
= string2isds_UserType((xmlChar
*)string
,
3160 (*db_user_info
)->userType
);
3162 zfree((*db_user_info
)->userType
);
3163 if (err
== IE_ENUM
) {
3165 char *string_locale
= _isds_utf82locale(string
);
3166 isds_printf_message(context
,
3167 _("Unknown isds:userType value: %s"), string_locale
);
3168 free(string_locale
);
3175 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3177 (*db_user_info
)->personName
=
3178 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3179 if (!(*db_user_info
)->personName
) {
3184 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3186 if (err
) goto leave
;
3188 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3189 if (err
) goto leave
;
3191 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3192 if (err
) goto leave
;
3194 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3195 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3197 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3198 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3199 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3201 /* ???: Default value is "CZ" according specification. Should we provide
3203 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3206 if (err
) isds_DbUserInfo_free(db_user_info
);
3208 xmlXPathFreeObject(result
);
3213 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3214 * @context is session context
3215 * @user is libisds structure with user description
3216 * @db_user_info is XML element of XSD:tDbUserInfo */
3217 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3218 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3220 isds_error err
= IE_SUCCESS
;
3222 xmlChar
*string
= NULL
;
3224 if (!context
) return IE_INVALID_CONTEXT
;
3225 if (!user
|| !db_user_info
) return IE_INVAL
;
3227 /* Build XSD:tDbUserInfo */
3228 if (user
->personName
) {
3229 INSERT_STRING(db_user_info
, "pnFirstName",
3230 user
->personName
->pnFirstName
);
3231 INSERT_STRING(db_user_info
, "pnMiddleName",
3232 user
->personName
->pnMiddleName
);
3233 INSERT_STRING(db_user_info
, "pnLastName",
3234 user
->personName
->pnLastName
);
3235 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3236 user
->personName
->pnLastNameAtBirth
);
3238 if (user
->address
) {
3239 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3240 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3241 INSERT_STRING(db_user_info
, "adNumberInStreet",
3242 user
->address
->adNumberInStreet
);
3243 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3244 user
->address
->adNumberInMunicipality
);
3245 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3246 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3249 if (!tm2datestring(user
->biDate
, &string
))
3250 INSERT_STRING(db_user_info
, "biDate", string
);
3253 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3254 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3257 if (user
->userType
) {
3258 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3260 isds_printf_message(context
, _("Invalid userType value: %d"),
3265 INSERT_STRING(db_user_info
, "userType", type_string
);
3268 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3269 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3270 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3271 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3272 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3273 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3274 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3275 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3276 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3284 /* Convert XSD:tPDZRec XML tree into structure
3285 * @context is ISDS context
3286 * @permission is automatically reallocated commercial permission structure
3287 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3288 * In case of error @permission will be freed. */
3289 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3290 struct isds_commercial_permission
**permission
,
3291 xmlXPathContextPtr xpath_ctx
) {
3292 isds_error err
= IE_SUCCESS
;
3293 xmlXPathObjectPtr result
= NULL
;
3294 char *string
= NULL
;
3296 if (!context
) return IE_INVALID_CONTEXT
;
3297 if (!permission
) return IE_INVAL
;
3298 isds_commercial_permission_free(permission
);
3299 if (!xpath_ctx
) return IE_INVAL
;
3302 *permission
= calloc(1, sizeof(**permission
));
3308 EXTRACT_STRING("isds:PDZType", string
);
3310 err
= string2isds_payment_type((xmlChar
*)string
,
3311 &(*permission
)->type
);
3313 if (err
== IE_ENUM
) {
3315 char *string_locale
= _isds_utf82locale(string
);
3316 isds_printf_message(context
,
3317 _("Unknown isds:PDZType value: %s"), string_locale
);
3318 free(string_locale
);
3325 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3326 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3328 EXTRACT_STRING("isds:PDZExpire", string
);
3330 err
= timestring2timeval((xmlChar
*) string
,
3331 &((*permission
)->expiration
));
3333 char *string_locale
= _isds_utf82locale(string
);
3334 if (err
== IE_DATE
) err
= IE_ISDS
;
3335 isds_printf_message(context
,
3336 _("Could not convert PDZExpire as ISO time: %s"),
3338 free(string_locale
);
3344 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3345 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3348 if (err
) isds_commercial_permission_free(permission
);
3350 xmlXPathFreeObject(result
);
3355 /* Convert XSD:tCiRecord XML tree into structure
3356 * @context is ISDS context
3357 * @event is automatically reallocated commercial credit event structure
3358 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3359 * In case of error @event will be freed. */
3360 static isds_error
extract_CiRecord(struct isds_ctx
*context
,
3361 struct isds_credit_event
**event
,
3362 xmlXPathContextPtr xpath_ctx
) {
3363 isds_error err
= IE_SUCCESS
;
3364 xmlXPathObjectPtr result
= NULL
;
3365 char *string
= NULL
;
3366 long int *number_ptr
;
3368 if (!context
) return IE_INVALID_CONTEXT
;
3369 if (!event
) return IE_INVAL
;
3370 isds_credit_event_free(event
);
3371 if (!xpath_ctx
) return IE_INVAL
;
3374 *event
= calloc(1, sizeof(**event
));
3380 EXTRACT_STRING("isds:ciEventTime", string
);
3382 err
= timestring2timeval((xmlChar
*) string
,
3385 char *string_locale
= _isds_utf82locale(string
);
3386 if (err
== IE_DATE
) err
= IE_ISDS
;
3387 isds_printf_message(context
,
3388 _("Could not convert ciEventTime as ISO time: %s"),
3390 free(string_locale
);
3396 EXTRACT_STRING("isds:ciEventType", string
);
3398 err
= string2isds_credit_event_type((xmlChar
*)string
,
3401 if (err
== IE_ENUM
) {
3403 char *string_locale
= _isds_utf82locale(string
);
3404 isds_printf_message(context
,
3405 _("Unknown isds:ciEventType value: %s"), string_locale
);
3406 free(string_locale
);
3413 number_ptr
= &((*event
)->credit_change
);
3414 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr
, 1);
3415 number_ptr
= &(*event
)->new_credit
;
3416 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr
, 1);
3418 switch((*event
)->type
) {
3419 case ISDS_CREDIT_CHARGED
:
3420 EXTRACT_STRING("isds:ciTransID",
3421 (*event
)->details
.charged
.transaction
);
3423 case ISDS_CREDIT_DISCHARGED
:
3424 EXTRACT_STRING("isds:ciTransID",
3425 (*event
)->details
.discharged
.transaction
);
3427 case ISDS_CREDIT_MESSAGE_SENT
:
3428 EXTRACT_STRING("isds:ciRecipientID",
3429 (*event
)->details
.message_sent
.recipient
);
3430 EXTRACT_STRING("isds:ciPDZID",
3431 (*event
)->details
.message_sent
.message_id
);
3433 case ISDS_CREDIT_STORAGE_SET
:
3434 number_ptr
= &((*event
)->details
.storage_set
.new_capacity
);
3435 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr
, 1);
3436 EXTRACT_DATE("isds:ciNewFrom",
3437 (*event
)->details
.storage_set
.new_valid_from
);
3438 EXTRACT_DATE("isds:ciNewTo",
3439 (*event
)->details
.storage_set
.new_valid_to
);
3440 EXTRACT_LONGINT("isds:ciOldCapacity",
3441 (*event
)->details
.storage_set
.old_capacity
, 0);
3442 EXTRACT_DATE("isds:ciOldFrom",
3443 (*event
)->details
.storage_set
.old_valid_from
);
3444 EXTRACT_DATE("isds:ciOldTo",
3445 (*event
)->details
.storage_set
.old_valid_to
);
3446 EXTRACT_STRING("isds:ciDoneBy",
3447 (*event
)->details
.storage_set
.initiator
);
3449 case ISDS_CREDIT_EXPIRED
:
3454 if (err
) isds_credit_event_free(event
);
3456 xmlXPathFreeObject(result
);
3461 #endif /* HAVE_LIBCURL */
3464 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3465 * isds_envelope structure. The envelope is automatically allocated but not
3466 * reallocated. The date are just appended into envelope structure.
3467 * @context is ISDS context
3468 * @envelope is automatically allocated message envelope structure
3469 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3470 * In case of error @envelope will be freed. */
3471 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3472 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3473 isds_error err
= IE_SUCCESS
;
3474 xmlXPathObjectPtr result
= NULL
;
3476 if (!context
) return IE_INVALID_CONTEXT
;
3477 if (!envelope
) return IE_INVAL
;
3478 if (!xpath_ctx
) return IE_INVAL
;
3482 /* Allocate envelope */
3483 *envelope
= calloc(1, sizeof(**envelope
));
3489 /* Else free former data */
3490 zfree((*envelope
)->dmSenderOrgUnit
);
3491 zfree((*envelope
)->dmSenderOrgUnitNum
);
3492 zfree((*envelope
)->dbIDRecipient
);
3493 zfree((*envelope
)->dmRecipientOrgUnit
);
3494 zfree((*envelope
)->dmRecipientOrgUnitNum
);
3495 zfree((*envelope
)->dmToHands
);
3496 zfree((*envelope
)->dmAnnotation
);
3497 zfree((*envelope
)->dmRecipientRefNumber
);
3498 zfree((*envelope
)->dmSenderRefNumber
);
3499 zfree((*envelope
)->dmRecipientIdent
);
3500 zfree((*envelope
)->dmSenderIdent
);
3501 zfree((*envelope
)->dmLegalTitleLaw
);
3502 zfree((*envelope
)->dmLegalTitleYear
);
3503 zfree((*envelope
)->dmLegalTitleSect
);
3504 zfree((*envelope
)->dmLegalTitlePar
);
3505 zfree((*envelope
)->dmLegalTitlePoint
);
3506 zfree((*envelope
)->dmPersonalDelivery
);
3507 zfree((*envelope
)->dmAllowSubstDelivery
);
3510 /* Extract envelope elements added by sender or ISDS
3511 * (XSD: gMessageEnvelopeSub type) */
3512 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3513 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3514 (*envelope
)->dmSenderOrgUnitNum
, 0);
3515 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3516 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3517 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3518 (*envelope
)->dmRecipientOrgUnitNum
, 0);
3519 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3520 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3521 EXTRACT_STRING("isds:dmRecipientRefNumber",
3522 (*envelope
)->dmRecipientRefNumber
);
3523 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3524 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3525 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3527 /* Extract envelope elements regarding law reference */
3528 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3529 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3530 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3531 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3532 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3534 /* Extract envelope other elements */
3535 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3536 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3537 (*envelope
)->dmAllowSubstDelivery
);
3540 if (err
) isds_envelope_free(envelope
);
3541 xmlXPathFreeObject(result
);
3547 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3548 * isds_envelope structure. The envelope is automatically allocated but not
3549 * reallocated. The date are just appended into envelope structure.
3550 * @context is ISDS context
3551 * @envelope is automatically allocated message envelope structure
3552 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3553 * In case of error @envelope will be freed. */
3554 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3555 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3556 isds_error err
= IE_SUCCESS
;
3557 xmlXPathObjectPtr result
= NULL
;
3559 if (!context
) return IE_INVALID_CONTEXT
;
3560 if (!envelope
) return IE_INVAL
;
3561 if (!xpath_ctx
) return IE_INVAL
;
3565 /* Allocate envelope */
3566 *envelope
= calloc(1, sizeof(**envelope
));
3572 /* Else free former data */
3573 zfree((*envelope
)->dmID
);
3574 zfree((*envelope
)->dbIDSender
);
3575 zfree((*envelope
)->dmSender
);
3576 zfree((*envelope
)->dmSenderAddress
);
3577 zfree((*envelope
)->dmSenderType
);
3578 zfree((*envelope
)->dmRecipient
);
3579 zfree((*envelope
)->dmRecipientAddress
);
3580 zfree((*envelope
)->dmAmbiguousRecipient
);
3583 /* Extract envelope elements added by ISDS
3584 * (XSD: gMessageEnvelope type) */
3585 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3586 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3587 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3588 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3589 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3590 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3591 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3592 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3593 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3594 (*envelope
)->dmAmbiguousRecipient
);
3596 /* Extract envelope elements added by sender and ISDS
3597 * (XSD: gMessageEnvelope type) */
3598 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3599 if (err
) goto leave
;
3602 if (err
) isds_envelope_free(envelope
);
3603 xmlXPathFreeObject(result
);
3608 /* Convert other envelope elements from XML tree into isds_envelope structure:
3609 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3610 * The envelope is automatically allocated but not reallocated.
3611 * The data are just appended into envelope structure.
3612 * @context is ISDS context
3613 * @envelope is automatically allocated message envelope structure
3614 * @xpath_ctx is XPath context with current node as parent desired elements
3615 * In case of error @envelope will be freed. */
3616 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3617 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3618 isds_error err
= IE_SUCCESS
;
3619 xmlXPathObjectPtr result
= NULL
;
3620 char *string
= NULL
;
3621 unsigned long int *unumber
= NULL
;
3623 if (!context
) return IE_INVALID_CONTEXT
;
3624 if (!envelope
) return IE_INVAL
;
3625 if (!xpath_ctx
) return IE_INVAL
;
3630 *envelope
= calloc(1, sizeof(**envelope
));
3637 zfree((*envelope
)->dmMessageStatus
);
3638 zfree((*envelope
)->dmAttachmentSize
);
3639 zfree((*envelope
)->dmDeliveryTime
);
3640 zfree((*envelope
)->dmAcceptanceTime
);
3644 /* dmMessageStatus element is mandatory */
3645 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3647 isds_log_message(context
,
3648 _("Missing mandatory sisds:dmMessageStatus integer"));
3652 err
= uint2isds_message_status(context
, unumber
,
3653 &((*envelope
)->dmMessageStatus
));
3655 if (err
== IE_ENUM
) err
= IE_ISDS
;
3658 free(unumber
); unumber
= NULL
;
3660 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3663 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3665 err
= timestring2timeval((xmlChar
*) string
,
3666 &((*envelope
)->dmDeliveryTime
));
3668 char *string_locale
= _isds_utf82locale(string
);
3669 if (err
== IE_DATE
) err
= IE_ISDS
;
3670 isds_printf_message(context
,
3671 _("Could not convert dmDeliveryTime as ISO time: %s"),
3673 free(string_locale
);
3679 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3681 err
= timestring2timeval((xmlChar
*) string
,
3682 &((*envelope
)->dmAcceptanceTime
));
3684 char *string_locale
= _isds_utf82locale(string
);
3685 if (err
== IE_DATE
) err
= IE_ISDS
;
3686 isds_printf_message(context
,
3687 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3689 free(string_locale
);
3696 if (err
) isds_envelope_free(envelope
);
3699 xmlXPathFreeObject(result
);
3704 /* Convert message type attribute of current element into isds_envelope
3706 * TODO: This function can be incorporated into append_status_size_times() as
3707 * they are called always together.
3708 * The envelope is automatically allocated but not reallocated.
3709 * The data are just appended into envelope structure.
3710 * @context is ISDS context
3711 * @envelope is automatically allocated message envelope structure
3712 * @xpath_ctx is XPath context with current node as parent of attribute
3713 * carrying message type
3714 * In case of error @envelope will be freed. */
3715 static isds_error
append_message_type(struct isds_ctx
*context
,
3716 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3717 isds_error err
= IE_SUCCESS
;
3719 if (!context
) return IE_INVALID_CONTEXT
;
3720 if (!envelope
) return IE_INVAL
;
3721 if (!xpath_ctx
) return IE_INVAL
;
3726 *envelope
= calloc(1, sizeof(**envelope
));
3733 zfree((*envelope
)->dmType
);
3737 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3739 if (!(*envelope
)->dmType
) {
3740 /* Use default value */
3741 (*envelope
)->dmType
= strdup("V");
3742 if (!(*envelope
)->dmType
) {
3746 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3747 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3748 isds_printf_message(context
,
3749 _("Message type in dmType attribute is not 1 character long: "
3758 if (err
) isds_envelope_free(envelope
);
3764 /* Convert dmType isds_envelope member into XML attribute and append it to
3766 * @context is ISDS context
3767 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3768 * @dm_envelope is XML element the resulting attribute will be appended to.
3769 * @return error code, in case of error context' message is filled. */
3770 static isds_error
insert_message_type(struct isds_ctx
*context
,
3771 const char *type
, xmlNodePtr dm_envelope
) {
3772 isds_error err
= IE_SUCCESS
;
3773 xmlAttrPtr attribute_node
;
3775 if (!context
) return IE_INVALID_CONTEXT
;
3776 if (!dm_envelope
) return IE_INVAL
;
3778 /* Insert optional message type */
3780 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3781 char *type_locale
= _isds_utf82locale(type
);
3782 isds_printf_message(context
,
3783 _("Message type in envelope is not 1 character long: %s"),
3789 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3795 #endif /* HAVE_LIBCURL */
3798 /* Extract message document into reallocated document structure
3799 * @context is ISDS context
3800 * @document is automatically reallocated message documents structure
3801 * @xpath_ctx is XPath context with current node as isds:dmFile
3802 * In case of error @document will be freed. */
3803 static isds_error
extract_document(struct isds_ctx
*context
,
3804 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3805 isds_error err
= IE_SUCCESS
;
3806 xmlXPathObjectPtr result
= NULL
;
3807 xmlNodePtr file_node
;
3808 char *string
= NULL
;
3810 if (!context
) return IE_INVALID_CONTEXT
;
3811 if (!document
) return IE_INVAL
;
3812 isds_document_free(document
);
3813 if (!xpath_ctx
) return IE_INVAL
;
3814 file_node
= xpath_ctx
->node
;
3816 *document
= calloc(1, sizeof(**document
));
3822 /* Extract document meta data */
3823 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3824 if (context
->normalize_mime_type
) {
3825 const char *normalized_type
=
3826 isds_normalize_mime_type((*document
)->dmMimeType
);
3827 if (NULL
!= normalized_type
&&
3828 normalized_type
!= (*document
)->dmMimeType
) {
3829 char *new_type
= strdup(normalized_type
);
3830 if (NULL
== new_type
) {
3831 isds_printf_message(context
,
3832 _("Not enough memory to normalize document MIME type"));
3836 free((*document
)->dmMimeType
);
3837 (*document
)->dmMimeType
= new_type
;
3841 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3842 err
= string2isds_FileMetaType((xmlChar
*)string
,
3843 &((*document
)->dmFileMetaType
));
3845 char *meta_type_locale
= _isds_utf82locale(string
);
3846 isds_printf_message(context
,
3847 _("Document has invalid dmFileMetaType attribute value: %s"),
3849 free(meta_type_locale
);
3855 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3856 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3857 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3858 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3861 /* Extract document data.
3862 * Base64 encoded blob or XML subtree must be presented. */
3864 /* Check for dmEncodedContent */
3865 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3872 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3873 /* Here we have Base64 blob */
3874 (*document
)->is_xml
= 0;
3876 if (result
->nodesetval
->nodeNr
> 1) {
3877 isds_printf_message(context
,
3878 _("Document has more dmEncodedContent elements"));
3883 xmlXPathFreeObject(result
); result
= NULL
;
3884 EXTRACT_STRING("isds:dmEncodedContent", string
);
3886 /* Decode non-empty document */
3887 if (string
&& string
[0] != '\0') {
3888 (*document
)->data_length
=
3889 _isds_b64decode(string
, &((*document
)->data
));
3890 if ((*document
)->data_length
== (size_t) -1) {
3891 isds_printf_message(context
,
3892 _("Error while Base64-decoding document content"));
3898 /* No Base64 blob, try XML document */
3899 xmlXPathFreeObject(result
); result
= NULL
;
3900 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3907 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3908 /* Here we have XML document */
3909 (*document
)->is_xml
= 1;
3911 if (result
->nodesetval
->nodeNr
> 1) {
3912 isds_printf_message(context
,
3913 _("Document has more dmXMLContent elements"));
3918 /* XXX: We cannot serialize the content simply because:
3919 * - XML document may point out of its scope (e.g. to message
3921 * - isds:dmXMLContent can contain more elements, no element,
3923 * - it's not the XML way
3924 * Thus we provide the only right solution: XML DOM. Let's
3925 * application to cope with this hot potato :) */
3926 (*document
)->xml_node_list
=
3927 result
->nodesetval
->nodeTab
[0]->children
;
3929 /* No base64 blob, nor XML document */
3930 isds_printf_message(context
,
3931 _("Document has no dmEncodedContent, nor dmXMLContent "
3940 if (err
) isds_document_free(document
);
3942 xmlXPathFreeObject(result
);
3943 xpath_ctx
->node
= file_node
;
3949 /* Extract message documents into reallocated list of documents
3950 * @context is ISDS context
3951 * @documents is automatically reallocated message documents list structure
3952 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3953 * In case of error @documents will be freed. */
3954 static isds_error
extract_documents(struct isds_ctx
*context
,
3955 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
3956 isds_error err
= IE_SUCCESS
;
3957 xmlXPathObjectPtr result
= NULL
;
3958 xmlNodePtr files_node
;
3959 struct isds_list
*document
, *prev_document
= NULL
;
3961 if (!context
) return IE_INVALID_CONTEXT
;
3962 if (!documents
) return IE_INVAL
;
3963 isds_list_free(documents
);
3964 if (!xpath_ctx
) return IE_INVAL
;
3965 files_node
= xpath_ctx
->node
;
3967 /* Find documents */
3968 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
3975 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3976 isds_printf_message(context
,
3977 _("Message does not contain any document"));
3983 /* Iterate over documents */
3984 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3986 /* Allocate and append list item */
3987 document
= calloc(1, sizeof(*document
));
3992 document
->destructor
= (void (*)(void **))isds_document_free
;
3993 if (i
== 0) *documents
= document
;
3994 else prev_document
->next
= document
;
3995 prev_document
= document
;
3997 /* Extract document */
3998 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3999 err
= extract_document(context
,
4000 (struct isds_document
**) &(document
->data
), xpath_ctx
);
4001 if (err
) goto leave
;
4006 if (err
) isds_list_free(documents
);
4007 xmlXPathFreeObject(result
);
4008 xpath_ctx
->node
= files_node
;
4014 /* Convert isds:dmRecord XML tree into structure
4015 * @context is ISDS context
4016 * @envelope is automatically reallocated message envelope structure
4017 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4018 * In case of error @envelope will be freed. */
4019 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
4020 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4021 isds_error err
= IE_SUCCESS
;
4022 xmlXPathObjectPtr result
= NULL
;
4024 if (!context
) return IE_INVALID_CONTEXT
;
4025 if (!envelope
) return IE_INVAL
;
4026 isds_envelope_free(envelope
);
4027 if (!xpath_ctx
) return IE_INVAL
;
4030 *envelope
= calloc(1, sizeof(**envelope
));
4037 /* Extract tRecord data */
4038 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
4040 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4041 * dmAcceptanceTime. */
4042 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
4043 if (err
) goto leave
;
4045 /* Extract envelope elements added by sender and ISDS
4046 * (XSD: gMessageEnvelope type) */
4047 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
4048 if (err
) goto leave
;
4050 /* Get message type */
4051 err
= append_message_type(context
, envelope
, xpath_ctx
);
4052 if (err
) goto leave
;
4056 if (err
) isds_envelope_free(envelope
);
4057 xmlXPathFreeObject(result
);
4062 /* Convert XSD:tStateChangesRecord type XML tree into structure
4063 * @context is ISDS context
4064 * @changed_status is automatically reallocated message state change structure
4065 * @xpath_ctx is XPath context with current node as element of
4066 * XSD:tStateChangesRecord type
4067 * In case of error @changed_status will be freed. */
4068 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
4069 struct isds_message_status_change
**changed_status
,
4070 xmlXPathContextPtr xpath_ctx
) {
4071 isds_error err
= IE_SUCCESS
;
4072 xmlXPathObjectPtr result
= NULL
;
4073 unsigned long int *unumber
= NULL
;
4074 char *string
= NULL
;
4076 if (!context
) return IE_INVALID_CONTEXT
;
4077 if (!changed_status
) return IE_INVAL
;
4078 isds_message_status_change_free(changed_status
);
4079 if (!xpath_ctx
) return IE_INVAL
;
4082 *changed_status
= calloc(1, sizeof(**changed_status
));
4083 if (!*changed_status
) {
4089 /* Extract tGetStateChangesInput data */
4090 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
4092 /* dmEventTime is mandatory */
4093 EXTRACT_STRING("isds:dmEventTime", string
);
4095 err
= timestring2timeval((xmlChar
*) string
,
4096 &((*changed_status
)->time
));
4098 char *string_locale
= _isds_utf82locale(string
);
4099 if (err
== IE_DATE
) err
= IE_ISDS
;
4100 isds_printf_message(context
,
4101 _("Could not convert dmEventTime as ISO time: %s"),
4103 free(string_locale
);
4109 /* dmMessageStatus element is mandatory */
4110 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
4112 isds_log_message(context
,
4113 _("Missing mandatory isds:dmMessageStatus integer"));
4117 err
= uint2isds_message_status(context
, unumber
,
4118 &((*changed_status
)->dmMessageStatus
));
4120 if (err
== IE_ENUM
) err
= IE_ISDS
;
4129 if (err
) isds_message_status_change_free(changed_status
);
4130 xmlXPathFreeObject(result
);
4133 #endif /* HAVE_LIBCURL */
4136 /* Find and convert isds:dmHash XML tree into structure
4137 * @context is ISDS context
4138 * @envelope is automatically reallocated message hash structure
4139 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4140 * In case of error @hash will be freed. */
4141 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
4142 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
4143 isds_error err
= IE_SUCCESS
;
4144 xmlNodePtr old_ctx_node
;
4145 xmlXPathObjectPtr result
= NULL
;
4146 char *string
= NULL
;
4148 if (!context
) return IE_INVALID_CONTEXT
;
4149 if (!hash
) return IE_INVAL
;
4150 isds_hash_free(hash
);
4151 if (!xpath_ctx
) return IE_INVAL
;
4153 old_ctx_node
= xpath_ctx
->node
;
4155 *hash
= calloc(1, sizeof(**hash
));
4162 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
4163 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4172 /* Get hash algorithm */
4173 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
4174 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
4176 if (err
== IE_ENUM
) {
4177 char *string_locale
= _isds_utf82locale(string
);
4178 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
4180 free(string_locale
);
4186 /* Get hash value */
4187 EXTRACT_STRING(".", string
);
4189 isds_printf_message(context
,
4190 _("sisds:dmHash element is missing hash value"));
4194 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
4195 if ((*hash
)->length
== (size_t) -1) {
4196 isds_printf_message(context
,
4197 _("Error while Base64-decoding hash value"));
4203 if (err
) isds_hash_free(hash
);
4205 xmlXPathFreeObject(result
);
4206 xpath_ctx
->node
= old_ctx_node
;
4211 /* Find and append isds:dmQTimestamp XML tree into envelope.
4212 * Because one service is allowed to miss time-stamp content, and we think
4213 * other could too (flaw in specification), this function is deliberated and
4214 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4215 * @context is ISDS context
4216 * @envelope is automatically allocated envelope structure
4217 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4219 * In case of error @envelope will be freed. */
4220 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
4221 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
4222 isds_error err
= IE_SUCCESS
;
4223 xmlXPathObjectPtr result
= NULL
;
4224 char *string
= NULL
;
4226 if (!context
) return IE_INVALID_CONTEXT
;
4227 if (!envelope
) return IE_INVAL
;
4229 isds_envelope_free(envelope
);
4234 *envelope
= calloc(1, sizeof(**envelope
));
4240 zfree((*envelope
)->timestamp
);
4241 (*envelope
)->timestamp_length
= 0;
4244 /* Get dmQTimestamp */
4245 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4247 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4250 (*envelope
)->timestamp_length
=
4251 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4252 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4253 isds_printf_message(context
,
4254 _("Error while Base64-decoding time stamp value"));
4260 if (err
) isds_envelope_free(envelope
);
4262 xmlXPathFreeObject(result
);
4267 /* Convert XSD tReturnedMessage XML tree into message structure.
4268 * It does not store serialized XML tree into message->raw.
4269 * It does store (pointer to) parsed XML tree into message->xml if needed.
4270 * @context is ISDS context
4271 * @include_documents Use true if documents must be extracted
4272 * (tReturnedMessage XSD type), use false if documents shall be omitted
4273 * (tReturnedMessageEnvelope).
4274 * @message is automatically reallocated message structure
4275 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4277 * In case of error @message will be freed. */
4278 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4279 const _Bool include_documents
, struct isds_message
**message
,
4280 xmlXPathContextPtr xpath_ctx
) {
4281 isds_error err
= IE_SUCCESS
;
4282 xmlNodePtr message_node
;
4284 if (!context
) return IE_INVALID_CONTEXT
;
4285 if (!message
) return IE_INVAL
;
4286 isds_message_free(message
);
4287 if (!xpath_ctx
) return IE_INVAL
;
4290 *message
= calloc(1, sizeof(**message
));
4296 /* Save message XPATH context node */
4297 message_node
= xpath_ctx
->node
;
4301 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4302 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4303 if (err
) { err
= IE_ERROR
; goto leave
; }
4304 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4305 if (err
) goto leave
;
4307 if (include_documents
) {
4308 struct isds_list
*item
;
4310 /* Extract dmFiles */
4311 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4313 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4314 err
= IE_ISDS
; goto leave
;
4316 if (err
) { err
= IE_ERROR
; goto leave
; }
4317 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4318 if (err
) goto leave
;
4320 /* Store xmlDoc of this message if needed */
4321 /* Only if we got a XML document in all the documents. */
4322 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4323 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4324 (*message
)->xml
= xpath_ctx
->doc
;
4331 /* Restore context to message */
4332 xpath_ctx
->node
= message_node
;
4334 /* Extract dmHash */
4335 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4337 if (err
) goto leave
;
4339 /* Extract dmQTimestamp, */
4340 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4342 if (err
) goto leave
;
4344 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4345 * dmAcceptanceTime. */
4346 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4347 if (err
) goto leave
;
4349 /* Get message type */
4350 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4351 if (err
) goto leave
;
4354 if (err
) isds_message_free(message
);
4359 /* Extract message event into reallocated isds_event structure
4360 * @context is ISDS context
4361 * @event is automatically reallocated message event structure
4362 * @xpath_ctx is XPath context with current node as isds:dmEvent
4363 * In case of error @event will be freed. */
4364 static isds_error
extract_event(struct isds_ctx
*context
,
4365 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4366 isds_error err
= IE_SUCCESS
;
4367 xmlXPathObjectPtr result
= NULL
;
4368 xmlNodePtr event_node
;
4369 char *string
= NULL
;
4371 if (!context
) return IE_INVALID_CONTEXT
;
4372 if (!event
) return IE_INVAL
;
4373 isds_event_free(event
);
4374 if (!xpath_ctx
) return IE_INVAL
;
4375 event_node
= xpath_ctx
->node
;
4377 *event
= calloc(1, sizeof(**event
));
4383 /* Extract event data.
4384 * All elements are optional according XSD. That's funny. */
4385 EXTRACT_STRING("sisds:dmEventTime", string
);
4387 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4389 char *string_locale
= _isds_utf82locale(string
);
4390 if (err
== IE_DATE
) err
= IE_ISDS
;
4391 isds_printf_message(context
,
4392 _("Could not convert dmEventTime as ISO time: %s"),
4394 free(string_locale
);
4400 /* dmEventDescr element has prefix and the rest */
4401 EXTRACT_STRING("sisds:dmEventDescr", string
);
4403 err
= eventstring2event((xmlChar
*) string
, *event
);
4404 if (err
) goto leave
;
4409 if (err
) isds_event_free(event
);
4411 xmlXPathFreeObject(result
);
4412 xpath_ctx
->node
= event_node
;
4417 /* Convert element of XSD tEventsArray type from XML tree into
4418 * isds_list of isds_event's structure. The list is automatically reallocated.
4419 * @context is ISDS context
4420 * @events is automatically reallocated list of event structures
4421 * @xpath_ctx is XPath context with current node as tEventsArray
4422 * In case of error @events will be freed. */
4423 static isds_error
extract_events(struct isds_ctx
*context
,
4424 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4425 isds_error err
= IE_SUCCESS
;
4426 xmlXPathObjectPtr result
= NULL
;
4427 xmlNodePtr events_node
;
4428 struct isds_list
*event
, *prev_event
= NULL
;
4430 if (!context
) return IE_INVALID_CONTEXT
;
4431 if (!events
) return IE_INVAL
;
4432 if (!xpath_ctx
) return IE_INVAL
;
4433 events_node
= xpath_ctx
->node
;
4436 isds_list_free(events
);
4439 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4446 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4447 isds_printf_message(context
,
4448 _("Delivery info does not contain any event"));
4454 /* Iterate over events */
4455 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4457 /* Allocate and append list item */
4458 event
= calloc(1, sizeof(*event
));
4463 event
->destructor
= (void (*)(void **))isds_event_free
;
4464 if (i
== 0) *events
= event
;
4465 else prev_event
->next
= event
;
4469 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4470 err
= extract_event(context
,
4471 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4472 if (err
) goto leave
;
4477 if (err
) isds_list_free(events
);
4478 xmlXPathFreeObject(result
);
4479 xpath_ctx
->node
= events_node
;
4485 /* Insert Base64 encoded data as element with text child.
4486 * @context is session context
4487 * @parent is XML node to append @element with @data as child
4488 * @ns is XML namespace of @element, use NULL to inherit from @parent
4489 * @element is UTF-8 encoded name of new element
4490 * @data is bit stream to encode into @element
4491 * @length is size of @data in bytes
4492 * @return standard error code and fill long error message if needed */
4493 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4494 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4495 const void *data
, size_t length
) {
4496 isds_error err
= IE_SUCCESS
;
4499 if (!context
) return IE_INVALID_CONTEXT
;
4500 if (!data
&& length
> 0) return IE_INVAL
;
4501 if (!parent
|| !element
) return IE_INVAL
;
4503 xmlChar
*base64data
= NULL
;
4504 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4506 isds_printf_message(context
,
4507 ngettext("Not enough memory to encode %zd byte into Base64",
4508 "Not enough memory to encode %zd bytes into Base64",
4514 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4522 /* Convert isds_document structure into XML tree and append to dmFiles node.
4523 * @context is session context
4524 * @document is ISDS document
4525 * @dm_files is XML element the resulting tree will be appended to as a child.
4526 * @return error code, in case of error context' message is filled. */
4527 static isds_error
insert_document(struct isds_ctx
*context
,
4528 struct isds_document
*document
, xmlNodePtr dm_files
) {
4529 isds_error err
= IE_SUCCESS
;
4530 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4531 xmlAttrPtr attribute_node
;
4533 if (!context
) return IE_INVALID_CONTEXT
;
4534 if (!document
|| !dm_files
) return IE_INVAL
;
4536 /* Allocate new dmFile */
4537 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4539 isds_printf_message(context
, _("Could not allocate main dmFile"));
4543 /* Append the new dmFile.
4544 * XXX: Main document must go first */
4545 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4546 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4548 file
= xmlAddChild(dm_files
, new_file
);
4551 xmlFreeNode(new_file
); new_file
= NULL
;
4552 isds_printf_message(context
, _("Could not add dmFile child to "
4553 "%s element"), dm_files
->name
);
4558 /* @dmMimeType is required */
4559 if (!document
->dmMimeType
) {
4560 isds_log_message(context
,
4561 _("Document is missing mandatory MIME type definition"));
4565 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4567 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4569 isds_printf_message(context
,
4570 _("Document has unknown dmFileMetaType: %ld"),
4571 document
->dmFileMetaType
);
4575 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4577 if (document
->dmFileGuid
) {
4578 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4580 if (document
->dmUpFileGuid
) {
4581 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4584 /* @dmFileDescr is required */
4585 if (!document
->dmFileDescr
) {
4586 isds_log_message(context
,
4587 _("Document is missing mandatory description (title)"));
4591 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4593 if (document
->dmFormat
) {
4594 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4598 /* Insert content (body) of the document. */
4599 if (document
->is_xml
) {
4600 /* XML document requested */
4602 /* Allocate new dmXMLContent */
4603 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4605 isds_printf_message(context
,
4606 _("Could not allocate dmXMLContent element"));
4611 node
= xmlAddChild(file
, xmlcontent
);
4613 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4614 isds_printf_message(context
,
4615 _("Could not add dmXMLContent child to %s element"),
4621 /* Copy non-empty node list */
4622 if (document
->xml_node_list
) {
4623 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4624 document
->xml_node_list
);
4626 isds_printf_message(context
,
4627 _("Not enough memory to copy XML document"));
4632 if (!xmlAddChildList(node
, content
)) {
4633 xmlFreeNodeList(content
);
4634 isds_printf_message(context
,
4635 _("Error while adding XML document into dmXMLContent"));
4639 /* XXX: We cannot free the content here because it's part of node's
4640 * document since now. It will be freed with it automatically. */
4643 /* Binary document requested */
4644 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4645 document
->data
, document
->data_length
);
4646 if (err
) goto leave
;
4654 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4655 * The copy must be preallocated, the date are just appended into structure.
4656 * @context is ISDS context
4657 * @copy is message copy structure
4658 * @xpath_ctx is XPath context with current node as tMStatus */
4659 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4660 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4661 isds_error err
= IE_SUCCESS
;
4662 xmlXPathObjectPtr result
= NULL
;
4663 char *code
= NULL
, *message
= NULL
;
4665 if (!context
) return IE_INVALID_CONTEXT
;
4666 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4668 /* Free old values */
4669 zfree(copy
->dmStatus
);
4672 /* Get error specific to this copy */
4673 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4675 isds_log_message(context
,
4676 _("Missing isds:dmStatusCode under "
4677 "XSD:tMStatus type element"));
4682 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4683 /* This copy failed */
4684 copy
->error
= IE_ISDS
;
4685 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4687 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4688 if (!copy
->dmStatus
) {
4689 copy
->dmStatus
= code
;
4693 copy
->dmStatus
= code
;
4697 /* This copy succeeded. In this case only, message ID is valid */
4698 copy
->error
= IE_SUCCESS
;
4700 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4702 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4703 "but did not returned assigned message ID\n"));
4711 xmlXPathFreeObject(result
);
4716 /* Insert struct isds_approval data (box approval) into XML tree
4717 * @context is session context
4718 * @approval is libisds structure with approval description. NULL is
4720 * @parent is XML element to append @approval to */
4721 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4722 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4724 isds_error err
= IE_SUCCESS
;
4727 if (!context
) return IE_INVALID_CONTEXT
;
4728 if (!parent
) return IE_INVAL
;
4730 if (!approval
) return IE_SUCCESS
;
4732 /* Build XSD:gExtApproval */
4733 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4734 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4741 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4743 * @context is session context
4744 * @service_name is name of SERVICE_DB_ACCESS
4745 * @response is reallocated server SOAP body response as XML document
4746 * @raw_response is reallocated bit stream with response body. Use
4747 * NULL if you don't care
4748 * @raw_response_length is size of @raw_response in bytes
4749 * @code is reallocated ISDS status code
4750 * @status_message is reallocated ISDS status message
4751 * @return error coded from lower layer, context message will be set up
4753 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4754 const xmlChar
*service_name
,
4755 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4756 xmlChar
**code
, xmlChar
**status_message
) {
4758 isds_error err
= IE_SUCCESS
;
4759 char *service_name_locale
= NULL
;
4760 xmlNodePtr request
= NULL
, node
;
4761 xmlNsPtr isds_ns
= NULL
;
4763 if (!context
) return IE_INVALID_CONTEXT
;
4764 if (!service_name
) return IE_INVAL
;
4765 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4766 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4768 /* Free output argument */
4769 xmlFreeDoc(*response
); *response
= NULL
;
4770 if (raw_response
) zfree(*raw_response
);
4772 zfree(*status_message
);
4775 /* Check if connection is established
4776 * TODO: This check should be done downstairs. */
4777 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4779 service_name_locale
= _isds_utf82locale((char*)service_name
);
4780 if (!service_name_locale
) {
4786 request
= xmlNewNode(NULL
, service_name
);
4788 isds_printf_message(context
,
4789 _("Could not build %s request"), service_name_locale
);
4793 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4795 isds_log_message(context
, _("Could not create ISDS name space"));
4799 xmlSetNs(request
, isds_ns
);
4802 /* Add XSD:tDummyInput child */
4803 INSERT_STRING(request
, "dbDummy", NULL
);
4806 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4807 service_name_locale
);
4810 err
= _isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4811 raw_response
, raw_response_length
);
4812 xmlFreeNode(request
); request
= NULL
;
4815 isds_log(ILF_ISDS
, ILL_DEBUG
,
4816 _("Processing ISDS response on %s request failed\n"),
4817 service_name_locale
);
4821 /* Check for response status */
4822 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4823 code
, status_message
, NULL
);
4825 isds_log(ILF_ISDS
, ILL_DEBUG
,
4826 _("ISDS response on %s request is missing status\n"),
4827 service_name_locale
);
4831 /* Request processed, but nothing found */
4832 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4833 char *code_locale
= _isds_utf82locale((char*) *code
);
4834 char *status_message_locale
=
4835 _isds_utf82locale((char*) *status_message
);
4836 isds_log(ILF_ISDS
, ILL_DEBUG
,
4837 _("Server refused %s request (code=%s, message=%s)\n"),
4838 service_name_locale
, code_locale
, status_message_locale
);
4839 isds_log_message(context
, status_message_locale
);
4841 free(status_message_locale
);
4847 free(service_name_locale
);
4848 xmlFreeNode(request
);
4854 /* Get data about logged in user and his box. */
4855 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4856 struct isds_DbOwnerInfo
**db_owner_info
) {
4857 isds_error err
= IE_SUCCESS
;
4859 xmlDocPtr response
= NULL
;
4860 xmlChar
*code
= NULL
, *message
= NULL
;
4861 xmlXPathContextPtr xpath_ctx
= NULL
;
4862 xmlXPathObjectPtr result
= NULL
;
4863 char *string
= NULL
;
4866 if (!context
) return IE_INVALID_CONTEXT
;
4867 zfree(context
->long_message
);
4868 if (!db_owner_info
) return IE_INVAL
;
4869 isds_DbOwnerInfo_free(db_owner_info
);
4872 /* Check if connection is established */
4873 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4876 /* Do request and check for success */
4877 err
= build_send_check_dbdummy_request(context
,
4878 BAD_CAST
"GetOwnerInfoFromLogin",
4879 &response
, NULL
, NULL
, &code
, &message
);
4880 if (err
) goto leave
;
4884 /* Prepare structure */
4885 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4886 if (!*db_owner_info
) {
4890 xpath_ctx
= xmlXPathNewContext(response
);
4895 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4900 /* Set context node */
4901 result
= xmlXPathEvalExpression(BAD_CAST
4902 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4907 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4908 isds_log_message(context
, _("Missing dbOwnerInfo element"));
4912 if (result
->nodesetval
->nodeNr
> 1) {
4913 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
4917 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4918 xmlXPathFreeObject(result
); result
= NULL
;
4921 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
4926 isds_DbOwnerInfo_free(db_owner_info
);
4930 xmlXPathFreeObject(result
);
4931 xmlXPathFreeContext(xpath_ctx
);
4935 xmlFreeDoc(response
);
4938 isds_log(ILF_ISDS
, ILL_DEBUG
,
4939 _("GetOwnerInfoFromLogin request processed by server "
4940 "successfully.\n"));
4941 #else /* not HAVE_LIBCURL */
4949 /* Get data about logged in user. */
4950 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
4951 struct isds_DbUserInfo
**db_user_info
) {
4952 isds_error err
= IE_SUCCESS
;
4954 xmlDocPtr response
= NULL
;
4955 xmlChar
*code
= NULL
, *message
= NULL
;
4956 xmlXPathContextPtr xpath_ctx
= NULL
;
4957 xmlXPathObjectPtr result
= NULL
;
4960 if (!context
) return IE_INVALID_CONTEXT
;
4961 zfree(context
->long_message
);
4962 if (!db_user_info
) return IE_INVAL
;
4963 isds_DbUserInfo_free(db_user_info
);
4966 /* Check if connection is established */
4967 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4970 /* Do request and check for success */
4971 err
= build_send_check_dbdummy_request(context
,
4972 BAD_CAST
"GetUserInfoFromLogin",
4973 &response
, NULL
, NULL
, &code
, &message
);
4974 if (err
) goto leave
;
4978 /* Prepare structure */
4979 *db_user_info
= calloc(1, sizeof(**db_user_info
));
4980 if (!*db_user_info
) {
4984 xpath_ctx
= xmlXPathNewContext(response
);
4989 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4994 /* Set context node */
4995 result
= xmlXPathEvalExpression(BAD_CAST
4996 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
5001 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5002 isds_log_message(context
, _("Missing dbUserInfo element"));
5006 if (result
->nodesetval
->nodeNr
> 1) {
5007 isds_log_message(context
, _("Multiple dbUserInfo element"));
5011 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5012 xmlXPathFreeObject(result
); result
= NULL
;
5015 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
5019 isds_DbUserInfo_free(db_user_info
);
5022 xmlXPathFreeObject(result
);
5023 xmlXPathFreeContext(xpath_ctx
);
5027 xmlFreeDoc(response
);
5030 isds_log(ILF_ISDS
, ILL_DEBUG
,
5031 _("GetUserInfoFromLogin request processed by server "
5032 "successfully.\n"));
5033 #else /* not HAVE_LIBCURL */
5041 /* Get expiration time of current password
5042 * @context is session context
5043 * @expiration is automatically reallocated time when password expires. If
5044 * password expiration is disabled, NULL will be returned. In case of error
5045 * it will be nulled too. */
5046 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
5047 struct timeval
**expiration
) {
5048 isds_error err
= IE_SUCCESS
;
5050 xmlDocPtr response
= NULL
;
5051 xmlChar
*code
= NULL
, *message
= NULL
;
5052 xmlXPathContextPtr xpath_ctx
= NULL
;
5053 xmlXPathObjectPtr result
= NULL
;
5054 char *string
= NULL
;
5057 if (!context
) return IE_INVALID_CONTEXT
;
5058 zfree(context
->long_message
);
5059 if (!expiration
) return IE_INVAL
;
5063 /* Check if connection is established */
5064 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5067 /* Do request and check for success */
5068 err
= build_send_check_dbdummy_request(context
,
5069 BAD_CAST
"GetPasswordInfo",
5070 &response
, NULL
, NULL
, &code
, &message
);
5071 if (err
) goto leave
;
5075 xpath_ctx
= xmlXPathNewContext(response
);
5080 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5085 /* Set context node */
5086 result
= xmlXPathEvalExpression(BAD_CAST
5087 "/isds:GetPasswordInfoResponse", xpath_ctx
);
5092 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5093 isds_log_message(context
,
5094 _("Missing GetPasswordInfoResponse element"));
5098 if (result
->nodesetval
->nodeNr
> 1) {
5099 isds_log_message(context
,
5100 _("Multiple GetPasswordInfoResponse element"));
5104 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5105 xmlXPathFreeObject(result
); result
= NULL
;
5107 /* Extract expiration date */
5108 EXTRACT_STRING("isds:pswExpDate", string
);
5110 /* And convert it if any returned. Otherwise expiration is disabled. */
5111 err
= timestring2timeval((xmlChar
*) string
, expiration
);
5113 char *string_locale
= _isds_utf82locale(string
);
5114 if (err
== IE_DATE
) err
= IE_ISDS
;
5115 isds_printf_message(context
,
5116 _("Could not convert pswExpDate as ISO time: %s"),
5118 free(string_locale
);
5131 xmlXPathFreeObject(result
);
5132 xmlXPathFreeContext(xpath_ctx
);
5136 xmlFreeDoc(response
);
5139 isds_log(ILF_ISDS
, ILL_DEBUG
,
5140 _("GetPasswordInfo request processed by server "
5141 "successfully.\n"));
5142 #else /* not HAVE_LIBCURL */
5151 /* Request delivering new TOTP code from ISDS through side channel before
5152 * changing password.
5153 * @context is session context
5154 * @password is current password.
5155 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5156 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5157 * function for more details.
5158 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5159 * NULL, if you don't care.
5160 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5162 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
5163 const char *password
, struct isds_otp
*otp
, char **refnumber
) {
5164 isds_error err
= IE_SUCCESS
;
5165 char *saved_url
= NULL
; /* No copy */
5166 #if HAVE_CURL_REAUTHORIZATION_BUG
5167 CURL
*saved_curl
= NULL
; /* No copy */
5169 xmlNsPtr isds_ns
= NULL
;
5170 xmlNodePtr request
= NULL
;
5171 xmlDocPtr response
= NULL
;
5172 xmlChar
*code
= NULL
, *message
= NULL
;
5173 const xmlChar
*codes
[] = {
5178 const char *meanings
[] = {
5179 N_("Unexpected error"),
5180 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5181 N_("One-time code could not been sent. Try later again.")
5183 const isds_otp_resolution resolutions
[] = {
5184 OTP_RESOLUTION_UNKNOWN
,
5185 OTP_RESOLUTION_TO_FAST
,
5186 OTP_RESOLUTION_TOTP_NOT_SENT
5189 if (NULL
== context
) return IE_INVALID_CONTEXT
;
5190 zfree(context
->long_message
);
5191 if (NULL
== password
) {
5192 isds_log_message(context
,
5193 _("Second argument (password) of isds_change_password() "
5198 /* Check if connection is established
5199 * TODO: This check should be done downstairs. */
5200 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5202 if (!context
->otp
) {
5203 isds_log_message(context
, _("This function requires OTP-authenticated "
5205 return IE_INVALID_CONTEXT
;
5208 isds_log_message(context
, _("If one-time password authentication "
5209 "method is in use, requesting new OTP code requires "
5210 "one-time credentials argument either"));
5213 if (otp
->method
!= OTP_TIME
) {
5214 isds_log_message(context
, _("Requesting new time-based OTP code from "
5215 "server requires one-time password authentication "
5219 if (otp
->otp_code
!= NULL
) {
5220 isds_log_message(context
, _("Requesting new time-based OTP code from "
5221 "server requires undefined OTP code member in "
5222 "one-time credentials argument"));
5228 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
5230 isds_log_message(context
, _("Could not build SendSMSCode request"));
5233 isds_ns
= xmlNewNs(request
, BAD_CAST OISDS_NS
, NULL
);
5235 isds_log_message(context
, _("Could not create ISDS name space"));
5236 xmlFreeNode(request
);
5239 xmlSetNs(request
, isds_ns
);
5241 /* Change URL temporarily for sending this request only */
5243 char *new_url
= NULL
;
5244 if ((err
= _isds_build_url_from_context(context
,
5245 "%1$.*2$sasws/changePassword", &new_url
))) {
5248 saved_url
= context
->url
;
5249 context
->url
= new_url
;
5252 /* Store credentials for sending this request only */
5253 context
->otp_credentials
= otp
;
5254 _isds_discard_credentials(context
, 0);
5255 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5257 _isds_discard_credentials(context
, 0);
5260 #if HAVE_CURL_REAUTHORIZATION_BUG
5261 saved_curl
= context
->curl
;
5262 context
->curl
= curl_easy_init();
5263 if (NULL
== context
->curl
) {
5267 if (context
->timeout
) {
5268 err
= isds_set_timeout(context
, context
->timeout
);
5269 if (err
) goto leave
;
5273 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5276 err
= _isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5278 /* Remove temporal credentials */
5279 _isds_discard_credentials(context
, 0);
5280 /* Detach pointer to OTP credentials from context */
5281 context
->otp_credentials
= NULL
;
5282 /* Keep context->otp true to keep signaling this is OTP session */
5284 /* Destroy request */
5285 xmlFreeNode(request
); request
= NULL
;
5288 isds_log(ILF_ISDS
, ILL_DEBUG
,
5289 _("Processing ISDS response on SendSMSCode request failed\n"));
5293 /* Check for response status */
5294 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5295 &code
, &message
, (xmlChar
**)refnumber
);
5297 isds_log(ILF_ISDS
, ILL_DEBUG
,
5298 _("ISDS response on SendSMSCode request is missing "
5303 /* Check for error */
5304 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5305 char *code_locale
= _isds_utf82locale((char*)code
);
5306 char *message_locale
= _isds_utf82locale((char*)message
);
5308 isds_log(ILF_ISDS
, ILL_DEBUG
,
5309 _("Server refused to send new code on SendSMSCode "
5310 "request (code=%s, message=%s)\n"),
5311 code_locale
, message_locale
);
5313 /* Check for known error codes */
5314 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5315 if (!xmlStrcmp(code
, codes
[i
])) break;
5317 if (i
< sizeof(codes
)/sizeof(*codes
)) {
5318 isds_log_message(context
, _(meanings
[i
]));
5319 /* Mimic otp->resolution according to the code, specification does
5320 * prescribe OTP header to be available. */
5321 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
&&
5322 OTP_RESOLUTION_UNKNOWN
!= resolutions
[i
])
5323 otp
->resolution
= resolutions
[i
];
5325 isds_log_message(context
, message_locale
);
5328 free(message_locale
);
5334 /* Otherwise new code sent successfully */
5335 /* Mimic otp->resolution according to the code, specification does
5336 * prescribe OTP header to be available. */
5337 if (OTP_RESOLUTION_SUCCESS
== otp
->resolution
)
5338 otp
->resolution
= OTP_RESOLUTION_TOTP_SENT
;
5341 if (NULL
!= saved_url
) {
5342 /* Revert URL to original one */
5343 zfree(context
->url
);
5344 context
->url
= saved_url
;
5346 #if HAVE_CURL_REAUTHORIZATION_BUG
5347 if (NULL
!= saved_curl
) {
5348 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5349 context
->curl
= saved_curl
;
5355 xmlFreeDoc(response
);
5356 xmlFreeNode(request
);
5359 isds_log(ILF_ISDS
, ILL_DEBUG
,
5360 _("New OTP code has been sent successfully on SendSMSCode "
5366 /* Convert response status code to isds_error code and set long message
5367 * @context is context to save long message to
5368 * @map is mapping from codes to errors and messages. Pass NULL for generic
5370 * @code is status code to translate
5371 * @message is non-localized status message to put into long message in case
5372 * of uknown error. It can be NULL if server did not provide any.
5373 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5374 * invalid invocation. */
5375 static isds_error
statuscode2isds_error(struct isds_ctx
*context
,
5376 const struct code_map_isds_error
*map
,
5377 const xmlChar
*code
, const xmlChar
*message
) {
5379 isds_log_message(context
,
5380 _("NULL status code passed to statuscode2isds_error()"));
5385 /* Check for known error codes */
5386 for (int i
=0; map
->codes
[i
] != NULL
; i
++) {
5387 if (!xmlStrcmp(code
, map
->codes
[i
])) {
5388 isds_log_message(context
, _(map
->meanings
[i
]));
5389 return map
->errors
[i
];
5395 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5396 char *message_locale
= _isds_utf82locale((char*)message
);
5397 if (NULL
== message_locale
)
5398 isds_log_message(context
, _("ISDS server returned unknown error"));
5400 isds_log_message(context
, message_locale
);
5401 free(message_locale
);
5410 /* Change user password in ISDS.
5411 * User must supply old password, new password will takes effect after some
5412 * time, current session can continue. Password must fulfill some constraints.
5413 * @context is session context
5414 * @old_password is current password.
5415 * @new_password is requested new password
5416 * @otp auxiliary data required if one-time password authentication is in use,
5417 * defines OTP code (if known) and returns fine grade resolution of OTP
5418 * procedure. Pass NULL, if one-time password authentication is not needed.
5419 * Please note the @otp argument must match OTP method used at log-in time. See
5420 * isds_login() function for more details.
5421 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5422 * NULL, if you don't care.
5423 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5424 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5425 * awaiting OTP code that has been delivered by side channel to the user. */
5426 isds_error
isds_change_password(struct isds_ctx
*context
,
5427 const char *old_password
, const char *new_password
,
5428 struct isds_otp
*otp
, char **refnumber
) {
5429 isds_error err
= IE_SUCCESS
;
5431 char *saved_url
= NULL
; /* No copy */
5432 #if HAVE_CURL_REAUTHORIZATION_BUG
5433 CURL
*saved_curl
= NULL
; /* No copy */
5435 xmlNsPtr isds_ns
= NULL
;
5436 xmlNodePtr request
= NULL
, node
;
5437 xmlDocPtr response
= NULL
;
5438 xmlChar
*code
= NULL
, *message
= NULL
;
5439 const xmlChar
*codes
[] = {
5452 const char *meanings
[] = {
5453 N_("Password length must be between 8 and 32 characters"),
5454 N_("Password cannot be reused"), /* Server does not distinguish 1067
5455 and 1091 on ChangePasswordOTP */
5456 N_("Password contains forbidden character"),
5457 N_("Password must contain at least one upper-case letter, "
5458 "one lower-case, and one digit"),
5459 N_("Password cannot contain sequence of three identical characters"),
5460 N_("Password cannot contain user identifier"),
5461 N_("Password is too simmple"),
5462 N_("Old password is not valid"),
5463 N_("Password cannot be reused"),
5464 N_("Unexpected error"),
5465 N_("LDAP update error")
5469 if (!context
) return IE_INVALID_CONTEXT
;
5470 zfree(context
->long_message
);
5471 if (NULL
!= refnumber
)
5473 if (NULL
== old_password
) {
5474 isds_log_message(context
,
5475 _("Second argument (old password) of isds_change_password() "
5479 if (NULL
== otp
&& NULL
== new_password
) {
5480 isds_log_message(context
,
5481 _("Third argument (new password) of isds_change_password() "
5487 /* Check if connection is established
5488 * TODO: This check should be done downstairs. */
5489 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5491 if (context
->otp
&& NULL
== otp
) {
5492 isds_log_message(context
, _("If one-time password authentication "
5493 "method is in use, changing password requires one-time "
5494 "credentials either"));
5498 /* Build ChangeISDSPassword request */
5499 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5500 BAD_CAST
"ChangePasswordOTP");
5502 isds_log_message(context
, (NULL
== otp
) ?
5503 _("Could not build ChangeISDSPassword request") :
5504 _("Could not build ChangePasswordOTP request"));
5507 isds_ns
= xmlNewNs(request
,
5508 (NULL
== otp
) ? BAD_CAST ISDS_NS
: BAD_CAST OISDS_NS
,
5511 isds_log_message(context
, _("Could not create ISDS name space"));
5512 xmlFreeNode(request
);
5515 xmlSetNs(request
, isds_ns
);
5517 INSERT_STRING(request
, "dbOldPassword", old_password
);
5518 INSERT_STRING(request
, "dbNewPassword", new_password
);
5521 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
5522 switch (otp
->method
) {
5524 isds_log(ILF_SEC
, ILL_INFO
,
5525 _("Selected authentication method: "
5526 "HMAC-based one-time password\n"));
5527 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5530 isds_log(ILF_SEC
, ILL_INFO
,
5531 _("Selected authentication method: "
5532 "Time-based one-time password\n"));
5533 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5534 if (otp
->otp_code
== NULL
) {
5535 isds_log(ILF_SEC
, ILL_INFO
,
5536 _("OTP code has not been provided by "
5537 "application, requesting server for "
5539 err
= _isds_request_totp_code(context
, old_password
, otp
,
5541 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5545 isds_log(ILF_SEC
, ILL_INFO
,
5546 _("OTP code has been provided by "
5547 "application, not requesting server "
5552 isds_log_message(context
,
5553 _("Unknown one-time password authentication "
5554 "method requested by application"));
5559 /* Change URL temporarily for sending this request only */
5561 char *new_url
= NULL
;
5562 if ((err
= _isds_build_url_from_context(context
,
5563 "%1$.*2$sasws/changePassword", &new_url
))) {
5566 saved_url
= context
->url
;
5567 context
->url
= new_url
;
5570 /* Store credentials for sending this request only */
5571 context
->otp_credentials
= otp
;
5572 _isds_discard_credentials(context
, 0);
5573 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5574 old_password
, NULL
))) {
5575 _isds_discard_credentials(context
, 0);
5578 #if HAVE_CURL_REAUTHORIZATION_BUG
5579 saved_curl
= context
->curl
;
5580 context
->curl
= curl_easy_init();
5581 if (NULL
== context
->curl
) {
5585 if (context
->timeout
) {
5586 err
= isds_set_timeout(context
, context
->timeout
);
5587 if (err
) goto leave
;
5592 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5593 _("Sending ChangeISDSPassword request to ISDS\n") :
5594 _("Sending ChangePasswordOTP request to ISDS\n"));
5597 err
= _isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5598 request
, &response
, NULL
, NULL
);
5601 /* Remove temporal credentials */
5602 _isds_discard_credentials(context
, 0);
5603 /* Detach pointer to OTP credentials from context */
5604 context
->otp_credentials
= NULL
;
5605 /* Keep context->otp true to keep signaling this is OTP session */
5608 /* Destroy request */
5609 xmlFreeNode(request
); request
= NULL
;
5612 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5613 _("Processing ISDS response on ChangeISDSPassword "
5614 "request failed\n") :
5615 _("Processing ISDS response on ChangePasswordOTP "
5616 "request failed\n"));
5620 /* Check for response status */
5621 err
= isds_response_status(context
,
5622 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5623 &code
, &message
, (xmlChar
**)refnumber
);
5625 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5626 _("ISDS response on ChangeISDSPassword request is missing "
5628 _("ISDS response on ChangePasswordOTP request is missing "
5633 /* Check for known error codes */
5634 for (int i
=0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5635 if (!xmlStrcmp(code
, codes
[i
])) {
5636 char *code_locale
= _isds_utf82locale((char*)code
);
5637 char *message_locale
= _isds_utf82locale((char*)message
);
5638 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5639 _("Server refused to change password on ChangeISDSPassword "
5640 "request (code=%s, message=%s)\n") :
5641 _("Server refused to change password on ChangePasswordOTP "
5642 "request (code=%s, message=%s)\n"),
5643 code_locale
, message_locale
);
5645 free(message_locale
);
5646 isds_log_message(context
, _(meanings
[i
]));
5653 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5654 char *code_locale
= _isds_utf82locale((char*)code
);
5655 char *message_locale
= _isds_utf82locale((char*)message
);
5656 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5657 _("Server refused to change password on ChangeISDSPassword "
5658 "request (code=%s, message=%s)\n") :
5659 _("Server refused to change password on ChangePasswordOTP "
5660 "request (code=%s, message=%s)\n"),
5661 code_locale
, message_locale
);
5662 isds_log_message(context
, message_locale
);
5664 free(message_locale
);
5669 /* Otherwise password changed successfully */
5672 if (NULL
!= saved_url
) {
5673 /* Revert URL to original one */
5674 zfree(context
->url
);
5675 context
->url
= saved_url
;
5677 #if HAVE_CURL_REAUTHORIZATION_BUG
5678 if (NULL
!= saved_curl
) {
5679 if (context
->curl
!= NULL
) curl_easy_cleanup(context
->curl
);
5680 context
->curl
= saved_curl
;
5686 xmlFreeDoc(response
);
5687 xmlFreeNode(request
);
5690 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5691 _("Password changed successfully on ChangeISDSPassword "
5693 _("Password changed successfully on ChangePasswordOTP "
5695 #else /* not HAVE_LIBCURL */
5704 /* Generic middle part with request sending and response check.
5705 * It sends prepared request and checks for error code.
5706 * @context is ISDS session context.
5707 * @service is ISDS service handler
5708 * @service_name is name in scope of given @service
5709 * @request is XML tree with request. Will be freed to save memory.
5710 * @response is XML document outputting ISDS response.
5711 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5712 * @map is mapping from status code to library error. Pass NULL if no special
5713 * handling is requested.
5714 * NULL, if you don't care. */
5715 static isds_error
send_destroy_request_check_response(
5716 struct isds_ctx
*context
,
5717 const isds_service service
, const xmlChar
*service_name
,
5718 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
,
5719 const struct code_map_isds_error
*map
) {
5720 isds_error err
= IE_SUCCESS
;
5721 char *service_name_locale
= NULL
;
5722 xmlChar
*code
= NULL
, *message
= NULL
;
5725 if (!context
) return IE_INVALID_CONTEXT
;
5726 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5730 /* Check if connection is established
5731 * TODO: This check should be done downstairs. */
5732 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5734 service_name_locale
= _isds_utf82locale((char*) service_name
);
5735 if (!service_name_locale
) {
5740 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5741 service_name_locale
);
5744 err
= _isds(context
, service
, *request
, response
, NULL
, NULL
);
5745 xmlFreeNode(*request
); *request
= NULL
;
5748 isds_log(ILF_ISDS
, ILL_DEBUG
,
5749 _("Processing ISDS response on %s request failed\n"),
5750 service_name_locale
);
5754 /* Check for response status */
5755 err
= isds_response_status(context
, service
, *response
,
5756 &code
, &message
, refnumber
);
5758 isds_log(ILF_ISDS
, ILL_DEBUG
,
5759 _("ISDS response on %s request is missing status\n"),
5760 service_name_locale
);
5764 err
= statuscode2isds_error(context
, map
, code
, message
);
5766 /* Request processed, but server failed */
5767 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5768 char *code_locale
= _isds_utf82locale((char*) code
);
5769 char *message_locale
= _isds_utf82locale((char*) message
);
5770 isds_log(ILF_ISDS
, ILL_DEBUG
,
5771 _("Server refused %s request (code=%s, message=%s)\n"),
5772 service_name_locale
, code_locale
, message_locale
);
5774 free(message_locale
);
5782 if (err
&& *response
) {
5783 xmlFreeDoc(*response
);
5787 xmlFreeNode(*request
);
5790 free(service_name_locale
);
5796 /* Generic bottom half with request sending.
5797 * It sends prepared request, checks for error code, destroys response and
5798 * request and log success or failure.
5799 * @context is ISDS session context.
5800 * @service is ISDS service handler
5801 * @service_name is name in scope of given @service
5802 * @request is XML tree with request. Will be freed to save memory.
5803 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5804 * NULL, if you don't care. */
5805 static isds_error
send_request_check_drop_response(
5806 struct isds_ctx
*context
,
5807 const isds_service service
, const xmlChar
*service_name
,
5808 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5809 isds_error err
= IE_SUCCESS
;
5810 xmlDocPtr response
= NULL
;
5813 if (!context
) return IE_INVALID_CONTEXT
;
5814 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5817 /* Send request and check response*/
5818 err
= send_destroy_request_check_response(context
,
5819 service
, service_name
, request
, &response
, refnumber
, NULL
);
5821 xmlFreeDoc(response
);
5824 xmlFreeNode(*request
);
5829 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5830 isds_log(ILF_ISDS
, ILL_DEBUG
,
5831 _("%s request processed by server successfully.\n"),
5832 service_name_locale
);
5833 free(service_name_locale
);
5840 /* Insert isds_credentials_delivery structure into XML request if not NULL
5841 * @context is session context
5842 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5843 * credentials delivery. The email field is passed.
5844 * @parent is XML element where to insert */
5845 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5846 const struct isds_credentials_delivery
*credentials_delivery
,
5847 xmlNodePtr parent
) {
5848 isds_error err
= IE_SUCCESS
;
5851 if (!context
) return IE_INVALID_CONTEXT
;
5852 if (!parent
) return IE_INVAL
;
5854 if (credentials_delivery
) {
5855 /* Following elements are valid only for services:
5856 * NewAccessData, AddDataBoxUser, CreateDataBox */
5857 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5858 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5866 /* Extract credentials delivery from ISDS response.
5867 * @context is session context
5868 * @credentials_delivery is pointer to valid structure to fill in returned
5869 * user's password (and new log-in name). If NULL, do not extract the data.
5870 * @response is pointer to XML document with ISDS response
5871 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5872 * @return IE_SUCCESS even if new user name has not been found because it's not
5873 * clear whether it's returned always. */
5874 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5875 struct isds_credentials_delivery
*credentials_delivery
,
5876 xmlDocPtr response
, const char *request_name
) {
5877 isds_error err
= IE_SUCCESS
;
5878 xmlXPathContextPtr xpath_ctx
= NULL
;
5879 xmlXPathObjectPtr result
= NULL
;
5880 char *xpath_query
= NULL
;
5882 if (!context
) return IE_INVALID_CONTEXT
;
5883 if (credentials_delivery
) {
5884 zfree(credentials_delivery
->token
);
5885 zfree(credentials_delivery
->new_user_name
);
5887 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5890 /* Extract optional token */
5891 if (credentials_delivery
) {
5892 xpath_ctx
= xmlXPathNewContext(response
);
5897 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5902 /* Verify root element */
5903 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5908 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
5913 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5914 char *request_name_locale
= _isds_utf82locale(request_name
);
5915 isds_log(ILF_ISDS
, ILL_WARNING
,
5916 _("Wrong element in ISDS response for %s request "
5917 "while extracting credentials delivery details\n"),
5918 request_name_locale
);
5919 free(request_name_locale
);
5923 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5926 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5928 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
5930 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
5931 if (!credentials_delivery
->token
) {
5932 char *request_name_locale
= _isds_utf82locale(request_name
);
5933 isds_log(ILF_ISDS
, ILL_ERR
,
5934 _("ISDS did not return token on %s request "
5935 "even if requested\n"), request_name_locale
);
5936 free(request_name_locale
);
5943 xmlXPathFreeObject(result
);
5944 xmlXPathFreeContext(xpath_ctx
);
5950 /* Build XSD:tCreateDBInput request type for box creating.
5951 * @context is session context
5952 * @request outputs built XML tree
5953 * @service_name is request name of SERVICE_DB_MANIPULATION service
5954 * @box is box description to create including single primary user (in case of
5956 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5957 * box, or contact address of PFO box owner)
5958 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5959 * @upper_box_id is optional ID of supper box if currently created box is
5961 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5963 * @credentials_delivery is valid pointer if ISDS should return token that box
5964 * owner can use to obtain his new credentials in on-line way. Then valid email
5965 * member value should be supplied.
5966 * @approval is optional external approval of box manipulation */
5967 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
5968 xmlNodePtr
*request
, const xmlChar
*service_name
,
5969 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5970 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
5971 const xmlChar
*ceo_label
,
5972 const struct isds_credentials_delivery
*credentials_delivery
,
5973 const struct isds_approval
*approval
) {
5974 isds_error err
= IE_SUCCESS
;
5975 xmlNsPtr isds_ns
= NULL
;
5976 xmlNodePtr node
, dbPrimaryUsers
;
5977 xmlChar
*string
= NULL
;
5978 const struct isds_list
*item
;
5981 if (!context
) return IE_INVALID_CONTEXT
;
5982 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
5986 /* Build CreateDataBox-similar request */
5987 *request
= xmlNewNode(NULL
, service_name
);
5989 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
5990 isds_printf_message(context
, _("Could build %s request"),
5991 service_name_locale
);
5992 free(service_name_locale
);
5995 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
5996 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
5998 isds_log_message(context
, _("Could not create ISDS1 name space"));
5999 xmlFreeNode(*request
);
6003 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
6005 isds_log_message(context
, _("Could not create ISDS name space"));
6006 xmlFreeNode(*request
);
6010 xmlSetNs(*request
, isds_ns
);
6012 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
6013 err
= insert_DbOwnerInfo(context
, box
, node
);
6014 if (err
) goto leave
;
6017 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6018 * verbose documentation allows none dbUserInfo */
6019 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
6020 for (item
= users
; item
; item
= item
->next
) {
6022 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
6023 err
= insert_DbUserInfo(context
,
6024 (struct isds_DbUserInfo
*) item
->data
, node
);
6025 if (err
) goto leave
;
6029 INSERT_STRING(*request
, "dbFormerNames", former_names
);
6030 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
6031 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
6033 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
6034 if (err
) goto leave
;
6036 err
= insert_GExtApproval(context
, approval
, *request
);
6037 if (err
) goto leave
;
6041 xmlFreeNode(*request
);
6047 #endif /* HAVE_LIBCURL */
6051 * @context is session context
6052 * @box is box description to create including single primary user (in case of
6053 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6054 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6055 * box, or contact address of PFO box owner)
6056 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6057 * @upper_box_id is optional ID of supper box if currently created box is
6059 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6060 * @credentials_delivery is NULL if new password should be delivered off-line
6061 * to box owner. It is valid pointer if owner should obtain new password on-line
6062 * on dedicated web server. Then input @credentials_delivery.email value is
6063 * his e-mail address he must provide to dedicated web server together
6064 * with output reallocated @credentials_delivery.token member. Output
6065 * member @credentials_delivery.new_user_name is unused up on this call.
6066 * @approval is optional external approval of box manipulation
6067 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6068 * NULL, if you don't care.*/
6069 isds_error
isds_add_box(struct isds_ctx
*context
,
6070 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6071 const char *former_names
, const char *upper_box_id
,
6072 const char *ceo_label
,
6073 struct isds_credentials_delivery
*credentials_delivery
,
6074 const struct isds_approval
*approval
, char **refnumber
) {
6075 isds_error err
= IE_SUCCESS
;
6077 xmlNodePtr request
= NULL
;
6078 xmlDocPtr response
= NULL
;
6079 xmlXPathContextPtr xpath_ctx
= NULL
;
6080 xmlXPathObjectPtr result
= NULL
;
6084 if (!context
) return IE_INVALID_CONTEXT
;
6085 zfree(context
->long_message
);
6086 if (credentials_delivery
) {
6087 zfree(credentials_delivery
->token
);
6088 zfree(credentials_delivery
->new_user_name
);
6090 if (!box
) return IE_INVAL
;
6093 /* Scratch box ID */
6096 /* Build CreateDataBox request */
6097 err
= build_CreateDBInput_request(context
,
6098 &request
, BAD_CAST
"CreateDataBox",
6099 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6100 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
6101 if (err
) goto leave
;
6103 /* Send it to server and process response */
6104 err
= send_destroy_request_check_response(context
,
6105 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6106 &response
, (xmlChar
**) refnumber
, NULL
);
6108 /* Extract box ID */
6109 xpath_ctx
= xmlXPathNewContext(response
);
6114 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6118 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
6120 /* Extract optional token */
6121 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6125 xmlXPathFreeObject(result
);
6126 xmlXPathFreeContext(xpath_ctx
);
6127 xmlFreeDoc(response
);
6128 xmlFreeNode(request
);
6131 isds_log(ILF_ISDS
, ILL_DEBUG
,
6132 _("CreateDataBox request processed by server successfully.\n"));
6134 #else /* not HAVE_LIBCURL */
6142 /* Notify ISDS about new PFO entity.
6143 * This function has no real effect.
6144 * @context is session context
6145 * @box is PFO description including single primary user.
6146 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6147 * @former_names is optional undocumented string. Pass NULL if you don't care.
6148 * @upper_box_id is optional ID of supper box if currently created box is
6150 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6151 * @approval is optional external approval of box manipulation
6152 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6153 * NULL, if you don't care.*/
6154 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
6155 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
6156 const char *former_names
, const char *upper_box_id
,
6157 const char *ceo_label
, const struct isds_approval
*approval
,
6159 isds_error err
= IE_SUCCESS
;
6161 xmlNodePtr request
= NULL
;
6164 if (!context
) return IE_INVALID_CONTEXT
;
6165 zfree(context
->long_message
);
6166 if (!box
) return IE_INVAL
;
6169 /* Build CreateDataBoxPFOInfo request */
6170 err
= build_CreateDBInput_request(context
,
6171 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
6172 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
6173 (xmlChar
*) ceo_label
, NULL
, approval
);
6174 if (err
) goto leave
;
6176 /* Send it to server and process response */
6177 err
= send_request_check_drop_response(context
,
6178 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
6179 (xmlChar
**) refnumber
);
6180 /* XXX: XML Schema names output dbID element but textual documentation
6181 * states no box identifier is returned. */
6183 xmlFreeNode(request
);
6184 #else /* not HAVE_LIBCURL */
6191 /* Common implementation for removing given box.
6192 * @context is session context
6193 * @service_name is UTF-8 encoded name fo ISDS service
6194 * @box is box description to delete
6195 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6196 * carry sane value. If NULL, do not inject this information into request.
6197 * @approval is optional external approval of box manipulation
6198 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6199 * NULL, if you don't care.*/
6200 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
6201 const xmlChar
*service_name
,
6202 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6203 const struct isds_approval
*approval
, char **refnumber
) {
6204 isds_error err
= IE_SUCCESS
;
6206 xmlNsPtr isds_ns
= NULL
;
6207 xmlNodePtr request
= NULL
;
6209 xmlChar
*string
= NULL
;
6213 if (!context
) return IE_INVALID_CONTEXT
;
6214 zfree(context
->long_message
);
6215 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
6219 /* Build DeleteDataBox(Promptly) request */
6220 request
= xmlNewNode(NULL
, service_name
);
6222 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
6223 isds_printf_message(context
,
6224 _("Could build %s request"), service_name_locale
);
6225 free(service_name_locale
);
6228 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6230 isds_log_message(context
, _("Could not create ISDS name space"));
6231 xmlFreeNode(request
);
6234 xmlSetNs(request
, isds_ns
);
6236 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6237 err
= insert_DbOwnerInfo(context
, box
, node
);
6238 if (err
) goto leave
;
6241 err
= tm2datestring(since
, &string
);
6243 isds_log_message(context
,
6244 _("Could not convert `since' argument to ISO date string"));
6247 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
6251 err
= insert_GExtApproval(context
, approval
, request
);
6252 if (err
) goto leave
;
6255 /* Send it to server and process response */
6256 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6257 service_name
, &request
, (xmlChar
**) refnumber
);
6260 xmlFreeNode(request
);
6262 #else /* not HAVE_LIBCURL */
6269 /* Remove given box permanently.
6270 * @context is session context
6271 * @box is box description to delete
6272 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6274 * @approval is optional external approval of box manipulation
6275 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6276 * NULL, if you don't care.*/
6277 isds_error
isds_delete_box(struct isds_ctx
*context
,
6278 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
6279 const struct isds_approval
*approval
, char **refnumber
) {
6280 if (!context
) return IE_INVALID_CONTEXT
;
6281 zfree(context
->long_message
);
6282 if (!box
|| !since
) return IE_INVAL
;
6284 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
6285 box
, since
, approval
, refnumber
);
6289 /* Undocumented function.
6290 * @context is session context
6291 * @box is box description to delete
6292 * @approval is optional external approval of box manipulation
6293 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6294 * NULL, if you don't care.*/
6295 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
6296 const struct isds_DbOwnerInfo
*box
,
6297 const struct isds_approval
*approval
, char **refnumber
) {
6298 if (!context
) return IE_INVALID_CONTEXT
;
6299 zfree(context
->long_message
);
6300 if (!box
) return IE_INVAL
;
6302 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
6303 box
, NULL
, approval
, refnumber
);
6307 /* Update data about given box.
6308 * @context is session context
6309 * @old_box current box description
6310 * @new_box are updated data about @old_box
6311 * @approval is optional external approval of box manipulation
6312 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6313 * NULL, if you don't care.*/
6314 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
6315 const struct isds_DbOwnerInfo
*old_box
,
6316 const struct isds_DbOwnerInfo
*new_box
,
6317 const struct isds_approval
*approval
, char **refnumber
) {
6318 isds_error err
= IE_SUCCESS
;
6320 xmlNsPtr isds_ns
= NULL
;
6321 xmlNodePtr request
= NULL
;
6326 if (!context
) return IE_INVALID_CONTEXT
;
6327 zfree(context
->long_message
);
6328 if (!old_box
|| !new_box
) return IE_INVAL
;
6332 /* Build UpdateDataBoxDescr request */
6333 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
6335 isds_log_message(context
,
6336 _("Could build UpdateDataBoxDescr request"));
6339 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6341 isds_log_message(context
, _("Could not create ISDS name space"));
6342 xmlFreeNode(request
);
6345 xmlSetNs(request
, isds_ns
);
6347 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
6348 err
= insert_DbOwnerInfo(context
, old_box
, node
);
6349 if (err
) goto leave
;
6351 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
6352 err
= insert_DbOwnerInfo(context
, new_box
, node
);
6353 if (err
) goto leave
;
6355 err
= insert_GExtApproval(context
, approval
, request
);
6356 if (err
) goto leave
;
6359 /* Send it to server and process response */
6360 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6361 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6364 xmlFreeNode(request
);
6365 #else /* not HAVE_LIBCURL */
6374 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6376 * @context is session context
6377 * @service is SOAP service
6378 * @service_name is name of request in @service
6379 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6380 * @box_id is box ID of interest
6381 * @approval is optional external approval of box manipulation
6382 * @response is server SOAP body response as XML document
6383 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6384 * NULL, if you don't care.
6385 * @return error coded from lower layer, context message will be set up
6387 static isds_error
build_send_dbid_request_check_response(
6388 struct isds_ctx
*context
, const isds_service service
,
6389 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6390 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6391 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6393 isds_error err
= IE_SUCCESS
;
6394 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6395 xmlNodePtr request
= NULL
, node
;
6396 xmlNsPtr isds_ns
= NULL
;
6398 if (!context
) return IE_INVALID_CONTEXT
;
6399 if (!service_name
|| !box_id
) return IE_INVAL
;
6400 if (!response
) return IE_INVAL
;
6402 /* Free output argument */
6403 xmlFreeDoc(*response
); *response
= NULL
;
6405 /* Prepare strings */
6406 service_name_locale
= _isds_utf82locale((char*)service_name
);
6407 if (!service_name_locale
) {
6411 box_id_locale
= _isds_utf82locale((char*)box_id
);
6412 if (!box_id_locale
) {
6418 request
= xmlNewNode(NULL
, service_name
);
6420 isds_printf_message(context
,
6421 _("Could not build %s request for %s box"), service_name_locale
,
6426 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6428 isds_log_message(context
, _("Could not create ISDS name space"));
6432 xmlSetNs(request
, isds_ns
);
6434 /* Add XSD:tIdDbInput children */
6435 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6436 INSERT_STRING(request
, box_id_element
, box_id
);
6437 err
= insert_GExtApproval(context
, approval
, request
);
6438 if (err
) goto leave
;
6440 /* Send request and check response*/
6441 err
= send_destroy_request_check_response(context
,
6442 service
, service_name
, &request
, response
, refnumber
, NULL
);
6445 free(service_name_locale
);
6446 free(box_id_locale
);
6447 xmlFreeNode(request
);
6450 #endif /* HAVE_LIBCURL */
6453 /* Get data about all users assigned to given box.
6454 * @context is session context
6456 * @users is automatically reallocated list of struct isds_DbUserInfo */
6457 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6458 struct isds_list
**users
) {
6459 isds_error err
= IE_SUCCESS
;
6461 xmlDocPtr response
= NULL
;
6462 xmlXPathContextPtr xpath_ctx
= NULL
;
6463 xmlXPathObjectPtr result
= NULL
;
6465 struct isds_list
*item
, *prev_item
= NULL
;
6468 if (!context
) return IE_INVALID_CONTEXT
;
6469 zfree(context
->long_message
);
6470 if (!users
|| !box_id
) return IE_INVAL
;
6471 isds_list_free(users
);
6475 /* Do request and check for success */
6476 err
= build_send_dbid_request_check_response(context
,
6477 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6478 BAD_CAST box_id
, NULL
, &response
, NULL
);
6479 if (err
) goto leave
;
6483 /* Prepare structure */
6484 xpath_ctx
= xmlXPathNewContext(response
);
6489 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6494 /* Set context node */
6495 result
= xmlXPathEvalExpression(BAD_CAST
6496 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6502 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6503 /* Iterate over all users */
6504 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6506 /* Prepare structure */
6507 item
= calloc(1, sizeof(*item
));
6512 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6513 if (i
== 0) *users
= item
;
6514 else prev_item
->next
= item
;
6518 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6519 err
= extract_DbUserInfo(context
,
6520 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6521 if (err
) goto leave
;
6527 isds_list_free(users
);
6530 xmlXPathFreeObject(result
);
6531 xmlXPathFreeContext(xpath_ctx
);
6532 xmlFreeDoc(response
);
6535 isds_log(ILF_ISDS
, ILL_DEBUG
,
6536 _("GetDataBoxUsers request processed by server "
6537 "successfully.\n"));
6538 #else /* not HAVE_LIBCURL */
6546 /* Update data about user assigned to given box.
6547 * @context is session context
6548 * @box is box identification
6549 * @old_user identifies user to update
6550 * @new_user are updated data about @old_user
6551 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6552 * NULL, if you don't care.*/
6553 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6554 const struct isds_DbOwnerInfo
*box
,
6555 const struct isds_DbUserInfo
*old_user
,
6556 const struct isds_DbUserInfo
*new_user
,
6558 isds_error err
= IE_SUCCESS
;
6560 xmlNsPtr isds_ns
= NULL
;
6561 xmlNodePtr request
= NULL
;
6566 if (!context
) return IE_INVALID_CONTEXT
;
6567 zfree(context
->long_message
);
6568 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6572 /* Build UpdateDataBoxUser request */
6573 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6575 isds_log_message(context
,
6576 _("Could build UpdateDataBoxUser request"));
6579 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6581 isds_log_message(context
, _("Could not create ISDS name space"));
6582 xmlFreeNode(request
);
6585 xmlSetNs(request
, isds_ns
);
6587 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6588 err
= insert_DbOwnerInfo(context
, box
, node
);
6589 if (err
) goto leave
;
6591 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6592 err
= insert_DbUserInfo(context
, old_user
, node
);
6593 if (err
) goto leave
;
6595 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6596 err
= insert_DbUserInfo(context
, new_user
, node
);
6597 if (err
) goto leave
;
6599 /* Send it to server and process response */
6600 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6601 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6604 xmlFreeNode(request
);
6605 #else /* not HAVE_LIBCURL */
6613 /* Undocumented function.
6614 * @context is session context
6615 * @box_id is UTF-8 encoded box identifier
6616 * @token is UTF-8 encoded temporary password
6617 * @user_id outputs UTF-8 encoded reallocated user identifier
6618 * @password outpus UTF-8 encoded reallocated user password
6619 * Output arguments will be nulled in case of error */
6620 isds_error
isds_activate(struct isds_ctx
*context
,
6621 const char *box_id
, const char *token
,
6622 char **user_id
, char **password
) {
6623 isds_error err
= IE_SUCCESS
;
6625 xmlNsPtr isds_ns
= NULL
;
6626 xmlNodePtr request
= NULL
, node
;
6627 xmlDocPtr response
= NULL
;
6628 xmlXPathContextPtr xpath_ctx
= NULL
;
6629 xmlXPathObjectPtr result
= NULL
;
6633 if (!context
) return IE_INVALID_CONTEXT
;
6634 zfree(context
->long_message
);
6636 if (user_id
) zfree(*user_id
);
6637 if (password
) zfree(*password
);
6639 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6643 /* Build Activate request */
6644 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6646 isds_log_message(context
, _("Could build Activate request"));
6649 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6651 isds_log_message(context
, _("Could not create ISDS name space"));
6652 xmlFreeNode(request
);
6655 xmlSetNs(request
, isds_ns
);
6657 INSERT_STRING(request
, "dbAccessDataId", token
);
6658 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6659 INSERT_STRING(request
, "dbID", box_id
);
6662 /* Send request and check response*/
6663 err
= send_destroy_request_check_response(context
,
6664 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6665 &response
, NULL
, NULL
);
6666 if (err
) goto leave
;
6670 xpath_ctx
= xmlXPathNewContext(response
);
6675 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6679 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6685 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6686 isds_log_message(context
, _("Missing ActivateResponse element"));
6690 if (result
->nodesetval
->nodeNr
> 1) {
6691 isds_log_message(context
, _("Multiple ActivateResponse element"));
6695 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6696 xmlXPathFreeObject(result
); result
= NULL
;
6698 EXTRACT_STRING("isds:userId", *user_id
);
6700 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6701 "but did not return `userId' element.\n"));
6703 EXTRACT_STRING("isds:password", *password
);
6705 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6706 "but did not return `password' element.\n"));
6709 xmlXPathFreeObject(result
);
6710 xmlXPathFreeContext(xpath_ctx
);
6711 xmlFreeDoc(response
);
6712 xmlFreeNode(request
);
6715 isds_log(ILF_ISDS
, ILL_DEBUG
,
6716 _("Activate request processed by server successfully.\n"));
6717 #else /* not HAVE_LIBCURL */
6725 /* Reset credentials of user assigned to given box.
6726 * @context is session context
6727 * @box is box identification
6728 * @user identifies user to reset password
6729 * @fee_paid is true if fee has been paid, false otherwise
6730 * @approval is optional external approval of box manipulation
6731 * @credentials_delivery is NULL if new password should be delivered off-line
6732 * to the user. It is valid pointer if user should obtain new password on-line
6733 * on dedicated web server. Then input @credentials_delivery.email value is
6734 * user's e-mail address user must provide to dedicated web server together
6735 * with @credentials_delivery.token. The output reallocated token user needs
6736 * to use to authorize on the web server to view his new password. Output
6737 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6738 * ISDS changed up on this call. (No reason why server could change the name
6740 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6741 * NULL, if you don't care.*/
6742 isds_error
isds_reset_password(struct isds_ctx
*context
,
6743 const struct isds_DbOwnerInfo
*box
,
6744 const struct isds_DbUserInfo
*user
,
6745 const _Bool fee_paid
, const struct isds_approval
*approval
,
6746 struct isds_credentials_delivery
*credentials_delivery
,
6748 isds_error err
= IE_SUCCESS
;
6750 xmlNsPtr isds_ns
= NULL
;
6751 xmlNodePtr request
= NULL
, node
;
6752 xmlDocPtr response
= NULL
;
6756 if (!context
) return IE_INVALID_CONTEXT
;
6757 zfree(context
->long_message
);
6759 if (credentials_delivery
) {
6760 zfree(credentials_delivery
->token
);
6761 zfree(credentials_delivery
->new_user_name
);
6763 if (!box
|| !user
) return IE_INVAL
;
6767 /* Build NewAccessData request */
6768 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6770 isds_log_message(context
,
6771 _("Could build NewAccessData request"));
6774 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6776 isds_log_message(context
, _("Could not create ISDS name space"));
6777 xmlFreeNode(request
);
6780 xmlSetNs(request
, isds_ns
);
6782 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6783 err
= insert_DbOwnerInfo(context
, box
, node
);
6784 if (err
) goto leave
;
6786 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6787 err
= insert_DbUserInfo(context
, user
, node
);
6788 if (err
) goto leave
;
6790 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6792 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6793 if (err
) goto leave
;
6795 err
= insert_GExtApproval(context
, approval
, request
);
6796 if (err
) goto leave
;
6798 /* Send request and check response*/
6799 err
= send_destroy_request_check_response(context
,
6800 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6801 &response
, (xmlChar
**) refnumber
, NULL
);
6802 if (err
) goto leave
;
6805 /* Extract optional token */
6806 err
= extract_credentials_delivery(context
, credentials_delivery
,
6807 response
, "NewAccessData");
6810 xmlFreeDoc(response
);
6811 xmlFreeNode(request
);
6814 isds_log(ILF_ISDS
, ILL_DEBUG
,
6815 _("NewAccessData request processed by server "
6816 "successfully.\n"));
6817 #else /* not HAVE_LIBCURL */
6825 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6826 * code, destroy response and log success.
6827 * @context is ISDS session context.
6828 * @service_name is name of SERVICE_DB_MANIPULATION service
6829 * @box is box identification
6830 * @user identifies user to remove
6831 * @credentials_delivery is NULL if new user's password should be delivered
6832 * off-line to the user. It is valid pointer if user should obtain new
6833 * password on-line on dedicated web server. Then input
6834 * @credentials_delivery.email value is user's e-mail address user must
6835 * provide to dedicated web server together with @credentials_delivery.token.
6836 * The output reallocated token user needs to use to authorize on the web
6837 * server to view his new password. Output reallocated
6838 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6839 * assingned or changed up on this call.
6840 * @approval is optional external approval of box manipulation
6841 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6842 * NULL, if you don't care. */
6843 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6844 struct isds_ctx
*context
, const xmlChar
*service_name
,
6845 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6846 struct isds_credentials_delivery
*credentials_delivery
,
6847 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6848 isds_error err
= IE_SUCCESS
;
6850 xmlNsPtr isds_ns
= NULL
;
6851 xmlNodePtr request
= NULL
, node
;
6852 xmlDocPtr response
= NULL
;
6856 if (!context
) return IE_INVALID_CONTEXT
;
6857 zfree(context
->long_message
);
6858 if (credentials_delivery
) {
6859 zfree(credentials_delivery
->token
);
6860 zfree(credentials_delivery
->new_user_name
);
6862 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6867 /* Build NewAccessData or similar request */
6868 request
= xmlNewNode(NULL
, service_name
);
6870 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6871 isds_printf_message(context
, _("Could not build %s request"),
6872 service_name_locale
);
6873 free(service_name_locale
);
6876 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6878 isds_log_message(context
, _("Could not create ISDS name space"));
6879 xmlFreeNode(request
);
6882 xmlSetNs(request
, isds_ns
);
6884 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6885 err
= insert_DbOwnerInfo(context
, box
, node
);
6886 if (err
) goto leave
;
6888 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6889 err
= insert_DbUserInfo(context
, user
, node
);
6890 if (err
) goto leave
;
6892 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6893 if (err
) goto leave
;
6895 err
= insert_GExtApproval(context
, approval
, request
);
6896 if (err
) goto leave
;
6899 /* Send request and check response*/
6900 err
= send_destroy_request_check_response(context
,
6901 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
,
6904 xmlFreeNode(request
);
6907 /* Pick up credentials_delivery if requested */
6908 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6909 (char *)service_name
);
6912 xmlFreeDoc(response
);
6913 if (request
) xmlFreeNode(request
);
6916 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6917 isds_log(ILF_ISDS
, ILL_DEBUG
,
6918 _("%s request processed by server successfully.\n"),
6919 service_name_locale
);
6920 free(service_name_locale
);
6922 #else /* not HAVE_LIBCURL */
6930 /* Assign new user to given box.
6931 * @context is session context
6932 * @box is box identification
6933 * @user defines new user to add
6934 * @credentials_delivery is NULL if new user's password should be delivered
6935 * off-line to the user. It is valid pointer if user should obtain new
6936 * password on-line on dedicated web server. Then input
6937 * @credentials_delivery.email value is user's e-mail address user must
6938 * provide to dedicated web server together with @credentials_delivery.token.
6939 * The output reallocated token user needs to use to authorize on the web
6940 * server to view his new password. Output reallocated
6941 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6942 * assingned up on this call.
6943 * @approval is optional external approval of box manipulation
6944 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6945 * NULL, if you don't care.*/
6946 isds_error
isds_add_user(struct isds_ctx
*context
,
6947 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6948 struct isds_credentials_delivery
*credentials_delivery
,
6949 const struct isds_approval
*approval
, char **refnumber
) {
6950 return build_send_manipulationboxuser_request_check_drop_response(context
,
6951 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
6952 approval
, (xmlChar
**) refnumber
);
6956 /* Remove user assigned to given box.
6957 * @context is session context
6958 * @box is box identification
6959 * @user identifies user to remove
6960 * @approval is optional external approval of box manipulation
6961 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6962 * NULL, if you don't care.*/
6963 isds_error
isds_delete_user(struct isds_ctx
*context
,
6964 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6965 const struct isds_approval
*approval
, char **refnumber
) {
6966 return build_send_manipulationboxuser_request_check_drop_response(context
,
6967 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
6968 (xmlChar
**) refnumber
);
6972 /* Get list of boxes in ZIP archive.
6973 * @context is session context
6974 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6975 * System recognizes following values currently: ALL (all boxes), UPG
6976 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6977 * receiving commercial messages). This argument is a string because
6978 * specification states new values can appear in the future. Not all list
6979 * types are available to all users.
6980 * @buffer is automatically reallocated memory to store the list of boxes. The
6981 * list is zipped CSV file.
6982 * @buffer_length is size of @buffer data in bytes.
6983 * In case of error @buffer will be freed and @buffer_length will be
6985 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
6986 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
6987 isds_error err
= IE_SUCCESS
;
6989 xmlNsPtr isds_ns
= NULL
;
6990 xmlNodePtr request
= NULL
, node
;
6991 xmlDocPtr response
= NULL
;
6992 xmlXPathContextPtr xpath_ctx
= NULL
;
6993 xmlXPathObjectPtr result
= NULL
;
6994 char *string
= NULL
;
6998 if (!context
) return IE_INVALID_CONTEXT
;
6999 zfree(context
->long_message
);
7000 if (buffer
) zfree(*buffer
);
7001 if (!buffer
|| !buffer_length
) return IE_INVAL
;
7005 /* Check if connection is established
7006 * TODO: This check should be done downstairs. */
7007 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7010 /* Build AuthenticateMessage request */
7011 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
7013 isds_log_message(context
,
7014 _("Could not build GetDataBoxList request"));
7017 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7019 isds_log_message(context
, _("Could not create ISDS name space"));
7020 xmlFreeNode(request
);
7023 xmlSetNs(request
, isds_ns
);
7024 INSERT_STRING(request
, "dblType", list_identifier
);
7026 /* Send request to server and process response */
7027 err
= send_destroy_request_check_response(context
,
7028 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
7029 &response
, NULL
, NULL
);
7030 if (err
) goto leave
;
7033 /* Extract Base-64 encoded ZIP file */
7034 xpath_ctx
= xmlXPathNewContext(response
);
7039 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7043 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
7045 /* Decode non-empty archive */
7046 if (string
&& string
[0] != '\0') {
7047 *buffer_length
= _isds_b64decode(string
, buffer
);
7048 if (*buffer_length
== (size_t) -1) {
7049 isds_printf_message(context
,
7050 _("Error while Base64-decoding box list archive"));
7059 xmlXPathFreeObject(result
);
7060 xmlXPathFreeContext(xpath_ctx
);
7061 xmlFreeDoc(response
);
7062 xmlFreeNode(request
);
7065 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
7066 "processed by server successfully.\n"));
7068 #else /* not HAVE_LIBCURL */
7076 /* Find boxes suiting given criteria.
7077 * @criteria is filter. You should fill in at least some members.
7078 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7079 * possibly empty. Input NULL or valid old structure.
7081 * IE_SUCCESS if search succeeded, @boxes contains useful data
7082 * IE_NOEXIST if no such box exists, @boxes will be NULL
7083 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7084 * contains still valid data
7085 * other code if something bad happens. @boxes will be NULL. */
7086 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
7087 const struct isds_DbOwnerInfo
*criteria
,
7088 struct isds_list
**boxes
) {
7089 isds_error err
= IE_SUCCESS
;
7091 _Bool truncated
= 0;
7092 xmlNsPtr isds_ns
= NULL
;
7093 xmlNodePtr request
= NULL
;
7094 xmlDocPtr response
= NULL
;
7095 xmlChar
*code
= NULL
, *message
= NULL
;
7096 xmlNodePtr db_owner_info
;
7097 xmlXPathContextPtr xpath_ctx
= NULL
;
7098 xmlXPathObjectPtr result
= NULL
;
7099 xmlChar
*string
= NULL
;
7103 if (!context
) return IE_INVALID_CONTEXT
;
7104 zfree(context
->long_message
);
7105 if (!boxes
) return IE_INVAL
;
7106 isds_list_free(boxes
);
7113 /* Check if connection is established
7114 * TODO: This check should be done downstairs. */
7115 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7118 /* Build FindDataBox request */
7119 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
7121 isds_log_message(context
,
7122 _("Could build FindDataBox request"));
7125 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7127 isds_log_message(context
, _("Could not create ISDS name space"));
7128 xmlFreeNode(request
);
7131 xmlSetNs(request
, isds_ns
);
7132 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
7133 if (!db_owner_info
) {
7134 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
7135 "FindDataBox element"));
7136 xmlFreeNode(request
);
7140 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
7141 if (err
) goto leave
;
7144 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
7147 err
= _isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
7149 /* Destroy request */
7150 xmlFreeNode(request
); request
= NULL
;
7153 isds_log(ILF_ISDS
, ILL_DEBUG
,
7154 _("Processing ISDS response on FindDataBox "
7155 "request failed\n"));
7159 /* Check for response status */
7160 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
7161 &code
, &message
, NULL
);
7163 isds_log(ILF_ISDS
, ILL_DEBUG
,
7164 _("ISDS response on FindDataBox request is missing status\n"));
7168 /* Request processed, but nothing found */
7169 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
7170 !xmlStrcmp(code
, BAD_CAST
"5001")) {
7171 char *code_locale
= _isds_utf82locale((char*)code
);
7172 char *message_locale
= _isds_utf82locale((char*)message
);
7173 isds_log(ILF_ISDS
, ILL_DEBUG
,
7174 _("Server did not found any box on FindDataBox request "
7175 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7176 isds_log_message(context
, message_locale
);
7178 free(message_locale
);
7183 /* Warning, not a error */
7184 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
7185 char *code_locale
= _isds_utf82locale((char*)code
);
7186 char *message_locale
= _isds_utf82locale((char*)message
);
7187 isds_log(ILF_ISDS
, ILL_DEBUG
,
7188 _("Server truncated response on FindDataBox request "
7189 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7190 isds_log_message(context
, message_locale
);
7192 free(message_locale
);
7197 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7198 char *code_locale
= _isds_utf82locale((char*)code
);
7199 char *message_locale
= _isds_utf82locale((char*)message
);
7200 isds_log(ILF_ISDS
, ILL_DEBUG
,
7201 _("Server refused FindDataBox request "
7202 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7203 isds_log_message(context
, message_locale
);
7205 free(message_locale
);
7210 xpath_ctx
= xmlXPathNewContext(response
);
7215 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7220 /* Extract boxes if they present */
7221 result
= xmlXPathEvalExpression(BAD_CAST
7222 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7228 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7229 struct isds_list
*item
, *prev_item
= NULL
;
7230 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7231 item
= calloc(1, sizeof(*item
));
7237 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
7238 if (i
== 0) *boxes
= item
;
7239 else prev_item
->next
= item
;
7242 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7243 err
= extract_DbOwnerInfo(context
,
7244 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
7245 if (err
) goto leave
;
7251 isds_list_free(boxes
);
7253 if (truncated
) err
= IE_2BIG
;
7257 xmlFreeNode(request
);
7258 xmlXPathFreeObject(result
);
7259 xmlXPathFreeContext(xpath_ctx
);
7263 xmlFreeDoc(response
);
7266 isds_log(ILF_ISDS
, ILL_DEBUG
,
7267 _("FindDataBox request processed by server successfully.\n"));
7268 #else /* not HAVE_LIBCURL */
7276 /* Get status of a box.
7277 * @context is ISDS session context.
7278 * @box_id is UTF-8 encoded box identifier as zero terminated string
7279 * @box_status is return value of box status.
7281 * IE_SUCCESS if box has been found and its status retrieved
7282 * IE_NOEXIST if box is not known to ISDS server
7283 * or other appropriate error.
7284 * You can use isds_DbState to enumerate box status. However out of enum
7285 * range value can be returned too. This is feature because ISDS
7286 * specification leaves the set of values open.
7287 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7288 * the box has been deleted, but ISDS still lists its former existence. */
7289 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
7290 long int *box_status
) {
7291 isds_error err
= IE_SUCCESS
;
7293 xmlNsPtr isds_ns
= NULL
;
7294 xmlNodePtr request
= NULL
, db_id
;
7295 xmlDocPtr response
= NULL
;
7296 xmlXPathContextPtr xpath_ctx
= NULL
;
7297 xmlXPathObjectPtr result
= NULL
;
7298 xmlChar
*string
= NULL
;
7300 const xmlChar
*codes
[] = {
7306 const char *meanings
[] = {
7307 "The box does not exist",
7308 "Box ID is malformed",
7311 const isds_error errors
[] = {
7316 struct code_map_isds_error map
= {
7318 .meanings
= meanings
,
7323 if (!context
) return IE_INVALID_CONTEXT
;
7324 zfree(context
->long_message
);
7325 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
7328 /* Check if connection is established
7329 * TODO: This check should be done downstairs. */
7330 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7333 /* Build CheckDataBox request */
7334 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
7336 isds_log_message(context
,
7337 _("Could build CheckDataBox request"));
7340 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7342 isds_log_message(context
, _("Could not create ISDS name space"));
7343 xmlFreeNode(request
);
7346 xmlSetNs(request
, isds_ns
);
7347 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
7349 isds_log_message(context
, _("Could not add dbID child to "
7350 "CheckDataBox element"));
7351 xmlFreeNode(request
);
7356 /* Send request and check response*/
7357 err
= send_destroy_request_check_response(context
,
7358 SERVICE_DB_SEARCH
, BAD_CAST
"CheckDataBox",
7359 &request
, &response
, NULL
, &map
);
7360 if (err
) goto leave
;
7364 xpath_ctx
= xmlXPathNewContext(response
);
7369 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7373 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7379 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7380 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7384 if (result
->nodesetval
->nodeNr
> 1) {
7385 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7389 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7390 xmlXPathFreeObject(result
); result
= NULL
;
7392 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7397 xmlXPathFreeObject(result
);
7398 xmlXPathFreeContext(xpath_ctx
);
7400 xmlFreeDoc(response
);
7403 isds_log(ILF_ISDS
, ILL_DEBUG
,
7404 _("CheckDataBox request processed by server successfully.\n"));
7405 #else /* not HAVE_LIBCURL */
7413 /* Get list of permissions to send commercial messages.
7414 * @context is ISDS session context.
7415 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7416 * @permissions is a reallocated list of permissions (struct
7417 * isds_commercial_permission*) to send commercial messages from @box_id. The
7418 * order of permissions is significant as the server applies the permissions
7419 * and associated pre-paid credits in the order. Empty list means no
7422 * IE_SUCCESS if the list has been obtained correctly,
7423 * or other appropriate error. */
7424 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7425 const char *box_id
, struct isds_list
**permissions
) {
7426 isds_error err
= IE_SUCCESS
;
7428 xmlDocPtr response
= NULL
;
7429 xmlXPathContextPtr xpath_ctx
= NULL
;
7430 xmlXPathObjectPtr result
= NULL
;
7433 if (!context
) return IE_INVALID_CONTEXT
;
7434 zfree(context
->long_message
);
7435 if (NULL
== permissions
) return IE_INVAL
;
7436 isds_list_free(permissions
);
7437 if (NULL
== box_id
) return IE_INVAL
;
7440 /* Check if connection is established */
7441 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7443 /* Do request and check for success */
7444 err
= build_send_dbid_request_check_response(context
,
7445 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7446 BAD_CAST box_id
, NULL
, &response
, NULL
);
7448 isds_log(ILF_ISDS
, ILL_DEBUG
,
7449 _("PDZInfo request processed by server successfully.\n"));
7453 /* Prepare structure */
7454 xpath_ctx
= xmlXPathNewContext(response
);
7459 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7464 /* Set context node */
7465 result
= xmlXPathEvalExpression(BAD_CAST
7466 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7472 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7473 struct isds_list
*prev_item
= NULL
;
7475 /* Iterate over all permission records */
7476 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7477 struct isds_list
*item
;
7479 /* Prepare structure */
7480 item
= calloc(1, sizeof(*item
));
7485 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
7486 if (i
== 0) *permissions
= item
;
7487 else prev_item
->next
= item
;
7491 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7492 err
= extract_DbPDZRecord(context
,
7493 (struct isds_commercial_permission
**) (&item
->data
),
7495 if (err
) goto leave
;
7501 isds_list_free(permissions
);
7504 xmlXPathFreeObject(result
);
7505 xmlXPathFreeContext(xpath_ctx
);
7506 xmlFreeDoc(response
);
7508 #else /* not HAVE_LIBCURL */
7516 /* Get details about credit for sending pre-paid commercial messages.
7517 * @context is ISDS session context.
7518 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7519 * @from_date is first day of credit history to return in @history. Only
7520 * tm_year, tm_mon and tm_mday carry sane value.
7521 * @to_date is last day of credit history to return in @history. Only
7522 * tm_year, tm_mon and tm_mday carry sane value.
7523 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7524 * if you don't care. This and all other credit values are integers in
7525 * hundredths of Czech Crowns.
7526 * @email outputs notification e-mail address where notifications about credit
7527 * are sent. This is automatically reallocated string. Pass NULL if you don't
7528 * care. It can return NULL if no address is defined.
7529 * @history outputs auto-reallocated list of pointers to struct
7530 * isds_credit_event. Events in closed interval @from_time to @to_time are
7531 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7532 * are sorted by time.
7534 * IE_SUCCESS if the credit details have been obtained correctly,
7535 * or other appropriate error. Please note that server allows to retrieve
7536 * only limited history of events. */
7537 isds_error
isds_get_commercial_credit(struct isds_ctx
*context
,
7539 const struct tm
*from_date
, const struct tm
*to_date
,
7540 long int *credit
, char **email
, struct isds_list
**history
) {
7541 isds_error err
= IE_SUCCESS
;
7543 char *box_id_locale
= NULL
;
7544 xmlNodePtr request
= NULL
, node
;
7545 xmlNsPtr isds_ns
= NULL
;
7546 xmlChar
*string
= NULL
;
7548 xmlDocPtr response
= NULL
;
7549 xmlXPathContextPtr xpath_ctx
= NULL
;
7550 xmlXPathObjectPtr result
= NULL
;
7552 const xmlChar
*codes
[] = {
7560 const char *meanings
[] = {
7561 "Insufficient priviledges for the box",
7562 "The box does not exist",
7563 "Date is too long (history is not available after 15 months)",
7564 "Interval is too long (limit is 3 months)",
7567 const isds_error errors
[] = {
7574 struct code_map_isds_error map
= {
7576 .meanings
= meanings
,
7581 if (!context
) return IE_INVALID_CONTEXT
;
7582 zfree(context
->long_message
);
7584 /* Free output argument */
7585 if (NULL
!= credit
) *credit
= 0;
7586 if (NULL
!= email
) zfree(*email
);
7587 isds_list_free(history
);
7589 if (NULL
== box_id
) return IE_INVAL
;
7592 /* Check if connection is established */
7593 if (NULL
== context
->curl
) return IE_CONNECTION_CLOSED
;
7595 box_id_locale
= _isds_utf82locale((char*)box_id
);
7596 if (NULL
== box_id_locale
) {
7602 request
= xmlNewNode(NULL
, BAD_CAST
"DataBoxCreditInfo");
7603 if (NULL
== request
) {
7604 isds_printf_message(context
,
7605 _("Could not build DataBoxCreditInfo request for %s box"),
7610 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7612 isds_log_message(context
, _("Could not create ISDS name space"));
7616 xmlSetNs(request
, isds_ns
);
7618 /* Add mandatory XSD:tIdDbInput child */
7619 INSERT_STRING(request
, BAD_CAST
"dbID", box_id
);
7620 /* Add mandatory dates elements with optional values */
7622 err
= tm2datestring(from_date
, &string
);
7624 isds_log_message(context
,
7625 _("Could not convert `from_date' argument to ISO date "
7629 INSERT_STRING(request
, "ciFromDate", string
);
7632 INSERT_STRING(request
, "ciFromDate", NULL
);
7635 err
= tm2datestring(to_date
, &string
);
7637 isds_log_message(context
,
7638 _("Could not convert `to_date' argument to ISO date "
7642 INSERT_STRING(request
, "ciTodate", string
);
7645 INSERT_STRING(request
, "ciTodate", NULL
);
7648 /* Send request and check response*/
7649 err
= send_destroy_request_check_response(context
,
7650 SERVICE_DB_SEARCH
, BAD_CAST
"DataBoxCreditInfo",
7651 &request
, &response
, NULL
, &map
);
7652 if (err
) goto leave
;
7656 /* Set context to the root */
7657 xpath_ctx
= xmlXPathNewContext(response
);
7662 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7666 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:DataBoxCreditInfoResponse",
7672 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7673 isds_log_message(context
, _("Missing DataBoxCreditInfoResponse element"));
7677 if (result
->nodesetval
->nodeNr
> 1) {
7678 isds_log_message(context
, _("Multiple DataBoxCreditInfoResponse element"));
7682 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7683 xmlXPathFreeObject(result
); result
= NULL
;
7685 /* Extract common data */
7686 if (NULL
!= credit
) EXTRACT_LONGINT("isds:currentCredit", credit
, 1);
7687 if (NULL
!= email
) EXTRACT_STRING("isds:notifEmail", *email
);
7689 /* Extract records */
7690 if (NULL
== history
) goto leave
;
7691 result
= xmlXPathEvalExpression(BAD_CAST
"isds:ciRecords/isds:ciRecord",
7697 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7698 struct isds_list
*prev_item
= NULL
;
7700 /* Iterate over all records */
7701 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7702 struct isds_list
*item
;
7704 /* Prepare structure */
7705 item
= calloc(1, sizeof(*item
));
7710 item
->destructor
= (void(*)(void**))isds_credit_event_free
;
7711 if (i
== 0) *history
= item
;
7712 else prev_item
->next
= item
;
7716 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7717 err
= extract_CiRecord(context
,
7718 (struct isds_credit_event
**) (&item
->data
),
7720 if (err
) goto leave
;
7726 isds_log(ILF_ISDS
, ILL_DEBUG
,
7727 _("DataBoxCreditInfo request processed by server successfully.\n"));
7730 isds_list_free(history
);
7731 if (NULL
!= email
) zfree(*email
)
7734 free(box_id_locale
);
7735 xmlXPathFreeObject(result
);
7736 xmlXPathFreeContext(xpath_ctx
);
7737 xmlFreeDoc(response
);
7739 #else /* not HAVE_LIBCURL */
7747 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7748 * code, destroy response and log success.
7749 * @context is ISDS session context.
7750 * @service_name is name of SERVICE_DB_MANIPULATION service
7751 * @box_id is UTF-8 encoded box identifier as zero terminated string
7752 * @approval is optional external approval of box manipulation
7753 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7754 * NULL, if you don't care. */
7755 static isds_error
build_send_manipulationdbid_request_check_drop_response(
7756 struct isds_ctx
*context
, const xmlChar
*service_name
,
7757 const xmlChar
*box_id
, const struct isds_approval
*approval
,
7758 xmlChar
**refnumber
) {
7759 isds_error err
= IE_SUCCESS
;
7761 xmlDocPtr response
= NULL
;
7764 if (!context
) return IE_INVALID_CONTEXT
;
7765 zfree(context
->long_message
);
7766 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
7769 /* Check if connection is established */
7770 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7772 /* Do request and check for success */
7773 err
= build_send_dbid_request_check_response(context
,
7774 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
7775 &response
, refnumber
);
7776 xmlFreeDoc(response
);
7779 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7780 isds_log(ILF_ISDS
, ILL_DEBUG
,
7781 _("%s request processed by server successfully.\n"),
7782 service_name_locale
);
7783 free(service_name_locale
);
7785 #else /* not HAVE_LIBCURL */
7793 /* Switch box into state where box can receive commercial messages (off by
7795 * @context is ISDS session context.
7796 * @box_id is UTF-8 encoded box identifier as zero terminated string
7797 * @allow is true for enable, false for disable commercial messages income
7798 * @approval is optional external approval of box manipulation
7799 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7800 * NULL, if you don't care. */
7801 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
7802 const char *box_id
, const _Bool allow
,
7803 const struct isds_approval
*approval
, char **refnumber
) {
7804 return build_send_manipulationdbid_request_check_drop_response(context
,
7805 (allow
) ? BAD_CAST
"SetOpenAddressing" :
7806 BAD_CAST
"ClearOpenAddressing",
7807 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7811 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7812 * message acceptance). This is just a box permission. Sender must apply
7813 * such role by sending each message.
7814 * @context is ISDS session context.
7815 * @box_id is UTF-8 encoded box identifier as zero terminated string
7816 * @allow is true for enable, false for disable OVM role permission
7817 * @approval is optional external approval of box manipulation
7818 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7819 * NULL, if you don't care. */
7820 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
7821 const char *box_id
, const _Bool allow
,
7822 const struct isds_approval
*approval
, char **refnumber
) {
7823 return build_send_manipulationdbid_request_check_drop_response(context
,
7824 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
7825 BAD_CAST
"ClearEffectiveOVM",
7826 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7830 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7831 * code, destroy response and log success.
7832 * @context is ISDS session context.
7833 * @service_name is name of SERVICE_DB_MANIPULATION service
7834 * @owner is structure describing box
7835 * @approval is optional external approval of box manipulation
7836 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7837 * NULL, if you don't care. */
7838 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
7839 struct isds_ctx
*context
, const xmlChar
*service_name
,
7840 const struct isds_DbOwnerInfo
*owner
,
7841 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7842 isds_error err
= IE_SUCCESS
;
7844 char *service_name_locale
= NULL
;
7845 xmlNodePtr request
= NULL
, db_owner_info
;
7846 xmlNsPtr isds_ns
= NULL
;
7850 if (!context
) return IE_INVALID_CONTEXT
;
7851 zfree(context
->long_message
);
7852 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
7855 service_name_locale
= _isds_utf82locale((char*)service_name
);
7856 if (!service_name_locale
) {
7862 request
= xmlNewNode(NULL
, service_name
);
7864 isds_printf_message(context
,
7865 _("Could not build %s request"), service_name_locale
);
7869 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7871 isds_log_message(context
, _("Could not create ISDS name space"));
7875 xmlSetNs(request
, isds_ns
);
7878 /* Add XSD:tOwnerInfoInput child*/
7879 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
7880 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
7881 if (err
) goto leave
;
7883 /* Add XSD:gExtApproval*/
7884 err
= insert_GExtApproval(context
, approval
, request
);
7885 if (err
) goto leave
;
7887 /* Send it to server and process response */
7888 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7889 service_name
, &request
, refnumber
);
7892 xmlFreeNode(request
);
7893 free(service_name_locale
);
7894 #else /* not HAVE_LIBCURL */
7902 /* Switch box accessibility state on request of box owner.
7903 * Despite the name, owner must do the request off-line. This function is
7904 * designed for such off-line meeting points (e.g. Czech POINT).
7905 * @context is ISDS session context.
7906 * @box identifies box to switch accessibility state.
7907 * @allow is true for making accessible, false to disallow access.
7908 * @approval is optional external approval of box manipulation
7909 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7910 * NULL, if you don't care. */
7911 isds_error
isds_switch_box_accessibility_on_owner_request(
7912 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7913 const _Bool allow
, const struct isds_approval
*approval
,
7915 return build_send_manipulationdbowner_request_check_drop_response(context
,
7916 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
7917 BAD_CAST
"DisableOwnDataBox",
7918 box
, approval
, (xmlChar
**) refnumber
);
7922 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7924 * @context is ISDS session context.
7925 * @box identifies box to switch accessibility state.
7926 * @since is date since accessibility has been denied. This can be past too.
7927 * Only tm_year, tm_mon and tm_mday carry sane value.
7928 * @approval is optional external approval of box manipulation
7929 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7930 * NULL, if you don't care. */
7931 isds_error
isds_disable_box_accessibility_externaly(
7932 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7933 const struct tm
*since
, const struct isds_approval
*approval
,
7935 isds_error err
= IE_SUCCESS
;
7937 char *service_name_locale
= NULL
;
7938 xmlNodePtr request
= NULL
, node
;
7939 xmlNsPtr isds_ns
= NULL
;
7940 xmlChar
*string
= NULL
;
7944 if (!context
) return IE_INVALID_CONTEXT
;
7945 zfree(context
->long_message
);
7946 if (!box
|| !since
) return IE_INVAL
;
7950 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
7952 isds_printf_message(context
,
7953 _("Could not build %s request"), "DisableDataBoxExternally");
7957 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7959 isds_log_message(context
, _("Could not create ISDS name space"));
7963 xmlSetNs(request
, isds_ns
);
7966 /* Add @box identification */
7967 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7968 err
= insert_DbOwnerInfo(context
, box
, node
);
7969 if (err
) goto leave
;
7971 /* Add @since date */
7972 err
= tm2datestring(since
, &string
);
7974 isds_log_message(context
,
7975 _("Could not convert `since' argument to ISO date string"));
7978 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
7982 err
= insert_GExtApproval(context
, approval
, request
);
7983 if (err
) goto leave
;
7985 /* Send it to server and process response */
7986 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7987 BAD_CAST
"DisableDataBoxExternally", &request
,
7988 (xmlChar
**) refnumber
);
7992 xmlFreeNode(request
);
7993 free(service_name_locale
);
7994 #else /* not HAVE_LIBCURL */
8003 /* Insert struct isds_message data (envelope (recipient data optional) and
8004 * documents into XML tree
8005 * @context is session context
8006 * @outgoing_message is libisds structure with message data
8007 * @create_message is XML CreateMessage or CreateMultipleMessage element
8008 * @process_recipient true for recipient data serialization, false for no
8010 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
8011 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
8012 const _Bool process_recipient
) {
8014 isds_error err
= IE_SUCCESS
;
8015 xmlNodePtr envelope
, dm_files
, node
;
8016 xmlChar
*string
= NULL
;
8018 if (!context
) return IE_INVALID_CONTEXT
;
8019 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
8022 /* Build envelope */
8023 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
8025 isds_printf_message(context
, _("Could not add dmEnvelope child to "
8026 "%s element"), create_message
->name
);
8030 if (!outgoing_message
->envelope
) {
8031 isds_log_message(context
, _("Outgoing message is missing envelope"));
8036 /* Insert optional message type */
8037 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
8039 if (err
) goto leave
;
8041 INSERT_STRING(envelope
, "dmSenderOrgUnit",
8042 outgoing_message
->envelope
->dmSenderOrgUnit
);
8043 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
8044 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
8046 if (process_recipient
) {
8047 if (!outgoing_message
->envelope
->dbIDRecipient
) {
8048 isds_log_message(context
,
8049 _("Outgoing message is missing recipient box identifier"));
8053 INSERT_STRING(envelope
, "dbIDRecipient",
8054 outgoing_message
->envelope
->dbIDRecipient
);
8056 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
8057 outgoing_message
->envelope
->dmRecipientOrgUnit
);
8058 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
8059 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
8060 INSERT_STRING(envelope
, "dmToHands",
8061 outgoing_message
->envelope
->dmToHands
);
8064 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
8066 INSERT_STRING(envelope
, "dmAnnotation",
8067 outgoing_message
->envelope
->dmAnnotation
);
8069 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
8070 0, 50, "dmRecipientRefNumber");
8071 INSERT_STRING(envelope
, "dmRecipientRefNumber",
8072 outgoing_message
->envelope
->dmRecipientRefNumber
);
8074 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
8075 0, 50, "dmSenderRefNumber");
8076 INSERT_STRING(envelope
, "dmSenderRefNumber",
8077 outgoing_message
->envelope
->dmSenderRefNumber
);
8079 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
8080 0, 50, "dmRecipientIdent");
8081 INSERT_STRING(envelope
, "dmRecipientIdent",
8082 outgoing_message
->envelope
->dmRecipientIdent
);
8084 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
8085 0, 50, "dmSenderIdent");
8086 INSERT_STRING(envelope
, "dmSenderIdent",
8087 outgoing_message
->envelope
->dmSenderIdent
);
8089 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
8090 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
8091 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
8092 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
8093 INSERT_STRING(envelope
, "dmLegalTitleSect",
8094 outgoing_message
->envelope
->dmLegalTitleSect
);
8095 INSERT_STRING(envelope
, "dmLegalTitlePar",
8096 outgoing_message
->envelope
->dmLegalTitlePar
);
8097 INSERT_STRING(envelope
, "dmLegalTitlePoint",
8098 outgoing_message
->envelope
->dmLegalTitlePoint
);
8100 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
8101 outgoing_message
->envelope
->dmPersonalDelivery
);
8102 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
8103 outgoing_message
->envelope
->dmAllowSubstDelivery
);
8105 /* ???: Should we require value for dbEffectiveOVM sender?
8106 * ISDS has default as true */
8107 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
8108 INSERT_BOOLEAN(envelope
, "dmOVM",
8109 outgoing_message
->envelope
->dmPublishOwnID
);
8112 /* Append dmFiles */
8113 if (!outgoing_message
->documents
) {
8114 isds_log_message(context
,
8115 _("Outgoing message is missing list of documents"));
8119 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
8121 isds_printf_message(context
, _("Could not add dmFiles child to "
8122 "%s element"), create_message
->name
);
8127 /* Check for document hierarchy */
8128 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
8129 if (err
) goto leave
;
8131 /* Process each document */
8132 for (struct isds_list
*item
=
8133 (struct isds_list
*) outgoing_message
->documents
;
8134 item
; item
= item
->next
) {
8136 isds_log_message(context
,
8137 _("List of documents contains empty item"));
8141 /* FIXME: Check for dmFileMetaType and for document references.
8142 * Only first document can be of MAIN type */
8143 err
= insert_document(context
, (struct isds_document
*) item
->data
,
8146 if (err
) goto leave
;
8153 #endif /* HAVE_LIBCURL */
8156 /* Send a message via ISDS to a recipient
8157 * @context is session context
8158 * @outgoing_message is message to send; Some members are mandatory (like
8159 * dbIDRecipient), some are optional and some are irrelevant (especially data
8160 * about sender). Included pointer to isds_list documents must contain at
8161 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8162 * members will be filled with valid data from ISDS. Exact list of write
8163 * members is subject to change. Currently dmID is changed.
8164 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8165 isds_error
isds_send_message(struct isds_ctx
*context
,
8166 struct isds_message
*outgoing_message
) {
8168 isds_error err
= IE_SUCCESS
;
8170 xmlNsPtr isds_ns
= NULL
;
8171 xmlNodePtr request
= NULL
;
8172 xmlDocPtr response
= NULL
;
8173 xmlChar
*code
= NULL
, *message
= NULL
;
8174 xmlXPathContextPtr xpath_ctx
= NULL
;
8175 xmlXPathObjectPtr result
= NULL
;
8176 /*_Bool message_is_complete = 0;*/
8179 if (!context
) return IE_INVALID_CONTEXT
;
8180 zfree(context
->long_message
);
8181 if (!outgoing_message
) return IE_INVAL
;
8184 /* Check if connection is established
8185 * TODO: This check should be done downstairs. */
8186 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8189 /* Build CreateMessage request */
8190 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
8192 isds_log_message(context
,
8193 _("Could not build CreateMessage request"));
8196 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8198 isds_log_message(context
, _("Could not create ISDS name space"));
8199 xmlFreeNode(request
);
8202 xmlSetNs(request
, isds_ns
);
8204 /* Append envelope and files */
8205 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
8206 if (err
) goto leave
;
8209 /* Signal we can serialize message since now */
8210 /*message_is_complete = 1;*/
8213 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
8216 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8218 /* Don't' destroy request, we want to provide it to application later */
8221 isds_log(ILF_ISDS
, ILL_DEBUG
,
8222 _("Processing ISDS response on CreateMessage "
8223 "request failed\n"));
8227 /* Check for response status */
8228 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8229 &code
, &message
, NULL
);
8231 isds_log(ILF_ISDS
, ILL_DEBUG
,
8232 _("ISDS response on CreateMessage request "
8233 "is missing status\n"));
8237 /* Request processed, but refused by server or server failed */
8238 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8239 char *box_id_locale
=
8240 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8241 char *code_locale
= _isds_utf82locale((char*)code
);
8242 char *message_locale
= _isds_utf82locale((char*)message
);
8243 isds_log(ILF_ISDS
, ILL_DEBUG
,
8244 _("Server did not accept message for %s on CreateMessage "
8245 "request (code=%s, message=%s)\n"),
8246 box_id_locale
, code_locale
, message_locale
);
8247 isds_log_message(context
, message_locale
);
8248 free(box_id_locale
);
8250 free(message_locale
);
8257 xpath_ctx
= xmlXPathNewContext(response
);
8262 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8266 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
8272 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8273 isds_log_message(context
, _("Missing CreateMessageResponse element"));
8277 if (result
->nodesetval
->nodeNr
> 1) {
8278 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
8282 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8283 xmlXPathFreeObject(result
); result
= NULL
;
8285 if (outgoing_message
->envelope
->dmID
) {
8286 free(outgoing_message
->envelope
->dmID
);
8287 outgoing_message
->envelope
->dmID
= NULL
;
8289 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
8290 if (!outgoing_message
->envelope
->dmID
) {
8291 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
8292 "but did not return assigned message ID\n"));
8296 /* TODO: Serialize message into structure member raw */
8297 /* XXX: Each web service transport message in different format.
8298 * Therefore it's not possible to save them directly.
8299 * To save them, one must figure out common format.
8300 * We can leave it on application, or we can implement the ESS format. */
8301 /*if (message_is_complete) {
8302 if (outgoing_message->envelope->dmID) {
8304 /* Add assigned message ID as first child*/
8305 /*xmlNodePtr dmid_text = xmlNewText(
8306 (xmlChar *) outgoing_message->envelope->dmID);
8307 if (!dmid_text) goto serialization_failed;
8309 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8311 if (!dmid_element) {
8312 xmlFreeNode(dmid_text);
8313 goto serialization_failed;
8316 xmlNodePtr dmid_element_with_text =
8317 xmlAddChild(dmid_element, dmid_text);
8318 if (!dmid_element_with_text) {
8319 xmlFreeNode(dmid_element);
8320 xmlFreeNode(dmid_text);
8321 goto serialization_failed;
8324 node = xmlAddPrevSibling(envelope->childern,
8325 dmid_element_with_text);
8327 xmlFreeNodeList(dmid_element_with_text);
8328 goto serialization_failed;
8332 /* Serialize message with ID into raw */
8333 /*buffer = serialize_element(envelope)*/
8336 serialization_failed:
8341 xmlXPathFreeObject(result
);
8342 xmlXPathFreeContext(xpath_ctx
);
8346 xmlFreeDoc(response
);
8347 xmlFreeNode(request
);
8350 isds_log(ILF_ISDS
, ILL_DEBUG
,
8351 _("CreateMessage request processed by server "
8352 "successfully.\n"));
8353 #else /* not HAVE_LIBCURL */
8361 /* Send a message via ISDS to a multiple recipients
8362 * @context is session context
8363 * @outgoing_message is message to send; Some members are mandatory,
8364 * some are optional and some are irrelevant (especially data
8365 * about sender). Data about recipient will be substituted by ISDS from
8366 * @copies. Included pointer to isds_list documents must
8367 * contain at least one document of FILEMETATYPE_MAIN.
8368 * @copies is list of isds_message_copy structures addressing all desired
8369 * recipients. This is read-write structure, some members will be filled with
8370 * valid data from ISDS (message IDs, error codes, error descriptions).
8372 * ISDS_SUCCESS if all messages have been sent
8373 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8374 * succeeded messages can be identified by copies->data->error),
8375 * or other error code if something other goes wrong. */
8376 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
8377 const struct isds_message
*outgoing_message
,
8378 struct isds_list
*copies
) {
8380 isds_error err
= IE_SUCCESS
;
8382 isds_error append_err
;
8383 xmlNsPtr isds_ns
= NULL
;
8384 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
8385 struct isds_list
*item
;
8386 struct isds_message_copy
*copy
;
8387 xmlDocPtr response
= NULL
;
8388 xmlChar
*code
= NULL
, *message
= NULL
;
8389 xmlXPathContextPtr xpath_ctx
= NULL
;
8390 xmlXPathObjectPtr result
= NULL
;
8391 xmlChar
*string
= NULL
;
8395 if (!context
) return IE_INVALID_CONTEXT
;
8396 zfree(context
->long_message
);
8397 if (!outgoing_message
|| !copies
) return IE_INVAL
;
8400 /* Check if connection is established
8401 * TODO: This check should be done downstairs. */
8402 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8405 /* Build CreateMultipleMessage request */
8406 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
8408 isds_log_message(context
,
8409 _("Could not build CreateMultipleMessage request"));
8412 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8414 isds_log_message(context
, _("Could not create ISDS name space"));
8415 xmlFreeNode(request
);
8418 xmlSetNs(request
, isds_ns
);
8421 /* Build recipients */
8422 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
8424 isds_log_message(context
, _("Could not add dmRecipients child to "
8425 "CreateMultipleMessage element"));
8426 xmlFreeNode(request
);
8430 /* Insert each recipient */
8431 for (item
= copies
; item
; item
= item
->next
) {
8432 copy
= (struct isds_message_copy
*) item
->data
;
8434 isds_log_message(context
,
8435 _("`copies' list item contains empty data"));
8440 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
8442 isds_log_message(context
, _("Could not add dmRecipient child to "
8443 "dmRecipients element"));
8448 if (!copy
->dbIDRecipient
) {
8449 isds_log_message(context
,
8450 _("Message copy is missing recipient box identifier"));
8454 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
8455 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
8456 copy
->dmRecipientOrgUnit
);
8457 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
8458 copy
->dmRecipientOrgUnitNum
, string
);
8459 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
8462 /* Append envelope and files */
8463 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
8464 if (err
) goto leave
;
8467 isds_log(ILF_ISDS
, ILL_DEBUG
,
8468 _("Sending CreateMultipleMessage request to ISDS\n"));
8471 err
= _isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8473 isds_log(ILF_ISDS
, ILL_DEBUG
,
8474 _("Processing ISDS response on CreateMultipleMessage "
8475 "request failed\n"));
8479 /* Check for response status */
8480 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8481 &code
, &message
, NULL
);
8483 isds_log(ILF_ISDS
, ILL_DEBUG
,
8484 _("ISDS response on CreateMultipleMessage request "
8485 "is missing status\n"));
8489 /* Request processed, but some copies failed */
8490 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
8491 char *box_id_locale
=
8492 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8493 char *code_locale
= _isds_utf82locale((char*)code
);
8494 char *message_locale
= _isds_utf82locale((char*)message
);
8495 isds_log(ILF_ISDS
, ILL_DEBUG
,
8496 _("Server did accept message for multiple recipients "
8497 "on CreateMultipleMessage request but delivery to "
8498 "some of them failed (code=%s, message=%s)\n"),
8499 box_id_locale
, code_locale
, message_locale
);
8500 isds_log_message(context
, message_locale
);
8501 free(box_id_locale
);
8503 free(message_locale
);
8504 err
= IE_PARTIAL_SUCCESS
;
8507 /* Request refused by server as whole */
8508 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8509 char *box_id_locale
=
8510 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
8511 char *code_locale
= _isds_utf82locale((char*)code
);
8512 char *message_locale
= _isds_utf82locale((char*)message
);
8513 isds_log(ILF_ISDS
, ILL_DEBUG
,
8514 _("Server did not accept message for multiple recipients "
8515 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8516 box_id_locale
, code_locale
, message_locale
);
8517 isds_log_message(context
, message_locale
);
8518 free(box_id_locale
);
8520 free(message_locale
);
8527 xpath_ctx
= xmlXPathNewContext(response
);
8532 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8536 result
= xmlXPathEvalExpression(
8537 BAD_CAST
"/isds:CreateMultipleMessageResponse"
8538 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8544 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8545 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
8550 /* Extract message ID and delivery status for each copy */
8551 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
8552 item
= item
->next
, i
++) {
8553 copy
= (struct isds_message_copy
*) item
->data
;
8554 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
8556 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
8562 if (item
|| i
< result
->nodesetval
->nodeNr
) {
8563 isds_printf_message(context
, _("ISDS returned unexpected number of "
8564 "message copy delivery states: %d"),
8565 result
->nodesetval
->nodeNr
);
8574 xmlXPathFreeObject(result
);
8575 xmlXPathFreeContext(xpath_ctx
);
8579 xmlFreeDoc(response
);
8580 xmlFreeNode(request
);
8583 isds_log(ILF_ISDS
, ILL_DEBUG
,
8584 _("CreateMultipleMessageResponse request processed by server "
8585 "successfully.\n"));
8586 #else /* not HAVE_LIBCURL */
8594 /* Get list of messages. This is common core for getting sent or received
8596 * Any criterion argument can be NULL, if you don't care about it.
8597 * @context is session context. Must not be NULL.
8598 * @outgoing_direction is true if you want list of outgoing messages,
8599 * it's false if you want incoming messages.
8600 * @from_time is minimal time and date of message sending inclusive.
8601 * @to_time is maximal time and date of message sending inclusive
8602 * @organization_unit_number is number of sender/recipient respectively.
8603 * @status_filter is bit field of isds_message_status values. Use special
8604 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8605 * all values, you can use bit-wise arithmetic if you want.)
8606 * @offset is index of first message we are interested in. First message is 1.
8607 * Set to 0 (or 1) if you don't care.
8608 * @number is maximal length of list you want to get as input value, outputs
8609 * number of messages matching these criteria. Can be NULL if you don't care
8610 * (applies to output value either).
8611 * @messages is automatically reallocated list of isds_message's. Be ware that
8612 * it returns only brief overview (envelope and some other fields) about each
8613 * message, not the complete message. FIXME: Specify exact fields.
8614 * The list is sorted by delivery time in ascending order.
8615 * Use NULL if you don't care about don't need the data (useful if you want to
8616 * know only the @number). If you provide &NULL, list will be allocated on
8617 * heap, if you provide pointer to non-NULL, list will be freed automatically
8618 * at first. Also in case of error the list will be NULLed.
8619 * @return IE_SUCCESS or appropriate error code. */
8620 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
8621 _Bool outgoing_direction
,
8622 const struct timeval
*from_time
, const struct timeval
*to_time
,
8623 const long int *organization_unit_number
,
8624 const unsigned int status_filter
,
8625 const unsigned long int offset
, unsigned long int *number
,
8626 struct isds_list
**messages
) {
8628 isds_error err
= IE_SUCCESS
;
8630 xmlNsPtr isds_ns
= NULL
;
8631 xmlNodePtr request
= NULL
, node
;
8632 xmlDocPtr response
= NULL
;
8633 xmlChar
*code
= NULL
, *message
= NULL
;
8634 xmlXPathContextPtr xpath_ctx
= NULL
;
8635 xmlXPathObjectPtr result
= NULL
;
8636 xmlChar
*string
= NULL
;
8637 long unsigned int count
= 0;
8640 if (!context
) return IE_INVALID_CONTEXT
;
8641 zfree(context
->long_message
);
8643 /* Free former message list if any */
8644 if (messages
) isds_list_free(messages
);
8647 /* Check if connection is established
8648 * TODO: This check should be done downstairs. */
8649 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8651 /* Build GetListOf*Messages request */
8652 request
= xmlNewNode(NULL
,
8653 (outgoing_direction
) ?
8654 BAD_CAST
"GetListOfSentMessages" :
8655 BAD_CAST
"GetListOfReceivedMessages"
8658 isds_log_message(context
,
8659 (outgoing_direction
) ?
8660 _("Could not build GetListOfSentMessages request") :
8661 _("Could not build GetListOfReceivedMessages request")
8665 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8667 isds_log_message(context
, _("Could not create ISDS name space"));
8668 xmlFreeNode(request
);
8671 xmlSetNs(request
, isds_ns
);
8675 err
= timeval2timestring(from_time
, &string
);
8676 if (err
) goto leave
;
8678 INSERT_STRING(request
, "dmFromTime", string
);
8679 free(string
); string
= NULL
;
8682 err
= timeval2timestring(to_time
, &string
);
8683 if (err
) goto leave
;
8685 INSERT_STRING(request
, "dmToTime", string
);
8686 free(string
); string
= NULL
;
8688 if (outgoing_direction
) {
8689 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
8690 organization_unit_number
, string
);
8692 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
8693 organization_unit_number
, string
);
8696 if (status_filter
> MESSAGESTATE_ANY
) {
8697 isds_printf_message(context
,
8698 _("Invalid message state filter value: %ld"), status_filter
);
8702 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
8705 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
8707 INSERT_STRING(request
, "dmOffset", "1");
8710 /* number 0 means no limit */
8711 if (number
&& *number
== 0) {
8712 INSERT_STRING(request
, "dmLimit", NULL
);
8714 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
8718 isds_log(ILF_ISDS
, ILL_DEBUG
,
8719 (outgoing_direction
) ?
8720 _("Sending GetListOfSentMessages request to ISDS\n") :
8721 _("Sending GetListOfReceivedMessages request to ISDS\n")
8725 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
8726 xmlFreeNode(request
); request
= NULL
;
8729 isds_log(ILF_ISDS
, ILL_DEBUG
,
8730 (outgoing_direction
) ?
8731 _("Processing ISDS response on GetListOfSentMessages "
8732 "request failed\n") :
8733 _("Processing ISDS response on GetListOfReceivedMessages "
8739 /* Check for response status */
8740 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
8741 &code
, &message
, NULL
);
8743 isds_log(ILF_ISDS
, ILL_DEBUG
,
8744 (outgoing_direction
) ?
8745 _("ISDS response on GetListOfSentMessages request "
8746 "is missing status\n") :
8747 _("ISDS response on GetListOfReceivedMessages request "
8748 "is missing status\n")
8753 /* Request processed, but nothing found */
8754 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8755 char *code_locale
= _isds_utf82locale((char*)code
);
8756 char *message_locale
= _isds_utf82locale((char*)message
);
8757 isds_log(ILF_ISDS
, ILL_DEBUG
,
8758 (outgoing_direction
) ?
8759 _("Server refused GetListOfSentMessages request "
8760 "(code=%s, message=%s)\n") :
8761 _("Server refused GetListOfReceivedMessages request "
8762 "(code=%s, message=%s)\n"),
8763 code_locale
, message_locale
);
8764 isds_log_message(context
, message_locale
);
8766 free(message_locale
);
8773 xpath_ctx
= xmlXPathNewContext(response
);
8778 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8782 result
= xmlXPathEvalExpression(
8783 (outgoing_direction
) ?
8784 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
8785 "isds:dmRecords/isds:dmRecord" :
8786 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
8787 "isds:dmRecords/isds:dmRecord",
8794 /* Fill output arguments in */
8795 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8796 struct isds_envelope
*envelope
;
8797 struct isds_list
*item
= NULL
, *last_item
= NULL
;
8799 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
8800 /* Create new message */
8801 item
= calloc(1, sizeof(*item
));
8806 item
->destructor
= (void(*)(void**)) &isds_message_free
;
8807 item
->data
= calloc(1, sizeof(struct isds_message
));
8809 isds_list_free(&item
);
8814 /* Extract envelope data */
8815 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
8817 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
8819 isds_list_free(&item
);
8823 /* Attach extracted envelope */
8824 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
8826 /* Append new message into the list */
8828 *messages
= last_item
= item
;
8830 last_item
->next
= item
;
8835 if (number
) *number
= count
;
8839 isds_list_free(messages
);
8843 xmlXPathFreeObject(result
);
8844 xmlXPathFreeContext(xpath_ctx
);
8848 xmlFreeDoc(response
);
8849 xmlFreeNode(request
);
8852 isds_log(ILF_ISDS
, ILL_DEBUG
,
8853 (outgoing_direction
) ?
8854 _("GetListOfSentMessages request processed by server "
8855 "successfully.\n") :
8856 _("GetListOfReceivedMessages request processed by server "
8859 #else /* not HAVE_LIBCURL */
8866 /* Get list of outgoing (already sent) messages.
8867 * Any criterion argument can be NULL, if you don't care about it.
8868 * @context is session context. Must not be NULL.
8869 * @from_time is minimal time and date of message sending inclusive.
8870 * @to_time is maximal time and date of message sending inclusive
8871 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8872 * @status_filter is bit field of isds_message_status values. Use special
8873 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8874 * all values, you can use bit-wise arithmetic if you want.)
8875 * @offset is index of first message we are interested in. First message is 1.
8876 * Set to 0 (or 1) if you don't care.
8877 * @number is maximal length of list you want to get as input value, outputs
8878 * number of messages matching these criteria. Can be NULL if you don't care
8879 * (applies to output value either).
8880 * @messages is automatically reallocated list of isds_message's. Be ware that
8881 * it returns only brief overview (envelope and some other fields) about each
8882 * message, not the complete message. FIXME: Specify exact fields.
8883 * The list is sorted by delivery time in ascending order.
8884 * Use NULL if you don't care about the meta data (useful if you want to know
8885 * only the @number). If you provide &NULL, list will be allocated on heap,
8886 * if you provide pointer to non-NULL, list will be freed automatically at
8887 * first. Also in case of error the list will be NULLed.
8888 * @return IE_SUCCESS or appropriate error code. */
8889 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
8890 const struct timeval
*from_time
, const struct timeval
*to_time
,
8891 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
8892 const unsigned long int offset
, unsigned long int *number
,
8893 struct isds_list
**messages
) {
8895 return isds_get_list_of_messages(
8897 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
8903 /* Get list of incoming (addressed to you) messages.
8904 * Any criterion argument can be NULL, if you don't care about it.
8905 * @context is session context. Must not be NULL.
8906 * @from_time is minimal time and date of message sending inclusive.
8907 * @to_time is maximal time and date of message sending inclusive
8908 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8909 * @status_filter is bit field of isds_message_status values. Use special
8910 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8911 * all values, you can use bit-wise arithmetic if you want.)
8912 * @offset is index of first message we are interested in. First message is 1.
8913 * Set to 0 (or 1) if you don't care.
8914 * @number is maximal length of list you want to get as input value, outputs
8915 * number of messages matching these criteria. Can be NULL if you don't care
8916 * (applies to output value either).
8917 * @messages is automatically reallocated list of isds_message's. Be ware that
8918 * it returns only brief overview (envelope and some other fields) about each
8919 * message, not the complete message. FIXME: Specify exact fields.
8920 * Use NULL if you don't care about the meta data (useful if you want to know
8921 * only the @number). If you provide &NULL, list will be allocated on heap,
8922 * if you provide pointer to non-NULL, list will be freed automatically at
8923 * first. Also in case of error the list will be NULLed.
8924 * @return IE_SUCCESS or appropriate error code. */
8925 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
8926 const struct timeval
*from_time
, const struct timeval
*to_time
,
8927 const long int *dmRecipientOrgUnitNum
,
8928 const unsigned int status_filter
,
8929 const unsigned long int offset
, unsigned long int *number
,
8930 struct isds_list
**messages
) {
8932 return isds_get_list_of_messages(
8934 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
8940 /* Get list of sent message state changes.
8941 * Any criterion argument can be NULL, if you don't care about it.
8942 * @context is session context. Must not be NULL.
8943 * @from_time is minimal time and date of status changes inclusive
8944 * @to_time is maximal time and date of status changes inclusive
8945 * @changed_states is automatically reallocated list of
8946 * isds_message_status_change's. If you provide &NULL, list will be allocated
8947 * on heap, if you provide pointer to non-NULL, list will be freed
8948 * automatically at first. Also in case of error the list will be NULLed.
8949 * XXX: The list item ordering is not specified.
8950 * XXX: Server provides only `recent' changes.
8951 * @return IE_SUCCESS or appropriate error code. */
8952 isds_error
isds_get_list_of_sent_message_state_changes(
8953 struct isds_ctx
*context
,
8954 const struct timeval
*from_time
, const struct timeval
*to_time
,
8955 struct isds_list
**changed_states
) {
8957 isds_error err
= IE_SUCCESS
;
8959 xmlNsPtr isds_ns
= NULL
;
8960 xmlNodePtr request
= NULL
, node
;
8961 xmlDocPtr response
= NULL
;
8962 xmlXPathContextPtr xpath_ctx
= NULL
;
8963 xmlXPathObjectPtr result
= NULL
;
8964 xmlChar
*string
= NULL
;
8965 long unsigned int count
= 0;
8968 if (!context
) return IE_INVALID_CONTEXT
;
8969 zfree(context
->long_message
);
8971 /* Free former message list if any */
8972 isds_list_free(changed_states
);
8975 /* Check if connection is established
8976 * TODO: This check should be done downstairs. */
8977 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8979 /* Build GetMessageStateChanges request */
8980 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
8982 isds_log_message(context
,
8983 _("Could not build GetMessageStateChanges request"));
8986 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8988 isds_log_message(context
, _("Could not create ISDS name space"));
8989 xmlFreeNode(request
);
8992 xmlSetNs(request
, isds_ns
);
8996 err
= timeval2timestring(from_time
, &string
);
8997 if (err
) goto leave
;
8999 INSERT_STRING(request
, "dmFromTime", string
);
9003 err
= timeval2timestring(to_time
, &string
);
9004 if (err
) goto leave
;
9006 INSERT_STRING(request
, "dmToTime", string
);
9011 err
= send_destroy_request_check_response(context
,
9012 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
9013 &response
, NULL
, NULL
);
9014 if (err
) goto leave
;
9018 xpath_ctx
= xmlXPathNewContext(response
);
9023 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9027 result
= xmlXPathEvalExpression(
9028 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
9029 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
9035 /* Fill output arguments in */
9036 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9037 struct isds_list
*item
= NULL
, *last_item
= NULL
;
9039 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
9040 /* Create new status change */
9041 item
= calloc(1, sizeof(*item
));
9047 (void(*)(void**)) &isds_message_status_change_free
;
9049 /* Extract message status change */
9050 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
9051 err
= extract_StateChangesRecord(context
,
9052 (struct isds_message_status_change
**) &item
->data
,
9055 isds_list_free(&item
);
9059 /* Append new message status change into the list */
9060 if (!*changed_states
) {
9061 *changed_states
= last_item
= item
;
9063 last_item
->next
= item
;
9071 isds_list_free(changed_states
);
9075 xmlXPathFreeObject(result
);
9076 xmlXPathFreeContext(xpath_ctx
);
9077 xmlFreeDoc(response
);
9078 xmlFreeNode(request
);
9081 isds_log(ILF_ISDS
, ILL_DEBUG
,
9082 _("GetMessageStateChanges request processed by server "
9083 "successfully.\n"));
9084 #else /* not HAVE_LIBCURL */
9092 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9094 * @context is session context
9095 * @service is ISDS WS service handler
9096 * @service_name is name of SERVICE_DM_OPERATIONS
9097 * @message_id is message ID to send as service argument to ISDS
9098 * @response is reallocated server SOAP body response as XML document
9099 * @raw_response is reallocated bit stream with response body. Use
9100 * NULL if you don't care
9101 * @raw_response_length is size of @raw_response in bytes
9102 * @code is reallocated ISDS status code
9103 * @status_message is reallocated ISDS status message
9104 * @return error coded from lower layer, context message will be set up
9106 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
9107 const isds_service service
, const xmlChar
*service_name
,
9108 const char *message_id
,
9109 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
9110 xmlChar
**code
, xmlChar
**status_message
) {
9112 isds_error err
= IE_SUCCESS
;
9113 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
9114 xmlNodePtr request
= NULL
, node
;
9115 xmlNsPtr isds_ns
= NULL
;
9117 if (!context
) return IE_INVALID_CONTEXT
;
9118 if (!service_name
|| !message_id
) return IE_INVAL
;
9119 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
9120 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
9122 /* Free output argument */
9123 xmlFreeDoc(*response
); *response
= NULL
;
9124 if (raw_response
) zfree(*raw_response
);
9126 zfree(*status_message
);
9129 /* Check if connection is established
9130 * TODO: This check should be done downstairs. */
9131 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
9133 service_name_locale
= _isds_utf82locale((char*)service_name
);
9134 message_id_locale
= _isds_utf82locale(message_id
);
9135 if (!service_name_locale
|| !message_id_locale
) {
9141 request
= xmlNewNode(NULL
, service_name
);
9143 isds_printf_message(context
,
9144 _("Could not build %s request for %s message ID"),
9145 service_name_locale
, message_id_locale
);
9149 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
9151 isds_log_message(context
, _("Could not create ISDS name space"));
9155 xmlSetNs(request
, isds_ns
);
9158 /* Add requested ID */
9159 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
9160 if (err
) goto leave
;
9161 INSERT_STRING(request
, "dmID", message_id
);
9164 isds_log(ILF_ISDS
, ILL_DEBUG
,
9165 _("Sending %s request for %s message ID to ISDS\n"),
9166 service_name_locale
, message_id_locale
);
9169 err
= _isds(context
, service
, request
, response
,
9170 raw_response
, raw_response_length
);
9171 xmlFreeNode(request
); request
= NULL
;
9174 isds_log(ILF_ISDS
, ILL_DEBUG
,
9175 _("Processing ISDS response on %s request failed\n"),
9176 service_name_locale
);
9180 /* Check for response status */
9181 err
= isds_response_status(context
, service
, *response
,
9182 code
, status_message
, NULL
);
9184 isds_log(ILF_ISDS
, ILL_DEBUG
,
9185 _("ISDS response on %s request is missing status\n"),
9186 service_name_locale
);
9190 /* Request processed, but nothing found */
9191 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
9192 char *code_locale
= _isds_utf82locale((char*) *code
);
9193 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
9194 isds_log(ILF_ISDS
, ILL_DEBUG
,
9195 _("Server refused %s request for %s message ID "
9196 "(code=%s, message=%s)\n"),
9197 service_name_locale
, message_id_locale
,
9198 code_locale
, status_message_locale
);
9199 isds_log_message(context
, status_message_locale
);
9201 free(status_message_locale
);
9207 free(message_id_locale
);
9208 free(service_name_locale
);
9209 xmlFreeNode(request
);
9214 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9215 * signed data and free ISDS response.
9216 * @context is session context
9217 * @message_id is UTF-8 encoded message ID for logging purpose
9218 * @response is parsed XML document. It will be freed and NULLed in the middle
9219 * of function run to save memory. This is not guaranteed in case of error.
9220 * @request_name is name of ISDS request used to construct response root
9221 * element name and for logging purpose.
9222 * @raw is reallocated output buffer with DER encoded CMS data
9223 * @raw_length is size of @raw buffer in bytes
9224 * @returns standard error codes, in case of error, @raw will be freed and
9225 * NULLed, @response sometimes. */
9226 static isds_error
find_extract_signed_data_free_response(
9227 struct isds_ctx
*context
, const xmlChar
*message_id
,
9228 xmlDocPtr
*response
, const xmlChar
*request_name
,
9229 void **raw
, size_t *raw_length
) {
9231 isds_error err
= IE_SUCCESS
;
9232 char *xpath_expression
= NULL
;
9233 xmlXPathContextPtr xpath_ctx
= NULL
;
9234 xmlXPathObjectPtr result
= NULL
;
9235 char *encoded_structure
= NULL
;
9237 if (!context
) return IE_INVALID_CONTEXT
;
9238 if (!raw
) return IE_INVAL
;
9240 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
9243 /* Build XPath expression */
9244 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
9245 "Response/isds:dmSignature");
9246 if (!xpath_expression
) return IE_NOMEM
;
9249 xpath_ctx
= xmlXPathNewContext(*response
);
9254 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9258 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
9263 /* Empty response */
9264 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9265 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9266 isds_printf_message(context
,
9267 _("Server did not return any signed data for message ID `%s' "
9269 message_id_locale
, request_name
);
9270 free(message_id_locale
);
9274 /* More responses */
9275 if (result
->nodesetval
->nodeNr
> 1) {
9276 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9277 isds_printf_message(context
,
9278 _("Server did return more signed data for message ID `%s' "
9280 message_id_locale
, request_name
);
9281 free(message_id_locale
);
9286 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9288 /* Extract PKCS#7 structure */
9289 EXTRACT_STRING(".", encoded_structure
);
9290 if (!encoded_structure
) {
9291 isds_log_message(context
, _("dmSignature element is empty"));
9294 /* Here we have delivery info as standalone CMS in encoded_structure.
9295 * We don't need any other data, free them: */
9296 xmlXPathFreeObject(result
); result
= NULL
;
9297 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
9298 xmlFreeDoc(*response
); *response
= NULL
;
9301 /* Decode PKCS#7 to DER format */
9302 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
9303 if (*raw_length
== (size_t) -1) {
9304 isds_log_message(context
,
9305 _("Error while Base64-decoding PKCS#7 structure"));
9316 free(encoded_structure
);
9317 xmlXPathFreeObject(result
);
9318 xmlXPathFreeContext(xpath_ctx
);
9319 free(xpath_expression
);
9323 #endif /* HAVE_LIBCURL */
9326 /* Download incoming message envelope identified by ID.
9327 * @context is session context
9328 * @message_id is message identifier (you can get them from
9329 * isds_get_list_of_received_messages())
9330 * @message is automatically reallocated message retrieved from ISDS.
9331 * It will miss documents per se. Use isds_get_received_message(), if you are
9332 * interested in documents (content) too.
9333 * Returned hash and timestamp require documents to be verifiable. */
9334 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
9335 const char *message_id
, struct isds_message
**message
) {
9337 isds_error err
= IE_SUCCESS
;
9339 xmlDocPtr response
= NULL
;
9340 xmlChar
*code
= NULL
, *status_message
= NULL
;
9341 xmlXPathContextPtr xpath_ctx
= NULL
;
9342 xmlXPathObjectPtr result
= NULL
;
9345 if (!context
) return IE_INVALID_CONTEXT
;
9346 zfree(context
->long_message
);
9348 /* Free former message if any */
9349 if (!message
) return IE_INVAL
;
9350 isds_message_free(message
);
9353 /* Do request and check for success */
9354 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9355 BAD_CAST
"MessageEnvelopeDownload", message_id
,
9356 &response
, NULL
, NULL
, &code
, &status_message
);
9357 if (err
) goto leave
;
9360 xpath_ctx
= xmlXPathNewContext(response
);
9365 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9369 result
= xmlXPathEvalExpression(
9370 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
9371 "isds:dmReturnedMessageEnvelope",
9377 /* Empty response */
9378 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9379 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9380 isds_printf_message(context
,
9381 _("Server did not return any envelope for ID `%s' "
9382 "on MessageEnvelopeDownload request"), message_id_locale
);
9383 free(message_id_locale
);
9388 if (result
->nodesetval
->nodeNr
> 1) {
9389 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9390 isds_printf_message(context
,
9391 _("Server did return more envelopes for ID `%s' "
9392 "on MessageEnvelopeDownload request"), message_id_locale
);
9393 free(message_id_locale
);
9398 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9400 /* Extract the envelope (= message without documents, hence 0) */
9401 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9402 if (err
) goto leave
;
9405 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
9406 &(*message
)->raw_length
);
9410 isds_message_free(message
);
9413 xmlXPathFreeObject(result
);
9414 xmlXPathFreeContext(xpath_ctx
);
9417 free(status_message
);
9418 if (!*message
|| !(*message
)->xml
) {
9419 xmlFreeDoc(response
);
9423 isds_log(ILF_ISDS
, ILL_DEBUG
,
9424 _("MessageEnvelopeDownload request processed by server "
9427 #else /* not HAVE_LIBCURL */
9434 /* Load delivery info of any format from buffer.
9435 * @context is session context
9436 * @raw_type advertises format of @buffer content. Only delivery info types
9438 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9439 * retrieve such data from message->raw after calling
9440 * isds_get_signed_delivery_info().
9441 * @length is length of buffer in bytes.
9442 * @message is automatically reallocated message parsed from @buffer.
9443 * @strategy selects how buffer will be attached into raw isds_message member.
9445 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
9446 const isds_raw_type raw_type
,
9447 const void *buffer
, const size_t length
,
9448 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9450 isds_error err
= IE_SUCCESS
;
9451 message_ns_type message_ns
;
9452 xmlDocPtr message_doc
= NULL
;
9453 xmlXPathContextPtr xpath_ctx
= NULL
;
9454 xmlXPathObjectPtr result
= NULL
;
9455 void *xml_stream
= NULL
;
9456 size_t xml_stream_length
= 0;
9458 if (!context
) return IE_INVALID_CONTEXT
;
9459 zfree(context
->long_message
);
9460 if (!message
) return IE_INVAL
;
9461 isds_message_free(message
);
9462 if (!buffer
) return IE_INVAL
;
9465 /* Select buffer format and extract XML from CMS*/
9467 case RAWTYPE_DELIVERYINFO
:
9468 message_ns
= MESSAGE_NS_UNSIGNED
;
9469 xml_stream
= (void *) buffer
;
9470 xml_stream_length
= length
;
9473 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
9474 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9475 xml_stream
= (void *) buffer
;
9476 xml_stream_length
= length
;
9479 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
9480 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
9481 err
= _isds_extract_cms_data(context
, buffer
, length
,
9482 &xml_stream
, &xml_stream_length
);
9483 if (err
) goto leave
;
9487 isds_log_message(context
, _("Bad raw delivery representation type"));
9492 isds_log(ILF_ISDS
, ILL_DEBUG
,
9493 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9494 xml_stream_length
, xml_stream
);
9496 /* Convert delivery info XML stream into XPath context */
9497 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9502 xpath_ctx
= xmlXPathNewContext(message_doc
);
9507 /* XXX: Name spaces mangled for signed delivery info:
9508 * http://isds.czechpoint.cz/v20/delivery:
9510 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9512 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9513 * <p:dmID>170272</p:dmID>
9516 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9518 * </q:dmEvents>...</q:dmEvents>
9520 * </q:GetDeliveryInfoResponse>
9522 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
9526 result
= xmlXPathEvalExpression(
9527 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9533 /* Empty delivery info */
9534 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9535 isds_printf_message(context
,
9536 _("XML document is not sisds:dmDelivery document"));
9540 /* More delivery info's */
9541 if (result
->nodesetval
->nodeNr
> 1) {
9542 isds_printf_message(context
,
9543 _("XML document has more sisds:dmDelivery elements"));
9547 /* One delivery info */
9548 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9550 /* Extract the envelope (= message without documents, hence 0).
9551 * XXX: extract_TReturnedMessage() can obtain attachments size,
9552 * but delivery info carries none. It's coded as option elements,
9553 * so it should work. */
9554 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
9555 if (err
) goto leave
;
9557 /* Extract events */
9558 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
9559 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
9560 if (err
) { err
= IE_ERROR
; goto leave
; }
9561 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
9562 if (err
) goto leave
;
9564 /* Append raw CMS structure into message */
9565 (*message
)->raw_type
= raw_type
;
9567 case BUFFER_DONT_STORE
:
9570 (*message
)->raw
= malloc(length
);
9571 if (!(*message
)->raw
) {
9575 memcpy((*message
)->raw
, buffer
, length
);
9576 (*message
)->raw_length
= length
;
9579 (*message
)->raw
= (void *) buffer
;
9580 (*message
)->raw_length
= length
;
9589 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
9590 isds_message_free(message
);
9593 xmlXPathFreeObject(result
);
9594 xmlXPathFreeContext(xpath_ctx
);
9595 if (!*message
|| !(*message
)->xml
) {
9596 xmlFreeDoc(message_doc
);
9598 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9601 isds_log(ILF_ISDS
, ILL_DEBUG
,
9602 _("Delivery info loaded successfully.\n"));
9607 /* Download signed delivery info-sheet of given message identified by ID.
9608 * @context is session context
9609 * @message_id is message identifier (you can get them from
9610 * isds_get_list_of_{sent,received}_messages())
9611 * @message is automatically reallocated message retrieved from ISDS.
9612 * It will miss documents per se. Use isds_get_signed_received_message(),
9613 * if you are interested in documents (content). OTOH, only this function
9614 * can get list events message has gone through. */
9615 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
9616 const char *message_id
, struct isds_message
**message
) {
9618 isds_error err
= IE_SUCCESS
;
9620 xmlDocPtr response
= NULL
;
9621 xmlChar
*code
= NULL
, *status_message
= NULL
;
9623 size_t raw_length
= 0;
9626 if (!context
) return IE_INVALID_CONTEXT
;
9627 zfree(context
->long_message
);
9629 /* Free former message if any */
9630 if (!message
) return IE_INVAL
;
9631 isds_message_free(message
);
9634 /* Do request and check for success */
9635 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9636 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
9637 &response
, NULL
, NULL
, &code
, &status_message
);
9638 if (err
) goto leave
;
9640 /* Find signed delivery info, extract it into raw and maybe free
9642 err
= find_extract_signed_data_free_response(context
,
9643 (xmlChar
*)message_id
, &response
,
9644 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
9645 if (err
) goto leave
;
9647 /* Parse delivery info */
9648 err
= isds_load_delivery_info(context
,
9649 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
9650 message
, BUFFER_MOVE
);
9651 if (err
) goto leave
;
9657 isds_message_free(message
);
9662 free(status_message
);
9663 xmlFreeDoc(response
);
9666 isds_log(ILF_ISDS
, ILL_DEBUG
,
9667 _("GetSignedDeliveryInfo request processed by server "
9670 #else /* not HAVE_LIBCURL */
9677 /* Download delivery info-sheet of given message identified by ID.
9678 * @context is session context
9679 * @message_id is message identifier (you can get them from
9680 * isds_get_list_of_{sent,received}_messages())
9681 * @message is automatically reallocated message retrieved from ISDS.
9682 * It will miss documents per se. Use isds_get_received_message(), if you are
9683 * interested in documents (content). OTOH, only this function can get list
9684 * of events message has gone through. */
9685 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
9686 const char *message_id
, struct isds_message
**message
) {
9688 isds_error err
= IE_SUCCESS
;
9690 xmlDocPtr response
= NULL
;
9691 xmlChar
*code
= NULL
, *status_message
= NULL
;
9692 xmlNodePtr delivery_node
= NULL
;
9694 size_t raw_length
= 0;
9697 if (!context
) return IE_INVALID_CONTEXT
;
9698 zfree(context
->long_message
);
9700 /* Free former message if any */
9701 if (!message
) return IE_INVAL
;
9702 isds_message_free(message
);
9705 /* Do request and check for success */
9706 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9707 BAD_CAST
"GetDeliveryInfo", message_id
,
9708 &response
, NULL
, NULL
, &code
, &status_message
);
9709 if (err
) goto leave
;
9712 /* Serialize delivery info */
9713 delivery_node
= xmlDocGetRootElement(response
);
9714 if (!delivery_node
) {
9715 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9716 isds_printf_message(context
,
9717 _("Server did not return any delivery info for ID `%s' "
9718 "on GetDeliveryInfo request"), message_id_locale
);
9719 free(message_id_locale
);
9723 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
9724 if (err
) goto leave
;
9726 /* Parse delivery info */
9727 /* TODO: Here we parse the response second time. We could single delivery
9728 * parser from isds_load_delivery_info() to make things faster. */
9729 err
= isds_load_delivery_info(context
,
9730 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
9731 message
, BUFFER_MOVE
);
9732 if (err
) goto leave
;
9739 isds_message_free(message
);
9744 free(status_message
);
9745 xmlFreeDoc(response
);
9748 isds_log(ILF_ISDS
, ILL_DEBUG
,
9749 _("GetDeliveryInfo request processed by server "
9752 #else /* not HAVE_LIBCURL */
9759 /* Download incoming message identified by ID.
9760 * @context is session context
9761 * @message_id is message identifier (you can get them from
9762 * isds_get_list_of_received_messages())
9763 * @message is automatically reallocated message retrieved from ISDS */
9764 isds_error
isds_get_received_message(struct isds_ctx
*context
,
9765 const char *message_id
, struct isds_message
**message
) {
9767 isds_error err
= IE_SUCCESS
;
9769 xmlDocPtr response
= NULL
;
9770 void *xml_stream
= NULL
;
9771 size_t xml_stream_length
;
9772 xmlChar
*code
= NULL
, *status_message
= NULL
;
9773 xmlXPathContextPtr xpath_ctx
= NULL
;
9774 xmlXPathObjectPtr result
= NULL
;
9775 char *phys_path
= NULL
;
9776 size_t phys_start
, phys_end
;
9779 if (!context
) return IE_INVALID_CONTEXT
;
9780 zfree(context
->long_message
);
9782 /* Free former message if any */
9783 if (NULL
== message
) return IE_INVAL
;
9784 if (message
) isds_message_free(message
);
9787 /* Do request and check for success */
9788 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
9789 BAD_CAST
"MessageDownload", message_id
,
9790 &response
, &xml_stream
, &xml_stream_length
,
9791 &code
, &status_message
);
9792 if (err
) goto leave
;
9795 xpath_ctx
= xmlXPathNewContext(response
);
9800 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9804 result
= xmlXPathEvalExpression(
9805 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9811 /* Empty response */
9812 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9813 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9814 isds_printf_message(context
,
9815 _("Server did not return any message for ID `%s' "
9816 "on MessageDownload request"), message_id_locale
);
9817 free(message_id_locale
);
9822 if (result
->nodesetval
->nodeNr
> 1) {
9823 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9824 isds_printf_message(context
,
9825 _("Server did return more messages for ID `%s' "
9826 "on MessageDownload request"), message_id_locale
);
9827 free(message_id_locale
);
9832 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9834 /* Extract the message */
9835 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
9836 if (err
) goto leave
;
9838 /* Locate raw XML blob */
9840 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
9841 PHYSXML_ELEMENT_SEPARATOR
9842 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
9843 PHYSXML_ELEMENT_SEPARATOR
9844 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
9850 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
9851 phys_path
, &phys_start
, &phys_end
);
9854 isds_log_message(context
,
9855 _("Substring with isds:MessageDownloadResponse element "
9856 "could not be located in raw SOAP message"));
9860 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9861 &(*message)->raw_length);*/
9862 /* TODO: Store name space declarations from ancestors */
9863 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9864 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
9865 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
9866 (*message
)->raw
= malloc((*message
)->raw_length
);
9867 if (!(*message
)->raw
) {
9871 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
9876 isds_message_free(message
);
9881 xmlXPathFreeObject(result
);
9882 xmlXPathFreeContext(xpath_ctx
);
9885 free(status_message
);
9887 if (!*message
|| !(*message
)->xml
) {
9888 xmlFreeDoc(response
);
9892 isds_log(ILF_ISDS
, ILL_DEBUG
,
9893 _("MessageDownload request processed by server "
9896 #else /* not HAVE_LIBCURL */
9903 /* Load message of any type from buffer.
9904 * @context is session context
9905 * @raw_type defines content type of @buffer. Only message types are allowed.
9906 * @buffer is message raw representation. Format (CMS, plain signed,
9907 * message direction) is defined in @raw_type. You can retrieve such data
9908 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9909 * @length is length of buffer in bytes.
9910 * @message is automatically reallocated message parsed from @buffer.
9911 * @strategy selects how buffer will be attached into raw isds_message member.
9913 isds_error
isds_load_message(struct isds_ctx
*context
,
9914 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
9915 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9917 isds_error err
= IE_SUCCESS
;
9918 void *xml_stream
= NULL
;
9919 size_t xml_stream_length
= 0;
9920 message_ns_type message_ns
;
9921 xmlDocPtr message_doc
= NULL
;
9922 xmlXPathContextPtr xpath_ctx
= NULL
;
9923 xmlXPathObjectPtr result
= NULL
;
9925 if (!context
) return IE_INVALID_CONTEXT
;
9926 zfree(context
->long_message
);
9927 if (!message
) return IE_INVAL
;
9928 isds_message_free(message
);
9929 if (!buffer
) return IE_INVAL
;
9932 /* Select buffer format and extract XML from CMS*/
9934 case RAWTYPE_INCOMING_MESSAGE
:
9935 message_ns
= MESSAGE_NS_UNSIGNED
;
9936 xml_stream
= (void *) buffer
;
9937 xml_stream_length
= length
;
9940 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
9941 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9942 xml_stream
= (void *) buffer
;
9943 xml_stream_length
= length
;
9946 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
9947 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9948 err
= _isds_extract_cms_data(context
, buffer
, length
,
9949 &xml_stream
, &xml_stream_length
);
9950 if (err
) goto leave
;
9953 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
9954 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9955 xml_stream
= (void *) buffer
;
9956 xml_stream_length
= length
;
9959 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
9960 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9961 err
= _isds_extract_cms_data(context
, buffer
, length
,
9962 &xml_stream
, &xml_stream_length
);
9963 if (err
) goto leave
;
9967 isds_log_message(context
, _("Bad raw message representation type"));
9972 isds_log(ILF_ISDS
, ILL_DEBUG
,
9973 _("Loading message:\n%.*s\nEnd of message\n"),
9974 xml_stream_length
, xml_stream
);
9976 /* Convert messages XML stream into XPath context */
9977 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9982 xpath_ctx
= xmlXPathNewContext(message_doc
);
9987 /* XXX: Standard name space for unsigned incoming direction:
9988 * http://isds.czechpoint.cz/v20/
9990 * XXX: Name spaces mangled for signed outgoing direction:
9991 * http://isds.czechpoint.cz/v20/SentMessage:
9993 * <q:MessageDownloadResponse
9994 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9995 * <q:dmReturnedMessage>
9996 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9997 * <p:dmID>151916</p:dmID>
10000 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10002 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10003 * </q:dmReturnedMessage>
10004 * </q:MessageDownloadResponse>
10006 * XXX: Name spaces mangled for signed incoming direction:
10007 * http://isds.czechpoint.cz/v20/message:
10009 * <q:MessageDownloadResponse
10010 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10011 * <q:dmReturnedMessage>
10012 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10013 * <p:dmID>151916</p:dmID>
10016 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10018 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10019 * </q:dmReturnedMessage>
10020 * </q:MessageDownloadResponse>
10022 * Stupidity of ISDS developers is unlimited */
10023 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
10027 result
= xmlXPathEvalExpression(
10028 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10034 /* Empty message */
10035 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10036 isds_printf_message(context
,
10037 _("XML document does not contain "
10038 "sisds:dmReturnedMessage element"));
10042 /* More messages */
10043 if (result
->nodesetval
->nodeNr
> 1) {
10044 isds_printf_message(context
,
10045 _("XML document has more sisds:dmReturnedMessage elements"));
10050 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10052 /* Extract the message */
10053 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
10054 if (err
) goto leave
;
10056 /* Append raw buffer into message */
10057 (*message
)->raw_type
= raw_type
;
10058 switch (strategy
) {
10059 case BUFFER_DONT_STORE
:
10062 (*message
)->raw
= malloc(length
);
10063 if (!(*message
)->raw
) {
10067 memcpy((*message
)->raw
, buffer
, length
);
10068 (*message
)->raw_length
= length
;
10071 (*message
)->raw
= (void *) buffer
;
10072 (*message
)->raw_length
= length
;
10082 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
10083 isds_message_free(message
);
10086 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10087 xmlXPathFreeObject(result
);
10088 xmlXPathFreeContext(xpath_ctx
);
10089 if (!*message
|| !(*message
)->xml
) {
10090 xmlFreeDoc(message_doc
);
10094 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
10099 /* Determine type of raw message or delivery info according some heuristics.
10100 * It does not validate the raw blob.
10101 * @context is session context
10102 * @raw_type returns content type of @buffer. Valid only if exit code of this
10103 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10104 * reallocated memory.
10105 * @buffer is message raw representation.
10106 * @length is length of buffer in bytes. */
10107 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
10108 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
10110 void *xml_stream
= NULL
;
10111 size_t xml_stream_length
= 0;
10112 xmlDocPtr document
= NULL
;
10113 xmlNodePtr root
= NULL
;
10115 if (!context
) return IE_INVALID_CONTEXT
;
10116 zfree(context
->long_message
);
10117 if (length
== 0 || !buffer
) return IE_INVAL
;
10118 if (!raw_type
) return IE_INVAL
;
10121 err
= _isds_extract_cms_data(context
, buffer
, length
,
10122 &xml_stream
, &xml_stream_length
);
10124 xml_stream
= (void *) buffer
;
10125 xml_stream_length
= (size_t) length
;
10130 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
10132 isds_printf_message(context
,
10133 _("Could not parse data as XML document"));
10138 /* Get root element */
10139 root
= xmlDocGetRootElement(document
);
10141 isds_printf_message(context
,
10142 _("XML document is missing root element"));
10147 if (!root
->ns
|| !root
->ns
->href
) {
10148 isds_printf_message(context
,
10149 _("Root element does not belong to any name space"));
10154 /* Test name space */
10155 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
10156 if (xml_stream
== buffer
)
10157 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
10159 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
10160 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
10161 if (xml_stream
== buffer
)
10162 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
10164 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
10165 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
10166 if (xml_stream
== buffer
)
10167 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
10169 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
10170 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
10171 if (xml_stream
!= buffer
) {
10172 isds_printf_message(context
,
10173 _("Document in ISDS name space is encapsulated into CMS" ));
10175 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
10176 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
10177 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
10178 *raw_type
= RAWTYPE_DELIVERYINFO
;
10180 isds_printf_message(context
,
10181 _("Unknown root element in ISDS name space"));
10185 isds_printf_message(context
,
10186 _("Unknown name space"));
10191 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
10192 xmlFreeDoc(document
);
10197 /* Download signed incoming/outgoing message identified by ID.
10198 * @context is session context
10199 * @output is true for outgoing message, false for incoming message
10200 * @message_id is message identifier (you can get them from
10201 * isds_get_list_of_{sent,received}_messages())
10202 * @message is automatically reallocated message retrieved from ISDS. The raw
10203 * member will be filled with PKCS#7 structure in DER format. */
10204 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
10205 const _Bool outgoing
, const char *message_id
,
10206 struct isds_message
**message
) {
10208 isds_error err
= IE_SUCCESS
;
10210 xmlDocPtr response
= NULL
;
10211 xmlChar
*code
= NULL
, *status_message
= NULL
;
10212 xmlXPathContextPtr xpath_ctx
= NULL
;
10213 xmlXPathObjectPtr result
= NULL
;
10214 char *encoded_structure
= NULL
;
10216 size_t raw_length
= 0;
10219 if (!context
) return IE_INVALID_CONTEXT
;
10220 zfree(context
->long_message
);
10221 if (!message
) return IE_INVAL
;
10222 isds_message_free(message
);
10225 /* Do request and check for success */
10226 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
10227 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10228 BAD_CAST
"SignedMessageDownload",
10229 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10230 if (err
) goto leave
;
10232 /* Find signed message, extract it into raw and maybe free
10234 err
= find_extract_signed_data_free_response(context
,
10235 (xmlChar
*)message_id
, &response
,
10236 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
10237 BAD_CAST
"SignedMessageDownload",
10238 &raw
, &raw_length
);
10239 if (err
) goto leave
;
10241 /* Parse message */
10242 err
= isds_load_message(context
,
10243 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10244 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
10245 raw
, raw_length
, message
, BUFFER_MOVE
);
10246 if (err
) goto leave
;
10252 isds_message_free(message
);
10255 free(encoded_structure
);
10256 xmlXPathFreeObject(result
);
10257 xmlXPathFreeContext(xpath_ctx
);
10261 free(status_message
);
10262 xmlFreeDoc(response
);
10265 isds_log(ILF_ISDS
, ILL_DEBUG
,
10267 _("SignedSentMessageDownload request processed by server "
10268 "successfully.\n") :
10269 _("SignedMessageDownload request processed by server "
10272 #else /* not HAVE_LIBCURL */
10279 /* Download signed incoming message identified by ID.
10280 * @context is session context
10281 * @message_id is message identifier (you can get them from
10282 * isds_get_list_of_received_messages())
10283 * @message is automatically reallocated message retrieved from ISDS. The raw
10284 * member will be filled with PKCS#7 structure in DER format. */
10285 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
10286 const char *message_id
, struct isds_message
**message
) {
10287 return isds_get_signed_message(context
, 0, message_id
, message
);
10291 /* Download signed outgoing message identified by ID.
10292 * @context is session context
10293 * @message_id is message identifier (you can get them from
10294 * isds_get_list_of_sent_messages())
10295 * @message is automatically reallocated message retrieved from ISDS. The raw
10296 * member will be filled with PKCS#7 structure in DER format. */
10297 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
10298 const char *message_id
, struct isds_message
**message
) {
10299 return isds_get_signed_message(context
, 1, message_id
, message
);
10303 /* Get type and name of user who sent a message identified by ID.
10304 * @context is session context
10305 * @message_id is message identifier
10306 * @sender_type is pointer to automatically allocated type of sender detected
10307 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10308 * library or to the server, NULL will be returned. Pass NULL if you don't
10310 * @raw_sender_type is automatically reallocated UTF-8 string describing
10311 * sender type or NULL if not known to server. Pass NULL if you don't care.
10312 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10313 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10314 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
10315 const char *message_id
, isds_sender_type
**sender_type
,
10316 char **raw_sender_type
, char **sender_name
) {
10317 isds_error err
= IE_SUCCESS
;
10319 xmlDocPtr response
= NULL
;
10320 xmlChar
*code
= NULL
, *status_message
= NULL
;
10321 xmlXPathContextPtr xpath_ctx
= NULL
;
10322 xmlXPathObjectPtr result
= NULL
;
10323 char *type_string
= NULL
;
10326 if (!context
) return IE_INVALID_CONTEXT
;
10327 zfree(context
->long_message
);
10328 if (sender_type
) zfree(*sender_type
);
10329 if (raw_sender_type
) zfree(*raw_sender_type
);
10330 if (sender_name
) zfree(*sender_name
);
10331 if (!message_id
) return IE_INVAL
;
10334 /* Do request and check for success */
10335 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10336 BAD_CAST
"GetMessageAuthor",
10337 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
10338 if (err
) goto leave
;
10341 xpath_ctx
= xmlXPathNewContext(response
);
10346 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10350 result
= xmlXPathEvalExpression(
10351 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
10356 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10357 isds_log_message(context
,
10358 _("Missing GetMessageAuthorResponse element"));
10362 if (result
->nodesetval
->nodeNr
> 1) {
10363 isds_log_message(context
,
10364 _("Multiple GetMessageAuthorResponse element"));
10368 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10369 xmlXPathFreeObject(result
); result
= NULL
;
10371 /* Fill output arguments in */
10372 EXTRACT_STRING("isds:userType", type_string
);
10373 if (NULL
!= type_string
) {
10374 if (NULL
!= sender_type
) {
10375 *sender_type
= calloc(1, sizeof(**sender_type
));
10376 if (NULL
== *sender_type
) {
10381 err
= string2isds_sender_type((xmlChar
*)type_string
,
10384 zfree(*sender_type
);
10385 if (err
== IE_ENUM
) {
10387 char *type_string_locale
= _isds_utf82locale(type_string
);
10388 isds_log(ILF_ISDS
, ILL_WARNING
,
10389 _("Unknown isds:userType value: %s"),
10390 type_string_locale
);
10391 free(type_string_locale
);
10396 if (NULL
!= sender_name
)
10397 EXTRACT_STRING("isds:authorName", *sender_name
);
10401 if (NULL
!= sender_type
) zfree(*sender_type
);
10402 zfree(type_string
);
10403 if (NULL
!= sender_name
) zfree(*sender_name
);
10405 if (NULL
!= raw_sender_type
) *raw_sender_type
= type_string
;
10407 xmlXPathFreeObject(result
);
10408 xmlXPathFreeContext(xpath_ctx
);
10411 free(status_message
);
10412 xmlFreeDoc(response
);
10415 isds_log(ILF_ISDS
, ILL_DEBUG
,
10416 _("GetMessageAuthor request processed by server "
10417 "successfully.\n"));
10418 #else /* not HAVE_LIBCURL */
10425 /* Retrieve hash of message identified by ID stored in ISDS.
10426 * @context is session context
10427 * @message_id is message identifier
10428 * @hash is automatically reallocated message hash downloaded from ISDS.
10429 * Message must exist in system and must not be deleted. */
10430 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
10431 const char *message_id
, struct isds_hash
**hash
) {
10433 isds_error err
= IE_SUCCESS
;
10435 xmlDocPtr response
= NULL
;
10436 xmlChar
*code
= NULL
, *status_message
= NULL
;
10437 xmlXPathContextPtr xpath_ctx
= NULL
;
10438 xmlXPathObjectPtr result
= NULL
;
10441 if (!context
) return IE_INVALID_CONTEXT
;
10442 zfree(context
->long_message
);
10444 isds_hash_free(hash
);
10447 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10448 BAD_CAST
"VerifyMessage", message_id
,
10449 &response
, NULL
, NULL
, &code
, &status_message
);
10450 if (err
) goto leave
;
10454 xpath_ctx
= xmlXPathNewContext(response
);
10459 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10463 result
= xmlXPathEvalExpression(
10464 BAD_CAST
"/isds:VerifyMessageResponse",
10470 /* Empty response */
10471 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10472 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10473 isds_printf_message(context
,
10474 _("Server did not return any response for ID `%s' "
10475 "on VerifyMessage request"), message_id_locale
);
10476 free(message_id_locale
);
10480 /* More responses */
10481 if (result
->nodesetval
->nodeNr
> 1) {
10482 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
10483 isds_printf_message(context
,
10484 _("Server did return more responses for ID `%s' "
10485 "on VerifyMessage request"), message_id_locale
);
10486 free(message_id_locale
);
10491 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10493 /* Extract the hash */
10494 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
10498 isds_hash_free(hash
);
10501 xmlXPathFreeObject(result
);
10502 xmlXPathFreeContext(xpath_ctx
);
10505 free(status_message
);
10506 xmlFreeDoc(response
);
10509 isds_log(ILF_ISDS
, ILL_DEBUG
,
10510 _("VerifyMessage request processed by server "
10513 #else /* not HAVE_LIBCURL */
10520 /* Erase message specified by @message_id from long term storage. Other
10521 * message cannot be erased on user request.
10522 * @context is session context
10523 * @message_id is message identifier.
10524 * @incoming is true for incoming message, false for outgoing message.
10526 * IE_SUCCESS if message has ben removed
10527 * IE_INVAL if message does not exist in long term storage or message
10528 * belongs to different box
10529 * TODO: IE_NOEPRM if user has no permission to erase a message */
10530 isds_error
isds_delete_message_from_storage(struct isds_ctx
*context
,
10531 const char *message_id
, _Bool incoming
) {
10532 isds_error err
= IE_SUCCESS
;
10534 xmlNodePtr request
= NULL
, node
;
10535 xmlNsPtr isds_ns
= NULL
;
10536 xmlDocPtr response
= NULL
;
10537 xmlChar
*code
= NULL
, *status_message
= NULL
;
10540 if (!context
) return IE_INVALID_CONTEXT
;
10541 zfree(context
->long_message
);
10542 if (NULL
== message_id
) return IE_INVAL
;
10544 /* Check if connection is established
10545 * TODO: This check should be done downstairs. */
10546 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10549 /* Build request */
10550 request
= xmlNewNode(NULL
, BAD_CAST
"EraseMessage");
10552 isds_log_message(context
,
10553 _("Could build EraseMessage request"));
10556 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10558 isds_log_message(context
, _("Could not create ISDS name space"));
10559 xmlFreeNode(request
);
10562 xmlSetNs(request
, isds_ns
);
10564 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
10565 if (err
) goto leave
;
10566 INSERT_STRING(request
, "dmID", message_id
);
10568 INSERT_SCALAR_BOOLEAN(request
, "dmIncoming", incoming
);
10572 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending EraseMessage request for "
10573 "message ID %s to ISDS\n"), message_id
);
10574 err
= _isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
10575 xmlFreeNode(request
); request
= NULL
;
10578 isds_log(ILF_ISDS
, ILL_DEBUG
,
10579 _("Processing ISDS response on EraseMessage request "
10584 /* Check for response status */
10585 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
10586 &code
, &status_message
, NULL
);
10588 isds_log(ILF_ISDS
, ILL_DEBUG
,
10589 _("ISDS response on EraseMessage request is missing "
10594 /* Check server status code */
10595 if (!xmlStrcmp(code
, BAD_CAST
"1211")) {
10596 isds_log_message(context
, _("Message to erase belongs to other box"));
10598 } else if (!xmlStrcmp(code
, BAD_CAST
"1219")) {
10599 isds_log_message(context
, _("Message to erase is not saved in "
10600 "long term storage or the direction does not match"));
10602 } else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
10603 char *code_locale
= _isds_utf82locale((char*) code
);
10604 char *message_locale
= _isds_utf82locale((char*) status_message
);
10605 isds_log(ILF_ISDS
, ILL_DEBUG
,
10606 _("Server refused EraseMessage request "
10607 "(code=%s, message=%s)\n"),
10608 code_locale
, message_locale
);
10609 isds_log_message(context
, message_locale
);
10611 free(message_locale
);
10618 free(status_message
);
10619 xmlFreeDoc(response
);
10620 xmlFreeNode(request
);
10623 isds_log(ILF_ISDS
, ILL_DEBUG
,
10624 _("EraseMessage request processed by server "
10627 #else /* not HAVE_LIBCURL */
10634 /* Mark message as read. This is a transactional commit function to acknowledge
10635 * to ISDS the message has been downloaded and processed by client properly.
10636 * @context is session context
10637 * @message_id is message identifier. */
10638 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
10639 const char *message_id
) {
10641 isds_error err
= IE_SUCCESS
;
10643 xmlDocPtr response
= NULL
;
10644 xmlChar
*code
= NULL
, *status_message
= NULL
;
10647 if (!context
) return IE_INVALID_CONTEXT
;
10648 zfree(context
->long_message
);
10651 /* Do request and check for success */
10652 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10653 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
10654 &response
, NULL
, NULL
, &code
, &status_message
);
10657 free(status_message
);
10658 xmlFreeDoc(response
);
10661 isds_log(ILF_ISDS
, ILL_DEBUG
,
10662 _("MarkMessageAsDownloaded request processed by server "
10665 #else /* not HAVE_LIBCURL */
10672 /* Mark message as received by recipient. This is applicable only to
10673 * commercial message. Use envelope->dmType message member to distinguish
10674 * commercial message from government message. Government message is
10675 * received automatically (by law), commercial message on recipient request.
10676 * @context is session context
10677 * @message_id is message identifier. */
10678 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
10679 const char *message_id
) {
10681 isds_error err
= IE_SUCCESS
;
10683 xmlDocPtr response
= NULL
;
10684 xmlChar
*code
= NULL
, *status_message
= NULL
;
10687 if (!context
) return IE_INVALID_CONTEXT
;
10688 zfree(context
->long_message
);
10691 /* Do request and check for success */
10692 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10693 BAD_CAST
"ConfirmDelivery", message_id
,
10694 &response
, NULL
, NULL
, &code
, &status_message
);
10697 free(status_message
);
10698 xmlFreeDoc(response
);
10701 isds_log(ILF_ISDS
, ILL_DEBUG
,
10702 _("ConfirmDelivery request processed by server "
10705 #else /* not HAVE_LIBCURL */
10712 /* Send document for authorized conversion into Czech POINT system.
10713 * This is public anonymous service, no log-in necessary. Special context is
10714 * used to reuse keep-a-live HTTPS connection.
10715 * @context is Czech POINT session context. DO NOT use context connected to
10716 * ISDS server. Use new context or context used by this function previously.
10717 * @document is document to convert. Only data, data_length, dmFileDescr and
10718 * is_xml members are significant. Be ware that not all document formats can be
10719 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10720 * @id is reallocated identifier assigned by Czech POINT system to
10721 * your document on submit. Use is to tell it to Czech POINT officer.
10722 * @date is reallocated document submit date (submitted documents
10723 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10725 isds_error
czp_convert_document(struct isds_ctx
*context
,
10726 const struct isds_document
*document
,
10727 char **id
, struct tm
**date
) {
10728 isds_error err
= IE_SUCCESS
;
10730 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
10731 xmlNodePtr request
= NULL
, node
;
10732 xmlDocPtr response
= NULL
;
10734 xmlXPathContextPtr xpath_ctx
= NULL
;
10735 xmlXPathObjectPtr result
= NULL
;
10736 long int status
= -1;
10737 long int *status_ptr
= &status
;
10738 char *string
= NULL
;
10742 if (!context
) return IE_INVALID_CONTEXT
;
10743 zfree(context
->long_message
);
10744 if (!document
|| !id
|| !date
) return IE_INVAL
;
10746 if (document
->is_xml
) {
10747 isds_log_message(context
,
10748 _("XML documents cannot be submitted to conversion"));
10752 /* Free output arguments */
10757 /* Store configuration */
10758 context
->type
= CTX_TYPE_CZP
;
10759 free(context
->url
);
10760 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
10761 if (!(context
->url
))
10764 /* Prepare CURL handle if not yet connected */
10765 if (!context
->curl
) {
10766 context
->curl
= curl_easy_init();
10767 if (!(context
->curl
))
10771 /* Build conversion request */
10772 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
10774 isds_log_message(context
,
10775 _("Could not build Czech POINT conversion request"));
10778 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
10780 isds_log_message(context
,
10781 _("Could not create Czech POINT deposit name space"));
10782 xmlFreeNode(request
);
10785 xmlSetNs(request
, deposit_ns
);
10787 /* Insert children. They are in empty namespace! */
10788 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
10790 isds_log_message(context
, _("Could not create empty name space"));
10794 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
10795 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
10796 document
->dmFileDescr
);
10798 /* Document encoded in Base64 */
10799 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
10800 document
->data
, document
->data_length
);
10801 if (err
) goto leave
;
10803 isds_log(ILF_ISDS
, ILL_DEBUG
,
10804 _("Submitting document for conversion into Czech POINT deposit"));
10806 /* Send conversion request */
10807 err
= _czp_czpdeposit(context
, request
, &response
);
10808 xmlFreeNode(request
); request
= NULL
;
10811 czp_do_close_connection(context
);
10816 /* Extract response */
10817 xpath_ctx
= xmlXPathNewContext(response
);
10822 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10826 result
= xmlXPathEvalExpression(
10827 BAD_CAST
"/deposit:saveDocumentResponse/return",
10833 /* Empty response */
10834 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10835 isds_printf_message(context
,
10836 _("Missing `return' element in Czech POINT deposit response"));
10840 /* More responses */
10841 if (result
->nodesetval
->nodeNr
> 1) {
10842 isds_printf_message(context
,
10843 _("Multiple `return' element in Czech POINT deposit response"));
10848 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10851 EXTRACT_LONGINT("status", status_ptr
, 1);
10853 EXTRACT_STRING("statusMsg", string
);
10854 char *string_locale
= _isds_utf82locale(string
);
10855 isds_printf_message(context
,
10856 _("Czech POINT deposit refused document for conversion "
10857 "(code=%ld, message=%s)"),
10858 status
, string_locale
);
10859 free(string_locale
);
10864 /* Get document ID */
10865 EXTRACT_STRING("documentID", *id
);
10867 /* Get submit date */
10868 EXTRACT_STRING("dateInserted", string
);
10870 *date
= calloc(1, sizeof(**date
));
10875 err
= _isds_datestring2tm((xmlChar
*)string
, *date
);
10877 if (err
== IE_NOTSUP
) {
10879 char *string_locale
= _isds_utf82locale(string
);
10880 isds_printf_message(context
,
10881 _("Invalid dateInserted value: %s"), string_locale
);
10882 free(string_locale
);
10890 xmlXPathFreeObject(result
);
10891 xmlXPathFreeContext(xpath_ctx
);
10893 xmlFreeDoc(response
);
10894 xmlFreeNode(request
);
10897 char *id_locale
= _isds_utf82locale((char *) *id
);
10898 isds_log(ILF_ISDS
, ILL_DEBUG
,
10899 _("Document %s has been submitted for conversion "
10900 "to server successfully\n"), id_locale
);
10903 #else /* not HAVE_LIBCURL */
10910 /* Close possibly opened connection to Czech POINT document deposit.
10911 * @context is Czech POINT session context. */
10912 isds_error
czp_close_connection(struct isds_ctx
*context
) {
10913 if (!context
) return IE_INVALID_CONTEXT
;
10914 zfree(context
->long_message
);
10916 return czp_do_close_connection(context
);
10923 /* Send request for new box creation in testing ISDS instance.
10924 * It's not possible to request for a production box currently, as it
10925 * communicates via e-mail.
10926 * XXX: This function does not work either. Server complains about invalid
10928 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10930 * @context is special session context for box creation request. DO NOT use
10931 * standard context as it could reveal your password. Use fresh new context or
10932 * context previously used by this function.
10933 * @box is box description to create including single primary user (in case of
10934 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10935 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10936 * box, or contact address of PFO box owner). The email member is mandatory as
10937 * it will be used to deliver credentials.
10938 * @former_names is former name of box owner. Pass NULL if you don't care.
10939 * @approval is optional external approval of box manipulation
10940 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10941 * NULL, if you don't care.*/
10942 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
10943 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
10944 const char *former_names
, const struct isds_approval
*approval
,
10945 char **refnumber
) {
10946 isds_error err
= IE_SUCCESS
;
10948 xmlNodePtr request
= NULL
;
10949 xmlDocPtr response
= NULL
;
10950 xmlXPathContextPtr xpath_ctx
= NULL
;
10951 xmlXPathObjectPtr result
= NULL
;
10955 if (!context
) return IE_INVALID_CONTEXT
;
10956 zfree(context
->long_message
);
10957 if (!box
) return IE_INVAL
;
10960 if (!box
->email
|| box
->email
[0] == '\0') {
10961 isds_log_message(context
, _("E-mail field is mandatory"));
10965 /* Scratch box ID */
10968 /* Store configuration */
10969 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
10970 free(context
->url
);
10971 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
10972 if (!(context
->url
))
10975 /* Prepare CURL handle if not yet connected */
10976 if (!context
->curl
) {
10977 context
->curl
= curl_easy_init();
10978 if (!(context
->curl
))
10982 /* Build CreateDataBox request */
10983 err
= build_CreateDBInput_request(context
,
10984 &request
, BAD_CAST
"CreateDataBox",
10985 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
10986 if (err
) goto leave
;
10988 /* Send it to server and process response */
10989 err
= send_destroy_request_check_response(context
,
10990 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
10991 &response
, (xmlChar
**) refnumber
, NULL
);
10992 if (err
) goto leave
;
10994 /* Extract box ID */
10995 xpath_ctx
= xmlXPathNewContext(response
);
11000 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11004 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
11007 xmlXPathFreeObject(result
);
11008 xmlXPathFreeContext(xpath_ctx
);
11009 xmlFreeDoc(response
);
11010 xmlFreeNode(request
);
11013 isds_log(ILF_ISDS
, ILL_DEBUG
,
11014 _("CreateDataBox request processed by server successfully.\n"));
11016 #else /* not HAVE_LIBCURL */
11024 /* Submit CMS signed message to ISDS to verify its originality. This is
11025 * stronger form of isds_verify_message_hash() because ISDS does more checks
11026 * than simple one (potentialy old weak) hash comparison.
11027 * @context is session context
11028 * @message is memory with raw CMS signed message bit stream
11029 * @length is @message size in bytes
11031 * IE_SUCCESS if message originates in ISDS
11032 * IE_NOTEQUAL if message is unknown to ISDS
11033 * other code for other errors */
11034 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
11035 const void *message
, size_t length
) {
11036 isds_error err
= IE_SUCCESS
;
11038 xmlNsPtr isds_ns
= NULL
;
11039 xmlNodePtr request
= NULL
;
11040 xmlDocPtr response
= NULL
;
11041 xmlXPathContextPtr xpath_ctx
= NULL
;
11042 xmlXPathObjectPtr result
= NULL
;
11043 _Bool
*authentic
= NULL
;
11046 if (!context
) return IE_INVALID_CONTEXT
;
11047 zfree(context
->long_message
);
11048 if (!message
|| length
== 0) return IE_INVAL
;
11051 /* Check if connection is established
11052 * TODO: This check should be done downstairs. */
11053 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11056 /* Build AuthenticateMessage request */
11057 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
11059 isds_log_message(context
,
11060 _("Could not build AuthenticateMessage request"));
11063 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11065 isds_log_message(context
, _("Could not create ISDS name space"));
11066 xmlFreeNode(request
);
11069 xmlSetNs(request
, isds_ns
);
11071 /* Insert Base64 encoded message */
11072 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
11074 if (err
) goto leave
;
11076 /* Send request to server and process response */
11077 err
= send_destroy_request_check_response(context
,
11078 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
11079 &response
, NULL
, NULL
);
11080 if (err
) goto leave
;
11083 /* ISDS has decided */
11084 xpath_ctx
= xmlXPathNewContext(response
);
11089 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11094 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
11097 isds_log_message(context
,
11098 _("Server did not return any response on "
11099 "AuthenticateMessage request"));
11104 isds_log(ILF_ISDS
, ILL_DEBUG
,
11105 _("ISDS authenticated the message successfully\n"));
11107 isds_log_message(context
, _("ISDS does not know the message"));
11114 xmlXPathFreeObject(result
);
11115 xmlXPathFreeContext(xpath_ctx
);
11117 xmlFreeDoc(response
);
11118 xmlFreeNode(request
);
11119 #else /* not HAVE_LIBCURL */
11127 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11128 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11130 * @context is session context
11131 * @input_data is memory with raw CMS signed message or delivery info bit
11132 * stream to re-sign
11133 * @input_length is @input_data size in bytes
11134 * @output_data is pointer to auto-allocated memory where to store re-signed
11135 * input data blob. Caller must free it.
11136 * @output_data is pointer where to store @output_data size in bytes
11137 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11138 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11140 * IE_SUCCESS if CMS blob has been re-signed successfully
11141 * other code for other errors */
11142 isds_error
isds_resign_message(struct isds_ctx
*context
,
11143 const void *input_data
, size_t input_length
,
11144 void **output_data
, size_t *output_length
, struct tm
**valid_to
) {
11145 isds_error err
= IE_SUCCESS
;
11147 xmlNsPtr isds_ns
= NULL
;
11148 xmlNodePtr request
= NULL
;
11149 xmlDocPtr response
= NULL
;
11150 xmlXPathContextPtr xpath_ctx
= NULL
;
11151 xmlXPathObjectPtr result
= NULL
;
11152 char *string
= NULL
;
11153 const xmlChar
*codes
[] = {
11160 const char *meanings
[] = {
11162 "Message is not original",
11163 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11164 "Time stamp could not been generated in time"
11166 const isds_error errors
[] = {
11172 struct code_map_isds_error map
= {
11174 .meanings
= meanings
,
11179 if (NULL
!= output_data
) *output_data
= NULL
;
11180 if (NULL
!= output_length
) *output_length
= 0;
11181 if (NULL
!= valid_to
) *valid_to
= NULL
;
11183 if (NULL
== context
) return IE_INVALID_CONTEXT
;
11184 zfree(context
->long_message
);
11185 if (NULL
== input_data
|| 0 == input_length
) {
11186 isds_log_message(context
, _("Empty CMS blob on input"));
11189 if (NULL
== output_data
|| NULL
== output_length
) {
11190 isds_log_message(context
,
11191 _("NULL pointer provided for output CMS blob"));
11196 /* Check if connection is established
11197 * TODO: This check should be done downstairs. */
11198 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
11201 /* Build Re-signISDSDocument request */
11202 request
= xmlNewNode(NULL
, BAD_CAST
"Re-signISDSDocument");
11204 isds_log_message(context
,
11205 _("Could not build Re-signISDSDocument request"));
11208 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
11210 isds_log_message(context
, _("Could not create ISDS name space"));
11211 xmlFreeNode(request
);
11214 xmlSetNs(request
, isds_ns
);
11216 /* Insert Base64 encoded CMS blob */
11217 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmDoc",
11218 input_data
, input_length
);
11219 if (err
) goto leave
;
11221 /* Send request to server and process response */
11222 err
= send_destroy_request_check_response(context
,
11223 SERVICE_DM_OPERATIONS
, BAD_CAST
"Re-signISDSDocument", &request
,
11224 &response
, NULL
, &map
);
11225 if (err
) goto leave
;
11228 /* Extract re-signed data */
11229 xpath_ctx
= xmlXPathNewContext(response
);
11234 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
11238 result
= xmlXPathEvalExpression(
11239 BAD_CAST
"/isds:Re-signISDSDocumentResponse", xpath_ctx
);
11244 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
11245 isds_log_message(context
,
11246 _("Missing Re-signISDSDocumentResponse element"));
11250 if (result
->nodesetval
->nodeNr
> 1) {
11251 isds_log_message(context
,
11252 _("Multiple Re-signISDSDocumentResponse element"));
11256 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
11257 xmlXPathFreeObject(result
); result
= NULL
;
11259 EXTRACT_STRING("isds:dmResultDoc", string
);
11260 /* Decode non-empty data */
11261 if (NULL
!= string
&& string
[0] != '\0') {
11262 *output_length
= _isds_b64decode(string
, output_data
);
11263 if (*output_length
== (size_t) -1) {
11264 isds_log_message(context
,
11265 _("Error while Base64-decoding re-signed data"));
11270 isds_log_message(context
, _("Server did not send re-signed data"));
11276 if (NULL
!= valid_to
) {
11277 /* Get time stamp expiration date */
11278 EXTRACT_STRING("isds:dmValidTo", string
);
11279 if (NULL
!= string
) {
11280 *valid_to
= calloc(1, sizeof(**valid_to
));
11285 err
= _isds_datestring2tm((xmlChar
*)string
, *valid_to
);
11287 if (err
== IE_NOTSUP
) {
11289 char *string_locale
= _isds_utf82locale(string
);
11290 isds_printf_message(context
,
11291 _("Invalid dmValidTo value: %s"), string_locale
);
11292 free(string_locale
);
11302 xmlXPathFreeObject(result
);
11303 xmlXPathFreeContext(xpath_ctx
);
11305 xmlFreeDoc(response
);
11306 xmlFreeNode(request
);
11307 #else /* not HAVE_LIBCURL */
11314 #undef INSERT_ELEMENT
11315 #undef CHECK_FOR_STRING_LENGTH
11316 #undef INSERT_STRING_ATTRIBUTE
11317 #undef INSERT_ULONGINTNOPTR
11318 #undef INSERT_ULONGINT
11319 #undef INSERT_LONGINT
11320 #undef INSERT_BOOLEAN
11321 #undef INSERT_SCALAR_BOOLEAN
11322 #undef INSERT_STRING
11323 #undef INSERT_STRING_WITH_NS
11324 #undef EXTRACT_STRING_ATTRIBUTE
11325 #undef EXTRACT_ULONGINT
11326 #undef EXTRACT_LONGINT
11327 #undef EXTRACT_BOOLEAN
11328 #undef EXTRACT_STRING
11331 /* Compute hash of message from raw representation and store it into envelope.
11332 * Original hash structure will be destroyed in envelope.
11333 * @context is session context
11334 * @message is message carrying raw XML message blob
11335 * @algorithm is desired hash algorithm to use */
11336 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
11337 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
11338 isds_error err
= IE_SUCCESS
;
11340 void *xml_stream
= NULL
;
11341 size_t xml_stream_length
;
11342 size_t phys_start
, phys_end
;
11343 char *phys_path
= NULL
;
11344 struct isds_hash
*new_hash
= NULL
;
11347 if (!context
) return IE_INVALID_CONTEXT
;
11348 zfree(context
->long_message
);
11349 if (!message
) return IE_INVAL
;
11351 if (!message
->raw
) {
11352 isds_log_message(context
,
11353 _("Message does not carry raw representation"));
11357 switch (message
->raw_type
) {
11358 case RAWTYPE_INCOMING_MESSAGE
:
11360 xml_stream
= message
->raw
;
11361 xml_stream_length
= message
->raw_length
;
11364 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
11365 nsuri
= SISDS_INCOMING_NS
;
11366 xml_stream
= message
->raw
;
11367 xml_stream_length
= message
->raw_length
;
11370 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
11371 nsuri
= SISDS_INCOMING_NS
;
11372 err
= _isds_extract_cms_data(context
,
11373 message
->raw
, message
->raw_length
,
11374 &xml_stream
, &xml_stream_length
);
11375 if (err
) goto leave
;
11378 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
11379 nsuri
= SISDS_OUTGOING_NS
;
11380 xml_stream
= message
->raw
;
11381 xml_stream_length
= message
->raw_length
;
11384 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
11385 nsuri
= SISDS_OUTGOING_NS
;
11386 err
= _isds_extract_cms_data(context
,
11387 message
->raw
, message
->raw_length
,
11388 &xml_stream
, &xml_stream_length
);
11389 if (err
) goto leave
;
11393 isds_log_message(context
, _("Bad raw representation type"));
11399 /* XXX: Hash is computed from original string representing isds:dmDm
11400 * subtree. That means no encoding, white space, xmlns attributes changes.
11401 * In other words, input for hash can be invalid XML stream. */
11402 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
11403 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
11404 PHYSXML_ELEMENT_SEPARATOR
,
11405 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
11406 PHYSXML_ELEMENT_SEPARATOR
11407 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
11411 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
11412 phys_path
, &phys_start
, &phys_end
);
11415 isds_log_message(context
,
11416 _("Substring with isds:dmDM element could not be located "
11417 "in raw message"));
11423 new_hash
= calloc(1, sizeof(*new_hash
));
11428 new_hash
->algorithm
= algorithm
;
11429 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
11432 isds_log_message(context
, _("Could not compute message hash"));
11436 /* Save computed hash */
11437 if (!message
->envelope
) {
11438 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
11439 if (!message
->envelope
) {
11444 isds_hash_free(&message
->envelope
->hash
);
11445 message
->envelope
->hash
= new_hash
;
11449 isds_hash_free(&new_hash
);
11453 if (xml_stream
!= message
->raw
) free(xml_stream
);
11458 /* Compare two hashes.
11459 * @h1 is first hash
11460 * @h2 is another hash
11462 * IE_SUCCESS if hashes equal
11463 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11464 * IE_ENUM if not comparable, but both structures defined
11465 * IE_INVAL if some of the structures are undefined (NULL)
11466 * IE_ERROR if internal error occurs */
11467 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
11468 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
11469 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
11470 if (h1
->length
!= h2
->length
) return IE_ERROR
;
11471 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
11472 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
11474 for (int i
= 0; i
< h1
->length
; i
++) {
11475 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
11476 return IE_NOTEQUAL
;
11482 /* Check message has gone through ISDS by comparing message hash stored in
11483 * ISDS and locally computed hash. You must provide message with valid raw
11484 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11485 * This is convenient wrapper for isds_download_message_hash(),
11486 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11487 * @context is session context
11488 * @message is message with valid raw and envelope member; envelope->hash
11489 * member will be changed during function run. Use envelope on heap only.
11491 * IE_SUCCESS if message originates in ISDS
11492 * IE_NOTEQUAL if message is unknown to ISDS
11493 * other code for other errors */
11494 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
11495 struct isds_message
*message
) {
11496 isds_error err
= IE_SUCCESS
;
11497 struct isds_hash
*downloaded_hash
= NULL
;
11499 if (!context
) return IE_INVALID_CONTEXT
;
11500 zfree(context
->long_message
);
11501 if (!message
) return IE_INVAL
;
11503 if (!message
->envelope
) {
11504 isds_log_message(context
,
11505 _("Given message structure is missing envelope"));
11508 if (!message
->raw
) {
11509 isds_log_message(context
,
11510 _("Given message structure is missing raw representation"));
11514 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
11516 if (err
) goto leave
;
11518 err
= isds_compute_message_hash(context
, message
,
11519 downloaded_hash
->algorithm
);
11520 if (err
) goto leave
;
11522 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
11525 isds_hash_free(&downloaded_hash
);
11530 /* Search for document by document ID in list of documents. IDs are compared
11532 * @documents is list of isds_documents
11533 * @id is document identifier
11534 * @return first matching document or NULL. */
11535 const struct isds_document
*isds_find_document_by_id(
11536 const struct isds_list
*documents
, const char *id
) {
11537 const struct isds_list
*item
;
11538 const struct isds_document
*document
;
11540 for (item
= documents
; item
; item
= item
->next
) {
11541 document
= (struct isds_document
*) item
->data
;
11542 if (!document
) continue;
11544 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
11552 /* Normalize @mime_type to be proper MIME type.
11553 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11554 * guess regular MIME type (e.g. "application/pdf").
11555 * @mime_type is UTF-8 encoded MIME type to fix
11556 * @return original @mime_type if no better interpretation exists, or
11557 * constant static UTF-8 encoded string with proper MIME type. */
11558 const char *isds_normalize_mime_type(const char *mime_type
) {
11559 if (!mime_type
) return NULL
;
11561 for (int offset
= 0;
11562 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
11564 if (!xmlStrcasecmp((const xmlChar
*) mime_type
,
11565 extension_map_mime
[offset
]))
11566 return (const char *) extension_map_mime
[offset
+ 1];
11573 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11574 struct isds_message **message);
11575 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11576 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11577 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11578 struct isds_address **address);
11580 int isds_message_free(struct isds_message **message);
11581 int isds_address_free(struct isds_address **address);
11585 /* Makes known all relevant namespaces to given XPath context
11586 * @xpath_ctx is XPath context
11587 * @message_ns selects proper message name space. Unsigned and signed
11588 * messages and delivery info's differ in prefix and URI. */
11589 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
11590 const message_ns_type message_ns
) {
11591 const xmlChar
*message_namespace
= NULL
;
11593 if (!xpath_ctx
) return IE_ERROR
;
11595 switch(message_ns
) {
11597 message_namespace
= BAD_CAST ISDS1_NS
; break;
11598 case MESSAGE_NS_UNSIGNED
:
11599 message_namespace
= BAD_CAST ISDS_NS
; break;
11600 case MESSAGE_NS_SIGNED_INCOMING
:
11601 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
11602 case MESSAGE_NS_SIGNED_OUTGOING
:
11603 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
11604 case MESSAGE_NS_SIGNED_DELIVERY
:
11605 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
11610 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
11612 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
11614 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"oisds", BAD_CAST OISDS_NS
))
11616 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
11618 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
11620 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))