8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
13 #include "validator.h"
15 #include <gpg-error.h> /* Because of ksba or gpgme */
19 /* Base URL of production ISDS instance */
20 const char isds_locator
[] = "https://ws1.mojedatovaschranka.cz/";
21 const char isds_cert_locator
[] = "https://ws1c.mojedatovaschranka.cz/";
22 const char isds_otp_locator
[] = "https://www.mojedatovaschranka.cz/";
24 /* Base URL of production ISDS instance */
25 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
26 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
27 const char isds_otp_testing_locator
[] = "https://www.czebox.cz/";
29 /* Extension to MIME type map */
30 static xmlChar
*extension_map_mime
[] = {
31 BAD_CAST
"cer", BAD_CAST
"application/x-x509-ca-cert",
32 BAD_CAST
"crt", BAD_CAST
"application/x-x509-ca-cert",
33 BAD_CAST
"der", BAD_CAST
"application/x-x509-ca-cert",
34 BAD_CAST
"doc", BAD_CAST
"application/msword",
35 BAD_CAST
"docx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
36 "wordprocessingml.document",
37 BAD_CAST
"dbf", BAD_CAST
"application/octet-stream",
38 BAD_CAST
"prj", BAD_CAST
"application/octet-stream",
39 BAD_CAST
"qix", BAD_CAST
"application/octet-stream",
40 BAD_CAST
"sbn", BAD_CAST
"application/octet-stream",
41 BAD_CAST
"sbx", BAD_CAST
"application/octet-stream",
42 BAD_CAST
"shp", BAD_CAST
"application/octet-stream",
43 BAD_CAST
"shx", BAD_CAST
"application/octet-stream",
44 BAD_CAST
"dgn", BAD_CAST
"application/octet-stream",
45 BAD_CAST
"dwg", BAD_CAST
"image/vnd.dwg",
46 BAD_CAST
"edi", BAD_CAST
"application/edifact",
47 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.form+xml",
48 BAD_CAST
"gfs", BAD_CAST
"application/xml",
49 BAD_CAST
"gml", BAD_CAST
"application/xml",
50 BAD_CAST
"gif", BAD_CAST
"image/gif",
51 BAD_CAST
"htm", BAD_CAST
"text/html",
52 BAD_CAST
"html", BAD_CAST
"text/html",
53 BAD_CAST
"isdoc", BAD_CAST
"text/isdoc",
54 BAD_CAST
"isdocx", BAD_CAST
"text/isdocx",
55 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
56 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
57 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
58 BAD_CAST
"mpeg", BAD_CAST
"video/mpeg",
59 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
60 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg",
61 BAD_CAST
"mpg", BAD_CAST
"video/mpeg",
62 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
63 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
64 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
65 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
66 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
67 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
68 BAD_CAST
"p7b", BAD_CAST
"application/pkcs7-certificates",
69 BAD_CAST
"p7c", BAD_CAST
"application/pkcs7-mime",
70 BAD_CAST
"p7m", BAD_CAST
"application/pkcs7-mime",
71 BAD_CAST
"p7f", BAD_CAST
"application/pkcs7-signature",
72 BAD_CAST
"p7s", BAD_CAST
"application/pkcs7-signature",
73 BAD_CAST
"pk7", BAD_CAST
"application/pkcs7-mime",
74 BAD_CAST
"png", BAD_CAST
"image/png",
75 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
76 BAD_CAST
"pptx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
77 "presentationml.presentation",
78 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
79 BAD_CAST
"tif", BAD_CAST
"image/tiff",
80 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
81 BAD_CAST
"tsr", BAD_CAST
"application/timestamp-reply",
82 BAD_CAST
"tst", BAD_CAST
"application/timestamp-reply",
83 BAD_CAST
"txt", BAD_CAST
"text/plain",
84 BAD_CAST
"wav", BAD_CAST
"audio/wav",
85 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
86 BAD_CAST
"xlsx", BAD_CAST
"application/vnd.openxmlformats-officedocument."
87 "spreadsheetml.sheet",
88 BAD_CAST
"xml", BAD_CAST
"application/xml",
89 BAD_CAST
"xsd", BAD_CAST
"application/xml",
90 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.form-xml-zip"
93 /* Deallocate structure isds_pki_credentials and NULL it.
94 * Pass-phrase is discarded.
95 * @pki credentials to to free */
96 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
97 if(!pki
|| !*pki
) return;
100 free((*pki
)->certificate
);
103 if ((*pki
)->passphrase
) {
104 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
105 free((*pki
)->passphrase
);
112 /* Free isds_list with all member data.
113 * @list list to free, on return will be NULL */
114 void isds_list_free(struct isds_list
**list
) {
115 struct isds_list
*item
, *next_item
;
117 if (!list
|| !*list
) return;
119 for(item
= *list
; item
; item
= next_item
) {
120 if (item
->destructor
) (item
->destructor
)(&(item
->data
));
121 next_item
= item
->next
;
129 /* Deallocate structure isds_hash and NULL it.
130 * @hash hash to to free */
131 void isds_hash_free(struct isds_hash
**hash
) {
132 if(!hash
|| !*hash
) return;
133 free((*hash
)->value
);
138 /* Deallocate structure isds_PersonName recursively and NULL it */
139 static void isds_PersonName_free(struct isds_PersonName
**person_name
) {
140 if (!person_name
|| !*person_name
) return;
142 free((*person_name
)->pnFirstName
);
143 free((*person_name
)->pnMiddleName
);
144 free((*person_name
)->pnLastName
);
145 free((*person_name
)->pnLastNameAtBirth
);
152 /* Deallocate structure isds_BirthInfo recursively and NULL it */
153 static void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
154 if (!birth_info
|| !*birth_info
) return;
156 free((*birth_info
)->biDate
);
157 free((*birth_info
)->biCity
);
158 free((*birth_info
)->biCounty
);
159 free((*birth_info
)->biState
);
166 /* Deallocate structure isds_Address recursively and NULL it */
167 static void isds_Address_free(struct isds_Address
**address
) {
168 if (!address
|| !*address
) return;
170 free((*address
)->adCity
);
171 free((*address
)->adStreet
);
172 free((*address
)->adNumberInStreet
);
173 free((*address
)->adNumberInMunicipality
);
174 free((*address
)->adZipCode
);
175 free((*address
)->adState
);
182 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
183 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
184 if (!db_owner_info
|| !*db_owner_info
) return;
186 free((*db_owner_info
)->dbID
);
187 free((*db_owner_info
)->dbType
);
188 free((*db_owner_info
)->ic
);
189 isds_PersonName_free(&((*db_owner_info
)->personName
));
190 free((*db_owner_info
)->firmName
);
191 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
192 isds_Address_free(&((*db_owner_info
)->address
));
193 free((*db_owner_info
)->nationality
);
194 free((*db_owner_info
)->email
);
195 free((*db_owner_info
)->telNumber
);
196 free((*db_owner_info
)->identifier
);
197 free((*db_owner_info
)->registryCode
);
198 free((*db_owner_info
)->dbState
);
199 free((*db_owner_info
)->dbEffectiveOVM
);
201 free(*db_owner_info
);
202 *db_owner_info
= NULL
;
205 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
206 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
207 if (!db_user_info
|| !*db_user_info
) return;
209 free((*db_user_info
)->userID
);
210 free((*db_user_info
)->userType
);
211 free((*db_user_info
)->userPrivils
);
212 isds_PersonName_free(&((*db_user_info
)->personName
));
213 isds_Address_free(&((*db_user_info
)->address
));
214 free((*db_user_info
)->biDate
);
215 free((*db_user_info
)->ic
);
216 free((*db_user_info
)->firmName
);
217 free((*db_user_info
)->caStreet
);
218 free((*db_user_info
)->caCity
);
219 free((*db_user_info
)->caZipCode
);
220 free((*db_user_info
)->caState
);
222 zfree(*db_user_info
);
226 /* Deallocate struct isds_event recursively and NULL it */
227 void isds_event_free(struct isds_event
**event
) {
228 if (!event
|| !*event
) return;
230 free((*event
)->time
);
231 free((*event
)->type
);
232 free((*event
)->description
);
237 /* Deallocate struct isds_envelope recursively and NULL it */
238 void isds_envelope_free(struct isds_envelope
**envelope
) {
239 if (!envelope
|| !*envelope
) return;
241 free((*envelope
)->dmID
);
242 free((*envelope
)->dbIDSender
);
243 free((*envelope
)->dmSender
);
244 free((*envelope
)->dmSenderAddress
);
245 free((*envelope
)->dmSenderType
);
246 free((*envelope
)->dmRecipient
);
247 free((*envelope
)->dmRecipientAddress
);
248 free((*envelope
)->dmAmbiguousRecipient
);
249 free((*envelope
)->dmType
);
251 free((*envelope
)->dmOrdinal
);
252 free((*envelope
)->dmMessageStatus
);
253 free((*envelope
)->dmDeliveryTime
);
254 free((*envelope
)->dmAcceptanceTime
);
255 isds_hash_free(&(*envelope
)->hash
);
256 free((*envelope
)->timestamp
);
257 isds_list_free(&(*envelope
)->events
);
259 free((*envelope
)->dmSenderOrgUnit
);
260 free((*envelope
)->dmSenderOrgUnitNum
);
261 free((*envelope
)->dbIDRecipient
);
262 free((*envelope
)->dmRecipientOrgUnit
);
263 free((*envelope
)->dmRecipientOrgUnitNum
);
264 free((*envelope
)->dmToHands
);
265 free((*envelope
)->dmAnnotation
);
266 free((*envelope
)->dmRecipientRefNumber
);
267 free((*envelope
)->dmSenderRefNumber
);
268 free((*envelope
)->dmRecipientIdent
);
269 free((*envelope
)->dmSenderIdent
);
271 free((*envelope
)->dmLegalTitleLaw
);
272 free((*envelope
)->dmLegalTitleYear
);
273 free((*envelope
)->dmLegalTitleSect
);
274 free((*envelope
)->dmLegalTitlePar
);
275 free((*envelope
)->dmLegalTitlePoint
);
277 free((*envelope
)->dmPersonalDelivery
);
278 free((*envelope
)->dmAllowSubstDelivery
);
280 free((*envelope
)->dmOVM
);
281 free((*envelope
)->dmPublishOwnID
);
288 /* Deallocate struct isds_message recursively and NULL it */
289 void isds_message_free(struct isds_message
**message
) {
290 if (!message
|| !*message
) return;
292 free((*message
)->raw
);
293 isds_envelope_free(&((*message
)->envelope
));
294 isds_list_free(&((*message
)->documents
));
295 xmlFreeDoc((*message
)->xml
); (*message
)->xml
= NULL
;
302 /* Deallocate struct isds_document recursively and NULL it */
303 void isds_document_free(struct isds_document
**document
) {
304 if (!document
|| !*document
) return;
306 if (!(*document
)->is_xml
) {
307 free((*document
)->data
);
309 free((*document
)->dmMimeType
);
310 free((*document
)->dmFileGuid
);
311 free((*document
)->dmUpFileGuid
);
312 free((*document
)->dmFileDescr
);
313 free((*document
)->dmFormat
);
320 /* Deallocate struct isds_message_copy recursively and NULL it */
321 void isds_message_copy_free(struct isds_message_copy
**copy
) {
322 if (!copy
|| !*copy
) return;
324 free((*copy
)->dbIDRecipient
);
325 free((*copy
)->dmRecipientOrgUnit
);
326 free((*copy
)->dmRecipientOrgUnitNum
);
327 free((*copy
)->dmToHands
);
329 free((*copy
)->dmStatus
);
336 /* Deallocate struct isds_message_status_change recursively and NULL it */
337 void isds_message_status_change_free(
338 struct isds_message_status_change
**message_status_change
) {
339 if (!message_status_change
|| !*message_status_change
) return;
341 free((*message_status_change
)->dmID
);
342 free((*message_status_change
)->time
);
343 free((*message_status_change
)->dmMessageStatus
);
345 zfree(*message_status_change
);
349 /* Deallocate struct isds_approval recursively and NULL it */
350 void isds_approval_free(struct isds_approval
**approval
) {
351 if (!approval
|| !*approval
) return;
353 free((*approval
)->refference
);
359 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
360 * The email string is deallocated too. */
361 void isds_credentials_delivery_free(
362 struct isds_credentials_delivery
**credentials_delivery
) {
363 if (!credentials_delivery
|| !*credentials_delivery
) return;
365 free((*credentials_delivery
)->email
);
366 free((*credentials_delivery
)->token
);
367 free((*credentials_delivery
)->new_user_name
);
369 zfree(*credentials_delivery
);
373 /* Deallocate struct isds_commercial_permission recursively and NULL it */
374 void isds_commercial_permission_free(
375 struct isds_commercial_permission
**permission
) {
376 if (NULL
== permission
|| NULL
== *permission
) return;
378 free((*permission
)->recipient
);
379 free((*permission
)->payer
);
380 free((*permission
)->expiration
);
381 free((*permission
)->count
);
382 free((*permission
)->reply_identifier
);
388 /* *DUP_OR_ERROR macros needs error label */
389 #define STRDUP_OR_ERROR(new, template) { \
393 (new) = strdup(template); \
394 if (!new) goto error; \
398 #define FLATDUP_OR_ERROR(new, template) { \
402 (new) = malloc(sizeof(*(new))); \
403 if (!new) goto error; \
404 memcpy((new), (template), sizeof(*(template))); \
408 /* Copy structure isds_pki_credentials recursively. */
409 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
410 const struct isds_pki_credentials
*template) {
411 struct isds_pki_credentials
*new = NULL
;
413 if(!template) return NULL
;
415 new = calloc(1, sizeof(*new));
416 if (!new) return NULL
;
418 STRDUP_OR_ERROR(new->engine
, template->engine
);
419 new->certificate_format
= template->certificate_format
;
420 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
421 new->key_format
= template->key_format
;
422 STRDUP_OR_ERROR(new->key
, template->key
);
423 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
428 isds_pki_credentials_free(&new);
433 /* Copy structure isds_PersonName recursively */
434 struct isds_PersonName
*isds_PersonName_duplicate(
435 const struct isds_PersonName
*template) {
436 struct isds_PersonName
*new = NULL
;
438 if (!template) return NULL
;
440 new = calloc(1, sizeof(*new));
441 if (!new) return NULL
;
443 STRDUP_OR_ERROR(new->pnFirstName
, template->pnFirstName
);
444 STRDUP_OR_ERROR(new->pnMiddleName
, template->pnMiddleName
);
445 STRDUP_OR_ERROR(new->pnLastName
, template->pnLastName
);
446 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, template->pnLastNameAtBirth
);
451 isds_PersonName_free(&new);
456 /* Copy structure isds_BirthInfo recursively */
457 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
458 const struct isds_BirthInfo
*template) {
459 struct isds_BirthInfo
*new = NULL
;
461 if (!template) return NULL
;
463 new = calloc(1, sizeof(*new));
464 if (!new) return NULL
;
466 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
467 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
468 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
469 STRDUP_OR_ERROR(new->biState
, template->biState
);
474 isds_BirthInfo_free(&new);
479 /* Copy structure isds_Address recursively */
480 struct isds_Address
*isds_Address_duplicate(
481 const struct isds_Address
*template) {
482 struct isds_Address
*new = NULL
;
484 if (!template) return NULL
;
486 new = calloc(1, sizeof(*new));
487 if (!new) return NULL
;
489 STRDUP_OR_ERROR(new->adCity
, template->adCity
);
490 STRDUP_OR_ERROR(new->adStreet
, template->adStreet
);
491 STRDUP_OR_ERROR(new->adNumberInStreet
, template->adNumberInStreet
);
492 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
493 template->adNumberInMunicipality
);
494 STRDUP_OR_ERROR(new->adZipCode
, template->adZipCode
);
495 STRDUP_OR_ERROR(new->adState
, template->adState
);
500 isds_Address_free(&new);
505 /* Copy structure isds_DbOwnerInfo recursively */
506 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
507 const struct isds_DbOwnerInfo
*template) {
508 struct isds_DbOwnerInfo
*new = NULL
;
509 if (!template) return NULL
;
511 new = calloc(1, sizeof(*new));
512 if (!new) return NULL
;
514 STRDUP_OR_ERROR(new->dbID
, template->dbID
);
515 FLATDUP_OR_ERROR(new->dbType
, template->dbType
);
516 STRDUP_OR_ERROR(new->ic
, template->ic
);
518 if (template->personName
) {
519 if (!(new->personName
=
520 isds_PersonName_duplicate(template->personName
)))
524 STRDUP_OR_ERROR(new->firmName
, template->firmName
);
526 if (template->birthInfo
) {
527 if (!(new->birthInfo
=
528 isds_BirthInfo_duplicate(template->birthInfo
)))
532 if (template->address
) {
533 if (!(new->address
= isds_Address_duplicate(template->address
)))
537 STRDUP_OR_ERROR(new->nationality
, template->nationality
);
538 STRDUP_OR_ERROR(new->email
, template->email
);
539 STRDUP_OR_ERROR(new->telNumber
, template->telNumber
);
540 STRDUP_OR_ERROR(new->identifier
, template->identifier
);
541 STRDUP_OR_ERROR(new->registryCode
, template->registryCode
);
542 FLATDUP_OR_ERROR(new->dbState
, template->dbState
);
543 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, template->dbEffectiveOVM
);
544 FLATDUP_OR_ERROR(new->dbOpenAddressing
, template->dbOpenAddressing
);
549 isds_DbOwnerInfo_free(&new);
554 /* Copy structure isds_DbUserInfo recursively */
555 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
556 const struct isds_DbUserInfo
*template) {
557 struct isds_DbUserInfo
*new = NULL
;
558 if (!template) return NULL
;
560 new = calloc(1, sizeof(*new));
561 if (!new) return NULL
;
563 STRDUP_OR_ERROR(new->userID
, template->userID
);
564 FLATDUP_OR_ERROR(new->userType
, template->userType
);
565 FLATDUP_OR_ERROR(new->userPrivils
, template->userPrivils
);
567 if (template->personName
) {
568 if (!(new->personName
=
569 isds_PersonName_duplicate(template->personName
)))
573 if (template->address
) {
574 if (!(new->address
= isds_Address_duplicate(template->address
)))
578 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
579 STRDUP_OR_ERROR(new->ic
, template->ic
);
580 STRDUP_OR_ERROR(new->firmName
, template->firmName
);
581 STRDUP_OR_ERROR(new->caStreet
, template->caStreet
);
582 STRDUP_OR_ERROR(new->caCity
, template->caCity
);
583 STRDUP_OR_ERROR(new->caZipCode
, template->caZipCode
);
584 STRDUP_OR_ERROR(new->caState
, template->caState
);
589 isds_DbUserInfo_free(&new);
593 #undef FLATDUP_OR_ERROR
594 #undef STRDUP_OR_ERROR
597 /* Logs libxml2 errors. Should be registered to libxml2 library.
598 * @ctx is unused currently
599 * @msg is printf-like formated message from libxml2 (UTF-8?)
600 * @... are variadic arguments for @msg */
601 static void log_xml(void *ctx
, const char *msg
, ...) {
608 isds_vasprintf(&text
, msg
, ap
);
612 isds_log(ILF_XML
, ILL_ERR
, "%s", text
);
617 /* Initialize ISDS library.
618 * Global function, must be called before other functions.
619 * If it fails you can not use ISDS library and must call isds_cleanup() to
620 * free partially initialized global variables. */
621 isds_error
isds_init(void) {
622 /* NULL global variables */
623 log_facilities
= ILF_ALL
;
624 log_level
= ILL_WARNING
;
626 log_callback_data
= NULL
;
629 /* Initialize gettext */
630 bindtextdomain(PACKAGE
, LOCALEDIR
);
634 /* Initialize CURL */
635 if (curl_global_init(CURL_GLOBAL_ALL
)) {
636 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
639 #endif /* HAVE_LIBCURL */
641 /* Initialize gpg-error because of gpgme and ksba */
642 if (gpg_err_init()) {
643 isds_log(ILF_ISDS
, ILL_CRIT
,
644 _("gpg-error library initialization failed\n"));
648 /* Initialize GPGME */
649 if (_isds_init_gpgme(&version_gpgme
)) {
650 isds_log(ILF_ISDS
, ILL_CRIT
,
651 _("GPGME library initialization failed\n"));
655 /* Initialize gcrypt */
656 if (_isds_init_gcrypt(&version_gcrypt
)) {
657 isds_log(ILF_ISDS
, ILL_CRIT
,
658 _("gcrypt library initialization failed\n"));
662 /* This can _exit() current program. Find not so assertive check. */
664 xmlSetGenericErrorFunc(NULL
, log_xml
);
667 if (_isds_init_expat(&version_expat
)) {
668 isds_log(ILF_ISDS
, ILL_CRIT
,
669 _("expat library initialization failed\n"));
673 /* Allocate global variables */
680 /* Deinitialize ISDS library.
681 * Global function, must be called as last library function. */
682 isds_error
isds_cleanup(void) {
688 curl_global_cleanup();
695 /* Return version string of this library. Version of dependencies can be
696 * embedded. Do no try to parse it. You must free it. */
697 char *isds_version(void) {
700 isds_asprintf(&buffer
,
702 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
704 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
710 version_gpgme
, version_gcrypt
,
711 version_expat
, xmlParserVersion
);
716 /* Return text description of ISDS error */
717 const char *isds_strerror(const isds_error error
) {
720 return(_("Success")); break;
722 return(_("Unspecified error")); break;
724 return(_("Not supported")); break;
726 return(_("Invalid value")); break;
727 case IE_INVALID_CONTEXT
:
728 return(_("Invalid context")); break;
729 case IE_NOT_LOGGED_IN
:
730 return(_("Not logged in")); break;
731 case IE_CONNECTION_CLOSED
:
732 return(_("Connection closed")); break;
734 return(_("Timed out")); break;
736 return(_("Not exist")); break;
738 return(_("Out of memory")); break;
740 return(_("Network problem")); break;
742 return(_("HTTP problem")); break;
744 return(_("SOAP problem")); break;
746 return(_("XML problem")); break;
748 return(_("ISDS server problem")); break;
750 return(_("Invalid enum value")); break;
752 return(_("Invalid date value")); break;
754 return(_("Too big")); break;
756 return(_("Too small")); break;
758 return(_("Value not unique")); break;
760 return(_("Values not equal")); break;
761 case IE_PARTIAL_SUCCESS
:
762 return(_("Some suboperations failed")); break;
764 return(_("Operation aborted")); break;
766 return(_("Unknown error"));
771 /* Create ISDS context.
772 * Each context can be used for different sessions to (possibly) different
773 * ISDS server with different credentials. */
774 struct isds_ctx
*isds_ctx_create(void) {
775 struct isds_ctx
*context
;
776 context
= malloc(sizeof(*context
));
777 if (context
) memset(context
, 0, sizeof(*context
));
782 /* Close possibly opened connection to Czech POINT document deposit without
783 * resetting long_message buffer.
784 * XXX: Do not use czp_close_connection() if you do not want to destroy log
786 * @context is Czech POINT session context. */
787 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
788 if (!context
) return IE_INVALID_CONTEXT
;
789 _isds_close_connection(context
);
794 /* Discard credentials.
795 * @context is ISDS context
796 * @discard_saved_username is true for removing saved username, false for
798 * Only that. It does not cause log out, connection close or similar. */
799 _hidden isds_error
_isds_discard_credentials(struct isds_ctx
*context
,
800 _Bool discard_saved_username
) {
801 if(!context
) return IE_INVALID_CONTEXT
;
803 if (context
->username
) {
804 memset(context
->username
, 0, strlen(context
->username
));
805 zfree(context
->username
);
807 if (context
->password
) {
808 memset(context
->password
, 0, strlen(context
->password
));
809 zfree(context
->password
);
811 isds_pki_credentials_free(&context
->pki_credentials
);
812 if (discard_saved_username
&& context
->saved_username
) {
813 memset(context
->saved_username
, 0, strlen(context
->saved_username
));
814 zfree(context
->saved_username
);
819 #endif /* HAVE_LIBCURL */
822 /* Destroy ISDS context and free memory.
823 * @context will be NULLed on success. */
824 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
825 if (!context
|| !*context
) {
826 return IE_INVALID_CONTEXT
;
830 /* Discard credentials and close connection */
831 switch ((*context
)->type
) {
832 case CTX_TYPE_NONE
: break;
833 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
835 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
836 czp_do_close_connection(*context
); break;
840 _isds_discard_credentials(*context
, 1);
842 /* Free other structures */
843 free((*context
)->tls_verify_server
);
844 free((*context
)->tls_ca_file
);
845 free((*context
)->tls_ca_dir
);
846 free((*context
)->tls_crl_file
);
847 #endif /* HAVE_LIBCURL */
848 free((*context
)->long_message
);
856 /* Return long message text produced by library function, e.g. detailed error
857 * message. Returned pointer is only valid until new library function is
858 * called for the same context. Could be NULL, especially if NULL context is
859 * supplied. Return string is locale encoded. */
860 char *isds_long_message(const struct isds_ctx
*context
) {
861 if (!context
) return NULL
;
862 return context
->long_message
;
866 /* Stores message into context' long_message buffer.
867 * Application can pick the message up using isds_long_message().
868 * NULL @message truncates the buffer but does not deallocate it.
869 * @message is coded in locale encoding */
870 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
871 const char *message
) {
875 if (!context
) return IE_INVALID_CONTEXT
;
877 /* FIXME: Check for integer overflow */
878 length
= 1 + ((message
) ? strlen(message
) : 0);
879 buffer
= realloc(context
->long_message
, length
);
880 if (!buffer
) return IE_NOMEM
;
883 strcpy(buffer
, message
);
887 context
->long_message
= buffer
;
892 /* Appends message into context' long_message buffer.
893 * Application can pick the message up using isds_long_message().
894 * NULL message has void effect. */
895 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
896 const char *message
) {
898 size_t old_length
, length
;
900 if (!context
) return IE_INVALID_CONTEXT
;
901 if (!message
) return IE_SUCCESS
;
902 if (!context
->long_message
)
903 return isds_log_message(context
, message
);
905 old_length
= strlen(context
->long_message
);
906 /* FIXME: Check for integer overflow */
907 length
= 1 + old_length
+ strlen(message
);
908 buffer
= realloc(context
->long_message
, length
);
909 if (!buffer
) return IE_NOMEM
;
911 strcpy(buffer
+ old_length
, message
);
913 context
->long_message
= buffer
;
918 /* Stores formatted message into context' long_message buffer.
919 * Application can pick the message up using isds_long_message(). */
920 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
921 const char *format
, ...) {
925 if (!context
) return IE_INVALID_CONTEXT
;
926 va_start(ap
, format
);
927 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
930 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
935 * @facilities is bit mask of isds_log_facility values,
936 * @level is verbosity level. */
937 void isds_set_logging(const unsigned int facilities
,
938 const isds_log_level level
) {
939 log_facilities
= facilities
;
944 /* Register callback function libisds calls when new global log message is
945 * produced by library. Library logs to stderr by default.
946 * @callback is function provided by application libisds will call. See type
947 * definition for @callback argument explanation. Pass NULL to revert logging to
949 * @data is application specific data @callback gets as last argument */
950 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
951 log_callback
= callback
;
952 log_callback_data
= data
;
956 /* Log @message in class @facility with log @level into global log. @message
957 * is printf(3) formatting string, variadic arguments may be necessary.
958 * For debugging purposes. */
959 _hidden isds_error
isds_log(const isds_log_facility facility
,
960 const isds_log_level level
, const char *message
, ...) {
965 if (level
> log_level
) return IE_SUCCESS
;
966 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
967 if (!message
) return IE_INVAL
;
970 /* Pass message to application supplied callback function */
971 va_start(ap
, message
);
972 length
= isds_vasprintf(&buffer
, message
, ap
);
979 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
983 /* Default: Log it to stderr */
984 va_start(ap
, message
);
985 vfprintf(stderr
, message
, ap
);
987 /* Line buffered printf is default.
995 /* Set timeout in milliseconds for each network job like connecting to server
996 * or sending message. Use 0 to disable timeout limits. */
997 isds_error
isds_set_timeout(struct isds_ctx
*context
,
998 const unsigned int timeout
) {
999 if (!context
) return IE_INVALID_CONTEXT
;
1000 zfree(context
->long_message
);
1003 context
->timeout
= timeout
;
1005 if (context
->curl
) {
1008 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
1010 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1011 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
1014 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
1015 context
->timeout
/ 1000);
1016 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1017 if (curl_err
) return IE_ERROR
;
1021 #else /* not HAVE_LIBCURL */
1027 /* Register callback function libisds calls periodically during HTTP data
1029 * @context is session context
1030 * @callback is function provided by application libisds will call. See type
1031 * definition for @callback argument explanation.
1032 * @data is application specific data @callback gets as last argument */
1033 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
1034 isds_progress_callback callback
, void *data
) {
1035 if (!context
) return IE_INVALID_CONTEXT
;
1036 zfree(context
->long_message
);
1039 context
->progress_callback
= callback
;
1040 context
->progress_callback_data
= data
;
1043 #else /* not HAVE_LIBCURL */
1049 /* Change context settings.
1050 * @context is context which setting will be applied to
1051 * @option is name of option. It determines the type of last argument. See
1052 * isds_option definition for more info.
1053 * @... is value of new setting. Type is determined by @option
1055 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
1057 isds_error err
= IE_SUCCESS
;
1060 char *pointer
, *string
;
1063 if (!context
) return IE_INVALID_CONTEXT
;
1064 zfree(context
->long_message
);
1066 va_start(ap
, option
);
1068 #define REPLACE_VA_BOOLEAN(destination) { \
1069 if (!(destination)) { \
1070 (destination) = malloc(sizeof(*(destination))); \
1071 if (!(destination)) { \
1072 err = IE_NOMEM; goto leave; \
1075 *(destination) = (_Bool) !!va_arg(ap, int); \
1078 #define REPLACE_VA_STRING(destination) { \
1079 string = va_arg(ap, char *); \
1081 pointer = realloc((destination), 1 + strlen(string)); \
1082 if (!pointer) { err = IE_NOMEM; goto leave; } \
1083 strcpy(pointer, string); \
1084 (destination) = pointer; \
1086 free(destination); \
1087 (destination) = NULL; \
1092 case IOPT_TLS_VERIFY_SERVER
:
1094 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
1096 err
= IE_NOTSUP
; goto leave
;
1099 case IOPT_TLS_CA_FILE
:
1101 REPLACE_VA_STRING(context
->tls_ca_file
);
1103 err
= IE_NOTSUP
; goto leave
;
1106 case IOPT_TLS_CA_DIRECTORY
:
1108 REPLACE_VA_STRING(context
->tls_ca_dir
);
1110 err
= IE_NOTSUP
; goto leave
;
1113 case IOPT_TLS_CRL_FILE
:
1115 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1116 REPLACE_VA_STRING(context
->tls_crl_file
);
1118 isds_log_message(context
,
1119 _("Curl library does not support CRL definition"));
1121 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1123 err
= IE_NOTSUP
; goto leave
;
1124 #endif /* not HAVE_LIBCURL */
1126 case IOPT_NORMALIZE_MIME_TYPE
:
1127 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
1131 err
= IE_ENUM
; goto leave
;
1134 #undef REPLACE_VA_STRING
1135 #undef REPLACE_VA_BOOLEAN
1144 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1145 * Destination for NULL argument will not be touched.
1146 * Destination pointers must be freed before calling this function.
1147 * If @username is @context->saved_username, the saved_username will not be
1148 * replaced. The saved_username is clobbered only if context has set otp
1150 * Return IE_SUCCESS on success. */
1151 static isds_error
_isds_store_credentials(struct isds_ctx
*context
,
1152 const char *username
, const char *password
,
1153 const struct isds_pki_credentials
*pki_credentials
) {
1154 if (NULL
== context
) return IE_INVALID_CONTEXT
;
1156 /* FIXME: mlock password
1157 * (I have a library) */
1160 context
->username
= strdup(username
);
1161 if (NULL
!= context
->otp
&& context
->saved_username
!= username
)
1162 context
->saved_username
= strdup(username
);
1165 if (NULL
== context
->otp
)
1166 context
->password
= strdup(password
);
1168 context
->password
= _isds_astrcat(password
, context
->otp
->otp_code
);
1170 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1172 if ((NULL
!= username
&& NULL
== context
->username
) ||
1173 (NULL
!= password
&& NULL
== context
->password
) ||
1174 (NULL
!= pki_credentials
&& NULL
== context
->pki_credentials
) ||
1175 (NULL
!= context
->otp
&& NULL
!= context
->username
&&
1176 NULL
== context
->saved_username
)) {
1185 /* Connect and log into ISDS server.
1186 * All required arguments will be copied, you do not have to keep them after
1188 * ISDS supports six different authentication methods. Exact method is
1189 * selected on @username, @password, @pki_credentials, and @otp arguments:
1190 * - If @pki_credentials == NULL, @username and @password must be supplied
1192 * - If @otp == NULL, simple authentication by username and password will
1194 * - If @otp != NULL, authentication by username and password and OTP
1196 * - If @pki_credentials != NULL, then
1197 * - If @username == NULL, only certificate will be used
1198 * - If @username != NULL, then
1199 * - If @password == NULL, then certificate will be used and
1200 * @username shifts meaning to box ID. This is used for hosted
1202 * - Otherwise all three arguments will be used.
1203 * Please note, that different cases require different certificate type
1204 * (system qualified one or commercial non qualified one). This library
1205 * does not check such political issues. Please see ISDS Specification
1207 * @url is base address of ISDS web service. Pass extern isds_locator
1208 * variable to use production ISDS instance without client certificate
1209 * authentication (or extern isds_cert_locator with client certificate
1210 * authentication or extern isds_otp_locators with OTP authentication).
1211 * Passing NULL has the same effect, autoselection between isds_locator,
1212 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1213 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1214 * isds_otp_testing_locator) variable to select testing instance.
1215 * @username is user name of ISDS user or box ID
1216 * @password is user's secret password
1217 * @pki_credentials defines public key cryptographic material to use in client
1219 * @otp selects one-time password authentication method to use, defines OTP
1220 * code (if known) and returns fine grade resolution of OTP procedure.
1222 * IE_SUCCESS if authentication succeeds
1223 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1224 * requested, fine grade reason will be set into @otp->resolution. Error
1225 * message from server can be obtained by isds_long_message() call.
1226 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1227 * server has sent OTP code through side channel. Application is expected to
1228 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1229 * this call to complete second phase of TOTP authentication;
1230 * or other appropriate error. */
1231 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1232 const char *username
, const char *password
,
1233 const struct isds_pki_credentials
*pki_credentials
,
1234 struct isds_otp
*otp
) {
1236 isds_error err
= IE_NOT_LOGGED_IN
;
1237 isds_error soap_err
;
1238 xmlNsPtr isds_ns
= NULL
;
1239 xmlNodePtr request
= NULL
;
1240 xmlNodePtr response
= NULL
;
1241 #endif /* HAVE_LIBCURL */
1243 if (!context
) return IE_INVALID_CONTEXT
;
1244 zfree(context
->long_message
);
1247 /* Close connection if already logged in */
1248 if (context
->curl
) {
1249 _isds_close_connection(context
);
1252 /* Store configuration */
1253 context
->type
= CTX_TYPE_ISDS
;
1254 zfree(context
->url
);
1256 /* Mangle base URI according to requested authentication method */
1257 if (NULL
== pki_credentials
) {
1258 isds_log(ILF_SEC
, ILL_INFO
,
1259 _("Selected authentication method: no certificate, "
1260 "username and password\n"));
1261 if (!username
|| !password
) {
1262 isds_log_message(context
,
1263 _("Both username and password must be supplied"));
1268 if (NULL
== context
->otp
) {
1269 /* Default locator is official system (without certificate or
1271 context
->url
= strdup((NULL
!= url
) ? url
: isds_locator
);
1273 const char *authenticator_uri
= NULL
;
1274 if (!url
) url
= isds_otp_locator
;
1275 otp
->resolution
= OTP_RESOLUTION_UNKNOWN
;
1276 switch (context
->otp
->method
) {
1278 isds_log(ILF_SEC
, ILL_INFO
,
1279 _("Selected authentication method: "
1280 "HMAC-based one-time password\n"));
1282 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1285 isds_log(ILF_SEC
, ILL_INFO
,
1286 _("Selected authentication method: "
1287 "Time-based one-time password\n"));
1288 if (context
->otp
->otp_code
== NULL
) {
1289 isds_log(ILF_SEC
, ILL_INFO
,
1290 _("OTP code has not been provided by "
1291 "application, requesting server for "
1294 "%1$sas/processLogin?type=totp&sendSms=true&"
1297 isds_log(ILF_SEC
, ILL_INFO
,
1298 _("OTP code has been provided by "
1299 "application, not requesting server "
1302 "%1$sas/processLogin?type=totp&"
1307 isds_log_message(context
,
1308 _("Unknown one-time password authentication "
1309 "method requested by application"));
1312 if (-1 == isds_asprintf(&context
->url
, authenticator_uri
, url
))
1316 /* Default locator is official system (with client certificate) */
1317 context
->otp
= NULL
;
1318 if (!url
) url
= isds_cert_locator
;
1321 isds_log(ILF_SEC
, ILL_INFO
,
1322 _("Selected authentication method: system certificate, "
1323 "no username and no password\n"));
1325 context
->url
= _isds_astrcat(url
, "cert/");
1328 isds_log(ILF_SEC
, ILL_INFO
,
1329 _("Selected authentication method: system certificate, "
1330 "box ID and no password\n"));
1331 context
->url
= _isds_astrcat(url
, "hspis/");
1333 isds_log(ILF_SEC
, ILL_INFO
,
1334 _("Selected authentication method: commercial "
1335 "certificate, username and password\n"));
1336 context
->url
= _isds_astrcat(url
, "certds/");
1340 if (!(context
->url
))
1343 /* Prepare CURL handle */
1344 context
->curl
= curl_easy_init();
1345 if (!(context
->curl
))
1348 /* Build log-in request */
1349 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1351 isds_log_message(context
, _("Could not build ISDS log-in request"));
1354 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1356 isds_log_message(context
, _("Could not create ISDS name space"));
1357 xmlFreeNode(request
);
1360 xmlSetNs(request
, isds_ns
);
1362 /* Store credentials */
1363 _isds_discard_credentials(context
, 1);
1364 if (_isds_store_credentials(context
, username
, password
, pki_credentials
)) {
1365 _isds_discard_credentials(context
, 1);
1366 xmlFreeNode(request
);
1370 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1373 /* Send log-in request */
1374 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1377 /* Revert context URL from OTP authentication service URL to OTP web
1378 * service base URL for subsequent calls. Potenial isds_login() retry
1379 * will re-set context URL again. */
1380 zfree(context
->url
);
1381 context
->url
= _isds_astrcat(url
, "apps/");
1382 if (context
->url
== NULL
) {
1383 soap_err
= IE_NOMEM
;
1387 /* Remove credentials */
1388 _isds_discard_credentials(context
, 0);
1390 /* Destroy log-in request */
1391 xmlFreeNode(request
);
1394 xmlFreeNodeList(response
);
1395 _isds_close_connection(context
);
1399 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1400 * authentication succeeded if soap_err == IE_SUCCESS */
1403 xmlFreeNodeList(response
);
1406 isds_log(ILF_ISDS
, ILL_DEBUG
,
1407 _("User %s has been logged into server %s successfully\n"),
1410 #else /* not HAVE_LIBCURL */
1416 /* Log out from ISDS server discards credentials and connection configuration. */
1417 isds_error
isds_logout(struct isds_ctx
*context
) {
1418 if (!context
) return IE_INVALID_CONTEXT
;
1419 zfree(context
->long_message
);
1422 if (context
->curl
) {
1423 if (context
->otp
!= NULL
) {
1424 isds_error err
= _isds_invalidate_otp_cookie(context
);
1425 if (err
) return err
;
1428 /* Close connection */
1429 _isds_close_connection(context
);
1431 /* Discard credentials for sure. They should not survive isds_login(),
1432 * even successful .*/
1433 _isds_discard_credentials(context
, 1);
1434 zfree(context
->url
);
1436 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1438 _isds_discard_credentials(context
, 1);
1441 #else /* not HAVE_LIBCURL */
1447 /* Verify connection to ISDS is alive and server is responding.
1448 * Sent dummy request to ISDS and expect dummy response. */
1449 isds_error
isds_ping(struct isds_ctx
*context
) {
1451 isds_error soap_err
;
1452 xmlNsPtr isds_ns
= NULL
;
1453 xmlNodePtr request
= NULL
;
1454 xmlNodePtr response
= NULL
;
1455 #endif /* HAVE_LIBCURL */
1457 if (!context
) return IE_INVALID_CONTEXT
;
1458 zfree(context
->long_message
);
1461 /* Check if connection is established */
1462 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1465 /* Build dummy request */
1466 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1468 isds_log_message(context
, _("Could build ISDS dummy request"));
1471 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1473 isds_log_message(context
, _("Could not create ISDS name space"));
1474 xmlFreeNode(request
);
1477 xmlSetNs(request
, isds_ns
);
1479 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1481 /* Sent dummy request */
1482 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1484 /* Destroy log-in request */
1485 xmlFreeNode(request
);
1488 isds_log(ILF_ISDS
, ILL_DEBUG
,
1489 _("ISDS server could not be contacted\n"));
1490 xmlFreeNodeList(response
);
1494 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1495 * authentication succeeded if soap_err == IE_SUCCESS */
1496 /* TODO: ISDS documentation does not specify response body.
1497 * However real server sends back DummyOperationResponse */
1500 xmlFreeNodeList(response
);
1502 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1505 #else /* not HAVE_LIBCURL */
1511 /* Send bogus request to ISDS.
1512 * Just for test purposes */
1513 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1516 xmlNsPtr isds_ns
= NULL
;
1517 xmlNodePtr request
= NULL
;
1518 xmlDocPtr response
= NULL
;
1519 xmlChar
*code
= NULL
, *message
= NULL
;
1522 if (!context
) return IE_INVALID_CONTEXT
;
1523 zfree(context
->long_message
);
1526 /* Check if connection is established */
1527 if (!context
->curl
) {
1528 /* Testing printf message */
1529 isds_printf_message(context
, "%s", _("I said connection closed"));
1530 return IE_CONNECTION_CLOSED
;
1534 /* Build dummy request */
1535 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1537 isds_log_message(context
, _("Could build ISDS bogus request"));
1540 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1542 isds_log_message(context
, _("Could not create ISDS name space"));
1543 xmlFreeNode(request
);
1546 xmlSetNs(request
, isds_ns
);
1548 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1550 /* Sent bogus request */
1551 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1553 /* Destroy request */
1554 xmlFreeNode(request
);
1557 isds_log(ILF_ISDS
, ILL_DEBUG
,
1558 _("Processing ISDS response on bogus request failed\n"));
1559 xmlFreeDoc(response
);
1563 /* Check for response status */
1564 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1565 &code
, &message
, NULL
);
1567 isds_log(ILF_ISDS
, ILL_DEBUG
,
1568 _("ISDS response on bogus request is missing status\n"));
1571 xmlFreeDoc(response
);
1574 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1575 char *code_locale
= _isds_utf82locale((char*)code
);
1576 char *message_locale
= _isds_utf82locale((char*)message
);
1577 isds_log(ILF_ISDS
, ILL_DEBUG
,
1578 _("Server refused bogus request (code=%s, message=%s)\n"),
1579 code_locale
, message_locale
);
1580 /* XXX: Literal error messages from ISDS are Czech messages
1581 * (English sometimes) in UTF-8. It's hard to catch them for
1582 * translation. Successfully gettextized would return in locale
1583 * encoding, unsuccessfully translated would pass in UTF-8. */
1584 isds_log_message(context
, message_locale
);
1586 free(message_locale
);
1589 xmlFreeDoc(response
);
1596 xmlFreeDoc(response
);
1598 isds_log(ILF_ISDS
, ILL_DEBUG
,
1599 _("Bogus message accepted by server. This should not happen.\n"));
1602 #else /* not HAVE_LIBCURL */
1609 /* Serialize XML subtree to buffer preserving XML indentation.
1610 * @context is session context
1611 * @subtree is XML element to be serialized (with children)
1612 * @buffer is automatically reallocated buffer where serialize to
1613 * @length is size of serialized stream in bytes
1614 * @return standard error code, free @buffer in case of error */
1615 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1616 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1617 isds_error err
= IE_SUCCESS
;
1618 xmlBufferPtr xml_buffer
= NULL
;
1619 xmlSaveCtxtPtr save_ctx
= NULL
;
1620 xmlDocPtr subtree_doc
= NULL
;
1621 xmlNodePtr subtree_copy
;
1625 if (!context
) return IE_INVALID_CONTEXT
;
1626 if (!buffer
) return IE_INVAL
;
1628 if (!subtree
|| !length
) return IE_INVAL
;
1630 /* Make temporary XML document with @subtree root element */
1631 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1632 * It can result in not well-formed on invalid XML tree (e.g. name space
1633 * prefix definition can miss. */
1636 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1638 isds_log_message(context
, _("Could not build temporary document"));
1643 /* XXX: Copy subtree and attach the copy to document.
1644 * One node can not bee attached into more document at the same time.
1645 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1647 * XXX: Check xmlSaveTree() too. */
1648 subtree_copy
= xmlCopyNodeList(subtree
);
1649 if (!subtree_copy
) {
1650 isds_log_message(context
, _("Could not copy subtree"));
1654 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1656 /* Only this way we get namespace definition as @xmlns:isds,
1657 * otherwise we get namespace prefix without definition */
1658 /* FIXME: Don't overwrite original default namespace */
1659 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1661 isds_log_message(context
, _("Could not create ISDS name space"));
1665 xmlSetNs(subtree_copy
, isds_ns
);
1668 /* Serialize the document into buffer */
1669 xml_buffer
= xmlBufferCreate();
1671 isds_log_message(context
, _("Could not create xmlBuffer"));
1675 /* Last argument 0 means to not format the XML tree */
1676 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1678 isds_log_message(context
, _("Could not create XML serializer"));
1682 /* XXX: According LibXML documentation, this function does not return
1683 * meaningful value yet */
1684 xmlSaveDoc(save_ctx
, subtree_doc
);
1685 if (-1 == xmlSaveFlush(save_ctx
)) {
1686 isds_log_message(context
,
1687 _("Could not serialize XML subtree"));
1691 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1692 * even after xmlSaveFlush(). Thus close it here */
1693 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1696 /* Store and detach buffer from xml_buffer */
1697 *buffer
= xml_buffer
->content
;
1698 *length
= xml_buffer
->use
;
1699 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1702 new_buffer
= realloc(*buffer
, *length
);
1703 if (new_buffer
) *buffer
= new_buffer
;
1711 xmlSaveClose(save_ctx
);
1712 xmlBufferFree(xml_buffer
);
1713 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1716 #endif /* HAVE_LIBCURL */
1720 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1721 * @context is session context
1722 * @document is original document where @nodeset points to
1723 * @nodeset is XPath node set to dump (recursively)
1724 * @buffer is automatically reallocated buffer where serialize to
1725 * @length is size of serialized stream in bytes
1726 * @return standard error code, free @buffer in case of error */
1727 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1728 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1729 void **buffer
, size_t *length
) {
1730 isds_error err
= IE_SUCCESS
;
1731 xmlBufferPtr xml_buffer
= NULL
;
1734 if (!context
) return IE_INVALID_CONTEXT
;
1735 if (!buffer
) return IE_INVAL
;
1737 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1740 /* Empty node set results into NULL buffer */
1741 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1745 /* Resulting the document into buffer */
1746 xml_buffer
= xmlBufferCreate();
1748 isds_log_message(context
, _("Could not create xmlBuffer"));
1753 /* Iterate over all nodes */
1754 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1756 * XXX: xmlNodeDump() appends to xml_buffer. */
1758 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1759 isds_log_message(context
, _("Could not dump XML node"));
1765 /* Store and detach buffer from xml_buffer */
1766 *buffer
= xml_buffer
->content
;
1767 *length
= xml_buffer
->use
;
1768 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1771 new_buffer
= realloc(*buffer
, *length
);
1772 if (new_buffer
) *buffer
= new_buffer
;
1781 xmlBufferFree(xml_buffer
);
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
;
1799 xmlSaveCtxtPtr save_ctx
= NULL
;
1802 if (!context
) return IE_INVALID_CONTEXT
;
1803 if (!buffer
) return IE_INVAL
;
1805 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1808 /* Empty node set results into NULL buffer */
1809 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1813 /* Resulting the document into buffer */
1814 xml_buffer
= xmlBufferCreate();
1816 isds_log_message(context
, _("Could not create xmlBuffer"));
1820 if (xmlSubstituteEntitiesDefault(1)) {
1821 isds_log_message(context
, _("Could not disable attribute escaping"));
1825 /* Last argument means:
1826 * 0 to not format the XML tree
1827 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1828 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1829 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1831 isds_log_message(context
, _("Could not create XML serializer"));
1835 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1836 isds_log_message(context, _("Could not disable attribute escaping"));
1842 /* Iterate over all nodes */
1843 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1845 * XXX: xmlNodeDump() appends to xml_buffer. */
1847 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1849 /* XXX: According LibXML documentation, this function does not return
1850 * meaningful value yet */
1851 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1852 if (-1 == xmlSaveFlush(save_ctx
)) {
1853 isds_log_message(context
,
1854 _("Could not serialize XML subtree"));
1860 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1861 * even after xmlSaveFlush(). Thus close it here */
1862 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1864 /* Store and detach buffer from xml_buffer */
1865 *buffer
= xml_buffer
->content
;
1866 *length
= xml_buffer
->use
;
1867 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1870 new_buffer
= realloc(*buffer
, *length
);
1871 if (new_buffer
) *buffer
= new_buffer
;
1879 xmlSaveClose(save_ctx
);
1880 xmlBufferFree(xml_buffer
);
1887 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1888 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1889 if (!string
|| !type
) return IE_INVAL
;
1891 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1893 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1895 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1896 *type
= DBTYPE_PFO_ADVOK
;
1897 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1898 *type
= DBTYPE_PFO_DANPOR
;
1899 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1900 *type
= DBTYPE_PFO_INSSPR
;
1901 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1903 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1904 *type
= DBTYPE_PO_ZAK
;
1905 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1906 *type
= DBTYPE_PO_REQ
;
1907 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1909 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1910 *type
= DBTYPE_OVM_NOTAR
;
1911 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1912 *type
= DBTYPE_OVM_EXEKUT
;
1913 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1914 *type
= DBTYPE_OVM_REQ
;
1921 /* Convert ISDS dbType enum @type to UTF-8 string.
1922 * @Return pointer to static string, or NULL if unknown enum value */
1923 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
1925 /* DBTYPE_SYSTEM is invalid value from point of view of public
1926 * SOAP interface. */
1927 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
1928 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
1929 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
1930 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
1931 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
1932 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
1933 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
1934 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
1935 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
1936 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
1937 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
1938 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
1939 default: return NULL
; break;
1944 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1945 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
1946 if (!string
|| !type
) return IE_INVAL
;
1948 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
1949 *type
= USERTYPE_PRIMARY
;
1950 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
1951 *type
= USERTYPE_ENTRUSTED
;
1952 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
1953 *type
= USERTYPE_ADMINISTRATOR
;
1954 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
1955 *type
= USERTYPE_OFFICIAL
;
1956 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
1957 *type
= USERTYPE_OFFICIAL_CERT
;
1958 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
1959 *type
= USERTYPE_LIQUIDATOR
;
1966 /* Convert ISDS userType enum @type to UTF-8 string.
1967 * @Return pointer to static string, or NULL if unknown enum value */
1968 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
1970 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
1971 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
1972 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
1973 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
1974 case USERTYPE_OFFICIAL_CERT
: return(BAD_CAST
"OFFICIAL_CERT"); break;
1975 case USERTYPE_LIQUIDATOR
: return(BAD_CAST
"LIQUIDATOR"); break;
1976 default: return NULL
; break;
1981 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
1982 static isds_error
string2isds_sender_type(const xmlChar
*string
,
1983 isds_sender_type
*type
) {
1984 if (!string
|| !type
) return IE_INVAL
;
1986 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
1987 *type
= SENDERTYPE_PRIMARY
;
1988 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
1989 *type
= SENDERTYPE_ENTRUSTED
;
1990 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
1991 *type
= SENDERTYPE_ADMINISTRATOR
;
1992 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
1993 *type
= SENDERTYPE_OFFICIAL
;
1994 else if (!xmlStrcmp(string
, BAD_CAST
"VIRTUAL"))
1995 *type
= SENDERTYPE_VIRTUAL
;
1996 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL_CERT"))
1997 *type
= SENDERTYPE_OFFICIAL_CERT
;
1998 else if (!xmlStrcmp(string
, BAD_CAST
"LIQUIDATOR"))
1999 *type
= SENDERTYPE_LIQUIDATOR
;
2006 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2007 static isds_error
string2isds_payment_type(const xmlChar
*string
,
2008 isds_payment_type
*type
) {
2009 if (!string
|| !type
) return IE_INVAL
;
2011 if (!xmlStrcmp(string
, BAD_CAST
"K"))
2012 *type
= PAYMENT_SENDER
;
2013 else if (!xmlStrcmp(string
, BAD_CAST
"O"))
2014 *type
= PAYMENT_RESPONSE
;
2015 else if (!xmlStrcmp(string
, BAD_CAST
"G"))
2016 *type
= PAYMENT_SPONSOR
;
2017 else if (!xmlStrcmp(string
, BAD_CAST
"Z"))
2018 *type
= PAYMENT_SPONSOR_LIMITED
;
2019 else if (!xmlStrcmp(string
, BAD_CAST
"D"))
2020 *type
= PAYMENT_SPONSOR_EXTERNAL
;
2021 else if (!xmlStrcmp(string
, BAD_CAST
"E"))
2022 *type
= PAYMENT_STAMP
;
2029 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2030 * @Return pointer to static string, or NULL if unknown enum value */
2031 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
2033 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
2034 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
2035 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
2036 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
2037 default: return NULL
; break;
2040 #endif /* HAVE_LIBCURL */
2043 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2044 * @Return IE_ENUM if @string is not valid enum member */
2045 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
2046 isds_FileMetaType
*type
) {
2047 if (!string
|| !type
) return IE_INVAL
;
2049 if (!xmlStrcmp(string
, BAD_CAST
"main"))
2050 *type
= FILEMETATYPE_MAIN
;
2051 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
2052 *type
= FILEMETATYPE_ENCLOSURE
;
2053 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
2054 *type
= FILEMETATYPE_SIGNATURE
;
2055 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
2056 *type
= FILEMETATYPE_META
;
2063 /* Convert UTF-8 @string to ISDS hash @algorithm.
2064 * @Return IE_ENUM if @string is not valid enum member */
2065 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
2066 isds_hash_algorithm
*algorithm
) {
2067 if (!string
|| !algorithm
) return IE_INVAL
;
2069 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
2070 *algorithm
= HASH_ALGORITHM_MD5
;
2071 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
2072 *algorithm
= HASH_ALGORITHM_SHA_1
;
2073 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
2074 *algorithm
= HASH_ALGORITHM_SHA_224
;
2075 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
2076 *algorithm
= HASH_ALGORITHM_SHA_256
;
2077 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
2078 *algorithm
= HASH_ALGORITHM_SHA_384
;
2079 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
2080 *algorithm
= HASH_ALGORITHM_SHA_512
;
2088 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
2089 * XXX: Not all ISO formats are supported */
2090 static isds_error
datestring2tm(const xmlChar
*string
, struct tm
*time
) {
2092 if (!string
|| !time
) return IE_INVAL
;
2094 /* xsd:date is ISO 8601 string, thus ASCII */
2095 offset
= strptime((char*)string
, "%Y-%m-%d", time
);
2096 if (offset
&& *offset
== '\0')
2099 offset
= strptime((char*)string
, "%Y%m%d", time
);
2100 if (offset
&& *offset
== '\0')
2103 offset
= strptime((char*)string
, "%Y-%j", time
);
2104 if (offset
&& *offset
== '\0')
2111 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2112 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
2113 if (!time
|| !string
) return IE_INVAL
;
2115 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
2116 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
2123 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2124 * respects the @time microseconds too. */
2125 static isds_error
timeval2timestring(const struct timeval
*time
,
2129 if (!time
|| !string
) return IE_INVAL
;
2131 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
2132 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
2134 /* TODO: small negative year should be formatted as "-0012". This is not
2135 * true for glibc "%04d". We should implement it.
2136 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2137 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2138 if (-1 == isds_asprintf((char **) string
,
2139 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2140 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
2141 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
2147 #endif /* HAVE_LIBCURL */
2150 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2151 * It respects microseconds too.
2152 * In case of error, @time will be freed. */
2153 static isds_error
timestring2timeval(const xmlChar
*string
,
2154 struct timeval
**time
) {
2156 char *offset
, *delim
, *endptr
;
2158 int offset_hours
, offset_minutes
;
2161 if (!time
) return IE_INVAL
;
2167 memset(&broken
, 0, sizeof(broken
));
2170 *time
= calloc(1, sizeof(**time
));
2171 if (!*time
) return IE_NOMEM
;
2173 memset(*time
, 0, sizeof(**time
));
2177 /* xsd:date is ISO 8601 string, thus ASCII */
2178 /*TODO: negative year */
2180 /* Parse date and time without subseconds and offset */
2181 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
2187 /* Get subseconds */
2188 if (*offset
== '.' ) {
2191 /* Copy first 6 digits, pad it with zeros.
2192 * XXX: It truncates longer number, no round.
2193 * Current server implementation uses only millisecond resolution. */
2194 /* TODO: isdigit() is locale sensitive */
2196 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
2198 subseconds
[i
] = *offset
;
2200 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
2201 subseconds
[i
] = '0';
2203 subseconds
[6] = '\0';
2205 /* Convert it into integer */
2206 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
2207 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
2208 (*time
)->tv_usec
== LONG_MAX
) {
2213 /* move to the zone offset delimiter or signal NULL*/
2214 delim
= strchr(offset
, '-');
2216 delim
= strchr(offset
, '+');
2218 delim
= strchr(offset
, 'Z');
2222 /* Get zone offset */
2223 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2224 * "" equals to "Z" and it means UTC zone. */
2225 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2226 * colon separator */
2227 if (offset
&& (*offset
== '-' || *offset
== '+')) {
2228 if (2 != sscanf(offset
+ 1, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
2232 if (*offset
== '+') {
2233 broken
.tm_hour
-= offset_hours
;
2234 broken
.tm_min
-= offset_minutes
;
2236 broken
.tm_hour
+= offset_hours
;
2237 broken
.tm_min
+= offset_minutes
;
2241 /* Convert to time_t */
2242 (*time
)->tv_sec
= _isds_timegm(&broken
);
2243 if ((*time
)->tv_sec
== (time_t) -1) {
2252 /* Convert unsigned int into isds_message_status.
2253 * @context is session context
2254 * @number is pointer to number value. NULL will be treated as invalid value.
2255 * @status is automatically reallocated status
2256 * @return IE_SUCCESS, or error code and free status */
2257 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
2258 const unsigned long int *number
, isds_message_status
**status
) {
2259 if (!context
) return IE_INVALID_CONTEXT
;
2260 if (!status
) return IE_INVAL
;
2262 free(*status
); *status
= NULL
;
2263 if (!number
) return IE_INVAL
;
2265 if (*number
< 1 || *number
> 10) {
2266 isds_printf_message(context
, _("Invalid message status value: %lu"),
2271 *status
= malloc(sizeof(**status
));
2272 if (!*status
) return IE_NOMEM
;
2274 **status
= 1 << *number
;
2279 /* Convert event description string into isds_event members type and
2281 * @string is raw event description starting with event prefix
2282 * @event is structure where to store type and stripped description to
2283 * @return standard error code, unknown prefix is not classified as an error.
2285 static isds_error
eventstring2event(const xmlChar
*string
,
2286 struct isds_event
* event
) {
2287 const xmlChar
*known_prefixes
[] = {
2298 const isds_event_type types
[] = {
2299 EVENT_ENTERED_SYSTEM
,
2300 EVENT_ACCEPTED_BY_RECIPIENT
,
2301 EVENT_ACCEPTED_BY_FICTION
,
2302 EVENT_UNDELIVERABLE
,
2303 EVENT_COMMERCIAL_ACCEPTED
,
2305 EVENT_PRIMARY_LOGIN
,
2306 EVENT_ENTRUSTED_LOGIN
,
2312 if (!string
|| !event
) return IE_INVAL
;
2315 event
->type
= malloc(sizeof(*event
->type
));
2316 if (!(event
->type
)) return IE_NOMEM
;
2318 zfree(event
->description
);
2320 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
2322 length
= xmlUTF8Strlen(known_prefixes
[index
]);
2324 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
2325 /* Prefix is known */
2326 *event
->type
= types
[index
];
2328 /* Strip prefix from description and spaces */
2329 /* TODO: Recognize all white spaces from UCS blank class and
2330 * operate on UTF-8 chars. */
2331 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
2332 event
->description
= strdup((char *) (string
+ length
));
2333 if (!(event
->description
)) return IE_NOMEM
;
2339 /* Unknown event prefix.
2340 * XSD allows any string */
2341 char *string_locale
= _isds_utf82locale((char *) string
);
2342 isds_log(ILF_ISDS
, ILL_WARNING
,
2343 _("Unknown delivery info event prefix: %s\n"), string_locale
);
2344 free(string_locale
);
2346 *event
->type
= EVENT_UKNOWN
;
2347 event
->description
= strdup((char *) string
);
2348 if (!(event
->description
)) return IE_NOMEM
;
2354 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2355 * and leave label */
2356 #define EXTRACT_STRING(element, string) { \
2357 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2362 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2363 if (result->nodesetval->nodeNr > 1) { \
2364 isds_printf_message(context, _("Multiple %s element"), element); \
2368 (string) = (char *) \
2369 xmlXPathCastNodeSetToString(result->nodesetval); \
2377 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2379 char *string = NULL; \
2380 EXTRACT_STRING(element, string); \
2383 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2384 if (!(booleanPtr)) { \
2390 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2391 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2392 *(booleanPtr) = 1; \
2393 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2394 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2395 *(booleanPtr) = 0; \
2397 char *string_locale = _isds_utf82locale((char*)string); \
2398 isds_printf_message(context, \
2399 _("%s value is not valid boolean: %s"), \
2400 element, string_locale); \
2401 free(string_locale); \
2411 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2413 char *string = NULL; \
2414 EXTRACT_STRING(element, string); \
2419 number = strtol((char*)string, &endptr, 10); \
2421 if (*endptr != '\0') { \
2422 char *string_locale = _isds_utf82locale((char *)string); \
2423 isds_printf_message(context, \
2424 _("%s is not valid integer: %s"), \
2425 element, string_locale); \
2426 free(string_locale); \
2432 if (number == LONG_MIN || number == LONG_MAX) { \
2433 char *string_locale = _isds_utf82locale((char *)string); \
2434 isds_printf_message(context, \
2435 _("%s value out of range of long int: %s"), \
2436 element, string_locale); \
2437 free(string_locale); \
2443 free(string); string = NULL; \
2445 if (!(preallocated)) { \
2446 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2447 if (!(longintPtr)) { \
2452 *(longintPtr) = number; \
2456 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2458 char *string = NULL; \
2459 EXTRACT_STRING(element, string); \
2464 number = strtol((char*)string, &endptr, 10); \
2466 if (*endptr != '\0') { \
2467 char *string_locale = _isds_utf82locale((char *)string); \
2468 isds_printf_message(context, \
2469 _("%s is not valid integer: %s"), \
2470 element, string_locale); \
2471 free(string_locale); \
2477 if (number == LONG_MIN || number == LONG_MAX) { \
2478 char *string_locale = _isds_utf82locale((char *)string); \
2479 isds_printf_message(context, \
2480 _("%s value out of range of long int: %s"), \
2481 element, string_locale); \
2482 free(string_locale); \
2488 free(string); string = NULL; \
2490 isds_printf_message(context, \
2491 _("%s value is negative: %ld"), element, number); \
2496 if (!(preallocated)) { \
2497 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2498 if (!(ulongintPtr)) { \
2503 *(ulongintPtr) = number; \
2507 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2508 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2510 if ((required) && (!string)) { \
2511 char *attribute_locale = _isds_utf82locale(attribute); \
2512 char *element_locale = \
2513 _isds_utf82locale((char *)xpath_ctx->node->name); \
2514 isds_printf_message(context, \
2515 _("Could not extract required %s attribute value from " \
2516 "%s element"), attribute_locale, element_locale); \
2517 free(element_locale); \
2518 free(attribute_locale); \
2525 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2527 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2528 (xmlChar *) (string)); \
2530 isds_printf_message(context, \
2531 _("Could not add %s child to %s element"), \
2532 element, (parent)->name); \
2538 #define INSERT_STRING(parent, element, string) \
2539 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2541 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2543 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2544 else { INSERT_STRING(parent, element, "false"); } \
2547 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2550 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2552 INSERT_STRING(parent, element, NULL); \
2556 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2557 if ((longintPtr)) { \
2558 /* FIXME: locale sensitive */ \
2559 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2563 INSERT_STRING(parent, element, buffer) \
2564 free(buffer); (buffer) = NULL; \
2565 } else { INSERT_STRING(parent, element, NULL) } \
2568 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2569 if ((ulongintPtr)) { \
2570 /* FIXME: locale sensitive */ \
2571 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2575 INSERT_STRING(parent, element, buffer) \
2576 free(buffer); (buffer) = NULL; \
2577 } else { INSERT_STRING(parent, element, NULL) } \
2580 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2582 /* FIXME: locale sensitive */ \
2583 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2587 INSERT_STRING(parent, element, buffer) \
2588 free(buffer); (buffer) = NULL; \
2591 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2593 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2595 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2596 (xmlChar *) (string)); \
2597 if (!attribute_node) { \
2598 isds_printf_message(context, _("Could not add %s " \
2599 "attribute to %s element"), \
2600 (attribute), (parent)->name); \
2606 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2608 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2609 if (length > (maximum)) { \
2610 isds_printf_message(context, \
2611 ngettext("%s has more than %d characters", \
2612 "%s has more than %d characters", (maximum)), \
2613 (name), (maximum)); \
2617 if (length < (minimum)) { \
2618 isds_printf_message(context, \
2619 ngettext("%s has less than %d characters", \
2620 "%s has less than %d characters", (minimum)), \
2621 (name), (minimum)); \
2628 #define INSERT_ELEMENT(child, parent, element) \
2630 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2632 isds_printf_message(context, \
2633 _("Could not add %s child to %s element"), \
2634 (element), (parent)->name); \
2641 /* Find child element by name in given XPath context and switch context onto
2642 * it. The child must be uniq and must exist. Otherwise fails.
2643 * @context is ISDS context
2644 * @child is child element name
2645 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2646 * into it child. In error case, the @xpath_ctx keeps original value. */
2647 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2648 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2649 isds_error err
= IE_SUCCESS
;
2650 xmlXPathObjectPtr result
= NULL
;
2652 if (!context
) return IE_INVALID_CONTEXT
;
2653 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2656 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2663 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2664 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2665 char *child_locale
= _isds_utf82locale((char*) child
);
2666 isds_printf_message(context
,
2667 _("%s element does not contain %s child"),
2668 parent_locale
, child_locale
);
2670 free(parent_locale
);
2676 if (result
->nodesetval
->nodeNr
> 1) {
2677 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2678 char *child_locale
= _isds_utf82locale((char*) child
);
2679 isds_printf_message(context
,
2680 _("%s element contains multiple %s children"),
2681 parent_locale
, child_locale
);
2683 free(parent_locale
);
2688 /* Switch context */
2689 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2692 xmlXPathFreeObject(result
);
2699 /* Find and convert XSD:gPersonName group in current node into structure
2700 * @context is ISDS context
2701 * @personName is automatically reallocated person name structure. If no member
2702 * value is found, will be freed.
2703 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2705 * In case of error @personName will be freed. */
2706 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2707 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2708 isds_error err
= IE_SUCCESS
;
2709 xmlXPathObjectPtr result
= NULL
;
2711 if (!context
) return IE_INVALID_CONTEXT
;
2712 if (!personName
) return IE_INVAL
;
2713 isds_PersonName_free(personName
);
2714 if (!xpath_ctx
) return IE_INVAL
;
2717 *personName
= calloc(1, sizeof(**personName
));
2723 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2724 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2725 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2726 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2728 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2729 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2730 isds_PersonName_free(personName
);
2733 if (err
) isds_PersonName_free(personName
);
2734 xmlXPathFreeObject(result
);
2739 /* Find and convert XSD:gAddress group in current node into structure
2740 * @context is ISDS context
2741 * @address is automatically reallocated address structure. If no member
2742 * value is found, will be freed.
2743 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2745 * In case of error @address will be freed. */
2746 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2747 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2748 isds_error err
= IE_SUCCESS
;
2749 xmlXPathObjectPtr result
= NULL
;
2751 if (!context
) return IE_INVALID_CONTEXT
;
2752 if (!address
) return IE_INVAL
;
2753 isds_Address_free(address
);
2754 if (!xpath_ctx
) return IE_INVAL
;
2757 *address
= calloc(1, sizeof(**address
));
2763 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2764 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2765 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2766 EXTRACT_STRING("isds:adNumberInMunicipality",
2767 (*address
)->adNumberInMunicipality
);
2768 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2769 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2771 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2772 !(*address
)->adNumberInStreet
&&
2773 !(*address
)->adNumberInMunicipality
&&
2774 !(*address
)->adZipCode
&& !(*address
)->adState
)
2775 isds_Address_free(address
);
2778 if (err
) isds_Address_free(address
);
2779 xmlXPathFreeObject(result
);
2784 /* Find and convert isds:biDate element in current node into structure
2785 * @context is ISDS context
2786 * @biDate is automatically reallocated birth date structure. If no member
2787 * value is found, will be freed.
2788 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2790 * In case of error @biDate will be freed. */
2791 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2792 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2793 isds_error err
= IE_SUCCESS
;
2794 xmlXPathObjectPtr result
= NULL
;
2795 char *string
= NULL
;
2797 if (!context
) return IE_INVALID_CONTEXT
;
2798 if (!biDate
) return IE_INVAL
;
2800 if (!xpath_ctx
) return IE_INVAL
;
2802 EXTRACT_STRING("isds:biDate", string
);
2804 *biDate
= calloc(1, sizeof(**biDate
));
2809 err
= datestring2tm((xmlChar
*)string
, *biDate
);
2811 if (err
== IE_NOTSUP
) {
2813 char *string_locale
= _isds_utf82locale(string
);
2814 isds_printf_message(context
,
2815 _("Invalid isds:biDate value: %s"), string_locale
);
2816 free(string_locale
);
2823 if (err
) zfree(*biDate
);
2825 xmlXPathFreeObject(result
);
2830 /* Convert isds:dBOwnerInfo XML tree into structure
2831 * @context is ISDS context
2832 * @db_owner_info is automatically reallocated box owner info structure
2833 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2834 * In case of error @db_owner_info will be freed. */
2835 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
2836 struct isds_DbOwnerInfo
**db_owner_info
,
2837 xmlXPathContextPtr xpath_ctx
) {
2838 isds_error err
= IE_SUCCESS
;
2839 xmlXPathObjectPtr result
= NULL
;
2840 char *string
= NULL
;
2842 if (!context
) return IE_INVALID_CONTEXT
;
2843 if (!db_owner_info
) return IE_INVAL
;
2844 isds_DbOwnerInfo_free(db_owner_info
);
2845 if (!xpath_ctx
) return IE_INVAL
;
2848 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
2849 if (!*db_owner_info
) {
2854 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
2856 EXTRACT_STRING("isds:dbType", string
);
2858 (*db_owner_info
)->dbType
=
2859 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
2860 if (!(*db_owner_info
)->dbType
) {
2864 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
2866 zfree((*db_owner_info
)->dbType
);
2867 if (err
== IE_ENUM
) {
2869 char *string_locale
= _isds_utf82locale(string
);
2870 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
2872 free(string_locale
);
2879 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
2881 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
2883 if (err
) goto leave
;
2885 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
2887 (*db_owner_info
)->birthInfo
=
2888 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
2889 if (!(*db_owner_info
)->birthInfo
) {
2893 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
2895 if (err
) goto leave
;
2896 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
2897 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
2898 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
2899 if (!(*db_owner_info
)->birthInfo
->biDate
&&
2900 !(*db_owner_info
)->birthInfo
->biCity
&&
2901 !(*db_owner_info
)->birthInfo
->biCounty
&&
2902 !(*db_owner_info
)->birthInfo
->biState
)
2903 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
2905 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
2906 if (err
) goto leave
;
2908 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
2909 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
2910 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
2911 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
2912 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
2914 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
2916 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
2917 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2918 (*db_owner_info
)->dbOpenAddressing
);
2921 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
2923 xmlXPathFreeObject(result
);
2928 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2929 * @context is session context
2930 * @owner is libisds structure with box description
2931 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2932 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
2933 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
2935 isds_error err
= IE_SUCCESS
;
2937 xmlChar
*string
= NULL
;
2939 if (!context
) return IE_INVALID_CONTEXT
;
2940 if (!owner
|| !db_owner_info
) return IE_INVAL
;
2943 /* Build XSD:tDbOwnerInfo */
2944 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
2945 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
2948 if (owner
->dbType
) {
2949 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
2951 isds_printf_message(context
, _("Invalid dbType value: %d"),
2956 INSERT_STRING(db_owner_info
, "dbType", type_string
);
2958 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
2959 if (owner
->personName
) {
2960 INSERT_STRING(db_owner_info
, "pnFirstName",
2961 owner
->personName
->pnFirstName
);
2962 INSERT_STRING(db_owner_info
, "pnMiddleName",
2963 owner
->personName
->pnMiddleName
);
2964 INSERT_STRING(db_owner_info
, "pnLastName",
2965 owner
->personName
->pnLastName
);
2966 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
2967 owner
->personName
->pnLastNameAtBirth
);
2969 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
2970 if (owner
->birthInfo
) {
2971 if (owner
->birthInfo
->biDate
) {
2972 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
2973 INSERT_STRING(db_owner_info
, "biDate", string
);
2974 free(string
); string
= NULL
;
2976 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
2977 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
2978 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
2980 if (owner
->address
) {
2981 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
2982 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
2983 INSERT_STRING(db_owner_info
, "adNumberInStreet",
2984 owner
->address
->adNumberInStreet
);
2985 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
2986 owner
->address
->adNumberInMunicipality
);
2987 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
2988 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
2990 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
2991 INSERT_STRING(db_owner_info
, "email", owner
->email
);
2992 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
2994 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
2995 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
2997 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
2998 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
3000 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
3002 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
3003 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3004 owner
->dbOpenAddressing
);
3012 /* Convert XSD:tDbUserInfo XML tree into structure
3013 * @context is ISDS context
3014 * @db_user_info is automatically reallocated user info structure
3015 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3016 * In case of error @db_user_info will be freed. */
3017 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
3018 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
3019 isds_error err
= IE_SUCCESS
;
3020 xmlXPathObjectPtr result
= NULL
;
3021 char *string
= NULL
;
3023 if (!context
) return IE_INVALID_CONTEXT
;
3024 if (!db_user_info
) return IE_INVAL
;
3025 isds_DbUserInfo_free(db_user_info
);
3026 if (!xpath_ctx
) return IE_INVAL
;
3029 *db_user_info
= calloc(1, sizeof(**db_user_info
));
3030 if (!*db_user_info
) {
3035 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
3037 EXTRACT_STRING("isds:userType", string
);
3039 (*db_user_info
)->userType
=
3040 calloc(1, sizeof(*((*db_user_info
)->userType
)));
3041 if (!(*db_user_info
)->userType
) {
3045 err
= string2isds_UserType((xmlChar
*)string
,
3046 (*db_user_info
)->userType
);
3048 zfree((*db_user_info
)->userType
);
3049 if (err
== IE_ENUM
) {
3051 char *string_locale
= _isds_utf82locale(string
);
3052 isds_printf_message(context
,
3053 _("Unknown isds:userType value: %s"), string_locale
);
3054 free(string_locale
);
3061 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
3063 (*db_user_info
)->personName
=
3064 calloc(1, sizeof(*((*db_user_info
)->personName
)));
3065 if (!(*db_user_info
)->personName
) {
3070 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
3072 if (err
) goto leave
;
3074 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
3075 if (err
) goto leave
;
3077 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
3078 if (err
) goto leave
;
3080 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
3081 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
3083 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
3084 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
3085 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
3087 /* ???: Default value is "CZ" according specification. Should we provide
3089 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
3092 if (err
) isds_DbUserInfo_free(db_user_info
);
3094 xmlXPathFreeObject(result
);
3099 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3100 * @context is session context
3101 * @user is libisds structure with user description
3102 * @db_user_info is XML element of XSD:tDbUserInfo */
3103 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
3104 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
3106 isds_error err
= IE_SUCCESS
;
3108 xmlChar
*string
= NULL
;
3110 if (!context
) return IE_INVALID_CONTEXT
;
3111 if (!user
|| !db_user_info
) return IE_INVAL
;
3113 /* Build XSD:tDbUserInfo */
3114 if (user
->personName
) {
3115 INSERT_STRING(db_user_info
, "pnFirstName",
3116 user
->personName
->pnFirstName
);
3117 INSERT_STRING(db_user_info
, "pnMiddleName",
3118 user
->personName
->pnMiddleName
);
3119 INSERT_STRING(db_user_info
, "pnLastName",
3120 user
->personName
->pnLastName
);
3121 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
3122 user
->personName
->pnLastNameAtBirth
);
3124 if (user
->address
) {
3125 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
3126 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
3127 INSERT_STRING(db_user_info
, "adNumberInStreet",
3128 user
->address
->adNumberInStreet
);
3129 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
3130 user
->address
->adNumberInMunicipality
);
3131 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
3132 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
3135 if (!tm2datestring(user
->biDate
, &string
))
3136 INSERT_STRING(db_user_info
, "biDate", string
);
3139 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
3140 INSERT_STRING(db_user_info
, "userID", user
->userID
);
3143 if (user
->userType
) {
3144 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
3146 isds_printf_message(context
, _("Invalid userType value: %d"),
3151 INSERT_STRING(db_user_info
, "userType", type_string
);
3154 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
3155 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
3156 INSERT_STRING(db_user_info
, "ic", user
->ic
);
3157 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
3158 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
3159 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
3160 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
3161 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
3162 INSERT_STRING(db_user_info
, "caState", user
->caState
);
3170 /* Convert XSD:tPDZRec XML tree into structure
3171 * @context is ISDS context
3172 * @permission is automatically reallocated commercial permission structure
3173 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3174 * In case of error @permission will be freed. */
3175 static isds_error
extract_DbPDZRecord(struct isds_ctx
*context
,
3176 struct isds_commercial_permission
**permission
,
3177 xmlXPathContextPtr xpath_ctx
) {
3178 isds_error err
= IE_SUCCESS
;
3179 xmlXPathObjectPtr result
= NULL
;
3180 char *string
= NULL
;
3182 if (!context
) return IE_INVALID_CONTEXT
;
3183 if (!permission
) return IE_INVAL
;
3184 isds_commercial_permission_free(permission
);
3185 if (!xpath_ctx
) return IE_INVAL
;
3188 *permission
= calloc(1, sizeof(**permission
));
3194 EXTRACT_STRING("isds:PDZType", string
);
3196 err
= string2isds_payment_type((xmlChar
*)string
,
3197 &(*permission
)->type
);
3199 if (err
== IE_ENUM
) {
3201 char *string_locale
= _isds_utf82locale(string
);
3202 isds_printf_message(context
,
3203 _("Unknown isds:PDZType value: %s"), string_locale
);
3204 free(string_locale
);
3211 EXTRACT_STRING("isds:PDZRecip", (*permission
)->recipient
);
3212 EXTRACT_STRING("isds:PDZPayer", (*permission
)->payer
);
3214 EXTRACT_STRING("isds:PDZExpire", string
);
3216 err
= timestring2timeval((xmlChar
*) string
,
3217 &((*permission
)->expiration
));
3219 char *string_locale
= _isds_utf82locale(string
);
3220 if (err
== IE_DATE
) err
= IE_ISDS
;
3221 isds_printf_message(context
,
3222 _("Could not convert PDZExpire as ISO time: %s"),
3224 free(string_locale
);
3230 EXTRACT_ULONGINT("isds:PDZCnt", (*permission
)->count
, 0);
3231 EXTRACT_STRING("isds:ODZIdent", (*permission
)->reply_identifier
);
3234 if (err
) isds_commercial_permission_free(permission
);
3236 xmlXPathFreeObject(result
);
3241 #endif /* HAVE_LIBCURL */
3244 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3245 * isds_envelope structure. The envelope is automatically allocated but not
3246 * reallocated. The date are just appended into envelope structure.
3247 * @context is ISDS context
3248 * @envelope is automatically allocated message envelope structure
3249 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3250 * In case of error @envelope will be freed. */
3251 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
3252 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3253 isds_error err
= IE_SUCCESS
;
3254 xmlXPathObjectPtr result
= NULL
;
3256 if (!context
) return IE_INVALID_CONTEXT
;
3257 if (!envelope
) return IE_INVAL
;
3258 if (!xpath_ctx
) return IE_INVAL
;
3262 /* Allocate envelope */
3263 *envelope
= calloc(1, sizeof(**envelope
));
3269 /* Else free former data */
3270 zfree((*envelope
)->dmSenderOrgUnit
);
3271 zfree((*envelope
)->dmSenderOrgUnitNum
);
3272 zfree((*envelope
)->dbIDRecipient
);
3273 zfree((*envelope
)->dmRecipientOrgUnit
);
3274 zfree((*envelope
)->dmSenderOrgUnitNum
);
3275 zfree((*envelope
)->dmToHands
);
3276 zfree((*envelope
)->dmAnnotation
);
3277 zfree((*envelope
)->dmRecipientRefNumber
);
3278 zfree((*envelope
)->dmSenderRefNumber
);
3279 zfree((*envelope
)->dmRecipientIdent
);
3280 zfree((*envelope
)->dmSenderIdent
);
3281 zfree((*envelope
)->dmLegalTitleLaw
);
3282 zfree((*envelope
)->dmLegalTitleYear
);
3283 zfree((*envelope
)->dmLegalTitleSect
);
3284 zfree((*envelope
)->dmLegalTitlePar
);
3285 zfree((*envelope
)->dmLegalTitlePoint
);
3286 zfree((*envelope
)->dmPersonalDelivery
);
3287 zfree((*envelope
)->dmAllowSubstDelivery
);
3290 /* Extract envelope elements added by sender or ISDS
3291 * (XSD: gMessageEnvelopeSub type) */
3292 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
3293 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3294 (*envelope
)->dmSenderOrgUnitNum
, 0);
3295 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
3296 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
3297 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3298 (*envelope
)->dmSenderOrgUnitNum
, 0);
3299 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
3300 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
3301 EXTRACT_STRING("isds:dmRecipientRefNumber",
3302 (*envelope
)->dmRecipientRefNumber
);
3303 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
3304 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
3305 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
3307 /* Extract envelope elements regarding law reference */
3308 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
3309 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
3310 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
3311 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
3312 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
3314 /* Extract envelope other elements */
3315 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
3316 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3317 (*envelope
)->dmAllowSubstDelivery
);
3320 if (err
) isds_envelope_free(envelope
);
3321 xmlXPathFreeObject(result
);
3327 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3328 * isds_envelope structure. The envelope is automatically allocated but not
3329 * reallocated. The date are just appended into envelope structure.
3330 * @context is ISDS context
3331 * @envelope is automatically allocated message envelope structure
3332 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3333 * In case of error @envelope will be freed. */
3334 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
3335 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3336 isds_error err
= IE_SUCCESS
;
3337 xmlXPathObjectPtr result
= NULL
;
3339 if (!context
) return IE_INVALID_CONTEXT
;
3340 if (!envelope
) return IE_INVAL
;
3341 if (!xpath_ctx
) return IE_INVAL
;
3345 /* Allocate envelope */
3346 *envelope
= calloc(1, sizeof(**envelope
));
3352 /* Else free former data */
3353 zfree((*envelope
)->dmID
);
3354 zfree((*envelope
)->dbIDSender
);
3355 zfree((*envelope
)->dmSender
);
3356 zfree((*envelope
)->dmSenderAddress
);
3357 zfree((*envelope
)->dmSenderType
);
3358 zfree((*envelope
)->dmRecipient
);
3359 zfree((*envelope
)->dmRecipientAddress
);
3360 zfree((*envelope
)->dmAmbiguousRecipient
);
3363 /* Extract envelope elements added by ISDS
3364 * (XSD: gMessageEnvelope type) */
3365 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
3366 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
3367 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
3368 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
3369 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3370 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
3371 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
3372 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
3373 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3374 (*envelope
)->dmAmbiguousRecipient
);
3376 /* Extract envelope elements added by sender and ISDS
3377 * (XSD: gMessageEnvelope type) */
3378 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
3379 if (err
) goto leave
;
3382 if (err
) isds_envelope_free(envelope
);
3383 xmlXPathFreeObject(result
);
3388 /* Convert other envelope elements from XML tree into isds_envelope structure:
3389 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3390 * The envelope is automatically allocated but not reallocated.
3391 * The data are just appended into envelope structure.
3392 * @context is ISDS context
3393 * @envelope is automatically allocated message envelope structure
3394 * @xpath_ctx is XPath context with current node as parent desired elements
3395 * In case of error @envelope will be freed. */
3396 static isds_error
append_status_size_times(struct isds_ctx
*context
,
3397 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3398 isds_error err
= IE_SUCCESS
;
3399 xmlXPathObjectPtr result
= NULL
;
3400 char *string
= NULL
;
3401 unsigned long int *unumber
= NULL
;
3403 if (!context
) return IE_INVALID_CONTEXT
;
3404 if (!envelope
) return IE_INVAL
;
3405 if (!xpath_ctx
) return IE_INVAL
;
3410 *envelope
= calloc(1, sizeof(**envelope
));
3417 zfree((*envelope
)->dmMessageStatus
);
3418 zfree((*envelope
)->dmAttachmentSize
);
3419 zfree((*envelope
)->dmDeliveryTime
);
3420 zfree((*envelope
)->dmAcceptanceTime
);
3424 /* dmMessageStatus element is mandatory */
3425 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3427 isds_log_message(context
,
3428 _("Missing mandatory sisds:dmMessageStatus integer"));
3432 err
= uint2isds_message_status(context
, unumber
,
3433 &((*envelope
)->dmMessageStatus
));
3435 if (err
== IE_ENUM
) err
= IE_ISDS
;
3438 free(unumber
); unumber
= NULL
;
3440 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3443 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3445 err
= timestring2timeval((xmlChar
*) string
,
3446 &((*envelope
)->dmDeliveryTime
));
3448 char *string_locale
= _isds_utf82locale(string
);
3449 if (err
== IE_DATE
) err
= IE_ISDS
;
3450 isds_printf_message(context
,
3451 _("Could not convert dmDeliveryTime as ISO time: %s"),
3453 free(string_locale
);
3459 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3461 err
= timestring2timeval((xmlChar
*) string
,
3462 &((*envelope
)->dmAcceptanceTime
));
3464 char *string_locale
= _isds_utf82locale(string
);
3465 if (err
== IE_DATE
) err
= IE_ISDS
;
3466 isds_printf_message(context
,
3467 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3469 free(string_locale
);
3476 if (err
) isds_envelope_free(envelope
);
3479 xmlXPathFreeObject(result
);
3484 /* Convert message type attribute of current element into isds_envelope
3486 * TODO: This function can be incorporated into append_status_size_times() as
3487 * they are called always together.
3488 * The envelope is automatically allocated but not reallocated.
3489 * The data are just appended into envelope structure.
3490 * @context is ISDS context
3491 * @envelope is automatically allocated message envelope structure
3492 * @xpath_ctx is XPath context with current node as parent of attribute
3493 * carrying message type
3494 * In case of error @envelope will be freed. */
3495 static isds_error
append_message_type(struct isds_ctx
*context
,
3496 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3497 isds_error err
= IE_SUCCESS
;
3499 if (!context
) return IE_INVALID_CONTEXT
;
3500 if (!envelope
) return IE_INVAL
;
3501 if (!xpath_ctx
) return IE_INVAL
;
3506 *envelope
= calloc(1, sizeof(**envelope
));
3513 zfree((*envelope
)->dmType
);
3517 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3519 if (!(*envelope
)->dmType
) {
3520 /* Use default value */
3521 (*envelope
)->dmType
= strdup("V");
3522 if (!(*envelope
)->dmType
) {
3526 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3527 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3528 isds_printf_message(context
,
3529 _("Message type in dmType attribute is not 1 character long: "
3538 if (err
) isds_envelope_free(envelope
);
3544 /* Convert dmType isds_envelope member into XML attribute and append it to
3546 * @context is ISDS context
3547 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3548 * @dm_envelope is XML element the resulting attribute will be appended to.
3549 * @return error code, in case of error context' message is filled. */
3550 static isds_error
insert_message_type(struct isds_ctx
*context
,
3551 const char *type
, xmlNodePtr dm_envelope
) {
3552 isds_error err
= IE_SUCCESS
;
3553 xmlAttrPtr attribute_node
;
3555 if (!context
) return IE_INVALID_CONTEXT
;
3556 if (!dm_envelope
) return IE_INVAL
;
3558 /* Insert optional message type */
3560 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3561 char *type_locale
= _isds_utf82locale(type
);
3562 isds_printf_message(context
,
3563 _("Message type in envelope is not 1 character long: %s"),
3569 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3575 #endif /* HAVE_LIBCURL */
3578 /* Extract message document into reallocated document structure
3579 * @context is ISDS context
3580 * @document is automatically reallocated message documents structure
3581 * @xpath_ctx is XPath context with current node as isds:dmFile
3582 * In case of error @document will be freed. */
3583 static isds_error
extract_document(struct isds_ctx
*context
,
3584 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3585 isds_error err
= IE_SUCCESS
;
3586 xmlXPathObjectPtr result
= NULL
;
3587 xmlNodePtr file_node
= xpath_ctx
->node
;
3588 char *string
= NULL
;
3590 if (!context
) return IE_INVALID_CONTEXT
;
3591 if (!document
) return IE_INVAL
;
3592 isds_document_free(document
);
3593 if (!xpath_ctx
) return IE_INVAL
;
3595 *document
= calloc(1, sizeof(**document
));
3601 /* Extract document meta data */
3602 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3603 if (context
->normalize_mime_type
) {
3604 char *normalized_type
=
3605 isds_normalize_mime_type((*document
)->dmMimeType
);
3606 if (normalized_type
&& normalized_type
!= (*document
)->dmMimeType
) {
3607 char *new_type
= strdup(normalized_type
);
3609 isds_printf_message(context
,
3610 _("Not enough memory to normalize document MIME type"));
3614 free((*document
)->dmMimeType
);
3615 (*document
)->dmMimeType
= new_type
;
3619 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3620 err
= string2isds_FileMetaType((xmlChar
*)string
,
3621 &((*document
)->dmFileMetaType
));
3623 char *meta_type_locale
= _isds_utf82locale(string
);
3624 isds_printf_message(context
,
3625 _("Document has invalid dmFileMetaType attribute value: %s"),
3627 free(meta_type_locale
);
3633 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3634 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3635 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3636 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3639 /* Extract document data.
3640 * Base64 encoded blob or XML subtree must be presented. */
3642 /* Check for dmEncodedContent */
3643 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3650 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3651 /* Here we have Base64 blob */
3652 (*document
)->is_xml
= 0;
3654 if (result
->nodesetval
->nodeNr
> 1) {
3655 isds_printf_message(context
,
3656 _("Document has more dmEncodedContent elements"));
3661 xmlXPathFreeObject(result
); result
= NULL
;
3662 EXTRACT_STRING("isds:dmEncodedContent", string
);
3664 /* Decode non-empty document */
3665 if (string
&& string
[0] != '\0') {
3666 (*document
)->data_length
=
3667 _isds_b64decode(string
, &((*document
)->data
));
3668 if ((*document
)->data_length
== (size_t) -1) {
3669 isds_printf_message(context
,
3670 _("Error while Base64-decoding document content"));
3676 /* No Base64 blob, try XML document */
3677 xmlXPathFreeObject(result
); result
= NULL
;
3678 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3685 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3686 /* Here we have XML document */
3687 (*document
)->is_xml
= 1;
3689 if (result
->nodesetval
->nodeNr
> 1) {
3690 isds_printf_message(context
,
3691 _("Document has more dmXMLContent elements"));
3696 /* XXX: We cannot serialize the content simply because:
3697 * - XML document may point out of its scope (e.g. to message
3699 * - isds:dmXMLContent can contain more elements, no element,
3701 * - it's not the XML way
3702 * Thus we provide the only right solution: XML DOM. Let's
3703 * application to cope with this hot potato :) */
3704 (*document
)->xml_node_list
=
3705 result
->nodesetval
->nodeTab
[0]->children
;
3707 /* No base64 blob, nor XML document */
3708 isds_printf_message(context
,
3709 _("Document has no dmEncodedContent, nor dmXMLContent "
3718 if (err
) isds_document_free(document
);
3720 xmlXPathFreeObject(result
);
3721 xpath_ctx
->node
= file_node
;
3727 /* Extract message documents into reallocated list of documents
3728 * @context is ISDS context
3729 * @documents is automatically reallocated message documents list structure
3730 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3731 * In case of error @documents will be freed. */
3732 static isds_error
extract_documents(struct isds_ctx
*context
,
3733 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
3734 isds_error err
= IE_SUCCESS
;
3735 xmlXPathObjectPtr result
= NULL
;
3736 xmlNodePtr files_node
= xpath_ctx
->node
;
3737 struct isds_list
*document
, *prev_document
= NULL
;
3739 if (!context
) return IE_INVALID_CONTEXT
;
3740 if (!documents
) return IE_INVAL
;
3741 isds_list_free(documents
);
3742 if (!xpath_ctx
) return IE_INVAL
;
3744 /* Find documents */
3745 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
3752 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3753 isds_printf_message(context
,
3754 _("Message does not contain any document"));
3760 /* Iterate over documents */
3761 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3763 /* Allocate and append list item */
3764 document
= calloc(1, sizeof(*document
));
3769 document
->destructor
= (void (*)(void **))isds_document_free
;
3770 if (i
== 0) *documents
= document
;
3771 else prev_document
->next
= document
;
3772 prev_document
= document
;
3774 /* Extract document */
3775 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3776 err
= extract_document(context
,
3777 (struct isds_document
**) &(document
->data
), xpath_ctx
);
3778 if (err
) goto leave
;
3783 if (err
) isds_list_free(documents
);
3784 xmlXPathFreeObject(result
);
3785 xpath_ctx
->node
= files_node
;
3791 /* Convert isds:dmRecord XML tree into structure
3792 * @context is ISDS context
3793 * @envelope is automatically reallocated message envelope structure
3794 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3795 * In case of error @envelope will be freed. */
3796 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
3797 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3798 isds_error err
= IE_SUCCESS
;
3799 xmlXPathObjectPtr result
= NULL
;
3801 if (!context
) return IE_INVALID_CONTEXT
;
3802 if (!envelope
) return IE_INVAL
;
3803 isds_envelope_free(envelope
);
3804 if (!xpath_ctx
) return IE_INVAL
;
3807 *envelope
= calloc(1, sizeof(**envelope
));
3814 /* Extract tRecord data */
3815 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
3817 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3818 * dmAcceptanceTime. */
3819 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
3820 if (err
) goto leave
;
3822 /* Extract envelope elements added by sender and ISDS
3823 * (XSD: gMessageEnvelope type) */
3824 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
3825 if (err
) goto leave
;
3827 /* Get message type */
3828 err
= append_message_type(context
, envelope
, xpath_ctx
);
3829 if (err
) goto leave
;
3833 if (err
) isds_envelope_free(envelope
);
3834 xmlXPathFreeObject(result
);
3839 /* Convert XSD:tStateChangesRecord type XML tree into structure
3840 * @context is ISDS context
3841 * @changed_status is automatically reallocated message state change structure
3842 * @xpath_ctx is XPath context with current node as element of
3843 * XSD:tStateChangesRecord type
3844 * In case of error @changed_status will be freed. */
3845 static isds_error
extract_StateChangesRecord(struct isds_ctx
*context
,
3846 struct isds_message_status_change
**changed_status
,
3847 xmlXPathContextPtr xpath_ctx
) {
3848 isds_error err
= IE_SUCCESS
;
3849 xmlXPathObjectPtr result
= NULL
;
3850 unsigned long int *unumber
= NULL
;
3851 char *string
= NULL
;
3853 if (!context
) return IE_INVALID_CONTEXT
;
3854 if (!changed_status
) return IE_INVAL
;
3855 isds_message_status_change_free(changed_status
);
3856 if (!xpath_ctx
) return IE_INVAL
;
3859 *changed_status
= calloc(1, sizeof(**changed_status
));
3860 if (!*changed_status
) {
3866 /* Extract tGetStateChangesInput data */
3867 EXTRACT_STRING("isds:dmID", (*changed_status
)->dmID
);
3869 /* dmEventTime is mandatory */
3870 EXTRACT_STRING("isds:dmEventTime", string
);
3872 err
= timestring2timeval((xmlChar
*) string
,
3873 &((*changed_status
)->time
));
3875 char *string_locale
= _isds_utf82locale(string
);
3876 if (err
== IE_DATE
) err
= IE_ISDS
;
3877 isds_printf_message(context
,
3878 _("Could not convert dmEventTime as ISO time: %s"),
3880 free(string_locale
);
3886 /* dmMessageStatus element is mandatory */
3887 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber
, 0);
3889 isds_log_message(context
,
3890 _("Missing mandatory isds:dmMessageStatus integer"));
3894 err
= uint2isds_message_status(context
, unumber
,
3895 &((*changed_status
)->dmMessageStatus
));
3897 if (err
== IE_ENUM
) err
= IE_ISDS
;
3906 if (err
) isds_message_status_change_free(changed_status
);
3907 xmlXPathFreeObject(result
);
3910 #endif /* HAVE_LIBCURL */
3913 /* Find and convert isds:dmHash XML tree into structure
3914 * @context is ISDS context
3915 * @envelope is automatically reallocated message hash structure
3916 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3917 * In case of error @hash will be freed. */
3918 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
3919 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
3920 isds_error err
= IE_SUCCESS
;
3921 xmlNodePtr old_ctx_node
;
3922 xmlXPathObjectPtr result
= NULL
;
3923 char *string
= NULL
;
3925 if (!context
) return IE_INVALID_CONTEXT
;
3926 if (!hash
) return IE_INVAL
;
3927 isds_hash_free(hash
);
3928 if (!xpath_ctx
) return IE_INVAL
;
3930 old_ctx_node
= xpath_ctx
->node
;
3932 *hash
= calloc(1, sizeof(**hash
));
3939 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
3940 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
3949 /* Get hash algorithm */
3950 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
3951 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
3953 if (err
== IE_ENUM
) {
3954 char *string_locale
= _isds_utf82locale(string
);
3955 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
3957 free(string_locale
);
3963 /* Get hash value */
3964 EXTRACT_STRING(".", string
);
3966 isds_printf_message(context
,
3967 _("sisds:dmHash element is missing hash value"));
3971 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
3972 if ((*hash
)->length
== (size_t) -1) {
3973 isds_printf_message(context
,
3974 _("Error while Base64-decoding hash value"));
3980 if (err
) isds_hash_free(hash
);
3982 xmlXPathFreeObject(result
);
3983 xpath_ctx
->node
= old_ctx_node
;
3988 /* Find and append isds:dmQTimestamp XML tree into envelope.
3989 * Because one service is allowed to miss time-stamp content, and we think
3990 * other could too (flaw in specification), this function is deliberated and
3991 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3992 * @context is ISDS context
3993 * @envelope is automatically allocated envelope structure
3994 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3996 * In case of error @envelope will be freed. */
3997 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
3998 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3999 isds_error err
= IE_SUCCESS
;
4000 xmlXPathObjectPtr result
= NULL
;
4001 char *string
= NULL
;
4003 if (!context
) return IE_INVALID_CONTEXT
;
4004 if (!envelope
) return IE_INVAL
;
4006 isds_envelope_free(envelope
);
4011 *envelope
= calloc(1, sizeof(**envelope
));
4017 zfree((*envelope
)->timestamp
);
4018 (*envelope
)->timestamp_length
= 0;
4021 /* Get dmQTimestamp */
4022 EXTRACT_STRING("sisds:dmQTimestamp", string
);
4024 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
4027 (*envelope
)->timestamp_length
=
4028 _isds_b64decode(string
, &((*envelope
)->timestamp
));
4029 if ((*envelope
)->timestamp_length
== (size_t) -1) {
4030 isds_printf_message(context
,
4031 _("Error while Base64-decoding time stamp value"));
4037 if (err
) isds_envelope_free(envelope
);
4039 xmlXPathFreeObject(result
);
4044 /* Convert XSD tReturnedMessage XML tree into message structure.
4045 * It does not store serialized XML tree into message->raw.
4046 * It does store (pointer to) parsed XML tree into message->xml if needed.
4047 * @context is ISDS context
4048 * @include_documents Use true if documents must be extracted
4049 * (tReturnedMessage XSD type), use false if documents shall be omitted
4050 * (tReturnedMessageEnvelope).
4051 * @message is automatically reallocated message structure
4052 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4054 * In case of error @message will be freed. */
4055 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
4056 const _Bool include_documents
, struct isds_message
**message
,
4057 xmlXPathContextPtr xpath_ctx
) {
4058 isds_error err
= IE_SUCCESS
;
4059 xmlNodePtr message_node
;
4061 if (!context
) return IE_INVALID_CONTEXT
;
4062 if (!message
) return IE_INVAL
;
4063 isds_message_free(message
);
4064 if (!xpath_ctx
) return IE_INVAL
;
4067 *message
= calloc(1, sizeof(**message
));
4073 /* Save message XPATH context node */
4074 message_node
= xpath_ctx
->node
;
4078 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
4079 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4080 if (err
) { err
= IE_ERROR
; goto leave
; }
4081 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
4082 if (err
) goto leave
;
4084 if (include_documents
) {
4085 struct isds_list
*item
;
4087 /* Extract dmFiles */
4088 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
4090 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
4091 err
= IE_ISDS
; goto leave
;
4093 if (err
) { err
= IE_ERROR
; goto leave
; }
4094 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
4095 if (err
) goto leave
;
4097 /* Store xmlDoc of this message if needed */
4098 /* Only if we got a XML document in all the documents. */
4099 for (item
= (*message
)->documents
; item
; item
= item
->next
) {
4100 if (item
->data
&& ((struct isds_document
*)item
->data
)->is_xml
) {
4101 (*message
)->xml
= xpath_ctx
->doc
;
4108 /* Restore context to message */
4109 xpath_ctx
->node
= message_node
;
4111 /* Extract dmHash */
4112 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
4114 if (err
) goto leave
;
4116 /* Extract dmQTimestamp, */
4117 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
4119 if (err
) goto leave
;
4121 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4122 * dmAcceptanceTime. */
4123 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
4124 if (err
) goto leave
;
4126 /* Get message type */
4127 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
4128 if (err
) goto leave
;
4131 if (err
) isds_message_free(message
);
4136 /* Extract message event into reallocated isds_event structure
4137 * @context is ISDS context
4138 * @event is automatically reallocated message event structure
4139 * @xpath_ctx is XPath context with current node as isds:dmEvent
4140 * In case of error @event will be freed. */
4141 static isds_error
extract_event(struct isds_ctx
*context
,
4142 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
4143 isds_error err
= IE_SUCCESS
;
4144 xmlXPathObjectPtr result
= NULL
;
4145 xmlNodePtr event_node
= xpath_ctx
->node
;
4146 char *string
= NULL
;
4148 if (!context
) return IE_INVALID_CONTEXT
;
4149 if (!event
) return IE_INVAL
;
4150 isds_event_free(event
);
4151 if (!xpath_ctx
) return IE_INVAL
;
4153 *event
= calloc(1, sizeof(**event
));
4159 /* Extract event data.
4160 * All elements are optional according XSD. That's funny. */
4161 EXTRACT_STRING("sisds:dmEventTime", string
);
4163 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
4165 char *string_locale
= _isds_utf82locale(string
);
4166 if (err
== IE_DATE
) err
= IE_ISDS
;
4167 isds_printf_message(context
,
4168 _("Could not convert dmEventTime as ISO time: %s"),
4170 free(string_locale
);
4176 /* dmEventDescr element has prefix and the rest */
4177 EXTRACT_STRING("sisds:dmEventDescr", string
);
4179 err
= eventstring2event((xmlChar
*) string
, *event
);
4180 if (err
) goto leave
;
4185 if (err
) isds_event_free(event
);
4187 xmlXPathFreeObject(result
);
4188 xpath_ctx
->node
= event_node
;
4193 /* Convert element of XSD tEventsArray type from XML tree into
4194 * isds_list of isds_event's structure. The list is automatically reallocated.
4195 * @context is ISDS context
4196 * @events is automatically reallocated list of event structures
4197 * @xpath_ctx is XPath context with current node as tEventsArray
4198 * In case of error @events will be freed. */
4199 static isds_error
extract_events(struct isds_ctx
*context
,
4200 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
4201 isds_error err
= IE_SUCCESS
;
4202 xmlXPathObjectPtr result
= NULL
;
4203 xmlNodePtr events_node
= xpath_ctx
->node
;
4204 struct isds_list
*event
, *prev_event
= NULL
;
4206 if (!context
) return IE_INVALID_CONTEXT
;
4207 if (!events
) return IE_INVAL
;
4208 if (!xpath_ctx
) return IE_INVAL
;
4211 isds_list_free(events
);
4214 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
4221 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4222 isds_printf_message(context
,
4223 _("Delivery info does not contain any event"));
4229 /* Iterate over events */
4230 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
4232 /* Allocate and append list item */
4233 event
= calloc(1, sizeof(*event
));
4238 event
->destructor
= (void (*)(void **))isds_event_free
;
4239 if (i
== 0) *events
= event
;
4240 else prev_event
->next
= event
;
4244 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
4245 err
= extract_event(context
,
4246 (struct isds_event
**) &(event
->data
), xpath_ctx
);
4247 if (err
) goto leave
;
4252 if (err
) isds_list_free(events
);
4253 xmlXPathFreeObject(result
);
4254 xpath_ctx
->node
= events_node
;
4260 /* Insert Base64 encoded data as element with text child.
4261 * @context is session context
4262 * @parent is XML node to append @element with @data as child
4263 * @ns is XML namespace of @element, use NULL to inherit from @parent
4264 * @element is UTF-8 encoded name of new element
4265 * @data is bit stream to encode into @element
4266 * @length is size of @data in bytes
4267 * @return standard error code and fill long error message if needed */
4268 static isds_error
insert_base64_encoded_string(struct isds_ctx
*context
,
4269 xmlNodePtr parent
, const xmlNsPtr ns
, const char *element
,
4270 const void *data
, size_t length
) {
4271 isds_error err
= IE_SUCCESS
;
4274 if (!context
) return IE_INVALID_CONTEXT
;
4275 if (!data
&& length
> 0) return IE_INVAL
;
4276 if (!parent
|| !element
) return IE_INVAL
;
4278 xmlChar
*base64data
= NULL
;
4279 base64data
= (xmlChar
*) _isds_b64encode(data
, length
);
4281 isds_printf_message(context
,
4282 ngettext("Not enough memory to encode %zd byte into Base64",
4283 "Not enough memory to encode %zd bytes into Base64",
4289 INSERT_STRING_WITH_NS(parent
, ns
, element
, base64data
);
4297 /* Convert isds_document structure into XML tree and append to dmFiles node.
4298 * @context is session context
4299 * @document is ISDS document
4300 * @dm_files is XML element the resulting tree will be appended to as a child.
4301 * @return error code, in case of error context' message is filled. */
4302 static isds_error
insert_document(struct isds_ctx
*context
,
4303 struct isds_document
*document
, xmlNodePtr dm_files
) {
4304 isds_error err
= IE_SUCCESS
;
4305 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
4306 xmlAttrPtr attribute_node
;
4308 if (!context
) return IE_INVALID_CONTEXT
;
4309 if (!document
|| !dm_files
) return IE_INVAL
;
4311 /* Allocate new dmFile */
4312 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
4314 isds_printf_message(context
, _("Could not allocate main dmFile"));
4318 /* Append the new dmFile.
4319 * XXX: Main document must go first */
4320 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
4321 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
4323 file
= xmlAddChild(dm_files
, new_file
);
4326 xmlFreeNode(new_file
); new_file
= NULL
;
4327 isds_printf_message(context
, _("Could not add dmFile child to "
4328 "%s element"), dm_files
->name
);
4333 /* @dmMimeType is required */
4334 if (!document
->dmMimeType
) {
4335 isds_log_message(context
,
4336 _("Document is missing mandatory MIME type definition"));
4340 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
4342 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
4344 isds_printf_message(context
,
4345 _("Document has unknown dmFileMetaType: %ld"),
4346 document
->dmFileMetaType
);
4350 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
4352 if (document
->dmFileGuid
) {
4353 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
4355 if (document
->dmUpFileGuid
) {
4356 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
4359 /* @dmFileDescr is required */
4360 if (!document
->dmFileDescr
) {
4361 isds_log_message(context
,
4362 _("Document is missing mandatory description (title)"));
4366 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
4368 if (document
->dmFormat
) {
4369 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
4373 /* Insert content (body) of the document. */
4374 if (document
->is_xml
) {
4375 /* XML document requested */
4377 /* Allocate new dmXMLContent */
4378 xmlNodePtr xmlcontent
= xmlNewNode(file
->ns
, BAD_CAST
"dmXMLContent");
4380 isds_printf_message(context
,
4381 _("Could not allocate dmXMLContent element"));
4386 node
= xmlAddChild(file
, xmlcontent
);
4388 xmlFreeNode(xmlcontent
); xmlcontent
= NULL
;
4389 isds_printf_message(context
,
4390 _("Could not add dmXMLContent child to %s element"),
4396 /* Copy non-empty node list */
4397 if (document
->xml_node_list
) {
4398 xmlNodePtr content
= xmlDocCopyNodeList(node
->doc
,
4399 document
->xml_node_list
);
4401 isds_printf_message(context
,
4402 _("Not enough memory to copy XML document"));
4407 if (!xmlAddChildList(node
, content
)) {
4408 xmlFreeNodeList(content
);
4409 isds_printf_message(context
,
4410 _("Error while adding XML document into dmXMLContent"));
4414 /* XXX: We cannot free the content here because it's part of node's
4415 * document since now. It will be freed with it automatically. */
4418 /* Binary document requested */
4419 err
= insert_base64_encoded_string(context
, file
, NULL
, "dmEncodedContent",
4420 document
->data
, document
->data_length
);
4421 if (err
) goto leave
;
4429 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4430 * The copy must be preallocated, the date are just appended into structure.
4431 * @context is ISDS context
4432 * @copy is message copy structure
4433 * @xpath_ctx is XPath context with current node as tMStatus */
4434 static isds_error
append_TMStatus(struct isds_ctx
*context
,
4435 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
4436 isds_error err
= IE_SUCCESS
;
4437 xmlXPathObjectPtr result
= NULL
;
4438 char *code
= NULL
, *message
= NULL
;
4440 if (!context
) return IE_INVALID_CONTEXT
;
4441 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
4443 /* Free old values */
4444 zfree(copy
->dmStatus
);
4447 /* Get error specific to this copy */
4448 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
4450 isds_log_message(context
,
4451 _("Missing isds:dmStatusCode under "
4452 "XSD:tMStatus type element"));
4457 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
4458 /* This copy failed */
4459 copy
->error
= IE_ISDS
;
4460 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
4462 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
4463 if (!copy
->dmStatus
) {
4464 copy
->dmStatus
= code
;
4468 copy
->dmStatus
= code
;
4472 /* This copy succeeded. In this case only, message ID is valid */
4473 copy
->error
= IE_SUCCESS
;
4475 EXTRACT_STRING("isds:dmID", copy
->dmID
);
4477 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
4478 "but did not returned assigned message ID\n"));
4486 xmlXPathFreeObject(result
);
4491 /* Insert struct isds_approval data (box approval) into XML tree
4492 * @context is session context
4493 * @approval is libisds structure with approval description. NULL is
4495 * @parent is XML element to append @approval to */
4496 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
4497 const struct isds_approval
*approval
, xmlNodePtr parent
) {
4499 isds_error err
= IE_SUCCESS
;
4502 if (!context
) return IE_INVALID_CONTEXT
;
4503 if (!parent
) return IE_INVAL
;
4505 if (!approval
) return IE_SUCCESS
;
4507 /* Build XSD:gExtApproval */
4508 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
4509 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
4516 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4518 * @context is session context
4519 * @service_name is name of SERVICE_DB_ACCESS
4520 * @response is server SOAP body response as XML document
4521 * @raw_response is automatically reallocated bit stream with response body. Use
4522 * NULL if you don't care
4523 * @raw_response_length is size of @raw_response in bytes
4524 * @code is ISDS status code
4525 * @status_message is ISDS status message
4526 * @return error coded from lower layer, context message will be set up
4528 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
4529 const xmlChar
*service_name
,
4530 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
4531 xmlChar
**code
, xmlChar
**status_message
) {
4533 isds_error err
= IE_SUCCESS
;
4534 char *service_name_locale
= NULL
;
4535 xmlNodePtr request
= NULL
, node
;
4536 xmlNsPtr isds_ns
= NULL
;
4538 if (!context
) return IE_INVALID_CONTEXT
;
4539 if (!service_name
) return IE_INVAL
;
4540 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4541 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
4543 /* Free output argument */
4544 xmlFreeDoc(*response
); *response
= NULL
;
4545 if (raw_response
) zfree(*raw_response
);
4547 free(*status_message
);
4550 /* Check if connection is established
4551 * TODO: This check should be done downstairs. */
4552 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4554 service_name_locale
= _isds_utf82locale((char*)service_name
);
4555 if (!service_name_locale
) {
4561 request
= xmlNewNode(NULL
, service_name
);
4563 isds_printf_message(context
,
4564 _("Could not build %s request"), service_name_locale
);
4568 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4570 isds_log_message(context
, _("Could not create ISDS name space"));
4574 xmlSetNs(request
, isds_ns
);
4577 /* Add XSD:tDummyInput child */
4578 INSERT_STRING(request
, "dbDummy", NULL
);
4581 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4582 service_name_locale
);
4585 err
= isds(context
, SERVICE_DB_ACCESS
, request
, response
,
4586 raw_response
, raw_response_length
);
4587 xmlFreeNode(request
); request
= NULL
;
4590 isds_log(ILF_ISDS
, ILL_DEBUG
,
4591 _("Processing ISDS response on %s request failed\n"),
4592 service_name_locale
);
4596 /* Check for response status */
4597 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4598 code
, status_message
, NULL
);
4600 isds_log(ILF_ISDS
, ILL_DEBUG
,
4601 _("ISDS response on %s request is missing status\n"),
4602 service_name_locale
);
4606 /* Request processed, but nothing found */
4607 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4608 char *code_locale
= _isds_utf82locale((char*) *code
);
4609 char *status_message_locale
=
4610 _isds_utf82locale((char*) *status_message
);
4611 isds_log(ILF_ISDS
, ILL_DEBUG
,
4612 _("Server refused %s request (code=%s, message=%s)\n"),
4613 service_name_locale
, code_locale
, status_message_locale
);
4614 isds_log_message(context
, status_message_locale
);
4616 free(status_message_locale
);
4622 free(service_name_locale
);
4623 xmlFreeNode(request
);
4629 /* Get data about logged in user and his box. */
4630 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4631 struct isds_DbOwnerInfo
**db_owner_info
) {
4632 isds_error err
= IE_SUCCESS
;
4634 xmlDocPtr response
= NULL
;
4635 xmlChar
*code
= NULL
, *message
= NULL
;
4636 xmlXPathContextPtr xpath_ctx
= NULL
;
4637 xmlXPathObjectPtr result
= NULL
;
4638 char *string
= NULL
;
4641 if (!context
) return IE_INVALID_CONTEXT
;
4642 zfree(context
->long_message
);
4643 if (!db_owner_info
) return IE_INVAL
;
4644 isds_DbOwnerInfo_free(db_owner_info
);
4647 /* Check if connection is established */
4648 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4651 /* Do request and check for success */
4652 err
= build_send_check_dbdummy_request(context
,
4653 BAD_CAST
"GetOwnerInfoFromLogin",
4654 &response
, NULL
, NULL
, &code
, &message
);
4655 if (err
) goto leave
;
4659 /* Prepare structure */
4660 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4661 if (!*db_owner_info
) {
4665 xpath_ctx
= xmlXPathNewContext(response
);
4670 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4675 /* Set context node */
4676 result
= xmlXPathEvalExpression(BAD_CAST
4677 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4682 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4683 isds_log_message(context
, _("Missing dbOwnerInfo element"));
4687 if (result
->nodesetval
->nodeNr
> 1) {
4688 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
4692 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4693 xmlXPathFreeObject(result
); result
= NULL
;
4696 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
4701 isds_DbOwnerInfo_free(db_owner_info
);
4705 xmlXPathFreeObject(result
);
4706 xmlXPathFreeContext(xpath_ctx
);
4710 xmlFreeDoc(response
);
4713 isds_log(ILF_ISDS
, ILL_DEBUG
,
4714 _("GetOwnerInfoFromLogin request processed by server "
4715 "successfully.\n"));
4716 #else /* not HAVE_LIBCURL */
4724 /* Get data about logged in user. */
4725 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
4726 struct isds_DbUserInfo
**db_user_info
) {
4727 isds_error err
= IE_SUCCESS
;
4729 xmlDocPtr response
= NULL
;
4730 xmlChar
*code
= NULL
, *message
= NULL
;
4731 xmlXPathContextPtr xpath_ctx
= NULL
;
4732 xmlXPathObjectPtr result
= NULL
;
4735 if (!context
) return IE_INVALID_CONTEXT
;
4736 zfree(context
->long_message
);
4737 if (!db_user_info
) return IE_INVAL
;
4738 isds_DbUserInfo_free(db_user_info
);
4741 /* Check if connection is established */
4742 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4745 /* Do request and check for success */
4746 err
= build_send_check_dbdummy_request(context
,
4747 BAD_CAST
"GetUserInfoFromLogin",
4748 &response
, NULL
, NULL
, &code
, &message
);
4749 if (err
) goto leave
;
4753 /* Prepare structure */
4754 *db_user_info
= calloc(1, sizeof(**db_user_info
));
4755 if (!*db_user_info
) {
4759 xpath_ctx
= xmlXPathNewContext(response
);
4764 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4769 /* Set context node */
4770 result
= xmlXPathEvalExpression(BAD_CAST
4771 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
4776 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4777 isds_log_message(context
, _("Missing dbUserInfo element"));
4781 if (result
->nodesetval
->nodeNr
> 1) {
4782 isds_log_message(context
, _("Multiple dbUserInfo element"));
4786 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4787 xmlXPathFreeObject(result
); result
= NULL
;
4790 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
4794 isds_DbUserInfo_free(db_user_info
);
4797 xmlXPathFreeObject(result
);
4798 xmlXPathFreeContext(xpath_ctx
);
4802 xmlFreeDoc(response
);
4805 isds_log(ILF_ISDS
, ILL_DEBUG
,
4806 _("GetUserInfoFromLogin request processed by server "
4807 "successfully.\n"));
4808 #else /* not HAVE_LIBCURL */
4816 /* Get expiration time of current password
4817 * @context is session context
4818 * @expiration is automatically reallocated time when password expires. If
4819 * password expiration is disables, NULL will be returned. In case of error
4820 * it will be nulled too. */
4821 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
4822 struct timeval
**expiration
) {
4823 isds_error err
= IE_SUCCESS
;
4825 xmlDocPtr response
= NULL
;
4826 xmlChar
*code
= NULL
, *message
= NULL
;
4827 xmlXPathContextPtr xpath_ctx
= NULL
;
4828 xmlXPathObjectPtr result
= NULL
;
4829 char *string
= NULL
;
4832 if (!context
) return IE_INVALID_CONTEXT
;
4833 zfree(context
->long_message
);
4834 if (!expiration
) return IE_INVAL
;
4838 /* Check if connection is established */
4839 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4842 /* Do request and check for success */
4843 err
= build_send_check_dbdummy_request(context
,
4844 BAD_CAST
"GetPasswordInfo",
4845 &response
, NULL
, NULL
, &code
, &message
);
4846 if (err
) goto leave
;
4850 xpath_ctx
= xmlXPathNewContext(response
);
4855 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4860 /* Set context node */
4861 result
= xmlXPathEvalExpression(BAD_CAST
4862 "/isds:GetPasswordInfoResponse", xpath_ctx
);
4867 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4868 isds_log_message(context
,
4869 _("Missing GetPasswordInfoResponse element"));
4873 if (result
->nodesetval
->nodeNr
> 1) {
4874 isds_log_message(context
,
4875 _("Multiple GetPasswordInfoResponse element"));
4879 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4880 xmlXPathFreeObject(result
); result
= NULL
;
4882 /* Extract expiration date */
4883 EXTRACT_STRING("isds:pswExpDate", string
);
4885 /* And convert it if any returned. Otherwise expiration is disabled. */
4886 err
= timestring2timeval((xmlChar
*) string
, expiration
);
4888 char *string_locale
= _isds_utf82locale(string
);
4889 if (err
== IE_DATE
) err
= IE_ISDS
;
4890 isds_printf_message(context
,
4891 _("Could not convert pswExpDate as ISO time: %s"),
4893 free(string_locale
);
4906 xmlXPathFreeObject(result
);
4907 xmlXPathFreeContext(xpath_ctx
);
4911 xmlFreeDoc(response
);
4914 isds_log(ILF_ISDS
, ILL_DEBUG
,
4915 _("GetPasswordInfo request processed by server "
4916 "successfully.\n"));
4917 #else /* not HAVE_LIBCURL */
4926 /* Request delivering new TOTP code from ISDS through side channel before
4927 * changing password.
4928 * @context is session context
4929 * @password is current password.
4930 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
4931 * Please note the @otp argument must have TOTP OTP method. See isds_login()
4932 * function for more details.
4933 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
4935 static isds_error
_isds_request_totp_code(struct isds_ctx
*context
,
4936 const char *password
, struct isds_otp
*otp
) {
4937 isds_error err
= IE_SUCCESS
;
4938 char *saved_url
= NULL
; /* No copy */
4939 xmlNsPtr isds_ns
= NULL
;
4940 xmlNodePtr request
= NULL
;
4941 xmlDocPtr response
= NULL
;
4942 xmlChar
*code
= NULL
, *message
= NULL
;
4943 const xmlChar
*codes
[] = {
4948 const char *meanings
[] = {
4949 N_("Unexpected error"),
4950 N_("One-time code cannot be re-send faster than once a 30 seconds"),
4951 N_("One-time code could not been sent. Try later again.")
4954 if (NULL
== context
) return IE_INVALID_CONTEXT
;
4955 zfree(context
->long_message
);
4956 if (NULL
== password
) return IE_INVAL
;
4958 /* Check if connection is established
4959 * TODO: This check should be done downstairs. */
4960 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4962 if (NULL
== context
->otp
) {
4963 isds_log_message(context
, _("This function requires OTP-authenticated "
4965 return IE_INVALID_CONTEXT
;
4968 isds_log_message(context
, _("If one-time password authentication "
4969 "method is in use, requesting new OTP code requires "
4970 "one-time credentials argument either"));
4973 if (otp
->method
!= OTP_TIME
) {
4974 isds_log_message(context
, _("Requesting new time-based OTP code from "
4975 "server requires one-time password authentication "
4979 if (context
->otp
->otp_code
!= NULL
) {
4980 isds_log_message(context
, _("Requesting new time-based OTP code from "
4981 "server requires undefined OTP code member in "
4982 "one-time credentials argument"));
4988 request
= xmlNewNode(NULL
, BAD_CAST
"SendSMSCode");
4990 isds_log_message(context
, _("Could not build SendSMSCode request"));
4993 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4995 isds_log_message(context
, _("Could not create ISDS name space"));
4996 xmlFreeNode(request
);
4999 xmlSetNs(request
, isds_ns
);
5001 /* Change URL temporarily for sending this request only */
5003 char *new_url
= NULL
;
5004 if ((err
= _isds_build_url_from_context(context
,
5005 "%1$.*2$sasws/changePassword", &new_url
))) {
5008 saved_url
= context
->url
;
5009 context
->url
= new_url
;
5012 /* Store credentials for sending this request only */
5014 _isds_discard_credentials(context
, 0);
5015 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5017 _isds_discard_credentials(context
, 0);
5021 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending SendSMSCode request to ISDS\n"));
5024 err
= isds(context
, SERVICE_ASWS
, request
, &response
, NULL
, NULL
);
5027 /* Remove temporal credentials */
5028 _isds_discard_credentials(context
, 0);
5029 /* Keep context->otp to keep signaling this is OTP session */
5032 /* Destroy request */
5033 xmlFreeNode(request
); request
= NULL
;
5036 isds_log(ILF_ISDS
, ILL_DEBUG
,
5037 _("Processing ISDS response on SendSMSCode request failed\n"));
5041 /* Check for response status */
5042 err
= isds_response_status(context
, SERVICE_ASWS
, response
,
5043 &code
, &message
, NULL
);
5045 isds_log(ILF_ISDS
, ILL_DEBUG
,
5046 _("ISDS response on SendSMSCode request is missing "
5051 /* Check for error */
5052 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5053 char *code_locale
= _isds_utf82locale((char*)code
);
5054 char *message_locale
= _isds_utf82locale((char*)message
);
5056 isds_log(ILF_ISDS
, ILL_DEBUG
,
5057 _("Server refused to send new code on SendSMSCode "
5058 "request (code=%s, message=%s)\n"),
5059 code_locale
, message_locale
);
5061 /* Check for known error codes */
5062 for (i
= 0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5063 if (!xmlStrcmp(code
, codes
[i
])) break;
5065 if (i
< sizeof(codes
)/sizeof(*codes
))
5066 isds_log_message(context
, _(meanings
[i
]));
5068 isds_log_message(context
, message_locale
);
5071 free(message_locale
);
5077 /* Otherwise new code sent successfully */
5080 if (NULL
!= saved_url
) {
5081 /* Revert URL to original one */
5082 zfree(context
->url
);
5083 context
->url
= saved_url
;
5088 xmlFreeDoc(response
);
5089 xmlFreeNode(request
);
5092 isds_log(ILF_ISDS
, ILL_DEBUG
,
5093 _("New OTP code has been sent successfully on SendSMSCode "
5100 /* Change user password in ISDS.
5101 * User must supply old password, new password will takes effect after some
5102 * time, current session can continue. Password must fulfill some constraints.
5103 * @context is session context
5104 * @old_password is current password.
5105 * @new_password is requested new password
5106 * @otp auxiliary data required if one-time password authentication is in use,
5107 * defines OTP code (if known) and returns fine grade resolution of OTP
5108 * procedure. Pass NULL, if one-time password authentication is not needed.
5109 * Please note the @otp argument must match OTP method used at log-in time. See
5110 * isds_login() function for more details.
5111 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5112 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5113 * awaiting OTP code that has been delivered by side channel to the user. */
5114 isds_error
isds_change_password(struct isds_ctx
*context
,
5115 const char *old_password
, const char *new_password
,
5116 struct isds_otp
*otp
) {
5117 isds_error err
= IE_SUCCESS
;
5119 char *saved_url
= NULL
; /* No copy */
5120 xmlNsPtr isds_ns
= NULL
;
5121 xmlNodePtr request
= NULL
, node
;
5122 xmlDocPtr response
= NULL
;
5123 xmlChar
*code
= NULL
, *message
= NULL
;
5124 const xmlChar
*codes
[] = {
5137 const char *meanings
[] = {
5138 N_("Password length must be between 8 and 32 characters"),
5139 N_("New password must differ from the current one"),
5140 N_("Password contains forbidden character"),
5141 N_("Password must contain at least one upper-case letter, "
5142 "one lower-case, and one digit"),
5143 N_("Password cannot contain sequence of three identical characters"),
5144 N_("Password cannot contain user identifier"),
5145 N_("Password is too simmple"),
5146 N_("Old password is not valid"),
5147 N_("Passwords cannot be reused"),
5148 N_("Unexpected error"),
5149 N_("LDAP update error")
5153 if (!context
) return IE_INVALID_CONTEXT
;
5154 zfree(context
->long_message
);
5155 if (!old_password
|| !new_password
) return IE_INVAL
;
5158 /* Check if connection is established
5159 * TODO: This check should be done downstairs. */
5160 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5162 if (NULL
!= context
->otp
&& NULL
== otp
) {
5163 isds_log_message(context
, _("If one-time password authentication "
5164 "method is in use, changing password requires one-time "
5165 "credentials either"));
5169 /* Build ChangeISDSPassword request */
5170 request
= xmlNewNode(NULL
, (NULL
== otp
) ? BAD_CAST
"ChangeISDSPassword" :
5171 BAD_CAST
"ChangePasswordOTP");
5173 isds_log_message(context
, (NULL
== otp
) ?
5174 _("Could not build ChangeISDSPassword request") :
5175 _("Could not build ChangePasswordOTP request"));
5178 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5180 isds_log_message(context
, _("Could not create ISDS name space"));
5181 xmlFreeNode(request
);
5184 xmlSetNs(request
, isds_ns
);
5186 INSERT_STRING(request
, "dbOldPassword", old_password
);
5187 INSERT_STRING(request
, "dbNewPassword", new_password
);
5190 switch (otp
->method
) {
5192 isds_log(ILF_SEC
, ILL_INFO
,
5193 _("Selected authentication method: "
5194 "HMAC-based one-time password\n"));
5195 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"HOTP");
5198 isds_log(ILF_SEC
, ILL_INFO
,
5199 _("Selected authentication method: "
5200 "Time-based one-time password\n"));
5201 INSERT_STRING(request
, "dbOTPType", BAD_CAST
"TOTP");
5202 if (context
->otp
->otp_code
== NULL
) {
5203 isds_log(ILF_SEC
, ILL_INFO
,
5204 _("OTP code has not been provided by "
5205 "application, requesting server for "
5207 err
= _isds_request_totp_code(context
, old_password
, otp
);
5208 if (err
== IE_SUCCESS
) err
= IE_PARTIAL_SUCCESS
;
5212 isds_log(ILF_SEC
, ILL_INFO
,
5213 _("OTP code has been provided by "
5214 "application, not requesting server "
5219 isds_log_message(context
,
5220 _("Unknown one-time password authentication "
5221 "method requested by application"));
5226 /* Change URL temporarily for sending this request only */
5228 char *new_url
= NULL
;
5229 if ((err
= _isds_build_url_from_context(context
,
5230 "%1$.*2$sasws/changePassword", &new_url
))) {
5233 saved_url
= context
->url
;
5234 context
->url
= new_url
;
5237 /* Store credentials for sending this request only */
5239 _isds_discard_credentials(context
, 0);
5240 if ((err
= _isds_store_credentials(context
, context
->saved_username
,
5241 old_password
, NULL
))) {
5242 _isds_discard_credentials(context
, 0);
5248 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5249 _("Sending ChangeISDSPassword request to ISDS\n") :
5250 _("Sending ChangePasswordOTP request to ISDS\n"));
5253 err
= isds(context
, (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
,
5254 request
, &response
, NULL
, NULL
);
5257 /* Remove temporal credentials */
5258 _isds_discard_credentials(context
, 0);
5259 /* Keep context->otp to keep signaling this is OTP session */
5262 /* Destroy request */
5263 xmlFreeNode(request
); request
= NULL
;
5266 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5267 _("Processing ISDS response on ChangeISDSPassword "
5268 "request failed\n") :
5269 _("Processing ISDS response on ChangePasswordOTP "
5270 "request failed\n"));
5274 /* Check for response status */
5275 err
= isds_response_status(context
,
5276 (NULL
== otp
) ? SERVICE_DB_ACCESS
: SERVICE_ASWS
, response
,
5277 &code
, &message
, NULL
);
5279 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5280 _("ISDS response on ChangeISDSPassword request is missing "
5282 _("ISDS response on ChangePasswordOTP request is missing "
5287 /* Check for known error codes */
5288 for (int i
=0; i
< sizeof(codes
)/sizeof(*codes
); i
++) {
5289 if (!xmlStrcmp(code
, codes
[i
])) {
5290 char *code_locale
= _isds_utf82locale((char*)code
);
5291 char *message_locale
= _isds_utf82locale((char*)message
);
5292 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5293 _("Server refused to change password on ChangeISDSPassword "
5294 "request (code=%s, message=%s)\n") :
5295 _("Server refused to change password on ChangePasswordOTP "
5296 "request (code=%s, message=%s)\n"),
5297 code_locale
, message_locale
);
5299 free(message_locale
);
5300 isds_log_message(context
, _(meanings
[i
]));
5307 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5308 char *code_locale
= _isds_utf82locale((char*)code
);
5309 char *message_locale
= _isds_utf82locale((char*)message
);
5310 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5311 _("Server refused to change password on ChangeISDSPassword "
5312 "request (code=%s, message=%s)\n") :
5313 _("Server refused to change password on ChangePasswordOTP "
5314 "request (code=%s, message=%s)\n"),
5315 code_locale
, message_locale
);
5316 isds_log_message(context
, message_locale
);
5318 free(message_locale
);
5323 /* Otherwise password changed successfully */
5326 if (NULL
!= saved_url
) {
5327 /* Revert URL to original one */
5328 zfree(context
->url
);
5329 context
->url
= saved_url
;
5334 xmlFreeDoc(response
);
5335 xmlFreeNode(request
);
5338 isds_log(ILF_ISDS
, ILL_DEBUG
, (NULL
== otp
) ?
5339 _("Password changed successfully on ChangeISDSPassword "
5341 _("Password changed successfully on ChangePasswordOTP "
5343 #else /* not HAVE_LIBCURL */
5352 /* Generic middle part with request sending and response check.
5353 * It sends prepared request and checks for error code.
5354 * @context is ISDS session context.
5355 * @service is ISDS service handler
5356 * @service_name is name in scope of given @service
5357 * @request is XML tree with request. Will be freed to save memory.
5358 * @response is XML document outputting ISDS response.
5359 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5360 * NULL, if you don't care. */
5361 static isds_error
send_destroy_request_check_response(
5362 struct isds_ctx
*context
,
5363 const isds_service service
, const xmlChar
*service_name
,
5364 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
) {
5365 isds_error err
= IE_SUCCESS
;
5366 char *service_name_locale
= NULL
;
5367 xmlChar
*code
= NULL
, *message
= NULL
;
5370 if (!context
) return IE_INVALID_CONTEXT
;
5371 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
5375 /* Check if connection is established
5376 * TODO: This check should be done downstairs. */
5377 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5379 service_name_locale
= _isds_utf82locale((char*) service_name
);
5380 if (!service_name_locale
) {
5385 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
5386 service_name_locale
);
5389 err
= isds(context
, service
, *request
, response
, NULL
, NULL
);
5390 xmlFreeNode(*request
); *request
= NULL
;
5393 isds_log(ILF_ISDS
, ILL_DEBUG
,
5394 _("Processing ISDS response on %s request failed\n"),
5395 service_name_locale
);
5399 /* Check for response status */
5400 err
= isds_response_status(context
, service
, *response
,
5401 &code
, &message
, refnumber
);
5403 isds_log(ILF_ISDS
, ILL_DEBUG
,
5404 _("ISDS response on %s request is missing status\n"),
5405 service_name_locale
);
5409 /* Request processed, but server failed */
5410 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5411 char *code_locale
= _isds_utf82locale((char*) code
);
5412 char *message_locale
= _isds_utf82locale((char*) message
);
5413 isds_log(ILF_ISDS
, ILL_DEBUG
,
5414 _("Server refused %s request (code=%s, message=%s)\n"),
5415 service_name_locale
, code_locale
, message_locale
);
5416 isds_log_message(context
, message_locale
);
5418 free(message_locale
);
5427 if (err
&& *response
) {
5428 xmlFreeDoc(*response
);
5432 xmlFreeNode(*request
);
5435 free(service_name_locale
);
5441 /* Generic bottom half with request sending.
5442 * It sends prepared request, checks for error code, destroys response and
5443 * request and log success or failure.
5444 * @context is ISDS session context.
5445 * @service is ISDS service handler
5446 * @service_name is name in scope of given @service
5447 * @request is XML tree with request. Will be freed to save memory.
5448 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5449 * NULL, if you don't care. */
5450 static isds_error
send_request_check_drop_response(
5451 struct isds_ctx
*context
,
5452 const isds_service service
, const xmlChar
*service_name
,
5453 xmlNodePtr
*request
, xmlChar
**refnumber
) {
5454 isds_error err
= IE_SUCCESS
;
5455 xmlDocPtr response
= NULL
;
5458 if (!context
) return IE_INVALID_CONTEXT
;
5459 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
5462 /* Send request and check response*/
5463 err
= send_destroy_request_check_response(context
,
5464 service
, service_name
, request
, &response
, refnumber
);
5466 xmlFreeDoc(response
);
5469 xmlFreeNode(*request
);
5474 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5475 isds_log(ILF_ISDS
, ILL_DEBUG
,
5476 _("%s request processed by server successfully.\n"),
5477 service_name_locale
);
5478 free(service_name_locale
);
5485 /* Insert isds_credentials_delivery structure into XML request if not NULL
5486 * @context is session context
5487 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5488 * credentials delivery. The email field is passed.
5489 * @parent is XML element where to insert */
5490 static isds_error
insert_credentials_delivery(struct isds_ctx
*context
,
5491 const struct isds_credentials_delivery
*credentials_delivery
,
5492 xmlNodePtr parent
) {
5493 isds_error err
= IE_SUCCESS
;
5496 if (!context
) return IE_INVALID_CONTEXT
;
5497 if (!parent
) return IE_INVAL
;
5499 if (credentials_delivery
) {
5500 /* Following elements are valid only for services:
5501 * NewAccessData, AddDataBoxUser, CreateDataBox */
5502 INSERT_SCALAR_BOOLEAN(parent
, "dbVirtual", 1);
5503 INSERT_STRING(parent
, "email", credentials_delivery
->email
);
5511 /* Extract credentials delivery from ISDS response.
5512 * @context is session context
5513 * @credentials_delivery is pointer to valid structure to fill in returned
5514 * user's password (and new log-in name). If NULL, do not extract the data.
5515 * @response is pointer to XML document with ISDS response
5516 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5517 * @return IE_SUCCESS even if new user name has not been found because it's not
5518 * clear whether it's returned always. */
5519 static isds_error
extract_credentials_delivery(struct isds_ctx
*context
,
5520 struct isds_credentials_delivery
*credentials_delivery
,
5521 xmlDocPtr response
, const char *request_name
) {
5522 isds_error err
= IE_SUCCESS
;
5523 xmlXPathContextPtr xpath_ctx
= NULL
;
5524 xmlXPathObjectPtr result
= NULL
;
5525 char *xpath_query
= NULL
;
5527 if (!context
) return IE_INVALID_CONTEXT
;
5528 if (credentials_delivery
) {
5529 zfree(credentials_delivery
->token
);
5530 zfree(credentials_delivery
->new_user_name
);
5532 if (!response
|| !request_name
|| !*request_name
) return IE_INVAL
;
5535 /* Extract optional token */
5536 if (credentials_delivery
) {
5537 xpath_ctx
= xmlXPathNewContext(response
);
5542 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5547 /* Verify root element */
5548 if (-1 == isds_asprintf(&xpath_query
, "/isds:%sResponse",
5553 result
= xmlXPathEvalExpression(BAD_CAST xpath_query
, xpath_ctx
);
5558 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5559 char *request_name_locale
= _isds_utf82locale(request_name
);
5560 isds_log(ILF_ISDS
, ILL_WARNING
,
5561 _("Wrong element in ISDS response for %s request "
5562 "while extracting credentials delivery details\n"),
5563 request_name_locale
);
5564 free(request_name_locale
);
5568 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5571 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5573 EXTRACT_STRING("isds:dbUserID", credentials_delivery
->new_user_name
);
5575 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery
->token
);
5576 if (!credentials_delivery
->token
) {
5577 char *request_name_locale
= _isds_utf82locale(request_name
);
5578 isds_log(ILF_ISDS
, ILL_ERR
,
5579 _("ISDS did not return token on %s request "
5580 "even if requested\n"), request_name_locale
);
5581 free(request_name_locale
);
5588 xmlXPathFreeObject(result
);
5589 xmlXPathFreeContext(xpath_ctx
);
5595 /* Build XSD:tCreateDBInput request type for box creating.
5596 * @context is session context
5597 * @request outputs built XML tree
5598 * @service_name is request name of SERVICE_DB_MANIPULATION service
5599 * @box is box description to create including single primary user (in case of
5601 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5602 * box, or contact address of PFO box owner)
5603 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5604 * @upper_box_id is optional ID of supper box if currently created box is
5606 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5608 * @credentials_delivery is valid pointer if ISDS should return token that box
5609 * owner can use to obtain his new credentials in on-line way. Then valid email
5610 * member value should be supplied.
5611 * @approval is optional external approval of box manipulation */
5612 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
5613 xmlNodePtr
*request
, const xmlChar
*service_name
,
5614 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5615 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
5616 const xmlChar
*ceo_label
,
5617 const struct isds_credentials_delivery
*credentials_delivery
,
5618 const struct isds_approval
*approval
) {
5619 isds_error err
= IE_SUCCESS
;
5620 xmlNsPtr isds_ns
= NULL
;
5621 xmlNodePtr node
, dbPrimaryUsers
;
5622 xmlChar
*string
= NULL
;
5623 const struct isds_list
*item
;
5626 if (!context
) return IE_INVALID_CONTEXT
;
5627 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
5631 /* Build CreateDataBox-similar request */
5632 *request
= xmlNewNode(NULL
, service_name
);
5634 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
5635 isds_printf_message(context
, _("Could build %s request"),
5636 service_name_locale
);
5637 free(service_name_locale
);
5640 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
5641 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
5643 isds_log_message(context
, _("Could not create ISDS1 name space"));
5644 xmlFreeNode(*request
);
5648 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
5650 isds_log_message(context
, _("Could not create ISDS name space"));
5651 xmlFreeNode(*request
);
5655 xmlSetNs(*request
, isds_ns
);
5657 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
5658 err
= insert_DbOwnerInfo(context
, box
, node
);
5659 if (err
) goto leave
;
5662 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5663 * verbose documentation allows none dbUserInfo */
5664 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
5665 for (item
= users
; item
; item
= item
->next
) {
5667 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
5668 err
= insert_DbUserInfo(context
,
5669 (struct isds_DbUserInfo
*) item
->data
, node
);
5670 if (err
) goto leave
;
5674 INSERT_STRING(*request
, "dbFormerNames", former_names
);
5675 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
5676 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
5678 err
= insert_credentials_delivery(context
, credentials_delivery
, *request
);
5679 if (err
) goto leave
;
5681 err
= insert_GExtApproval(context
, approval
, *request
);
5682 if (err
) goto leave
;
5686 xmlFreeNode(*request
);
5692 #endif /* HAVE_LIBCURL */
5696 * @context is session context
5697 * @box is box description to create including single primary user (in case of
5698 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5699 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5700 * box, or contact address of PFO box owner)
5701 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5702 * @upper_box_id is optional ID of supper box if currently created box is
5704 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5705 * @credentials_delivery is NULL if new password should be delivered off-line
5706 * to box owner. It is valid pointer if owner should obtain new password on-line
5707 * on dedicated web server. Then input @credentials_delivery.email value is
5708 * his e-mail address he must provide to dedicated web server together
5709 * with output reallocated @credentials_delivery.token member. Output
5710 * member @credentials_delivery.new_user_name is unused up on this call.
5711 * @approval is optional external approval of box manipulation
5712 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5713 * NULL, if you don't care.*/
5714 isds_error
isds_add_box(struct isds_ctx
*context
,
5715 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5716 const char *former_names
, const char *upper_box_id
,
5717 const char *ceo_label
,
5718 struct isds_credentials_delivery
*credentials_delivery
,
5719 const struct isds_approval
*approval
, char **refnumber
) {
5720 isds_error err
= IE_SUCCESS
;
5722 xmlNodePtr request
= NULL
;
5723 xmlDocPtr response
= NULL
;
5724 xmlXPathContextPtr xpath_ctx
= NULL
;
5725 xmlXPathObjectPtr result
= NULL
;
5729 if (!context
) return IE_INVALID_CONTEXT
;
5730 zfree(context
->long_message
);
5731 if (credentials_delivery
) {
5732 zfree(credentials_delivery
->token
);
5733 zfree(credentials_delivery
->new_user_name
);
5735 if (!box
) return IE_INVAL
;
5738 /* Scratch box ID */
5741 /* Build CreateDataBox request */
5742 err
= build_CreateDBInput_request(context
,
5743 &request
, BAD_CAST
"CreateDataBox",
5744 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
5745 (xmlChar
*) ceo_label
, credentials_delivery
, approval
);
5746 if (err
) goto leave
;
5748 /* Send it to server and process response */
5749 err
= send_destroy_request_check_response(context
,
5750 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
5751 &response
, (xmlChar
**) refnumber
);
5753 /* Extract box ID */
5754 xpath_ctx
= xmlXPathNewContext(response
);
5759 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5763 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
5765 /* Extract optional token */
5766 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
5770 xmlXPathFreeObject(result
);
5771 xmlXPathFreeContext(xpath_ctx
);
5772 xmlFreeDoc(response
);
5773 xmlFreeNode(request
);
5776 isds_log(ILF_ISDS
, ILL_DEBUG
,
5777 _("CreateDataBox request processed by server successfully.\n"));
5779 #else /* not HAVE_LIBCURL */
5787 /* Notify ISDS about new PFO entity.
5788 * This function has no real effect.
5789 * @context is session context
5790 * @box is PFO description including single primary user.
5791 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5792 * @former_names is optional undocumented string. Pass NULL if you don't care.
5793 * @upper_box_id is optional ID of supper box if currently created box is
5795 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5796 * @approval is optional external approval of box manipulation
5797 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5798 * NULL, if you don't care.*/
5799 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
5800 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
5801 const char *former_names
, const char *upper_box_id
,
5802 const char *ceo_label
, const struct isds_approval
*approval
,
5804 isds_error err
= IE_SUCCESS
;
5806 xmlNodePtr request
= NULL
;
5809 if (!context
) return IE_INVALID_CONTEXT
;
5810 zfree(context
->long_message
);
5811 if (!box
) return IE_INVAL
;
5814 /* Build CreateDataBoxPFOInfo request */
5815 err
= build_CreateDBInput_request(context
,
5816 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
5817 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
5818 (xmlChar
*) ceo_label
, NULL
, approval
);
5819 if (err
) goto leave
;
5821 /* Send it to server and process response */
5822 err
= send_request_check_drop_response(context
,
5823 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
5824 (xmlChar
**) refnumber
);
5825 /* XXX: XML Schema names output dbID element but textual documentation
5826 * states no box identifier is returned. */
5828 xmlFreeNode(request
);
5829 #else /* not HAVE_LIBCURL */
5836 /* Common implementation for removing given box.
5837 * @context is session context
5838 * @service_name is UTF-8 encoded name fo ISDS service
5839 * @box is box description to delete
5840 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5841 * carry sane value. If NULL, do not inject this information into request.
5842 * @approval is optional external approval of box manipulation
5843 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5844 * NULL, if you don't care.*/
5845 isds_error
_isds_delete_box_common(struct isds_ctx
*context
,
5846 const xmlChar
*service_name
,
5847 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
5848 const struct isds_approval
*approval
, char **refnumber
) {
5849 isds_error err
= IE_SUCCESS
;
5851 xmlNsPtr isds_ns
= NULL
;
5852 xmlNodePtr request
= NULL
;
5854 xmlChar
*string
= NULL
;
5858 if (!context
) return IE_INVALID_CONTEXT
;
5859 zfree(context
->long_message
);
5860 if (!service_name
|| !*service_name
|| !box
) return IE_INVAL
;
5864 /* Build DeleteDataBox(Promptly) request */
5865 request
= xmlNewNode(NULL
, service_name
);
5867 char *service_name_locale
= _isds_utf82locale((char*)service_name
);
5868 isds_printf_message(context
,
5869 _("Could build %s request"), service_name_locale
);
5870 free(service_name_locale
);
5873 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5875 isds_log_message(context
, _("Could not create ISDS name space"));
5876 xmlFreeNode(request
);
5879 xmlSetNs(request
, isds_ns
);
5881 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
5882 err
= insert_DbOwnerInfo(context
, box
, node
);
5883 if (err
) goto leave
;
5886 err
= tm2datestring(since
, &string
);
5888 isds_log_message(context
,
5889 _("Could not convert `since' argument to ISO date string"));
5892 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
5896 err
= insert_GExtApproval(context
, approval
, request
);
5897 if (err
) goto leave
;
5900 /* Send it to server and process response */
5901 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
5902 service_name
, &request
, (xmlChar
**) refnumber
);
5905 xmlFreeNode(request
);
5907 #else /* not HAVE_LIBCURL */
5914 /* Remove given box permanently.
5915 * @context is session context
5916 * @box is box description to delete
5917 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5919 * @approval is optional external approval of box manipulation
5920 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5921 * NULL, if you don't care.*/
5922 isds_error
isds_delete_box(struct isds_ctx
*context
,
5923 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
5924 const struct isds_approval
*approval
, char **refnumber
) {
5925 if (!context
) return IE_INVALID_CONTEXT
;
5926 zfree(context
->long_message
);
5927 if (!box
|| !since
) return IE_INVAL
;
5929 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBox",
5930 box
, since
, approval
, refnumber
);
5934 /* Undocumented function.
5935 * @context is session context
5936 * @box is box description to delete
5937 * @approval is optional external approval of box manipulation
5938 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5939 * NULL, if you don't care.*/
5940 isds_error
isds_delete_box_promptly(struct isds_ctx
*context
,
5941 const struct isds_DbOwnerInfo
*box
,
5942 const struct isds_approval
*approval
, char **refnumber
) {
5943 if (!context
) return IE_INVALID_CONTEXT
;
5944 zfree(context
->long_message
);
5945 if (!box
) return IE_INVAL
;
5947 return _isds_delete_box_common(context
, BAD_CAST
"DeleteDataBoxPromptly",
5948 box
, NULL
, approval
, refnumber
);
5952 /* Update data about given box.
5953 * @context is session context
5954 * @old_box current box description
5955 * @new_box are updated data about @old_box
5956 * @approval is optional external approval of box manipulation
5957 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5958 * NULL, if you don't care.*/
5959 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
5960 const struct isds_DbOwnerInfo
*old_box
,
5961 const struct isds_DbOwnerInfo
*new_box
,
5962 const struct isds_approval
*approval
, char **refnumber
) {
5963 isds_error err
= IE_SUCCESS
;
5965 xmlNsPtr isds_ns
= NULL
;
5966 xmlNodePtr request
= NULL
;
5971 if (!context
) return IE_INVALID_CONTEXT
;
5972 zfree(context
->long_message
);
5973 if (!old_box
|| !new_box
) return IE_INVAL
;
5977 /* Build UpdateDataBoxDescr request */
5978 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
5980 isds_log_message(context
,
5981 _("Could build UpdateDataBoxDescr request"));
5984 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5986 isds_log_message(context
, _("Could not create ISDS name space"));
5987 xmlFreeNode(request
);
5990 xmlSetNs(request
, isds_ns
);
5992 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
5993 err
= insert_DbOwnerInfo(context
, old_box
, node
);
5994 if (err
) goto leave
;
5996 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
5997 err
= insert_DbOwnerInfo(context
, new_box
, node
);
5998 if (err
) goto leave
;
6000 err
= insert_GExtApproval(context
, approval
, request
);
6001 if (err
) goto leave
;
6004 /* Send it to server and process response */
6005 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6006 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
6009 xmlFreeNode(request
);
6010 #else /* not HAVE_LIBCURL */
6019 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6021 * @context is session context
6022 * @service is SOAP service
6023 * @service_name is name of request in @service
6024 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6025 * @box_id is box ID of interest
6026 * @approval is optional external approval of box manipulation
6027 * @response is server SOAP body response as XML document
6028 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6029 * NULL, if you don't care.
6030 * @return error coded from lower layer, context message will be set up
6032 static isds_error
build_send_dbid_request_check_response(
6033 struct isds_ctx
*context
, const isds_service service
,
6034 const xmlChar
*service_name
, const xmlChar
*box_id_element
,
6035 const xmlChar
*box_id
, const struct isds_approval
*approval
,
6036 xmlDocPtr
*response
, xmlChar
**refnumber
) {
6038 isds_error err
= IE_SUCCESS
;
6039 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
6040 xmlNodePtr request
= NULL
, node
;
6041 xmlNsPtr isds_ns
= NULL
;
6043 if (!context
) return IE_INVALID_CONTEXT
;
6044 if (!service_name
|| !box_id
) return IE_INVAL
;
6045 if (!response
) return IE_INVAL
;
6047 /* Free output argument */
6048 xmlFreeDoc(*response
); *response
= NULL
;
6050 /* Prepare strings */
6051 service_name_locale
= _isds_utf82locale((char*)service_name
);
6052 if (!service_name_locale
) {
6056 box_id_locale
= _isds_utf82locale((char*)box_id
);
6057 if (!box_id_locale
) {
6063 request
= xmlNewNode(NULL
, service_name
);
6065 isds_printf_message(context
,
6066 _("Could not build %s request for %s box"), service_name_locale
,
6071 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6073 isds_log_message(context
, _("Could not create ISDS name space"));
6077 xmlSetNs(request
, isds_ns
);
6079 /* Add XSD:tIdDbInput children */
6080 if (NULL
== box_id_element
) box_id_element
= BAD_CAST
"dbID";
6081 INSERT_STRING(request
, box_id_element
, box_id
);
6082 err
= insert_GExtApproval(context
, approval
, request
);
6083 if (err
) goto leave
;
6085 /* Send request and check response*/
6086 err
= send_destroy_request_check_response(context
,
6087 service
, service_name
, &request
, response
, refnumber
);
6090 free(service_name_locale
);
6091 free(box_id_locale
);
6092 xmlFreeNode(request
);
6095 #endif /* HAVE_LIBCURL */
6098 /* Get data about all users assigned to given box.
6099 * @context is session context
6101 * @users is automatically reallocated list of struct isds_DbUserInfo */
6102 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
6103 struct isds_list
**users
) {
6104 isds_error err
= IE_SUCCESS
;
6106 xmlDocPtr response
= NULL
;
6107 xmlXPathContextPtr xpath_ctx
= NULL
;
6108 xmlXPathObjectPtr result
= NULL
;
6110 struct isds_list
*item
, *prev_item
= NULL
;
6113 if (!context
) return IE_INVALID_CONTEXT
;
6114 zfree(context
->long_message
);
6115 if (!users
|| !box_id
) return IE_INVAL
;
6116 isds_list_free(users
);
6120 /* Do request and check for success */
6121 err
= build_send_dbid_request_check_response(context
,
6122 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers", NULL
,
6123 BAD_CAST box_id
, NULL
, &response
, NULL
);
6124 if (err
) goto leave
;
6128 /* Prepare structure */
6129 xpath_ctx
= xmlXPathNewContext(response
);
6134 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6139 /* Set context node */
6140 result
= xmlXPathEvalExpression(BAD_CAST
6141 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6147 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6148 /* Iterate over all users */
6149 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6151 /* Prepare structure */
6152 item
= calloc(1, sizeof(*item
));
6157 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
6158 if (i
== 0) *users
= item
;
6159 else prev_item
->next
= item
;
6163 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6164 err
= extract_DbUserInfo(context
,
6165 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
6166 if (err
) goto leave
;
6172 isds_list_free(users
);
6175 xmlXPathFreeObject(result
);
6176 xmlXPathFreeContext(xpath_ctx
);
6177 xmlFreeDoc(response
);
6180 isds_log(ILF_ISDS
, ILL_DEBUG
,
6181 _("GetDataBoxUsers request processed by server "
6182 "successfully.\n"));
6183 #else /* not HAVE_LIBCURL */
6191 /* Update data about user assigned to given box.
6192 * @context is session context
6193 * @box is box identification
6194 * @old_user identifies user to update
6195 * @new_user are updated data about @old_user
6196 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6197 * NULL, if you don't care.*/
6198 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
6199 const struct isds_DbOwnerInfo
*box
,
6200 const struct isds_DbUserInfo
*old_user
,
6201 const struct isds_DbUserInfo
*new_user
,
6203 isds_error err
= IE_SUCCESS
;
6205 xmlNsPtr isds_ns
= NULL
;
6206 xmlNodePtr request
= NULL
;
6211 if (!context
) return IE_INVALID_CONTEXT
;
6212 zfree(context
->long_message
);
6213 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
6217 /* Build UpdateDataBoxUser request */
6218 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
6220 isds_log_message(context
,
6221 _("Could build UpdateDataBoxUser request"));
6224 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6226 isds_log_message(context
, _("Could not create ISDS name space"));
6227 xmlFreeNode(request
);
6230 xmlSetNs(request
, isds_ns
);
6232 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6233 err
= insert_DbOwnerInfo(context
, box
, node
);
6234 if (err
) goto leave
;
6236 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
6237 err
= insert_DbUserInfo(context
, old_user
, node
);
6238 if (err
) goto leave
;
6240 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
6241 err
= insert_DbUserInfo(context
, new_user
, node
);
6242 if (err
) goto leave
;
6244 /* Send it to server and process response */
6245 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
6246 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
6249 xmlFreeNode(request
);
6250 #else /* not HAVE_LIBCURL */
6258 /* Undocumented function.
6259 * @context is session context
6260 * @box_id is UTF-8 encoded box identifier
6261 * @token is UTF-8 encoded temporary password
6262 * @user_id outputs UTF-8 encoded reallocated user identifier
6263 * @password outpus UTF-8 encoded reallocated user password
6264 * Output arguments will be nulled in case of error */
6265 isds_error
isds_activate(struct isds_ctx
*context
,
6266 const char *box_id
, const char *token
,
6267 char **user_id
, char **password
) {
6268 isds_error err
= IE_SUCCESS
;
6270 xmlNsPtr isds_ns
= NULL
;
6271 xmlNodePtr request
= NULL
, node
;
6272 xmlDocPtr response
= NULL
;
6273 xmlXPathContextPtr xpath_ctx
= NULL
;
6274 xmlXPathObjectPtr result
= NULL
;
6278 if (!context
) return IE_INVALID_CONTEXT
;
6279 zfree(context
->long_message
);
6281 if (user_id
) zfree(*user_id
);
6282 if (password
) zfree(*password
);
6284 if (!box_id
|| !token
|| !user_id
|| !password
) return IE_INVAL
;
6288 /* Build Activate request */
6289 request
= xmlNewNode(NULL
, BAD_CAST
"Activate");
6291 isds_log_message(context
, _("Could build Activate request"));
6294 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6296 isds_log_message(context
, _("Could not create ISDS name space"));
6297 xmlFreeNode(request
);
6300 xmlSetNs(request
, isds_ns
);
6302 INSERT_STRING(request
, "dbAccessDataId", token
);
6303 CHECK_FOR_STRING_LENGTH(box_id
, 7, 7, "dbID");
6304 INSERT_STRING(request
, "dbID", box_id
);
6307 /* Send request and check response*/
6308 err
= send_destroy_request_check_response(context
,
6309 SERVICE_DB_MANIPULATION
, BAD_CAST
"Activate", &request
,
6311 if (err
) goto leave
;
6315 xpath_ctx
= xmlXPathNewContext(response
);
6320 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6324 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:ActivateResponse",
6330 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6331 isds_log_message(context
, _("Missing ActivateResponse element"));
6335 if (result
->nodesetval
->nodeNr
> 1) {
6336 isds_log_message(context
, _("Multiple ActivateResponse element"));
6340 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6341 xmlXPathFreeObject(result
); result
= NULL
;
6343 EXTRACT_STRING("isds:userId", *user_id
);
6345 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6346 "but did not return `userId' element.\n"));
6348 EXTRACT_STRING("isds:password", *password
);
6350 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted Activate request, "
6351 "but did not return `password' element.\n"));
6354 xmlXPathFreeObject(result
);
6355 xmlXPathFreeContext(xpath_ctx
);
6356 xmlFreeDoc(response
);
6357 xmlFreeNode(request
);
6360 isds_log(ILF_ISDS
, ILL_DEBUG
,
6361 _("Activate request processed by server successfully.\n"));
6362 #else /* not HAVE_LIBCURL */
6370 /* Reset credentials of user assigned to given box.
6371 * @context is session context
6372 * @box is box identification
6373 * @user identifies user to reset password
6374 * @fee_paid is true if fee has been paid, false otherwise
6375 * @approval is optional external approval of box manipulation
6376 * @credentials_delivery is NULL if new password should be delivered off-line
6377 * to the user. It is valid pointer if user should obtain new password on-line
6378 * on dedicated web server. Then input @credentials_delivery.email value is
6379 * user's e-mail address user must provide to dedicated web server together
6380 * with @credentials_delivery.token. The output reallocated token user needs
6381 * to use to authorize on the web server to view his new password. Output
6382 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6383 * ISDS changed up on this call. (No reason why server could change the name
6385 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6386 * NULL, if you don't care.*/
6387 isds_error
isds_reset_password(struct isds_ctx
*context
,
6388 const struct isds_DbOwnerInfo
*box
,
6389 const struct isds_DbUserInfo
*user
,
6390 const _Bool fee_paid
, const struct isds_approval
*approval
,
6391 struct isds_credentials_delivery
*credentials_delivery
,
6393 isds_error err
= IE_SUCCESS
;
6395 xmlNsPtr isds_ns
= NULL
;
6396 xmlNodePtr request
= NULL
, node
;
6397 xmlDocPtr response
= NULL
;
6401 if (!context
) return IE_INVALID_CONTEXT
;
6402 zfree(context
->long_message
);
6404 if (credentials_delivery
) {
6405 zfree(credentials_delivery
->token
);
6406 zfree(credentials_delivery
->new_user_name
);
6408 if (!box
|| !user
) return IE_INVAL
;
6412 /* Build NewAccessData request */
6413 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
6415 isds_log_message(context
,
6416 _("Could build NewAccessData request"));
6419 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6421 isds_log_message(context
, _("Could not create ISDS name space"));
6422 xmlFreeNode(request
);
6425 xmlSetNs(request
, isds_ns
);
6427 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6428 err
= insert_DbOwnerInfo(context
, box
, node
);
6429 if (err
) goto leave
;
6431 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6432 err
= insert_DbUserInfo(context
, user
, node
);
6433 if (err
) goto leave
;
6435 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
6437 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6438 if (err
) goto leave
;
6440 err
= insert_GExtApproval(context
, approval
, request
);
6441 if (err
) goto leave
;
6443 /* Send request and check response*/
6444 err
= send_destroy_request_check_response(context
,
6445 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
6446 &response
, (xmlChar
**) refnumber
);
6447 if (err
) goto leave
;
6450 /* Extract optional token */
6451 err
= extract_credentials_delivery(context
, credentials_delivery
,
6452 response
, "NewAccessData");
6455 xmlFreeDoc(response
);
6456 xmlFreeNode(request
);
6459 isds_log(ILF_ISDS
, ILL_DEBUG
,
6460 _("NewAccessData request processed by server "
6461 "successfully.\n"));
6462 #else /* not HAVE_LIBCURL */
6470 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6471 * code, destroy response and log success.
6472 * @context is ISDS session context.
6473 * @service_name is name of SERVICE_DB_MANIPULATION service
6474 * @box is box identification
6475 * @user identifies user to remove
6476 * @credentials_delivery is NULL if new user's password should be delivered
6477 * off-line to the user. It is valid pointer if user should obtain new
6478 * password on-line on dedicated web server. Then input
6479 * @credentials_delivery.email value is user's e-mail address user must
6480 * provide to dedicated web server together with @credentials_delivery.token.
6481 * The output reallocated token user needs to use to authorize on the web
6482 * server to view his new password. Output reallocated
6483 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6484 * assingned or changed up on this call.
6485 * @approval is optional external approval of box manipulation
6486 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6487 * NULL, if you don't care. */
6488 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
6489 struct isds_ctx
*context
, const xmlChar
*service_name
,
6490 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6491 struct isds_credentials_delivery
*credentials_delivery
,
6492 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
6493 isds_error err
= IE_SUCCESS
;
6495 xmlNsPtr isds_ns
= NULL
;
6496 xmlNodePtr request
= NULL
, node
;
6497 xmlDocPtr response
= NULL
;
6501 if (!context
) return IE_INVALID_CONTEXT
;
6502 zfree(context
->long_message
);
6503 if (credentials_delivery
) {
6504 zfree(credentials_delivery
->token
);
6505 zfree(credentials_delivery
->new_user_name
);
6507 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
6512 /* Build NewAccessData or similar request */
6513 request
= xmlNewNode(NULL
, service_name
);
6515 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6516 isds_printf_message(context
, _("Could not build %s request"),
6517 service_name_locale
);
6518 free(service_name_locale
);
6521 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6523 isds_log_message(context
, _("Could not create ISDS name space"));
6524 xmlFreeNode(request
);
6527 xmlSetNs(request
, isds_ns
);
6529 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
6530 err
= insert_DbOwnerInfo(context
, box
, node
);
6531 if (err
) goto leave
;
6533 INSERT_ELEMENT(node
, request
, "dbUserInfo");
6534 err
= insert_DbUserInfo(context
, user
, node
);
6535 if (err
) goto leave
;
6537 err
= insert_credentials_delivery(context
, credentials_delivery
, request
);
6538 if (err
) goto leave
;
6540 err
= insert_GExtApproval(context
, approval
, request
);
6541 if (err
) goto leave
;
6544 /* Send request and check response*/
6545 err
= send_destroy_request_check_response(context
,
6546 SERVICE_DB_MANIPULATION
, service_name
, &request
, &response
, refnumber
);
6548 xmlFreeNode(request
);
6551 /* Pick up credentials_delivery if requested */
6552 err
= extract_credentials_delivery(context
, credentials_delivery
, response
,
6553 (char *)service_name
);
6556 xmlFreeDoc(response
);
6557 if (request
) xmlFreeNode(request
);
6560 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
6561 isds_log(ILF_ISDS
, ILL_DEBUG
,
6562 _("%s request processed by server successfully.\n"),
6563 service_name_locale
);
6564 free(service_name_locale
);
6566 #else /* not HAVE_LIBCURL */
6574 /* Assign new user to given box.
6575 * @context is session context
6576 * @box is box identification
6577 * @user defines new user to add
6578 * @credentials_delivery is NULL if new user's password should be delivered
6579 * off-line to the user. It is valid pointer if user should obtain new
6580 * password on-line on dedicated web server. Then input
6581 * @credentials_delivery.email value is user's e-mail address user must
6582 * provide to dedicated web server together with @credentials_delivery.token.
6583 * The output reallocated token user needs to use to authorize on the web
6584 * server to view his new password. Output reallocated
6585 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6586 * assingned up on this call.
6587 * @approval is optional external approval of box manipulation
6588 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6589 * NULL, if you don't care.*/
6590 isds_error
isds_add_user(struct isds_ctx
*context
,
6591 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6592 struct isds_credentials_delivery
*credentials_delivery
,
6593 const struct isds_approval
*approval
, char **refnumber
) {
6594 return build_send_manipulationboxuser_request_check_drop_response(context
,
6595 BAD_CAST
"AddDataBoxUser", box
, user
, credentials_delivery
,
6596 approval
, (xmlChar
**) refnumber
);
6600 /* Remove user assigned to given box.
6601 * @context is session context
6602 * @box is box identification
6603 * @user identifies user to remove
6604 * @approval is optional external approval of box manipulation
6605 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6606 * NULL, if you don't care.*/
6607 isds_error
isds_delete_user(struct isds_ctx
*context
,
6608 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
6609 const struct isds_approval
*approval
, char **refnumber
) {
6610 return build_send_manipulationboxuser_request_check_drop_response(context
,
6611 BAD_CAST
"DeleteDataBoxUser", box
, user
, NULL
, approval
,
6612 (xmlChar
**) refnumber
);
6616 /* Get list of boxes in ZIP archive.
6617 * @context is session context
6618 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6619 * System recognizes following values currently: ALL (all boxes), UPG
6620 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6621 * receiving commercial messages). This argument is a string because
6622 * specification states new values can appear in the future. Not all list
6623 * types are available to all users.
6624 * @buffer is automatically reallocated memory to store the list of boxes. The
6625 * list is zipped CSV file.
6626 * @buffer_length is size of @buffer data in bytes.
6627 * In case of error @buffer will be freed and @buffer_length will be
6629 isds_error
isds_get_box_list_archive(struct isds_ctx
*context
,
6630 const char *list_identifier
, void **buffer
, size_t *buffer_length
) {
6631 isds_error err
= IE_SUCCESS
;
6633 xmlNsPtr isds_ns
= NULL
;
6634 xmlNodePtr request
= NULL
, node
;
6635 xmlDocPtr response
= NULL
;
6636 xmlXPathContextPtr xpath_ctx
= NULL
;
6637 xmlXPathObjectPtr result
= NULL
;
6638 char *string
= NULL
;
6642 if (!context
) return IE_INVALID_CONTEXT
;
6643 zfree(context
->long_message
);
6644 if (buffer
) zfree(*buffer
);
6645 if (!buffer
|| !buffer_length
) return IE_INVAL
;
6649 /* Check if connection is established
6650 * TODO: This check should be done downstairs. */
6651 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6654 /* Build AuthenticateMessage request */
6655 request
= xmlNewNode(NULL
, BAD_CAST
"GetDataBoxList");
6657 isds_log_message(context
,
6658 _("Could not build GetDataBoxList request"));
6661 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6663 isds_log_message(context
, _("Could not create ISDS name space"));
6664 xmlFreeNode(request
);
6667 xmlSetNs(request
, isds_ns
);
6668 INSERT_STRING(request
, "dblType", list_identifier
);
6670 /* Send request to server and process response */
6671 err
= send_destroy_request_check_response(context
,
6672 SERVICE_DB_SEARCH
, BAD_CAST
"GetDataBoxList", &request
,
6674 if (err
) goto leave
;
6677 /* Extract Base-64 encoded ZIP file */
6678 xpath_ctx
= xmlXPathNewContext(response
);
6683 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6687 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string
);
6689 /* Decode non-empty archive */
6690 if (string
&& string
[0] != '\0') {
6691 *buffer_length
= _isds_b64decode(string
, buffer
);
6692 if (*buffer_length
== (size_t) -1) {
6693 isds_printf_message(context
,
6694 _("Error while Base64-decoding box list archive"));
6703 xmlXPathFreeObject(result
);
6704 xmlXPathFreeContext(xpath_ctx
);
6705 xmlFreeDoc(response
);
6706 xmlFreeNode(request
);
6709 isds_log(ILF_ISDS
, ILL_DEBUG
, _("GetDataBoxList request "
6710 "processed by server successfully.\n"));
6712 #else /* not HAVE_LIBCURL */
6720 /* Find boxes suiting given criteria.
6721 * @criteria is filter. You should fill in at least some members.
6722 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6723 * possibly empty. Input NULL or valid old structure.
6725 * IE_SUCCESS if search succeeded, @boxes contains useful data
6726 * IE_NOEXIST if no such box exists, @boxes will be NULL
6727 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6728 * contains still valid data
6729 * other code if something bad happens. @boxes will be NULL. */
6730 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
6731 const struct isds_DbOwnerInfo
*criteria
,
6732 struct isds_list
**boxes
) {
6733 isds_error err
= IE_SUCCESS
;
6735 _Bool truncated
= 0;
6736 xmlNsPtr isds_ns
= NULL
;
6737 xmlNodePtr request
= NULL
;
6738 xmlDocPtr response
= NULL
;
6739 xmlChar
*code
= NULL
, *message
= NULL
;
6740 xmlNodePtr db_owner_info
;
6741 xmlXPathContextPtr xpath_ctx
= NULL
;
6742 xmlXPathObjectPtr result
= NULL
;
6743 xmlChar
*string
= NULL
;
6747 if (!context
) return IE_INVALID_CONTEXT
;
6748 zfree(context
->long_message
);
6749 if (!boxes
) return IE_INVAL
;
6750 isds_list_free(boxes
);
6757 /* Check if connection is established
6758 * TODO: This check should be done downstairs. */
6759 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6762 /* Build FindDataBox request */
6763 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
6765 isds_log_message(context
,
6766 _("Could build FindDataBox request"));
6769 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6771 isds_log_message(context
, _("Could not create ISDS name space"));
6772 xmlFreeNode(request
);
6775 xmlSetNs(request
, isds_ns
);
6776 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
6777 if (!db_owner_info
) {
6778 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
6779 "FindDataBox element"));
6780 xmlFreeNode(request
);
6784 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
6785 if (err
) goto leave
;
6788 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
6791 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
6793 /* Destroy request */
6794 xmlFreeNode(request
); request
= NULL
;
6797 isds_log(ILF_ISDS
, ILL_DEBUG
,
6798 _("Processing ISDS response on FindDataBox "
6799 "request failed\n"));
6803 /* Check for response status */
6804 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
6805 &code
, &message
, NULL
);
6807 isds_log(ILF_ISDS
, ILL_DEBUG
,
6808 _("ISDS response on FindDataBox request is missing status\n"));
6812 /* Request processed, but nothing found */
6813 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
6814 !xmlStrcmp(code
, BAD_CAST
"5001")) {
6815 char *code_locale
= _isds_utf82locale((char*)code
);
6816 char *message_locale
= _isds_utf82locale((char*)message
);
6817 isds_log(ILF_ISDS
, ILL_DEBUG
,
6818 _("Server did not found any box on FindDataBox request "
6819 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
6820 isds_log_message(context
, message_locale
);
6822 free(message_locale
);
6827 /* Warning, not a error */
6828 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
6829 char *code_locale
= _isds_utf82locale((char*)code
);
6830 char *message_locale
= _isds_utf82locale((char*)message
);
6831 isds_log(ILF_ISDS
, ILL_DEBUG
,
6832 _("Server truncated response on FindDataBox request "
6833 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
6834 isds_log_message(context
, message_locale
);
6836 free(message_locale
);
6841 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6842 char *code_locale
= _isds_utf82locale((char*)code
);
6843 char *message_locale
= _isds_utf82locale((char*)message
);
6844 isds_log(ILF_ISDS
, ILL_DEBUG
,
6845 _("Server refused FindDataBox request "
6846 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
6847 isds_log_message(context
, message_locale
);
6849 free(message_locale
);
6854 xpath_ctx
= xmlXPathNewContext(response
);
6859 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6864 /* Extract boxes if they present */
6865 result
= xmlXPathEvalExpression(BAD_CAST
6866 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
6872 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6873 struct isds_list
*item
, *prev_item
= NULL
;
6874 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
6875 item
= calloc(1, sizeof(*item
));
6881 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
6882 if (i
== 0) *boxes
= item
;
6883 else prev_item
->next
= item
;
6886 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6887 err
= extract_DbOwnerInfo(context
,
6888 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
6889 if (err
) goto leave
;
6895 isds_list_free(boxes
);
6897 if (truncated
) err
= IE_2BIG
;
6901 xmlFreeNode(request
);
6902 xmlXPathFreeObject(result
);
6903 xmlXPathFreeContext(xpath_ctx
);
6907 xmlFreeDoc(response
);
6910 isds_log(ILF_ISDS
, ILL_DEBUG
,
6911 _("FindDataBox request processed by server successfully.\n"));
6912 #else /* not HAVE_LIBCURL */
6920 /* Get status of a box.
6921 * @context is ISDS session context.
6922 * @box_id is UTF-8 encoded box identifier as zero terminated string
6923 * @box_status is return value of box status.
6925 * IE_SUCCESS if box has been found and its status retrieved
6926 * IE_NOEXIST if box is not known to ISDS server
6927 * or other appropriate error.
6928 * You can use isds_DbState to enumerate box status. However out of enum
6929 * range value can be returned too. This is feature because ISDS
6930 * specification leaves the set of values open.
6931 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
6932 * the box has been deleted, but ISDS still lists its former existence. */
6933 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
6934 long int *box_status
) {
6935 isds_error err
= IE_SUCCESS
;
6937 xmlNsPtr isds_ns
= NULL
;
6938 xmlNodePtr request
= NULL
, db_id
;
6939 xmlDocPtr response
= NULL
;
6940 xmlChar
*code
= NULL
, *message
= NULL
;
6941 xmlXPathContextPtr xpath_ctx
= NULL
;
6942 xmlXPathObjectPtr result
= NULL
;
6943 xmlChar
*string
= NULL
;
6946 if (!context
) return IE_INVALID_CONTEXT
;
6947 zfree(context
->long_message
);
6948 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
6951 /* Check if connection is established
6952 * TODO: This check should be done downstairs. */
6953 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6956 /* Build CheckDataBox request */
6957 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
6959 isds_log_message(context
,
6960 _("Could build CheckDataBox request"));
6963 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6965 isds_log_message(context
, _("Could not create ISDS name space"));
6966 xmlFreeNode(request
);
6969 xmlSetNs(request
, isds_ns
);
6970 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
6972 isds_log_message(context
, _("Could not add dbID child to "
6973 "CheckDataBox element"));
6974 xmlFreeNode(request
);
6979 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CheckDataBox request to ISDS\n"));
6982 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
6984 /* Destroy request */
6985 xmlFreeNode(request
);
6988 isds_log(ILF_ISDS
, ILL_DEBUG
,
6989 _("Processing ISDS response on CheckDataBox "
6990 "request failed\n"));
6994 /* Check for response status */
6995 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
6996 &code
, &message
, NULL
);
6998 isds_log(ILF_ISDS
, ILL_DEBUG
,
6999 _("ISDS response on CheckDataBox request is missing status\n"));
7003 /* Request processed, but nothing found */
7004 if (!xmlStrcmp(code
, BAD_CAST
"5001")) {
7005 char *box_id_locale
= _isds_utf82locale((char*)box_id
);
7006 char *code_locale
= _isds_utf82locale((char*)code
);
7007 char *message_locale
= _isds_utf82locale((char*)message
);
7008 isds_log(ILF_ISDS
, ILL_DEBUG
,
7009 _("Server did not found box %s on CheckDataBox request "
7010 "(code=%s, message=%s)\n"),
7011 box_id_locale
, code_locale
, message_locale
);
7012 isds_log_message(context
, message_locale
);
7013 free(box_id_locale
);
7015 free(message_locale
);
7021 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7022 char *code_locale
= _isds_utf82locale((char*)code
);
7023 char *message_locale
= _isds_utf82locale((char*)message
);
7024 isds_log(ILF_ISDS
, ILL_DEBUG
,
7025 _("Server refused CheckDataBox request "
7026 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
7027 isds_log_message(context
, message_locale
);
7029 free(message_locale
);
7035 xpath_ctx
= xmlXPathNewContext(response
);
7040 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7044 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
7050 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7051 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
7055 if (result
->nodesetval
->nodeNr
> 1) {
7056 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
7060 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7061 xmlXPathFreeObject(result
); result
= NULL
;
7063 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
7068 xmlXPathFreeObject(result
);
7069 xmlXPathFreeContext(xpath_ctx
);
7073 xmlFreeDoc(response
);
7076 isds_log(ILF_ISDS
, ILL_DEBUG
,
7077 _("CheckDataBox request processed by server successfully.\n"));
7078 #else /* not HAVE_LIBCURL */
7086 /* Get list of permissions to send commercial messages.
7087 * @context is ISDS session context.
7088 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7089 * @permissions is a reallocated list of permissions (struct
7090 * isds_commercial_permission*) to send commercial messages from @box_id. The
7091 * order of permissions is significant as the server applies the permissions
7092 * and associated pre-paid credits in the order. Empty list means no
7095 * IE_SUCCESS if the list has been obtained correctly,
7096 * or other appropriate error. */
7097 isds_error
isds_get_commercial_permissions(struct isds_ctx
*context
,
7098 const char *box_id
, struct isds_list
**permissions
) {
7099 isds_error err
= IE_SUCCESS
;
7101 xmlDocPtr response
= NULL
;
7102 xmlXPathContextPtr xpath_ctx
= NULL
;
7103 xmlXPathObjectPtr result
= NULL
;
7106 if (!context
) return IE_INVALID_CONTEXT
;
7107 zfree(context
->long_message
);
7108 if (NULL
== permissions
) return IE_INVAL
;
7109 isds_list_free(permissions
);
7110 if (NULL
== box_id
) return IE_INVAL
;
7113 /* Check if connection is established */
7114 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7116 /* Do request and check for success */
7117 err
= build_send_dbid_request_check_response(context
,
7118 SERVICE_DB_SEARCH
, BAD_CAST
"PDZInfo", BAD_CAST
"PDZSender",
7119 BAD_CAST box_id
, NULL
, &response
, NULL
);
7121 isds_log(ILF_ISDS
, ILL_DEBUG
,
7122 _("PDZInfo request processed by server successfully.\n"));
7126 /* Prepare structure */
7127 xpath_ctx
= xmlXPathNewContext(response
);
7132 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7137 /* Set context node */
7138 result
= xmlXPathEvalExpression(BAD_CAST
7139 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7145 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7146 /* Iterate over all permission records */
7147 for (long unsigned int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
7148 struct isds_list
*item
, *prev_item
= NULL
;
7150 /* Prepare structure */
7151 item
= calloc(1, sizeof(*item
));
7156 item
->destructor
= (void(*)(void**))isds_commercial_permission_free
;
7157 if (i
== 0) *permissions
= item
;
7158 else prev_item
->next
= item
;
7162 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7163 err
= extract_DbPDZRecord(context
,
7164 (struct isds_commercial_permission
**) (&item
->data
),
7166 if (err
) goto leave
;
7172 isds_list_free(permissions
);
7175 xmlXPathFreeObject(result
);
7176 xmlXPathFreeContext(xpath_ctx
);
7177 xmlFreeDoc(response
);
7179 #else /* not HAVE_LIBCURL */
7187 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7188 * code, destroy response and log success.
7189 * @context is ISDS session context.
7190 * @service_name is name of SERVICE_DB_MANIPULATION service
7191 * @box_id is UTF-8 encoded box identifier as zero terminated string
7192 * @approval is optional external approval of box manipulation
7193 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7194 * NULL, if you don't care. */
7195 static isds_error
build_send_manipulationdbid_request_check_drop_response(
7196 struct isds_ctx
*context
, const xmlChar
*service_name
,
7197 const xmlChar
*box_id
, const struct isds_approval
*approval
,
7198 xmlChar
**refnumber
) {
7199 isds_error err
= IE_SUCCESS
;
7201 xmlDocPtr response
= NULL
;
7204 if (!context
) return IE_INVALID_CONTEXT
;
7205 zfree(context
->long_message
);
7206 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
7209 /* Check if connection is established */
7210 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7212 /* Do request and check for success */
7213 err
= build_send_dbid_request_check_response(context
,
7214 SERVICE_DB_MANIPULATION
, service_name
, NULL
, box_id
, approval
,
7215 &response
, refnumber
);
7216 xmlFreeDoc(response
);
7219 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
7220 isds_log(ILF_ISDS
, ILL_DEBUG
,
7221 _("%s request processed by server successfully.\n"),
7222 service_name_locale
);
7223 free(service_name_locale
);
7225 #else /* not HAVE_LIBCURL */
7233 /* Switch box into state where box can receive commercial messages (off by
7235 * @context is ISDS session context.
7236 * @box_id is UTF-8 encoded box identifier as zero terminated string
7237 * @allow is true for enable, false for disable commercial messages income
7238 * @approval is optional external approval of box manipulation
7239 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7240 * NULL, if you don't care. */
7241 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
7242 const char *box_id
, const _Bool allow
,
7243 const struct isds_approval
*approval
, char **refnumber
) {
7244 return build_send_manipulationdbid_request_check_drop_response(context
,
7245 (allow
) ? BAD_CAST
"SetOpenAddressing" :
7246 BAD_CAST
"ClearOpenAddressing",
7247 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7251 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7252 * message acceptance). This is just a box permission. Sender must apply
7253 * such role by sending each message.
7254 * @context is ISDS session context.
7255 * @box_id is UTF-8 encoded box identifier as zero terminated string
7256 * @allow is true for enable, false for disable OVM role permission
7257 * @approval is optional external approval of box manipulation
7258 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7259 * NULL, if you don't care. */
7260 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
7261 const char *box_id
, const _Bool allow
,
7262 const struct isds_approval
*approval
, char **refnumber
) {
7263 return build_send_manipulationdbid_request_check_drop_response(context
,
7264 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
7265 BAD_CAST
"ClearEffectiveOVM",
7266 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
7270 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7271 * code, destroy response and log success.
7272 * @context is ISDS session context.
7273 * @service_name is name of SERVICE_DB_MANIPULATION service
7274 * @owner is structure describing box
7275 * @approval is optional external approval of box manipulation
7276 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7277 * NULL, if you don't care. */
7278 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
7279 struct isds_ctx
*context
, const xmlChar
*service_name
,
7280 const struct isds_DbOwnerInfo
*owner
,
7281 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
7282 isds_error err
= IE_SUCCESS
;
7284 char *service_name_locale
= NULL
;
7285 xmlNodePtr request
= NULL
, db_owner_info
;
7286 xmlNsPtr isds_ns
= NULL
;
7290 if (!context
) return IE_INVALID_CONTEXT
;
7291 zfree(context
->long_message
);
7292 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
7295 service_name_locale
= _isds_utf82locale((char*)service_name
);
7296 if (!service_name_locale
) {
7302 request
= xmlNewNode(NULL
, service_name
);
7304 isds_printf_message(context
,
7305 _("Could not build %s request"), service_name_locale
);
7309 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7311 isds_log_message(context
, _("Could not create ISDS name space"));
7315 xmlSetNs(request
, isds_ns
);
7318 /* Add XSD:tOwnerInfoInput child*/
7319 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
7320 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
7321 if (err
) goto leave
;
7323 /* Add XSD:gExtApproval*/
7324 err
= insert_GExtApproval(context
, approval
, request
);
7325 if (err
) goto leave
;
7327 /* Send it to server and process response */
7328 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7329 service_name
, &request
, refnumber
);
7332 xmlFreeNode(request
);
7333 free(service_name_locale
);
7334 #else /* not HAVE_LIBCURL */
7342 /* Switch box accessibility state on request of box owner.
7343 * Despite the name, owner must do the request off-line. This function is
7344 * designed for such off-line meeting points (e.g. Czech POINT).
7345 * @context is ISDS session context.
7346 * @box identifies box to switch accessibility state.
7347 * @allow is true for making accessible, false to disallow access.
7348 * @approval is optional external approval of box manipulation
7349 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7350 * NULL, if you don't care. */
7351 isds_error
isds_switch_box_accessibility_on_owner_request(
7352 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7353 const _Bool allow
, const struct isds_approval
*approval
,
7355 return build_send_manipulationdbowner_request_check_drop_response(context
,
7356 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
7357 BAD_CAST
"DisableOwnDataBox",
7358 box
, approval
, (xmlChar
**) refnumber
);
7362 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7364 * @context is ISDS session context.
7365 * @box identifies box to switch accessibility state.
7366 * @since is date since accessibility has been denied. This can be past too.
7367 * Only tm_year, tm_mon and tm_mday carry sane value.
7368 * @approval is optional external approval of box manipulation
7369 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7370 * NULL, if you don't care. */
7371 isds_error
isds_disable_box_accessibility_externaly(
7372 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
7373 const struct tm
*since
, const struct isds_approval
*approval
,
7375 isds_error err
= IE_SUCCESS
;
7377 char *service_name_locale
= NULL
;
7378 xmlNodePtr request
= NULL
, node
;
7379 xmlNsPtr isds_ns
= NULL
;
7380 xmlChar
*string
= NULL
;
7384 if (!context
) return IE_INVALID_CONTEXT
;
7385 zfree(context
->long_message
);
7386 if (!box
|| !since
) return IE_INVAL
;
7390 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
7392 isds_printf_message(context
,
7393 _("Could not build %s request"), "DisableDataBoxExternally");
7397 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7399 isds_log_message(context
, _("Could not create ISDS name space"));
7403 xmlSetNs(request
, isds_ns
);
7406 /* Add @box identification */
7407 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
7408 err
= insert_DbOwnerInfo(context
, box
, node
);
7409 if (err
) goto leave
;
7411 /* Add @since date */
7412 err
= tm2datestring(since
, &string
);
7414 isds_log_message(context
,
7415 _("Could not convert `since' argument to ISO date string"));
7418 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
7422 err
= insert_GExtApproval(context
, approval
, request
);
7423 if (err
) goto leave
;
7425 /* Send it to server and process response */
7426 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
7427 BAD_CAST
"DisableDataBoxExternally", &request
,
7428 (xmlChar
**) refnumber
);
7432 xmlFreeNode(request
);
7433 free(service_name_locale
);
7434 #else /* not HAVE_LIBCURL */
7443 /* Insert struct isds_message data (envelope (recipient data optional) and
7444 * documents into XML tree
7445 * @context is session context
7446 * @outgoing_message is libisds structure with message data
7447 * @create_message is XML CreateMessage or CreateMultipleMessage element
7448 * @process_recipient true for recipient data serialization, false for no
7450 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
7451 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
7452 const _Bool process_recipient
) {
7454 isds_error err
= IE_SUCCESS
;
7455 xmlNodePtr envelope
, dm_files
, node
;
7456 xmlChar
*string
= NULL
;
7458 if (!context
) return IE_INVALID_CONTEXT
;
7459 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
7462 /* Build envelope */
7463 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
7465 isds_printf_message(context
, _("Could not add dmEnvelope child to "
7466 "%s element"), create_message
->name
);
7470 if (!outgoing_message
->envelope
) {
7471 isds_log_message(context
, _("Outgoing message is missing envelope"));
7476 /* Insert optional message type */
7477 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
7479 if (err
) goto leave
;
7481 INSERT_STRING(envelope
, "dmSenderOrgUnit",
7482 outgoing_message
->envelope
->dmSenderOrgUnit
);
7483 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
7484 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
7486 if (process_recipient
) {
7487 if (!outgoing_message
->envelope
->dbIDRecipient
) {
7488 isds_log_message(context
,
7489 _("Outgoing message is missing recipient box identifier"));
7493 INSERT_STRING(envelope
, "dbIDRecipient",
7494 outgoing_message
->envelope
->dbIDRecipient
);
7496 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
7497 outgoing_message
->envelope
->dmRecipientOrgUnit
);
7498 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
7499 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
7500 INSERT_STRING(envelope
, "dmToHands",
7501 outgoing_message
->envelope
->dmToHands
);
7504 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
7506 INSERT_STRING(envelope
, "dmAnnotation",
7507 outgoing_message
->envelope
->dmAnnotation
);
7509 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
7510 0, 50, "dmRecipientRefNumber");
7511 INSERT_STRING(envelope
, "dmRecipientRefNumber",
7512 outgoing_message
->envelope
->dmRecipientRefNumber
);
7514 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
7515 0, 50, "dmSenderRefNumber");
7516 INSERT_STRING(envelope
, "dmSenderRefNumber",
7517 outgoing_message
->envelope
->dmSenderRefNumber
);
7519 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
7520 0, 50, "dmRecipientIdent");
7521 INSERT_STRING(envelope
, "dmRecipientIdent",
7522 outgoing_message
->envelope
->dmRecipientIdent
);
7524 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
7525 0, 50, "dmSenderIdent");
7526 INSERT_STRING(envelope
, "dmSenderIdent",
7527 outgoing_message
->envelope
->dmSenderIdent
);
7529 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
7530 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
7531 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
7532 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
7533 INSERT_STRING(envelope
, "dmLegalTitleSect",
7534 outgoing_message
->envelope
->dmLegalTitleSect
);
7535 INSERT_STRING(envelope
, "dmLegalTitlePar",
7536 outgoing_message
->envelope
->dmLegalTitlePar
);
7537 INSERT_STRING(envelope
, "dmLegalTitlePoint",
7538 outgoing_message
->envelope
->dmLegalTitlePoint
);
7540 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
7541 outgoing_message
->envelope
->dmPersonalDelivery
);
7542 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
7543 outgoing_message
->envelope
->dmAllowSubstDelivery
);
7545 /* ???: Should we require value for dbEffectiveOVM sender?
7546 * ISDS has default as true */
7547 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
7548 INSERT_BOOLEAN(envelope
, "dmOVM",
7549 outgoing_message
->envelope
->dmPublishOwnID
);
7552 /* Append dmFiles */
7553 if (!outgoing_message
->documents
) {
7554 isds_log_message(context
,
7555 _("Outgoing message is missing list of documents"));
7559 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
7561 isds_printf_message(context
, _("Could not add dmFiles child to "
7562 "%s element"), create_message
->name
);
7567 /* Check for document hierarchy */
7568 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
7569 if (err
) goto leave
;
7571 /* Process each document */
7572 for (struct isds_list
*item
=
7573 (struct isds_list
*) outgoing_message
->documents
;
7574 item
; item
= item
->next
) {
7576 isds_log_message(context
,
7577 _("List of documents contains empty item"));
7581 /* FIXME: Check for dmFileMetaType and for document references.
7582 * Only first document can be of MAIN type */
7583 err
= insert_document(context
, (struct isds_document
*) item
->data
,
7586 if (err
) goto leave
;
7593 #endif /* HAVE_LIBCURL */
7596 /* Send a message via ISDS to a recipient
7597 * @context is session context
7598 * @outgoing_message is message to send; Some members are mandatory (like
7599 * dbIDRecipient), some are optional and some are irrelevant (especially data
7600 * about sender). Included pointer to isds_list documents must contain at
7601 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
7602 * members will be filled with valid data from ISDS. Exact list of write
7603 * members is subject to change. Currently dmID is changed.
7604 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
7605 isds_error
isds_send_message(struct isds_ctx
*context
,
7606 struct isds_message
*outgoing_message
) {
7608 isds_error err
= IE_SUCCESS
;
7610 xmlNsPtr isds_ns
= NULL
;
7611 xmlNodePtr request
= NULL
;
7612 xmlDocPtr response
= NULL
;
7613 xmlChar
*code
= NULL
, *message
= NULL
;
7614 xmlXPathContextPtr xpath_ctx
= NULL
;
7615 xmlXPathObjectPtr result
= NULL
;
7616 /*_Bool message_is_complete = 0;*/
7619 if (!context
) return IE_INVALID_CONTEXT
;
7620 zfree(context
->long_message
);
7621 if (!outgoing_message
) return IE_INVAL
;
7624 /* Check if connection is established
7625 * TODO: This check should be done downstairs. */
7626 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7629 /* Build CreateMessage request */
7630 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
7632 isds_log_message(context
,
7633 _("Could not build CreateMessage request"));
7636 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7638 isds_log_message(context
, _("Could not create ISDS name space"));
7639 xmlFreeNode(request
);
7642 xmlSetNs(request
, isds_ns
);
7644 /* Append envelope and files */
7645 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
7646 if (err
) goto leave
;
7649 /* Signal we can serialize message since now */
7650 /*message_is_complete = 1;*/
7653 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
7656 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
7658 /* Don't' destroy request, we want to provide it to application later */
7661 isds_log(ILF_ISDS
, ILL_DEBUG
,
7662 _("Processing ISDS response on CreateMessage "
7663 "request failed\n"));
7667 /* Check for response status */
7668 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
7669 &code
, &message
, NULL
);
7671 isds_log(ILF_ISDS
, ILL_DEBUG
,
7672 _("ISDS response on CreateMessage request "
7673 "is missing status\n"));
7677 /* Request processed, but refused by server or server failed */
7678 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7679 char *box_id_locale
=
7680 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
7681 char *code_locale
= _isds_utf82locale((char*)code
);
7682 char *message_locale
= _isds_utf82locale((char*)message
);
7683 isds_log(ILF_ISDS
, ILL_DEBUG
,
7684 _("Server did not accept message for %s on CreateMessage "
7685 "request (code=%s, message=%s)\n"),
7686 box_id_locale
, code_locale
, message_locale
);
7687 isds_log_message(context
, message_locale
);
7688 free(box_id_locale
);
7690 free(message_locale
);
7697 xpath_ctx
= xmlXPathNewContext(response
);
7702 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7706 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
7712 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7713 isds_log_message(context
, _("Missing CreateMessageResponse element"));
7717 if (result
->nodesetval
->nodeNr
> 1) {
7718 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
7722 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7723 xmlXPathFreeObject(result
); result
= NULL
;
7725 if (outgoing_message
->envelope
->dmID
) {
7726 free(outgoing_message
->envelope
->dmID
);
7727 outgoing_message
->envelope
->dmID
= NULL
;
7729 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
7730 if (!outgoing_message
->envelope
->dmID
) {
7731 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
7732 "but did not return assigned message ID\n"));
7736 /* TODO: Serialize message into structure member raw */
7737 /* XXX: Each web service transport message in different format.
7738 * Therefore it's not possible to save them directly.
7739 * To save them, one must figure out common format.
7740 * We can leave it on application, or we can implement the ESS format. */
7741 /*if (message_is_complete) {
7742 if (outgoing_message->envelope->dmID) {
7744 /* Add assigned message ID as first child*/
7745 /*xmlNodePtr dmid_text = xmlNewText(
7746 (xmlChar *) outgoing_message->envelope->dmID);
7747 if (!dmid_text) goto serialization_failed;
7749 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7751 if (!dmid_element) {
7752 xmlFreeNode(dmid_text);
7753 goto serialization_failed;
7756 xmlNodePtr dmid_element_with_text =
7757 xmlAddChild(dmid_element, dmid_text);
7758 if (!dmid_element_with_text) {
7759 xmlFreeNode(dmid_element);
7760 xmlFreeNode(dmid_text);
7761 goto serialization_failed;
7764 node = xmlAddPrevSibling(envelope->childern,
7765 dmid_element_with_text);
7767 xmlFreeNodeList(dmid_element_with_text);
7768 goto serialization_failed;
7772 /* Serialize message with ID into raw */
7773 /*buffer = serialize_element(envelope)*/
7776 serialization_failed:
7781 xmlXPathFreeObject(result
);
7782 xmlXPathFreeContext(xpath_ctx
);
7786 xmlFreeDoc(response
);
7787 xmlFreeNode(request
);
7790 isds_log(ILF_ISDS
, ILL_DEBUG
,
7791 _("CreateMessage request processed by server "
7792 "successfully.\n"));
7793 #else /* not HAVE_LIBCURL */
7801 /* Send a message via ISDS to a multiple recipients
7802 * @context is session context
7803 * @outgoing_message is message to send; Some members are mandatory,
7804 * some are optional and some are irrelevant (especially data
7805 * about sender). Data about recipient will be substituted by ISDS from
7806 * @copies. Included pointer to isds_list documents must
7807 * contain at least one document of FILEMETATYPE_MAIN.
7808 * @copies is list of isds_message_copy structures addressing all desired
7809 * recipients. This is read-write structure, some members will be filled with
7810 * valid data from ISDS (message IDs, error codes, error descriptions).
7812 * ISDS_SUCCESS if all messages have been sent
7813 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7814 * succeeded messages can be identified by copies->data->error),
7815 * or other error code if something other goes wrong. */
7816 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
7817 const struct isds_message
*outgoing_message
,
7818 struct isds_list
*copies
) {
7820 isds_error err
= IE_SUCCESS
;
7822 isds_error append_err
;
7823 xmlNsPtr isds_ns
= NULL
;
7824 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
7825 struct isds_list
*item
;
7826 struct isds_message_copy
*copy
;
7827 xmlDocPtr response
= NULL
;
7828 xmlChar
*code
= NULL
, *message
= NULL
;
7829 xmlXPathContextPtr xpath_ctx
= NULL
;
7830 xmlXPathObjectPtr result
= NULL
;
7831 xmlChar
*string
= NULL
;
7835 if (!context
) return IE_INVALID_CONTEXT
;
7836 zfree(context
->long_message
);
7837 if (!outgoing_message
|| !copies
) return IE_INVAL
;
7840 /* Check if connection is established
7841 * TODO: This check should be done downstairs. */
7842 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
7845 /* Build CreateMultipleMessage request */
7846 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
7848 isds_log_message(context
,
7849 _("Could not build CreateMultipleMessage request"));
7852 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
7854 isds_log_message(context
, _("Could not create ISDS name space"));
7855 xmlFreeNode(request
);
7858 xmlSetNs(request
, isds_ns
);
7861 /* Build recipients */
7862 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
7864 isds_log_message(context
, _("Could not add dmRecipients child to "
7865 "CreateMultipleMessage element"));
7866 xmlFreeNode(request
);
7870 /* Insert each recipient */
7871 for (item
= copies
; item
; item
= item
->next
) {
7872 copy
= (struct isds_message_copy
*) item
->data
;
7874 isds_log_message(context
,
7875 _("`copies' list item contains empty data"));
7880 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
7882 isds_log_message(context
, _("Could not add dmRecipient child to "
7883 "dmRecipients element"));
7888 if (!copy
->dbIDRecipient
) {
7889 isds_log_message(context
,
7890 _("Message copy is missing recipient box identifier"));
7894 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
7895 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
7896 copy
->dmRecipientOrgUnit
);
7897 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
7898 copy
->dmRecipientOrgUnitNum
, string
);
7899 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
7902 /* Append envelope and files */
7903 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
7904 if (err
) goto leave
;
7907 isds_log(ILF_ISDS
, ILL_DEBUG
,
7908 _("Sending CreateMultipleMessage request to ISDS\n"));
7911 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
7913 isds_log(ILF_ISDS
, ILL_DEBUG
,
7914 _("Processing ISDS response on CreateMultipleMessage "
7915 "request failed\n"));
7919 /* Check for response status */
7920 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
7921 &code
, &message
, NULL
);
7923 isds_log(ILF_ISDS
, ILL_DEBUG
,
7924 _("ISDS response on CreateMultipleMessage request "
7925 "is missing status\n"));
7929 /* Request processed, but some copies failed */
7930 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
7931 char *box_id_locale
=
7932 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
7933 char *code_locale
= _isds_utf82locale((char*)code
);
7934 char *message_locale
= _isds_utf82locale((char*)message
);
7935 isds_log(ILF_ISDS
, ILL_DEBUG
,
7936 _("Server did accept message for multiple recipients "
7937 "on CreateMultipleMessage request but delivery to "
7938 "some of them failed (code=%s, message=%s)\n"),
7939 box_id_locale
, code_locale
, message_locale
);
7940 isds_log_message(context
, message_locale
);
7941 free(box_id_locale
);
7943 free(message_locale
);
7944 err
= IE_PARTIAL_SUCCESS
;
7947 /* Request refused by server as whole */
7948 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
7949 char *box_id_locale
=
7950 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
7951 char *code_locale
= _isds_utf82locale((char*)code
);
7952 char *message_locale
= _isds_utf82locale((char*)message
);
7953 isds_log(ILF_ISDS
, ILL_DEBUG
,
7954 _("Server did not accept message for multiple recipients "
7955 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
7956 box_id_locale
, code_locale
, message_locale
);
7957 isds_log_message(context
, message_locale
);
7958 free(box_id_locale
);
7960 free(message_locale
);
7967 xpath_ctx
= xmlXPathNewContext(response
);
7972 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7976 result
= xmlXPathEvalExpression(
7977 BAD_CAST
"/isds:CreateMultipleMessageResponse"
7978 "/isds:dmMultipleStatus/isds:dmSingleStatus",
7984 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7985 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
7990 /* Extract message ID and delivery status for each copy */
7991 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
7992 item
= item
->next
, i
++) {
7993 copy
= (struct isds_message_copy
*) item
->data
;
7994 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
7996 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
8002 if (item
|| i
< result
->nodesetval
->nodeNr
) {
8003 isds_printf_message(context
, _("ISDS returned unexpected number of "
8004 "message copy delivery states: %d"),
8005 result
->nodesetval
->nodeNr
);
8014 xmlXPathFreeObject(result
);
8015 xmlXPathFreeContext(xpath_ctx
);
8019 xmlFreeDoc(response
);
8020 xmlFreeNode(request
);
8023 isds_log(ILF_ISDS
, ILL_DEBUG
,
8024 _("CreateMultipleMessageResponse request processed by server "
8025 "successfully.\n"));
8026 #else /* not HAVE_LIBCURL */
8034 /* Get list of messages. This is common core for getting sent or received
8036 * Any criterion argument can be NULL, if you don't care about it.
8037 * @context is session context. Must not be NULL.
8038 * @outgoing_direction is true if you want list of outgoing messages,
8039 * it's false if you want incoming messages.
8040 * @from_time is minimal time and date of message sending inclusive.
8041 * @to_time is maximal time and date of message sending inclusive
8042 * @organization_unit_number is number of sender/recipient respectively.
8043 * @status_filter is bit field of isds_message_status values. Use special
8044 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8045 * all values, you can use bit-wise arithmetic if you want.)
8046 * @offset is index of first message we are interested in. First message is 1.
8047 * Set to 0 (or 1) if you don't care.
8048 * @number is maximal length of list you want to get as input value, outputs
8049 * number of messages matching these criteria. Can be NULL if you don't care
8050 * (applies to output value either).
8051 * @messages is automatically reallocated list of isds_message's. Be ware that
8052 * it returns only brief overview (envelope and some other fields) about each
8053 * message, not the complete message. FIXME: Specify exact fields.
8054 * The list is sorted by delivery time in ascending order.
8055 * Use NULL if you don't care about don't need the data (useful if you want to
8056 * know only the @number). If you provide &NULL, list will be allocated on
8057 * heap, if you provide pointer to non-NULL, list will be freed automatically
8058 * at first. Also in case of error the list will be NULLed.
8059 * @return IE_SUCCESS or appropriate error code. */
8060 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
8061 _Bool outgoing_direction
,
8062 const struct timeval
*from_time
, const struct timeval
*to_time
,
8063 const long int *organization_unit_number
,
8064 const unsigned int status_filter
,
8065 const unsigned long int offset
, unsigned long int *number
,
8066 struct isds_list
**messages
) {
8068 isds_error err
= IE_SUCCESS
;
8070 xmlNsPtr isds_ns
= NULL
;
8071 xmlNodePtr request
= NULL
, node
;
8072 xmlDocPtr response
= NULL
;
8073 xmlChar
*code
= NULL
, *message
= NULL
;
8074 xmlXPathContextPtr xpath_ctx
= NULL
;
8075 xmlXPathObjectPtr result
= NULL
;
8076 xmlChar
*string
= NULL
;
8077 long unsigned int count
= 0;
8080 if (!context
) return IE_INVALID_CONTEXT
;
8081 zfree(context
->long_message
);
8083 /* Free former message list if any */
8084 if (messages
) isds_list_free(messages
);
8087 /* Check if connection is established
8088 * TODO: This check should be done downstairs. */
8089 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8091 /* Build GetListOf*Messages request */
8092 request
= xmlNewNode(NULL
,
8093 (outgoing_direction
) ?
8094 BAD_CAST
"GetListOfSentMessages" :
8095 BAD_CAST
"GetListOfReceivedMessages"
8098 isds_log_message(context
,
8099 (outgoing_direction
) ?
8100 _("Could not build GetListOfSentMessages request") :
8101 _("Could not build GetListOfReceivedMessages request")
8105 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8107 isds_log_message(context
, _("Could not create ISDS name space"));
8108 xmlFreeNode(request
);
8111 xmlSetNs(request
, isds_ns
);
8115 err
= timeval2timestring(from_time
, &string
);
8116 if (err
) goto leave
;
8118 INSERT_STRING(request
, "dmFromTime", string
);
8119 free(string
); string
= NULL
;
8122 err
= timeval2timestring(to_time
, &string
);
8123 if (err
) goto leave
;
8125 INSERT_STRING(request
, "dmToTime", string
);
8126 free(string
); string
= NULL
;
8128 if (outgoing_direction
) {
8129 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
8130 organization_unit_number
, string
);
8132 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
8133 organization_unit_number
, string
);
8136 if (status_filter
> MESSAGESTATE_ANY
) {
8137 isds_printf_message(context
,
8138 _("Invalid message state filter value: %ld"), status_filter
);
8142 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
8145 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
8147 INSERT_STRING(request
, "dmOffset", "1");
8150 /* number 0 means no limit */
8151 if (number
&& *number
== 0) {
8152 INSERT_STRING(request
, "dmLimit", NULL
);
8154 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
8158 isds_log(ILF_ISDS
, ILL_DEBUG
,
8159 (outgoing_direction
) ?
8160 _("Sending GetListOfSentMessages request to ISDS\n") :
8161 _("Sending GetListOfReceivedMessages request to ISDS\n")
8165 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
8166 xmlFreeNode(request
); request
= NULL
;
8169 isds_log(ILF_ISDS
, ILL_DEBUG
,
8170 (outgoing_direction
) ?
8171 _("Processing ISDS response on GetListOfSentMessages "
8172 "request failed\n") :
8173 _("Processing ISDS response on GetListOfReceivedMessages "
8179 /* Check for response status */
8180 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
8181 &code
, &message
, NULL
);
8183 isds_log(ILF_ISDS
, ILL_DEBUG
,
8184 (outgoing_direction
) ?
8185 _("ISDS response on GetListOfSentMessages request "
8186 "is missing status\n") :
8187 _("ISDS response on GetListOfReceivedMessages request "
8188 "is missing status\n")
8193 /* Request processed, but nothing found */
8194 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8195 char *code_locale
= _isds_utf82locale((char*)code
);
8196 char *message_locale
= _isds_utf82locale((char*)message
);
8197 isds_log(ILF_ISDS
, ILL_DEBUG
,
8198 (outgoing_direction
) ?
8199 _("Server refused GetListOfSentMessages request "
8200 "(code=%s, message=%s)\n") :
8201 _("Server refused GetListOfReceivedMessages request "
8202 "(code=%s, message=%s)\n"),
8203 code_locale
, message_locale
);
8204 isds_log_message(context
, message_locale
);
8206 free(message_locale
);
8213 xpath_ctx
= xmlXPathNewContext(response
);
8218 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8222 result
= xmlXPathEvalExpression(
8223 (outgoing_direction
) ?
8224 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
8225 "isds:dmRecords/isds:dmRecord" :
8226 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
8227 "isds:dmRecords/isds:dmRecord",
8234 /* Fill output arguments in */
8235 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8236 struct isds_envelope
*envelope
;
8237 struct isds_list
*item
= NULL
, *last_item
= NULL
;
8239 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
8240 /* Create new message */
8241 item
= calloc(1, sizeof(*item
));
8246 item
->destructor
= (void(*)(void**)) &isds_message_free
;
8247 item
->data
= calloc(1, sizeof(struct isds_message
));
8249 isds_list_free(&item
);
8254 /* Extract envelope data */
8255 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
8257 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
8259 isds_list_free(&item
);
8263 /* Attach extracted envelope */
8264 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
8266 /* Append new message into the list */
8268 *messages
= last_item
= item
;
8270 last_item
->next
= item
;
8275 if (number
) *number
= count
;
8279 isds_list_free(messages
);
8283 xmlXPathFreeObject(result
);
8284 xmlXPathFreeContext(xpath_ctx
);
8288 xmlFreeDoc(response
);
8289 xmlFreeNode(request
);
8292 isds_log(ILF_ISDS
, ILL_DEBUG
,
8293 (outgoing_direction
) ?
8294 _("GetListOfSentMessages request processed by server "
8295 "successfully.\n") :
8296 _("GetListOfReceivedMessages request processed by server "
8299 #else /* not HAVE_LIBCURL */
8306 /* Get list of outgoing (already sent) messages.
8307 * Any criterion argument can be NULL, if you don't care about it.
8308 * @context is session context. Must not be NULL.
8309 * @from_time is minimal time and date of message sending inclusive.
8310 * @to_time is maximal time and date of message sending inclusive
8311 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8312 * @status_filter is bit field of isds_message_status values. Use special
8313 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8314 * all values, you can use bit-wise arithmetic if you want.)
8315 * @offset is index of first message we are interested in. First message is 1.
8316 * Set to 0 (or 1) if you don't care.
8317 * @number is maximal length of list you want to get as input value, outputs
8318 * number of messages matching these criteria. Can be NULL if you don't care
8319 * (applies to output value either).
8320 * @messages is automatically reallocated list of isds_message's. Be ware that
8321 * it returns only brief overview (envelope and some other fields) about each
8322 * message, not the complete message. FIXME: Specify exact fields.
8323 * The list is sorted by delivery time in ascending order.
8324 * Use NULL if you don't care about the meta data (useful if you want to know
8325 * only the @number). If you provide &NULL, list will be allocated on heap,
8326 * if you provide pointer to non-NULL, list will be freed automatically at
8327 * first. Also in case of error the list will be NULLed.
8328 * @return IE_SUCCESS or appropriate error code. */
8329 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
8330 const struct timeval
*from_time
, const struct timeval
*to_time
,
8331 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
8332 const unsigned long int offset
, unsigned long int *number
,
8333 struct isds_list
**messages
) {
8335 return isds_get_list_of_messages(
8337 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
8343 /* Get list of incoming (addressed to you) messages.
8344 * Any criterion argument can be NULL, if you don't care about it.
8345 * @context is session context. Must not be NULL.
8346 * @from_time is minimal time and date of message sending inclusive.
8347 * @to_time is maximal time and date of message sending inclusive
8348 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8349 * @status_filter is bit field of isds_message_status values. Use special
8350 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8351 * all values, you can use bit-wise arithmetic if you want.)
8352 * @offset is index of first message we are interested in. First message is 1.
8353 * Set to 0 (or 1) if you don't care.
8354 * @number is maximal length of list you want to get as input value, outputs
8355 * number of messages matching these criteria. Can be NULL if you don't care
8356 * (applies to output value either).
8357 * @messages is automatically reallocated list of isds_message's. Be ware that
8358 * it returns only brief overview (envelope and some other fields) about each
8359 * message, not the complete message. FIXME: Specify exact fields.
8360 * Use NULL if you don't care about the meta data (useful if you want to know
8361 * only the @number). If you provide &NULL, list will be allocated on heap,
8362 * if you provide pointer to non-NULL, list will be freed automatically at
8363 * first. Also in case of error the list will be NULLed.
8364 * @return IE_SUCCESS or appropriate error code. */
8365 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
8366 const struct timeval
*from_time
, const struct timeval
*to_time
,
8367 const long int *dmRecipientOrgUnitNum
,
8368 const unsigned int status_filter
,
8369 const unsigned long int offset
, unsigned long int *number
,
8370 struct isds_list
**messages
) {
8372 return isds_get_list_of_messages(
8374 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
8380 /* Get list of sent message state changes.
8381 * Any criterion argument can be NULL, if you don't care about it.
8382 * @context is session context. Must not be NULL.
8383 * @from_time is minimal time and date of status changes inclusive
8384 * @to_time is maximal time and date of status changes inclusive
8385 * @changed_states is automatically reallocated list of
8386 * isds_message_status_change's. If you provide &NULL, list will be allocated
8387 * on heap, if you provide pointer to non-NULL, list will be freed
8388 * automatically at first. Also in case of error the list will be NULLed.
8389 * XXX: The list item ordering is not specified.
8390 * XXX: Server provides only `recent' changes.
8391 * @return IE_SUCCESS or appropriate error code. */
8392 isds_error
isds_get_list_of_sent_message_state_changes(
8393 struct isds_ctx
*context
,
8394 const struct timeval
*from_time
, const struct timeval
*to_time
,
8395 struct isds_list
**changed_states
) {
8397 isds_error err
= IE_SUCCESS
;
8399 xmlNsPtr isds_ns
= NULL
;
8400 xmlNodePtr request
= NULL
, node
;
8401 xmlDocPtr response
= NULL
;
8402 xmlXPathContextPtr xpath_ctx
= NULL
;
8403 xmlXPathObjectPtr result
= NULL
;
8404 xmlChar
*string
= NULL
;
8405 long unsigned int count
= 0;
8408 if (!context
) return IE_INVALID_CONTEXT
;
8409 zfree(context
->long_message
);
8411 /* Free former message list if any */
8412 isds_list_free(changed_states
);
8415 /* Check if connection is established
8416 * TODO: This check should be done downstairs. */
8417 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8419 /* Build GetMessageStateChanges request */
8420 request
= xmlNewNode(NULL
, BAD_CAST
"GetMessageStateChanges");
8422 isds_log_message(context
,
8423 _("Could not build GetMessageStateChanges request"));
8426 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8428 isds_log_message(context
, _("Could not create ISDS name space"));
8429 xmlFreeNode(request
);
8432 xmlSetNs(request
, isds_ns
);
8436 err
= timeval2timestring(from_time
, &string
);
8437 if (err
) goto leave
;
8439 INSERT_STRING(request
, "dmFromTime", string
);
8443 err
= timeval2timestring(to_time
, &string
);
8444 if (err
) goto leave
;
8446 INSERT_STRING(request
, "dmToTime", string
);
8451 err
= send_destroy_request_check_response(context
,
8452 SERVICE_DM_INFO
, BAD_CAST
"GetMessageStateChanges", &request
,
8454 if (err
) goto leave
;
8458 xpath_ctx
= xmlXPathNewContext(response
);
8463 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8467 result
= xmlXPathEvalExpression(
8468 BAD_CAST
"/isds:GetMessageStateChangesResponse/"
8469 "isds:dmRecords/isds:dmRecord", xpath_ctx
);
8475 /* Fill output arguments in */
8476 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8477 struct isds_list
*item
= NULL
, *last_item
= NULL
;
8479 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
8480 /* Create new status change */
8481 item
= calloc(1, sizeof(*item
));
8487 (void(*)(void**)) &isds_message_status_change_free
;
8489 /* Extract message status change */
8490 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
8491 err
= extract_StateChangesRecord(context
,
8492 (struct isds_message_status_change
**) &item
->data
,
8495 isds_list_free(&item
);
8499 /* Append new message status change into the list */
8500 if (!*changed_states
) {
8501 *changed_states
= last_item
= item
;
8503 last_item
->next
= item
;
8511 isds_list_free(changed_states
);
8515 xmlXPathFreeObject(result
);
8516 xmlXPathFreeContext(xpath_ctx
);
8517 xmlFreeDoc(response
);
8518 xmlFreeNode(request
);
8521 isds_log(ILF_ISDS
, ILL_DEBUG
,
8522 _("GetMessageStateChanges request processed by server "
8523 "successfully.\n"));
8524 #else /* not HAVE_LIBCURL */
8532 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
8534 * @context is session context
8535 * @service is ISDS WS service handler
8536 * @service_name is name of SERVICE_DM_OPERATIONS
8537 * @message_id is message ID to send as service argument to ISDS
8538 * @response is server SOAP body response as XML document
8539 * @raw_response is automatically reallocated bit stream with response body. Use
8540 * NULL if you don't care
8541 * @raw_response_length is size of @raw_response in bytes
8542 * @code is ISDS status code
8543 * @status_message is ISDS status message
8544 * @return error coded from lower layer, context message will be set up
8546 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
8547 const isds_service service
, const xmlChar
*service_name
,
8548 const char *message_id
,
8549 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
8550 xmlChar
**code
, xmlChar
**status_message
) {
8552 isds_error err
= IE_SUCCESS
;
8553 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
8554 xmlNodePtr request
= NULL
, node
;
8555 xmlNsPtr isds_ns
= NULL
;
8557 if (!context
) return IE_INVALID_CONTEXT
;
8558 if (!service_name
|| !message_id
) return IE_INVAL
;
8559 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
8560 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
8562 /* Free output argument */
8563 xmlFreeDoc(*response
); *response
= NULL
;
8564 if (raw_response
) zfree(*raw_response
);
8566 free(*status_message
);
8569 /* Check if connection is established
8570 * TODO: This check should be done downstairs. */
8571 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8573 service_name_locale
= _isds_utf82locale((char*)service_name
);
8574 message_id_locale
= _isds_utf82locale(message_id
);
8575 if (!service_name_locale
|| !message_id_locale
) {
8581 request
= xmlNewNode(NULL
, service_name
);
8583 isds_printf_message(context
,
8584 _("Could not build %s request for %s message ID"),
8585 service_name_locale
, message_id_locale
);
8589 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8591 isds_log_message(context
, _("Could not create ISDS name space"));
8595 xmlSetNs(request
, isds_ns
);
8598 /* Add requested ID */
8599 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
8600 if (err
) goto leave
;
8601 INSERT_STRING(request
, "dmID", message_id
);
8604 isds_log(ILF_ISDS
, ILL_DEBUG
,
8605 _("Sending %s request for %s message ID to ISDS\n"),
8606 service_name_locale
, message_id_locale
);
8609 err
= isds(context
, service
, request
, response
,
8610 raw_response
, raw_response_length
);
8611 xmlFreeNode(request
); request
= NULL
;
8614 isds_log(ILF_ISDS
, ILL_DEBUG
,
8615 _("Processing ISDS response on %s request failed\n"),
8616 service_name_locale
);
8620 /* Check for response status */
8621 err
= isds_response_status(context
, service
, *response
,
8622 code
, status_message
, NULL
);
8624 isds_log(ILF_ISDS
, ILL_DEBUG
,
8625 _("ISDS response on %s request is missing status\n"),
8626 service_name_locale
);
8630 /* Request processed, but nothing found */
8631 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
8632 char *code_locale
= _isds_utf82locale((char*) *code
);
8633 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
8634 isds_log(ILF_ISDS
, ILL_DEBUG
,
8635 _("Server refused %s request for %s message ID "
8636 "(code=%s, message=%s)\n"),
8637 service_name_locale
, message_id_locale
,
8638 code_locale
, status_message_locale
);
8639 isds_log_message(context
, status_message_locale
);
8641 free(status_message_locale
);
8647 free(message_id_locale
);
8648 free(service_name_locale
);
8649 xmlFreeNode(request
);
8654 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8655 * signed data and free ISDS response.
8656 * @context is session context
8657 * @message_id is UTF-8 encoded message ID for logging purpose
8658 * @response is parsed XML document. It will be freed and NULLed in the middle
8659 * of function run to save memory. This is not guaranteed in case of error.
8660 * @request_name is name of ISDS request used to construct response root
8661 * element name and for logging purpose.
8662 * @raw is reallocated output buffer with DER encoded CMS data
8663 * @raw_length is size of @raw buffer in bytes
8664 * @returns standard error codes, in case of error, @raw will be freed and
8665 * NULLed, @response sometimes. */
8666 static isds_error
find_extract_signed_data_free_response(
8667 struct isds_ctx
*context
, const xmlChar
*message_id
,
8668 xmlDocPtr
*response
, const xmlChar
*request_name
,
8669 void **raw
, size_t *raw_length
) {
8671 isds_error err
= IE_SUCCESS
;
8672 char *xpath_expression
= NULL
;
8673 xmlXPathContextPtr xpath_ctx
= NULL
;
8674 xmlXPathObjectPtr result
= NULL
;
8675 char *encoded_structure
= NULL
;
8677 if (!context
) return IE_INVALID_CONTEXT
;
8678 if (!raw
) return IE_INVAL
;
8680 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
8683 /* Build XPath expression */
8684 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
8685 "Response/isds:dmSignature");
8686 if (!xpath_expression
) return IE_NOMEM
;
8689 xpath_ctx
= xmlXPathNewContext(*response
);
8694 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8698 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
8703 /* Empty response */
8704 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8705 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8706 isds_printf_message(context
,
8707 _("Server did not return any signed data for message ID `%s' "
8709 message_id_locale
, request_name
);
8710 free(message_id_locale
);
8714 /* More responses */
8715 if (result
->nodesetval
->nodeNr
> 1) {
8716 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8717 isds_printf_message(context
,
8718 _("Server did return more signed data for message ID `%s' "
8720 message_id_locale
, request_name
);
8721 free(message_id_locale
);
8726 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8728 /* Extract PKCS#7 structure */
8729 EXTRACT_STRING(".", encoded_structure
);
8730 if (!encoded_structure
) {
8731 isds_log_message(context
, _("dmSignature element is empty"));
8734 /* Here we have delivery info as standalone CMS in encoded_structure.
8735 * We don't need any other data, free them: */
8736 xmlXPathFreeObject(result
); result
= NULL
;
8737 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
8738 xmlFreeDoc(*response
); *response
= NULL
;
8741 /* Decode PKCS#7 to DER format */
8742 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
8743 if (*raw_length
== (size_t) -1) {
8744 isds_log_message(context
,
8745 _("Error while Base64-decoding PKCS#7 structure"));
8756 free(encoded_structure
);
8757 xmlXPathFreeObject(result
);
8758 xmlXPathFreeContext(xpath_ctx
);
8759 free(xpath_expression
);
8763 #endif /* HAVE_LIBCURL */
8766 /* Download incoming message envelope identified by ID.
8767 * @context is session context
8768 * @message_id is message identifier (you can get them from
8769 * isds_get_list_of_received_messages())
8770 * @message is automatically reallocated message retrieved from ISDS.
8771 * It will miss documents per se. Use isds_get_received_message(), if you are
8772 * interested in documents (content) too.
8773 * Returned hash and timestamp require documents to be verifiable. */
8774 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
8775 const char *message_id
, struct isds_message
**message
) {
8777 isds_error err
= IE_SUCCESS
;
8779 xmlDocPtr response
= NULL
;
8780 xmlChar
*code
= NULL
, *status_message
= NULL
;
8781 xmlXPathContextPtr xpath_ctx
= NULL
;
8782 xmlXPathObjectPtr result
= NULL
;
8785 if (!context
) return IE_INVALID_CONTEXT
;
8786 zfree(context
->long_message
);
8788 /* Free former message if any */
8789 if (!message
) return IE_INVAL
;
8790 isds_message_free(message
);
8793 /* Do request and check for success */
8794 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
8795 BAD_CAST
"MessageEnvelopeDownload", message_id
,
8796 &response
, NULL
, NULL
, &code
, &status_message
);
8797 if (err
) goto leave
;
8800 xpath_ctx
= xmlXPathNewContext(response
);
8805 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8809 result
= xmlXPathEvalExpression(
8810 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
8811 "isds:dmReturnedMessageEnvelope",
8817 /* Empty response */
8818 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8819 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8820 isds_printf_message(context
,
8821 _("Server did not return any envelope for ID `%s' "
8822 "on MessageEnvelopeDownload request"), message_id_locale
);
8823 free(message_id_locale
);
8828 if (result
->nodesetval
->nodeNr
> 1) {
8829 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8830 isds_printf_message(context
,
8831 _("Server did return more envelopes for ID `%s' "
8832 "on MessageEnvelopeDownload request"), message_id_locale
);
8833 free(message_id_locale
);
8838 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8840 /* Extract the envelope (= message without documents, hence 0) */
8841 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
8842 if (err
) goto leave
;
8845 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
8846 &(*message
)->raw_length
);
8850 isds_message_free(message
);
8853 xmlXPathFreeObject(result
);
8854 xmlXPathFreeContext(xpath_ctx
);
8857 free(status_message
);
8858 if (!*message
|| !(*message
)->xml
) {
8859 xmlFreeDoc(response
);
8863 isds_log(ILF_ISDS
, ILL_DEBUG
,
8864 _("MessageEnvelopeDownload request processed by server "
8867 #else /* not HAVE_LIBCURL */
8874 /* Load delivery info of any format from buffer.
8875 * @context is session context
8876 * @raw_type advertises format of @buffer content. Only delivery info types
8878 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
8879 * retrieve such data from message->raw after calling
8880 * isds_get_signed_delivery_info().
8881 * @length is length of buffer in bytes.
8882 * @message is automatically reallocated message parsed from @buffer.
8883 * @strategy selects how buffer will be attached into raw isds_message member.
8885 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
8886 const isds_raw_type raw_type
,
8887 const void *buffer
, const size_t length
,
8888 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
8890 isds_error err
= IE_SUCCESS
;
8891 message_ns_type message_ns
;
8892 xmlDocPtr message_doc
= NULL
;
8893 xmlXPathContextPtr xpath_ctx
= NULL
;
8894 xmlXPathObjectPtr result
= NULL
;
8895 void *xml_stream
= NULL
;
8896 size_t xml_stream_length
= 0;
8898 if (!context
) return IE_INVALID_CONTEXT
;
8899 zfree(context
->long_message
);
8900 if (!message
) return IE_INVAL
;
8901 isds_message_free(message
);
8902 if (!buffer
) return IE_INVAL
;
8905 /* Select buffer format and extract XML from CMS*/
8907 case RAWTYPE_DELIVERYINFO
:
8908 message_ns
= MESSAGE_NS_UNSIGNED
;
8909 xml_stream
= (void *) buffer
;
8910 xml_stream_length
= length
;
8913 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
8914 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
8915 xml_stream
= (void *) buffer
;
8916 xml_stream_length
= length
;
8919 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
8920 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
8921 err
= _isds_extract_cms_data(context
, buffer
, length
,
8922 &xml_stream
, &xml_stream_length
);
8923 if (err
) goto leave
;
8927 isds_log_message(context
, _("Bad raw delivery representation type"));
8932 isds_log(ILF_ISDS
, ILL_DEBUG
,
8933 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
8934 xml_stream_length
, xml_stream
);
8936 /* Convert delivery info XML stream into XPath context */
8937 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
8942 xpath_ctx
= xmlXPathNewContext(message_doc
);
8947 /* XXX: Name spaces mangled for signed delivery info:
8948 * http://isds.czechpoint.cz/v20/delivery:
8950 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
8952 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8953 * <p:dmID>170272</p:dmID>
8956 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8958 * </q:dmEvents>...</q:dmEvents>
8960 * </q:GetDeliveryInfoResponse>
8962 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
8966 result
= xmlXPathEvalExpression(
8967 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
8973 /* Empty delivery info */
8974 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8975 isds_printf_message(context
,
8976 _("XML document is not sisds:dmDelivery document"));
8980 /* More delivery info's */
8981 if (result
->nodesetval
->nodeNr
> 1) {
8982 isds_printf_message(context
,
8983 _("XML document has more sisds:dmDelivery elements"));
8987 /* One delivery info */
8988 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8990 /* Extract the envelope (= message without documents, hence 0).
8991 * XXX: extract_TReturnedMessage() can obtain attachments size,
8992 * but delivery info carries none. It's coded as option elements,
8993 * so it should work. */
8994 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
8995 if (err
) goto leave
;
8997 /* Extract events */
8998 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
8999 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
9000 if (err
) { err
= IE_ERROR
; goto leave
; }
9001 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
9002 if (err
) goto leave
;
9004 /* Append raw CMS structure into message */
9005 (*message
)->raw_type
= raw_type
;
9007 case BUFFER_DONT_STORE
:
9010 (*message
)->raw
= malloc(length
);
9011 if (!(*message
)->raw
) {
9015 memcpy((*message
)->raw
, buffer
, length
);
9016 (*message
)->raw_length
= length
;
9019 (*message
)->raw
= (void *) buffer
;
9020 (*message
)->raw_length
= length
;
9029 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
9030 isds_message_free(message
);
9033 xmlXPathFreeObject(result
);
9034 xmlXPathFreeContext(xpath_ctx
);
9035 if (!*message
|| !(*message
)->xml
) {
9036 xmlFreeDoc(message_doc
);
9038 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9041 isds_log(ILF_ISDS
, ILL_DEBUG
,
9042 _("Delivery info loaded successfully.\n"));
9047 /* Download signed delivery info-sheet of given message identified by ID.
9048 * @context is session context
9049 * @message_id is message identifier (you can get them from
9050 * isds_get_list_of_{sent,received}_messages())
9051 * @message is automatically reallocated message retrieved from ISDS.
9052 * It will miss documents per se. Use isds_get_signed_received_message(),
9053 * if you are interested in documents (content). OTOH, only this function
9054 * can get list events message has gone through. */
9055 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
9056 const char *message_id
, struct isds_message
**message
) {
9058 isds_error err
= IE_SUCCESS
;
9060 xmlDocPtr response
= NULL
;
9061 xmlChar
*code
= NULL
, *status_message
= NULL
;
9063 size_t raw_length
= 0;
9066 if (!context
) return IE_INVALID_CONTEXT
;
9067 zfree(context
->long_message
);
9069 /* Free former message if any */
9070 if (!message
) return IE_INVAL
;
9071 isds_message_free(message
);
9074 /* Do request and check for success */
9075 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9076 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
9077 &response
, NULL
, NULL
, &code
, &status_message
);
9078 if (err
) goto leave
;
9080 /* Find signed delivery info, extract it into raw and maybe free
9082 err
= find_extract_signed_data_free_response(context
,
9083 (xmlChar
*)message_id
, &response
,
9084 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
9085 if (err
) goto leave
;
9087 /* Parse delivery info */
9088 err
= isds_load_delivery_info(context
,
9089 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
9090 message
, BUFFER_MOVE
);
9091 if (err
) goto leave
;
9097 isds_message_free(message
);
9102 free(status_message
);
9103 xmlFreeDoc(response
);
9106 isds_log(ILF_ISDS
, ILL_DEBUG
,
9107 _("GetSignedDeliveryInfo request processed by server "
9110 #else /* not HAVE_LIBCURL */
9117 /* Download delivery info-sheet of given message identified by ID.
9118 * @context is session context
9119 * @message_id is message identifier (you can get them from
9120 * isds_get_list_of_{sent,received}_messages())
9121 * @message is automatically reallocated message retrieved from ISDS.
9122 * It will miss documents per se. Use isds_get_received_message(), if you are
9123 * interested in documents (content). OTOH, only this function can get list
9124 * of events message has gone through. */
9125 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
9126 const char *message_id
, struct isds_message
**message
) {
9128 isds_error err
= IE_SUCCESS
;
9130 xmlDocPtr response
= NULL
;
9131 xmlChar
*code
= NULL
, *status_message
= NULL
;
9132 xmlNodePtr delivery_node
= NULL
;
9134 size_t raw_length
= 0;
9137 if (!context
) return IE_INVALID_CONTEXT
;
9138 zfree(context
->long_message
);
9140 /* Free former message if any */
9141 if (!message
) return IE_INVAL
;
9142 isds_message_free(message
);
9145 /* Do request and check for success */
9146 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9147 BAD_CAST
"GetDeliveryInfo", message_id
,
9148 &response
, NULL
, NULL
, &code
, &status_message
);
9149 if (err
) goto leave
;
9152 /* Serialize delivery info */
9153 delivery_node
= xmlDocGetRootElement(response
);
9154 if (!delivery_node
) {
9155 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9156 isds_printf_message(context
,
9157 _("Server did not return any delivery info for ID `%s' "
9158 "on GetDeliveryInfo request"), message_id_locale
);
9159 free(message_id_locale
);
9163 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
9164 if (err
) goto leave
;
9166 /* Parse delivery info */
9167 /* TODO: Here we parse the response second time. We could single delivery
9168 * parser from isds_load_delivery_info() to make things faster. */
9169 err
= isds_load_delivery_info(context
,
9170 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
9171 message
, BUFFER_MOVE
);
9172 if (err
) goto leave
;
9179 isds_message_free(message
);
9184 free(status_message
);
9185 xmlFreeDoc(response
);
9188 isds_log(ILF_ISDS
, ILL_DEBUG
,
9189 _("GetDeliveryInfo request processed by server "
9192 #else /* not HAVE_LIBCURL */
9199 /* Download incoming message identified by ID.
9200 * @context is session context
9201 * @message_id is message identifier (you can get them from
9202 * isds_get_list_of_received_messages())
9203 * @message is automatically reallocated message retrieved from ISDS */
9204 isds_error
isds_get_received_message(struct isds_ctx
*context
,
9205 const char *message_id
, struct isds_message
**message
) {
9207 isds_error err
= IE_SUCCESS
;
9209 xmlDocPtr response
= NULL
;
9210 void *xml_stream
= NULL
;
9211 size_t xml_stream_length
;
9212 xmlChar
*code
= NULL
, *status_message
= NULL
;
9213 xmlXPathContextPtr xpath_ctx
= NULL
;
9214 xmlXPathObjectPtr result
= NULL
;
9215 char *phys_path
= NULL
;
9216 size_t phys_start
, phys_end
;
9219 if (!context
) return IE_INVALID_CONTEXT
;
9220 zfree(context
->long_message
);
9222 /* Free former message if any */
9223 if (message
) isds_message_free(message
);
9226 /* Do request and check for success */
9227 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
9228 BAD_CAST
"MessageDownload", message_id
,
9229 &response
, &xml_stream
, &xml_stream_length
,
9230 &code
, &status_message
);
9231 if (err
) goto leave
;
9234 xpath_ctx
= xmlXPathNewContext(response
);
9239 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9243 result
= xmlXPathEvalExpression(
9244 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9250 /* Empty response */
9251 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9252 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9253 isds_printf_message(context
,
9254 _("Server did not return any message for ID `%s' "
9255 "on MessageDownload request"), message_id_locale
);
9256 free(message_id_locale
);
9261 if (result
->nodesetval
->nodeNr
> 1) {
9262 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9263 isds_printf_message(context
,
9264 _("Server did return more messages for ID `%s' "
9265 "on MessageDownload request"), message_id_locale
);
9266 free(message_id_locale
);
9271 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9273 /* Extract the message */
9274 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
9275 if (err
) goto leave
;
9277 /* Locate raw XML blob */
9279 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
9280 PHYSXML_ELEMENT_SEPARATOR
9281 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
9282 PHYSXML_ELEMENT_SEPARATOR
9283 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
9289 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
9290 phys_path
, &phys_start
, &phys_end
);
9293 isds_log_message(context
,
9294 _("Substring with isds:MessageDownloadResponse element "
9295 "could not be located in raw SOAP message"));
9299 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9300 &(*message)->raw_length);*/
9301 /* TODO: Store name space declarations from ancestors */
9302 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9303 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
9304 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
9305 (*message
)->raw
= malloc((*message
)->raw_length
);
9306 if (!(*message
)->raw
) {
9310 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
9315 isds_message_free(message
);
9320 xmlXPathFreeObject(result
);
9321 xmlXPathFreeContext(xpath_ctx
);
9324 free(status_message
);
9326 if (!*message
|| !(*message
)->xml
) {
9327 xmlFreeDoc(response
);
9331 isds_log(ILF_ISDS
, ILL_DEBUG
,
9332 _("MessageDownload request processed by server "
9335 #else /* not HAVE_LIBCURL */
9342 /* Load message of any type from buffer.
9343 * @context is session context
9344 * @raw_type defines content type of @buffer. Only message types are allowed.
9345 * @buffer is message raw representation. Format (CMS, plain signed,
9346 * message direction) is defined in @raw_type. You can retrieve such data
9347 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9348 * @length is length of buffer in bytes.
9349 * @message is automatically reallocated message parsed from @buffer.
9350 * @strategy selects how buffer will be attached into raw isds_message member.
9352 isds_error
isds_load_message(struct isds_ctx
*context
,
9353 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
9354 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
9356 isds_error err
= IE_SUCCESS
;
9357 void *xml_stream
= NULL
;
9358 size_t xml_stream_length
= 0;
9359 message_ns_type message_ns
;
9360 xmlDocPtr message_doc
= NULL
;
9361 xmlXPathContextPtr xpath_ctx
= NULL
;
9362 xmlXPathObjectPtr result
= NULL
;
9364 if (!context
) return IE_INVALID_CONTEXT
;
9365 zfree(context
->long_message
);
9366 if (!message
) return IE_INVAL
;
9367 isds_message_free(message
);
9368 if (!buffer
) return IE_INVAL
;
9371 /* Select buffer format and extract XML from CMS*/
9373 case RAWTYPE_INCOMING_MESSAGE
:
9374 message_ns
= MESSAGE_NS_UNSIGNED
;
9375 xml_stream
= (void *) buffer
;
9376 xml_stream_length
= length
;
9379 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
9380 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9381 xml_stream
= (void *) buffer
;
9382 xml_stream_length
= length
;
9385 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
9386 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
9387 err
= _isds_extract_cms_data(context
, buffer
, length
,
9388 &xml_stream
, &xml_stream_length
);
9389 if (err
) goto leave
;
9392 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
9393 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9394 xml_stream
= (void *) buffer
;
9395 xml_stream_length
= length
;
9398 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
9399 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
9400 err
= _isds_extract_cms_data(context
, buffer
, length
,
9401 &xml_stream
, &xml_stream_length
);
9402 if (err
) goto leave
;
9406 isds_log_message(context
, _("Bad raw message representation type"));
9411 isds_log(ILF_ISDS
, ILL_DEBUG
,
9412 _("Loading message:\n%.*s\nEnd of message\n"),
9413 xml_stream_length
, xml_stream
);
9415 /* Convert messages XML stream into XPath context */
9416 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
9421 xpath_ctx
= xmlXPathNewContext(message_doc
);
9426 /* XXX: Standard name space for unsigned incoming direction:
9427 * http://isds.czechpoint.cz/v20/
9429 * XXX: Name spaces mangled for signed outgoing direction:
9430 * http://isds.czechpoint.cz/v20/SentMessage:
9432 * <q:MessageDownloadResponse
9433 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9434 * <q:dmReturnedMessage>
9435 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9436 * <p:dmID>151916</p:dmID>
9439 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9441 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9442 * </q:dmReturnedMessage>
9443 * </q:MessageDownloadResponse>
9445 * XXX: Name spaces mangled for signed incoming direction:
9446 * http://isds.czechpoint.cz/v20/message:
9448 * <q:MessageDownloadResponse
9449 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9450 * <q:dmReturnedMessage>
9451 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9452 * <p:dmID>151916</p:dmID>
9455 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9457 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9458 * </q:dmReturnedMessage>
9459 * </q:MessageDownloadResponse>
9461 * Stupidity of ISDS developers is unlimited */
9462 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
9466 result
= xmlXPathEvalExpression(
9467 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
9474 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9475 isds_printf_message(context
,
9476 _("XML document does not contain "
9477 "sisds:dmReturnedMessage element"));
9482 if (result
->nodesetval
->nodeNr
> 1) {
9483 isds_printf_message(context
,
9484 _("XML document has more sisds:dmReturnedMessage elements"));
9489 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9491 /* Extract the message */
9492 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
9493 if (err
) goto leave
;
9495 /* Append raw buffer into message */
9496 (*message
)->raw_type
= raw_type
;
9498 case BUFFER_DONT_STORE
:
9501 (*message
)->raw
= malloc(length
);
9502 if (!(*message
)->raw
) {
9506 memcpy((*message
)->raw
, buffer
, length
);
9507 (*message
)->raw_length
= length
;
9510 (*message
)->raw
= (void *) buffer
;
9511 (*message
)->raw_length
= length
;
9521 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
9522 isds_message_free(message
);
9525 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9526 xmlXPathFreeObject(result
);
9527 xmlXPathFreeContext(xpath_ctx
);
9528 if (!*message
|| !(*message
)->xml
) {
9529 xmlFreeDoc(message_doc
);
9533 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
9538 /* Determine type of raw message or delivery info according some heuristics.
9539 * It does not validate the raw blob.
9540 * @context is session context
9541 * @raw_type returns content type of @buffer. Valid only if exit code of this
9542 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
9543 * reallocated memory.
9544 * @buffer is message raw representation.
9545 * @length is length of buffer in bytes. */
9546 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
9547 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
9549 void *xml_stream
= NULL
;
9550 size_t xml_stream_length
= 0;
9551 xmlDocPtr document
= NULL
;
9552 xmlNodePtr root
= NULL
;
9554 if (!context
) return IE_INVALID_CONTEXT
;
9555 zfree(context
->long_message
);
9556 if (length
== 0 || !buffer
) return IE_INVAL
;
9557 if (!raw_type
) return IE_INVAL
;
9560 err
= _isds_extract_cms_data(context
, buffer
, length
,
9561 &xml_stream
, &xml_stream_length
);
9563 xml_stream
= (void *) buffer
;
9564 xml_stream_length
= (size_t) length
;
9569 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
9571 isds_printf_message(context
,
9572 _("Could not parse data as XML document"));
9577 /* Get root element */
9578 root
= xmlDocGetRootElement(document
);
9580 isds_printf_message(context
,
9581 _("XML document is missing root element"));
9586 if (!root
->ns
|| !root
->ns
->href
) {
9587 isds_printf_message(context
,
9588 _("Root element does not belong to any name space"));
9593 /* Test name space */
9594 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
9595 if (xml_stream
== buffer
)
9596 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
9598 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
9599 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
9600 if (xml_stream
== buffer
)
9601 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
9603 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
9604 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
9605 if (xml_stream
== buffer
)
9606 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
9608 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
9609 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
9610 if (xml_stream
!= buffer
) {
9611 isds_printf_message(context
,
9612 _("Document in ISDS name space is encapsulated into CMS" ));
9614 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
9615 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
9616 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
9617 *raw_type
= RAWTYPE_DELIVERYINFO
;
9619 isds_printf_message(context
,
9620 _("Unknown root element in ISDS name space"));
9624 isds_printf_message(context
,
9625 _("Unknown name space"));
9630 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
9631 xmlFreeDoc(document
);
9636 /* Download signed incoming/outgoing message identified by ID.
9637 * @context is session context
9638 * @output is true for outgoing message, false for incoming message
9639 * @message_id is message identifier (you can get them from
9640 * isds_get_list_of_{sent,received}_messages())
9641 * @message is automatically reallocated message retrieved from ISDS. The raw
9642 * member will be filled with PKCS#7 structure in DER format. */
9643 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
9644 const _Bool outgoing
, const char *message_id
,
9645 struct isds_message
**message
) {
9647 isds_error err
= IE_SUCCESS
;
9649 xmlDocPtr response
= NULL
;
9650 xmlChar
*code
= NULL
, *status_message
= NULL
;
9651 xmlXPathContextPtr xpath_ctx
= NULL
;
9652 xmlXPathObjectPtr result
= NULL
;
9653 char *encoded_structure
= NULL
;
9655 size_t raw_length
= 0;
9658 if (!context
) return IE_INVALID_CONTEXT
;
9659 zfree(context
->long_message
);
9660 if (!message
) return IE_INVAL
;
9661 isds_message_free(message
);
9664 /* Do request and check for success */
9665 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
9666 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
9667 BAD_CAST
"SignedMessageDownload",
9668 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
9669 if (err
) goto leave
;
9671 /* Find signed message, extract it into raw and maybe free
9673 err
= find_extract_signed_data_free_response(context
,
9674 (xmlChar
*)message_id
, &response
,
9675 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
9676 BAD_CAST
"SignedMessageDownload",
9678 if (err
) goto leave
;
9681 err
= isds_load_message(context
,
9682 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
9683 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
9684 raw
, raw_length
, message
, BUFFER_MOVE
);
9685 if (err
) goto leave
;
9691 isds_message_free(message
);
9694 free(encoded_structure
);
9695 xmlXPathFreeObject(result
);
9696 xmlXPathFreeContext(xpath_ctx
);
9700 free(status_message
);
9701 xmlFreeDoc(response
);
9704 isds_log(ILF_ISDS
, ILL_DEBUG
,
9706 _("SignedSentMessageDownload request processed by server "
9707 "successfully.\n") :
9708 _("SignedMessageDownload request processed by server "
9711 #else /* not HAVE_LIBCURL */
9718 /* Download signed incoming message identified by ID.
9719 * @context is session context
9720 * @message_id is message identifier (you can get them from
9721 * isds_get_list_of_received_messages())
9722 * @message is automatically reallocated message retrieved from ISDS. The raw
9723 * member will be filled with PKCS#7 structure in DER format. */
9724 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
9725 const char *message_id
, struct isds_message
**message
) {
9726 return isds_get_signed_message(context
, 0, message_id
, message
);
9730 /* Download signed outgoing message identified by ID.
9731 * @context is session context
9732 * @message_id is message identifier (you can get them from
9733 * isds_get_list_of_sent_messages())
9734 * @message is automatically reallocated message retrieved from ISDS. The raw
9735 * member will be filled with PKCS#7 structure in DER format. */
9736 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
9737 const char *message_id
, struct isds_message
**message
) {
9738 return isds_get_signed_message(context
, 1, message_id
, message
);
9742 /* Get type and name of user who sent a message identified by ID.
9743 * @context is session context
9744 * @message_id is message identifier
9745 * @sender_type is pointer to automatically allocated type of sender detected
9746 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9747 * library or to the server, NULL will be returned. Pass NULL if you don't
9749 * @raw_sender_type is automatically reallocated UTF-8 string describing
9750 * sender type or NULL if not known to server. Pass NULL if you don't care.
9751 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9752 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9753 isds_error
isds_get_message_sender(struct isds_ctx
*context
,
9754 const char *message_id
, isds_sender_type
**sender_type
,
9755 char **raw_sender_type
, char **sender_name
) {
9756 isds_error err
= IE_SUCCESS
;
9758 xmlDocPtr response
= NULL
;
9759 xmlChar
*code
= NULL
, *status_message
= NULL
;
9760 xmlXPathContextPtr xpath_ctx
= NULL
;
9761 xmlXPathObjectPtr result
= NULL
;
9762 char *type_string
= NULL
;
9765 if (!context
) return IE_INVALID_CONTEXT
;
9766 zfree(context
->long_message
);
9767 if (sender_type
) zfree(*sender_type
);
9768 if (raw_sender_type
) zfree(*raw_sender_type
);
9769 if (sender_name
) zfree(*sender_name
);
9770 if (!message_id
) return IE_INVAL
;
9773 /* Do request and check for success */
9774 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9775 BAD_CAST
"GetMessageAuthor",
9776 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
9777 if (err
) goto leave
;
9780 xpath_ctx
= xmlXPathNewContext(response
);
9785 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9789 result
= xmlXPathEvalExpression(
9790 BAD_CAST
"/isds:GetMessageAuthorResponse", xpath_ctx
);
9795 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9796 isds_log_message(context
,
9797 _("Missing GetMessageAuthorResponse element"));
9801 if (result
->nodesetval
->nodeNr
> 1) {
9802 isds_log_message(context
,
9803 _("Multiple GetMessageAuthorResponse element"));
9807 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9808 xmlXPathFreeObject(result
); result
= NULL
;
9810 /* Fill output arguments in */
9811 EXTRACT_STRING("isds:userType", type_string
);
9813 *sender_type
= calloc(1, sizeof(**sender_type
));
9814 if (!*sender_type
) {
9820 err
= string2isds_sender_type((xmlChar
*)type_string
,
9823 zfree(*sender_type
);
9824 if (err
== IE_ENUM
) {
9826 char *type_string_locale
= _isds_utf82locale(type_string
);
9827 isds_log(ILF_ISDS
, ILL_WARNING
,
9828 _("Unknown isds:userType value: %s"),
9829 type_string_locale
);
9830 free(type_string_locale
);
9836 EXTRACT_STRING("isds:authorName", *sender_name
);
9840 if (sender_type
) zfree(*sender_type
);
9842 if (sender_name
) zfree(*sender_name
);
9844 if (raw_sender_type
) *raw_sender_type
= type_string
;
9846 xmlXPathFreeObject(result
);
9847 xmlXPathFreeContext(xpath_ctx
);
9850 free(status_message
);
9851 xmlFreeDoc(response
);
9854 isds_log(ILF_ISDS
, ILL_DEBUG
,
9855 _("GetMessageAuthor request processed by server "
9856 "successfully.\n"));
9857 #else /* not HAVE_LIBCURL */
9864 /* Retrieve hash of message identified by ID stored in ISDS.
9865 * @context is session context
9866 * @message_id is message identifier
9867 * @hash is automatically reallocated message hash downloaded from ISDS.
9868 * Message must exist in system and must not be deleted. */
9869 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
9870 const char *message_id
, struct isds_hash
**hash
) {
9872 isds_error err
= IE_SUCCESS
;
9874 xmlDocPtr response
= NULL
;
9875 xmlChar
*code
= NULL
, *status_message
= NULL
;
9876 xmlXPathContextPtr xpath_ctx
= NULL
;
9877 xmlXPathObjectPtr result
= NULL
;
9880 if (!context
) return IE_INVALID_CONTEXT
;
9881 zfree(context
->long_message
);
9883 isds_hash_free(hash
);
9886 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9887 BAD_CAST
"VerifyMessage", message_id
,
9888 &response
, NULL
, NULL
, &code
, &status_message
);
9889 if (err
) goto leave
;
9893 xpath_ctx
= xmlXPathNewContext(response
);
9898 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
9902 result
= xmlXPathEvalExpression(
9903 BAD_CAST
"/isds:VerifyMessageResponse",
9909 /* Empty response */
9910 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
9911 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9912 isds_printf_message(context
,
9913 _("Server did not return any response for ID `%s' "
9914 "on VerifyMessage request"), message_id_locale
);
9915 free(message_id_locale
);
9919 /* More responses */
9920 if (result
->nodesetval
->nodeNr
> 1) {
9921 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
9922 isds_printf_message(context
,
9923 _("Server did return more responses for ID `%s' "
9924 "on VerifyMessage request"), message_id_locale
);
9925 free(message_id_locale
);
9930 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
9932 /* Extract the hash */
9933 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
9937 isds_hash_free(hash
);
9940 xmlXPathFreeObject(result
);
9941 xmlXPathFreeContext(xpath_ctx
);
9944 free(status_message
);
9945 xmlFreeDoc(response
);
9948 isds_log(ILF_ISDS
, ILL_DEBUG
,
9949 _("VerifyMessage request processed by server "
9952 #else /* not HAVE_LIBCURL */
9959 /* Mark message as read. This is a transactional commit function to acknowledge
9960 * to ISDS the message has been downloaded and processed by client properly.
9961 * @context is session context
9962 * @message_id is message identifier. */
9963 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
9964 const char *message_id
) {
9966 isds_error err
= IE_SUCCESS
;
9968 xmlDocPtr response
= NULL
;
9969 xmlChar
*code
= NULL
, *status_message
= NULL
;
9972 if (!context
) return IE_INVALID_CONTEXT
;
9973 zfree(context
->long_message
);
9976 /* Do request and check for success */
9977 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
9978 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
9979 &response
, NULL
, NULL
, &code
, &status_message
);
9982 free(status_message
);
9983 xmlFreeDoc(response
);
9986 isds_log(ILF_ISDS
, ILL_DEBUG
,
9987 _("MarkMessageAsDownloaded request processed by server "
9990 #else /* not HAVE_LIBCURL */
9997 /* Mark message as received by recipient. This is applicable only to
9998 * commercial message. Use envelope->dmType message member to distinguish
9999 * commercial message from government message. Government message is
10000 * received automatically (by law), commercial message on recipient request.
10001 * @context is session context
10002 * @message_id is message identifier. */
10003 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
10004 const char *message_id
) {
10006 isds_error err
= IE_SUCCESS
;
10008 xmlDocPtr response
= NULL
;
10009 xmlChar
*code
= NULL
, *status_message
= NULL
;
10012 if (!context
) return IE_INVALID_CONTEXT
;
10013 zfree(context
->long_message
);
10016 /* Do request and check for success */
10017 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
10018 BAD_CAST
"ConfirmDelivery", message_id
,
10019 &response
, NULL
, NULL
, &code
, &status_message
);
10022 free(status_message
);
10023 xmlFreeDoc(response
);
10026 isds_log(ILF_ISDS
, ILL_DEBUG
,
10027 _("ConfirmDelivery request processed by server "
10030 #else /* not HAVE_LIBCURL */
10037 /* Send document for authorized conversion into Czech POINT system.
10038 * This is public anonymous service, no log-in necessary. Special context is
10039 * used to reuse keep-a-live HTTPS connection.
10040 * @context is Czech POINT session context. DO NOT use context connected to
10041 * ISDS server. Use new context or context used by this function previously.
10042 * @document is document to convert. Only data, data_length, dmFileDescr and
10043 * is_xml members are significant. Be ware that not all document formats can be
10044 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10045 * @id is reallocated identifier assigned by Czech POINT system to
10046 * your document on submit. Use is to tell it to Czech POINT officer.
10047 * @date is reallocated document submit date (submitted documents
10048 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10050 isds_error
czp_convert_document(struct isds_ctx
*context
,
10051 const struct isds_document
*document
,
10052 char **id
, struct tm
**date
) {
10053 isds_error err
= IE_SUCCESS
;
10055 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
10056 xmlNodePtr request
= NULL
, node
;
10057 xmlDocPtr response
= NULL
;
10059 xmlXPathContextPtr xpath_ctx
= NULL
;
10060 xmlXPathObjectPtr result
= NULL
;
10061 long int status
= -1;
10062 long int *status_ptr
= &status
;
10063 char *string
= NULL
;
10067 if (!context
) return IE_INVALID_CONTEXT
;
10068 zfree(context
->long_message
);
10069 if (!document
|| !id
|| !date
) return IE_INVAL
;
10071 if (document
->is_xml
) {
10072 isds_log_message(context
,
10073 _("XML documents cannot be submitted to conversion"));
10077 /* Free output arguments */
10082 /* Store configuration */
10083 context
->type
= CTX_TYPE_CZP
;
10084 free(context
->url
);
10085 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
10086 if (!(context
->url
))
10089 /* Prepare CURL handle if not yet connected */
10090 if (!context
->curl
) {
10091 context
->curl
= curl_easy_init();
10092 if (!(context
->curl
))
10096 /* Build conversion request */
10097 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
10099 isds_log_message(context
,
10100 _("Could not build Czech POINT conversion request"));
10103 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
10105 isds_log_message(context
,
10106 _("Could not create Czech POINT deposit name space"));
10107 xmlFreeNode(request
);
10110 xmlSetNs(request
, deposit_ns
);
10112 /* Insert children. They are in empty namespace! */
10113 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
10115 isds_log_message(context
, _("Could not create empty name space"));
10119 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
10120 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
10121 document
->dmFileDescr
);
10123 /* Document encoded in Base64 */
10124 err
= insert_base64_encoded_string(context
, request
, empty_ns
, "document",
10125 document
->data
, document
->data_length
);
10126 if (err
) goto leave
;
10128 isds_log(ILF_ISDS
, ILL_DEBUG
,
10129 _("Submitting document for conversion into Czech POINT deposit"));
10131 /* Send conversion request */
10132 err
= _czp_czpdeposit(context
, request
, &response
);
10133 xmlFreeNode(request
); request
= NULL
;
10136 czp_do_close_connection(context
);
10141 /* Extract response */
10142 xpath_ctx
= xmlXPathNewContext(response
);
10147 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10151 result
= xmlXPathEvalExpression(
10152 BAD_CAST
"/deposit:saveDocumentResponse/return",
10158 /* Empty response */
10159 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
10160 isds_printf_message(context
,
10161 _("Missing `return' element in Czech POINT deposit response"));
10165 /* More responses */
10166 if (result
->nodesetval
->nodeNr
> 1) {
10167 isds_printf_message(context
,
10168 _("Multiple `return' element in Czech POINT deposit response"));
10173 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
10176 EXTRACT_LONGINT("status", status_ptr
, 1);
10178 EXTRACT_STRING("statusMsg", string
);
10179 char *string_locale
= _isds_utf82locale(string
);
10180 isds_printf_message(context
,
10181 _("Czech POINT deposit refused document for conversion "
10182 "(code=%ld, message=%s)"),
10183 status
, string_locale
);
10184 free(string_locale
);
10189 /* Get document ID */
10190 EXTRACT_STRING("documentID", *id
);
10192 /* Get submit date */
10193 EXTRACT_STRING("dateInserted", string
);
10195 *date
= calloc(1, sizeof(**date
));
10200 err
= datestring2tm((xmlChar
*)string
, *date
);
10202 if (err
== IE_NOTSUP
) {
10204 char *string_locale
= _isds_utf82locale(string
);
10205 isds_printf_message(context
,
10206 _("Invalid dateInserted value: %s"), string_locale
);
10207 free(string_locale
);
10215 xmlXPathFreeObject(result
);
10216 xmlXPathFreeContext(xpath_ctx
);
10218 xmlFreeDoc(response
);
10219 xmlFreeNode(request
);
10222 char *id_locale
= _isds_utf82locale((char *) *id
);
10223 isds_log(ILF_ISDS
, ILL_DEBUG
,
10224 _("Document %s has been submitted for conversion "
10225 "to server successfully\n"), id_locale
);
10228 #else /* not HAVE_LIBCURL */
10235 /* Close possibly opened connection to Czech POINT document deposit.
10236 * @context is Czech POINT session context. */
10237 isds_error
czp_close_connection(struct isds_ctx
*context
) {
10238 if (!context
) return IE_INVALID_CONTEXT
;
10239 zfree(context
->long_message
);
10241 return czp_do_close_connection(context
);
10248 /* Send request for new box creation in testing ISDS instance.
10249 * It's not possible to request for a production box currently, as it
10250 * communicates via e-mail.
10251 * XXX: This function does not work either. Server complains about invalid
10253 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10255 * @context is special session context for box creation request. DO NOT use
10256 * standard context as it could reveal your password. Use fresh new context or
10257 * context previously used by this function.
10258 * @box is box description to create including single primary user (in case of
10259 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10260 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10261 * box, or contact address of PFO box owner). The email member is mandatory as
10262 * it will be used to deliver credentials.
10263 * @former_names is former name of box owner. Pass NULL if you don't care.
10264 * @approval is optional external approval of box manipulation
10265 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10266 * NULL, if you don't care.*/
10267 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
10268 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
10269 const char *former_names
, const struct isds_approval
*approval
,
10270 char **refnumber
) {
10271 isds_error err
= IE_SUCCESS
;
10273 xmlNodePtr request
= NULL
;
10274 xmlDocPtr response
= NULL
;
10275 xmlXPathContextPtr xpath_ctx
= NULL
;
10276 xmlXPathObjectPtr result
= NULL
;
10280 if (!context
) return IE_INVALID_CONTEXT
;
10281 zfree(context
->long_message
);
10282 if (!box
) return IE_INVAL
;
10285 if (!box
->email
|| box
->email
[0] == '\0') {
10286 isds_log_message(context
, _("E-mail field is mandatory"));
10290 /* Scratch box ID */
10293 /* Store configuration */
10294 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
10295 free(context
->url
);
10296 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
10297 if (!(context
->url
))
10300 /* Prepare CURL handle if not yet connected */
10301 if (!context
->curl
) {
10302 context
->curl
= curl_easy_init();
10303 if (!(context
->curl
))
10307 /* Build CreateDataBox request */
10308 err
= build_CreateDBInput_request(context
,
10309 &request
, BAD_CAST
"CreateDataBox",
10310 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, NULL
, approval
);
10311 if (err
) goto leave
;
10313 /* Send it to server and process response */
10314 err
= send_destroy_request_check_response(context
,
10315 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
10316 &response
, (xmlChar
**) refnumber
);
10317 if (err
) goto leave
;
10319 /* Extract box ID */
10320 xpath_ctx
= xmlXPathNewContext(response
);
10325 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10329 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box
->dbID
);
10332 xmlXPathFreeObject(result
);
10333 xmlXPathFreeContext(xpath_ctx
);
10334 xmlFreeDoc(response
);
10335 xmlFreeNode(request
);
10338 isds_log(ILF_ISDS
, ILL_DEBUG
,
10339 _("CreateDataBox request processed by server successfully.\n"));
10341 #else /* not HAVE_LIBCURL */
10349 /* Submit CMS signed message to ISDS to verify its originality. This is
10350 * stronger form of isds_verify_message_hash() because ISDS does more checks
10351 * than simple one (potentialy old weak) hash comparison.
10352 * @context is session context
10353 * @message is memory with raw CMS signed message bit stream
10354 * @length is @message size in bytes
10356 * IE_SUCCESS if message originates in ISDS
10357 * IE_NOTEQUAL if message is unknown to ISDS
10358 * other code for other errors */
10359 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
10360 const void *message
, size_t length
) {
10361 isds_error err
= IE_SUCCESS
;
10363 xmlNsPtr isds_ns
= NULL
;
10364 xmlNodePtr request
= NULL
;
10365 xmlDocPtr response
= NULL
;
10366 xmlXPathContextPtr xpath_ctx
= NULL
;
10367 xmlXPathObjectPtr result
= NULL
;
10368 _Bool
*authentic
= NULL
;
10371 if (!context
) return IE_INVALID_CONTEXT
;
10372 zfree(context
->long_message
);
10373 if (!message
|| length
== 0) return IE_INVAL
;
10376 /* Check if connection is established
10377 * TODO: This check should be done downstairs. */
10378 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
10381 /* Build AuthenticateMessage request */
10382 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
10384 isds_log_message(context
,
10385 _("Could not build AuthenticateMessage request"));
10388 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
10390 isds_log_message(context
, _("Could not create ISDS name space"));
10391 xmlFreeNode(request
);
10394 xmlSetNs(request
, isds_ns
);
10396 /* Insert Base64 encoded message */
10397 err
= insert_base64_encoded_string(context
, request
, NULL
, "dmMessage",
10399 if (err
) goto leave
;
10401 /* Send request to server and process response */
10402 err
= send_destroy_request_check_response(context
,
10403 SERVICE_DM_OPERATIONS
, BAD_CAST
"AuthenticateMessage", &request
,
10405 if (err
) goto leave
;
10408 /* ISDS has decided */
10409 xpath_ctx
= xmlXPathNewContext(response
);
10414 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
10419 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic
);
10422 isds_log_message(context
,
10423 _("Server did not return any response on "
10424 "AuthenticateMessage request"));
10429 isds_log(ILF_ISDS
, ILL_DEBUG
,
10430 _("ISDS authenticated the message successfully\n"));
10432 isds_log_message(context
, _("ISDS does not know the message"));
10439 xmlXPathFreeObject(result
);
10440 xmlXPathFreeContext(xpath_ctx
);
10442 xmlFreeDoc(response
);
10443 xmlFreeNode(request
);
10444 #else /* not HAVE_LIBCURL */
10451 #undef INSERT_ELEMENT
10452 #undef CHECK_FOR_STRING_LENGTH
10453 #undef INSERT_STRING_ATTRIBUTE
10454 #undef INSERT_ULONGINTNOPTR
10455 #undef INSERT_ULONGINT
10456 #undef INSERT_LONGINT
10457 #undef INSERT_BOOLEAN
10458 #undef INSERT_SCALAR_BOOLEAN
10459 #undef INSERT_STRING
10460 #undef INSERT_STRING_WITH_NS
10461 #undef EXTRACT_STRING_ATTRIBUTE
10462 #undef EXTRACT_ULONGINT
10463 #undef EXTRACT_LONGINT
10464 #undef EXTRACT_BOOLEAN
10465 #undef EXTRACT_STRING
10468 /* Compute hash of message from raw representation and store it into envelope.
10469 * Original hash structure will be destroyed in envelope.
10470 * @context is session context
10471 * @message is message carrying raw XML message blob
10472 * @algorithm is desired hash algorithm to use */
10473 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
10474 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
10475 isds_error err
= IE_SUCCESS
;
10477 void *xml_stream
= NULL
;
10478 size_t xml_stream_length
;
10479 size_t phys_start
, phys_end
;
10480 char *phys_path
= NULL
;
10481 struct isds_hash
*new_hash
= NULL
;
10484 if (!context
) return IE_INVALID_CONTEXT
;
10485 zfree(context
->long_message
);
10486 if (!message
) return IE_INVAL
;
10488 if (!message
->raw
) {
10489 isds_log_message(context
,
10490 _("Message does not carry raw representation"));
10494 switch (message
->raw_type
) {
10495 case RAWTYPE_INCOMING_MESSAGE
:
10497 xml_stream
= message
->raw
;
10498 xml_stream_length
= message
->raw_length
;
10501 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
10502 nsuri
= SISDS_INCOMING_NS
;
10503 xml_stream
= message
->raw
;
10504 xml_stream_length
= message
->raw_length
;
10507 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
10508 nsuri
= SISDS_INCOMING_NS
;
10509 err
= _isds_extract_cms_data(context
,
10510 message
->raw
, message
->raw_length
,
10511 &xml_stream
, &xml_stream_length
);
10512 if (err
) goto leave
;
10515 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
10516 nsuri
= SISDS_OUTGOING_NS
;
10517 xml_stream
= message
->raw
;
10518 xml_stream_length
= message
->raw_length
;
10521 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
10522 nsuri
= SISDS_OUTGOING_NS
;
10523 err
= _isds_extract_cms_data(context
,
10524 message
->raw
, message
->raw_length
,
10525 &xml_stream
, &xml_stream_length
);
10526 if (err
) goto leave
;
10530 isds_log_message(context
, _("Bad raw representation type"));
10536 /* XXX: Hash is computed from original string representing isds:dmDm
10537 * subtree. That means no encoding, white space, xmlns attributes changes.
10538 * In other words, input for hash can be invalid XML stream. */
10539 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
10540 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
10541 PHYSXML_ELEMENT_SEPARATOR
,
10542 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
10543 PHYSXML_ELEMENT_SEPARATOR
10544 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
10548 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
10549 phys_path
, &phys_start
, &phys_end
);
10552 isds_log_message(context
,
10553 _("Substring with isds:dmDM element could not be located "
10554 "in raw message"));
10560 new_hash
= calloc(1, sizeof(*new_hash
));
10565 new_hash
->algorithm
= algorithm
;
10566 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
10569 isds_log_message(context
, _("Could not compute message hash"));
10573 /* Save computed hash */
10574 if (!message
->envelope
) {
10575 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
10576 if (!message
->envelope
) {
10581 isds_hash_free(&message
->envelope
->hash
);
10582 message
->envelope
->hash
= new_hash
;
10586 isds_hash_free(&new_hash
);
10590 if (xml_stream
!= message
->raw
) free(xml_stream
);
10595 /* Compare two hashes.
10596 * @h1 is first hash
10597 * @h2 is another hash
10599 * IE_SUCCESS if hashes equal
10600 * IE_NOTUNIQ if hashes are comparable, but they don't equal
10601 * IE_ENUM if not comparable, but both structures defined
10602 * IE_INVAL if some of the structures are undefined (NULL)
10603 * IE_ERROR if internal error occurs */
10604 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
10605 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
10606 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
10607 if (h1
->length
!= h2
->length
) return IE_ERROR
;
10608 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
10609 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
10611 for (int i
= 0; i
< h1
->length
; i
++) {
10612 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
10613 return IE_NOTEQUAL
;
10619 /* Check message has gone through ISDS by comparing message hash stored in
10620 * ISDS and locally computed hash. You must provide message with valid raw
10621 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
10622 * This is convenient wrapper for isds_download_message_hash(),
10623 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
10624 * @context is session context
10625 * @message is message with valid raw and envelope member; envelope->hash
10626 * member will be changed during function run. Use envelope on heap only.
10628 * IE_SUCCESS if message originates in ISDS
10629 * IE_NOTEQUAL if message is unknown to ISDS
10630 * other code for other errors */
10631 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
10632 struct isds_message
*message
) {
10633 isds_error err
= IE_SUCCESS
;
10634 struct isds_hash
*downloaded_hash
= NULL
;
10636 if (!context
) return IE_INVALID_CONTEXT
;
10637 zfree(context
->long_message
);
10638 if (!message
) return IE_INVAL
;
10640 if (!message
->envelope
) {
10641 isds_log_message(context
,
10642 _("Given message structure is missing envelope"));
10645 if (!message
->raw
) {
10646 isds_log_message(context
,
10647 _("Given message structure is missing raw representation"));
10651 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
10653 if (err
) goto leave
;
10655 err
= isds_compute_message_hash(context
, message
,
10656 downloaded_hash
->algorithm
);
10657 if (err
) goto leave
;
10659 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
10662 isds_hash_free(&downloaded_hash
);
10667 /* Search for document by document ID in list of documents. IDs are compared
10669 * @documents is list of isds_documents
10670 * @id is document identifier
10671 * @return first matching document or NULL. */
10672 const struct isds_document
*isds_find_document_by_id(
10673 const struct isds_list
*documents
, const char *id
) {
10674 const struct isds_list
*item
;
10675 const struct isds_document
*document
;
10677 for (item
= documents
; item
; item
= item
->next
) {
10678 document
= (struct isds_document
*) item
->data
;
10679 if (!document
) continue;
10681 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
10689 /* Normalize @mime_type to be proper MIME type.
10690 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
10691 * guess regular MIME type (e.g. "application/pdf").
10692 * @mime_type is UTF-8 encoded MIME type to fix
10693 * @return original @mime_type if no better interpretation exists, or array to
10694 * constant static UTF-8 encoded string with proper MIME type. */
10695 char *isds_normalize_mime_type(const char* mime_type
) {
10696 if (!mime_type
) return NULL
;
10698 for (int offset
= 0;
10699 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
10701 if (!xmlStrcmp((const xmlChar
*) mime_type
, extension_map_mime
[offset
]))
10702 return (char *) extension_map_mime
[offset
+ 1];
10705 return (char *) mime_type
;
10709 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
10710 struct isds_message **message);
10711 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
10712 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
10713 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
10714 struct isds_address **address);
10716 int isds_message_free(struct isds_message **message);
10717 int isds_address_free(struct isds_address **address);
10721 /* Makes known all relevant namespaces to given XPath context
10722 * @xpath_ctx is XPath context
10723 * @message_ns selects proper message name space. Unsigned and signed
10724 * messages and delivery info's differ in prefix and URI. */
10725 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
10726 const message_ns_type message_ns
) {
10727 const xmlChar
*message_namespace
= NULL
;
10729 if (!xpath_ctx
) return IE_ERROR
;
10731 switch(message_ns
) {
10733 message_namespace
= BAD_CAST ISDS1_NS
; break;
10734 case MESSAGE_NS_UNSIGNED
:
10735 message_namespace
= BAD_CAST ISDS_NS
; break;
10736 case MESSAGE_NS_SIGNED_INCOMING
:
10737 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
10738 case MESSAGE_NS_SIGNED_OUTGOING
:
10739 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
10740 case MESSAGE_NS_SIGNED_DELIVERY
:
10741 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
10746 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
10748 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
10750 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
10752 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
10754 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))